From dd93b9840779203fad207a342ef55d6866be4cc0 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Sat, 19 Dec 2020 22:42:25 +0100 Subject: [PATCH] Added Player properties, started working on Bungee skins, fixed errors --- .../floodgate/api/player/FloodgatePlayer.java | 17 +++ .../floodgate/api/player/PropertyKey.java | 94 +++++++++++++ bungee/pom.xml | 10 +- .../org/geysermc/floodgate/BungeePlugin.java | 4 +- .../floodgate/handler/BungeeDataHandler.java | 33 +++-- .../floodgate/listener/BungeeListener.java | 26 ++++ .../module/BungeePlatformModule.java | 39 +++--- .../BungeePluginMessageHandler.java | 104 +++++++++++--- .../pluginmessage/BungeeSkinApplier.java | 73 ++++++++++ common/pom.xml | 11 +- .../geysermc/floodgate/FloodgatePlatform.java | 10 +- .../floodgate/FloodgatePlayerImpl.java | 127 ++++++++++++++++-- .../geysermc/floodgate/HandshakeHandler.java | 11 +- .../floodgate/addon/AddonManagerAddon.java | 7 +- .../geysermc/floodgate/addon/DebugAddon.java | 5 +- .../floodgate/link/PlayerLinkLoader.java | 16 ++- .../floodgate/module/CommonModule.java | 2 +- .../floodgate/module/ProxyCommonModule.java | 72 ++++++++++ .../floodgate/module/ServerCommonModule.java | 70 ++++++++++ .../pluginmessage/PluginMessageHandler.java | 46 ++++++- .../floodgate/skin/ServerSkinHandler.java | 59 ++++++++ .../SkinApplier.java} | 28 +--- .../geysermc/floodgate/skin/SkinHandler.java | 56 +++++--- .../geysermc/floodgate/skin/SkinUploader.java | 7 +- .../org/geysermc/floodgate/util/Utils.java | 47 +++++++ pom.xml | 1 + .../geysermc/floodgate/SpigotPlatform.java | 11 +- .../org/geysermc/floodgate/SpigotPlugin.java | 4 +- .../floodgate/addon/data/SpigotDataAddon.java | 3 +- .../addon/data/SpigotDataHandler.java | 12 +- .../floodgate/listener/SpigotListener.java | 4 +- .../module/SpigotPlatformModule.java | 91 ++++++++----- .../SpigotPluginMessageHandler.java | 48 ++----- .../SpigotPluginMessageRegister.java | 107 +++++++++++++++ ...kinHandler.java => SpigotSkinApplier.java} | 19 ++- .../geysermc/floodgate/VelocityPlugin.java | 4 +- .../addon/data/VelocityDataAddon.java | 3 +- .../addon/data/VelocityProxyDataHandler.java | 2 +- .../VelocityPluginMessageHandler.java | 12 +- 39 files changed, 1036 insertions(+), 259 deletions(-) create mode 100644 api/src/main/java/org/geysermc/floodgate/api/player/PropertyKey.java create mode 100644 bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java create mode 100644 common/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java create mode 100644 common/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java create mode 100644 common/src/main/java/org/geysermc/floodgate/skin/ServerSkinHandler.java rename common/src/main/java/org/geysermc/floodgate/{module/ConfigLoadedModule.java => skin/SkinApplier.java} (61%) create mode 100644 common/src/main/java/org/geysermc/floodgate/util/Utils.java create mode 100644 spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageRegister.java rename spigot/src/main/java/org/geysermc/floodgate/pluginmessage/{SpigotSkinHandler.java => SpigotSkinApplier.java} (84%) diff --git a/api/src/main/java/org/geysermc/floodgate/api/player/FloodgatePlayer.java b/api/src/main/java/org/geysermc/floodgate/api/player/FloodgatePlayer.java index 9fee05f6..e87357eb 100644 --- a/api/src/main/java/org/geysermc/floodgate/api/player/FloodgatePlayer.java +++ b/api/src/main/java/org/geysermc/floodgate/api/player/FloodgatePlayer.java @@ -97,6 +97,11 @@ public interface FloodgatePlayer { */ InputMode getInputMode(); + /** + * Returns if the Floodgate player is connected through a proxy + */ + boolean isFromProxy(); + /** * Returns the LinkedPlayer object if the player is linked to a Java account. */ @@ -115,6 +120,18 @@ public interface FloodgatePlayer { return sendForm(formBuilder.build()); } + T getProperty(PropertyKey key); + + T getProperty(String key); + + T removeProperty(PropertyKey key); + + T removeProperty(String key); + + T addProperty(PropertyKey key, Object value); + + T addProperty(String key, Object value); + /** * Casts the FloodgatePlayer instance to a class that extends FloodgatePlayer. * diff --git a/api/src/main/java/org/geysermc/floodgate/api/player/PropertyKey.java b/api/src/main/java/org/geysermc/floodgate/api/player/PropertyKey.java new file mode 100644 index 00000000..73300e82 --- /dev/null +++ b/api/src/main/java/org/geysermc/floodgate/api/player/PropertyKey.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.api.player; + +import lombok.Getter; + +@Getter +public class PropertyKey { + /** + * Socket Address returns the InetSocketAddress of the Bedrock player + */ + public static final PropertyKey SOCKET_ADDRESS = + new PropertyKey("socket_address", false, false); + + /** + * Skin Uploaded returns a JsonObject containing the value and signature of the Skin + */ + public static final PropertyKey SKIN_UPLOADED = + new PropertyKey("skin_uploaded", false, false); + + private final String key; + private final boolean changeable; + private final boolean removeable; + + public PropertyKey(String key, boolean changeable, boolean removeable) { + this.key = key; + this.changeable = changeable; + this.removeable = removeable; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PropertyKey) { + return key.equals(((PropertyKey) obj).key); + } + + if (obj instanceof String) { + return key.equals(obj); + } + return false; + } + + public AllowedResult isAddAllowed(Object obj) { + if (obj instanceof PropertyKey) { + PropertyKey propertyKey = (PropertyKey) obj; + + if (key.equals(propertyKey.key)) { + if ((propertyKey.changeable == changeable || propertyKey.changeable) && + (propertyKey.removeable == removeable || propertyKey.removeable)) { + return AllowedResult.CORRECT; + } + return AllowedResult.INVALID_TAGS; + } + return AllowedResult.NOT_EQUALS; + } + + if (obj instanceof String) { + if (changeable) { + return AllowedResult.CORRECT; + } + return AllowedResult.INVALID_TAGS; + } + return AllowedResult.NOT_EQUALS; + } + + public enum AllowedResult { + NOT_EQUALS, + INVALID_TAGS, + CORRECT + } +} diff --git a/bungee/pom.xml b/bungee/pom.xml index 5d740807..eb9c9a8f 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -39,9 +39,9 @@ provided - net.md-5 - bungeecord-protocol - ${bungee.version} + com.github.SpigotMC.BungeeCord + bungeecord-proxy + master-SNAPSHOT provided @@ -88,5 +88,9 @@ bungeecord-repo https://oss.sonatype.org/content/repositories/snapshots + + jitpack + https://jitpack.io + \ No newline at end of file diff --git a/bungee/src/main/java/org/geysermc/floodgate/BungeePlugin.java b/bungee/src/main/java/org/geysermc/floodgate/BungeePlugin.java index b8bf412d..74c3af80 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/BungeePlugin.java +++ b/bungee/src/main/java/org/geysermc/floodgate/BungeePlugin.java @@ -33,7 +33,7 @@ import org.geysermc.floodgate.module.BungeeAddonModule; import org.geysermc.floodgate.module.BungeeListenerModule; import org.geysermc.floodgate.module.BungeePlatformModule; import org.geysermc.floodgate.module.CommandModule; -import org.geysermc.floodgate.module.CommonModule; +import org.geysermc.floodgate.module.ProxyCommonModule; import org.geysermc.floodgate.util.ReflectionUtils; public final class BungeePlugin extends Plugin { @@ -45,7 +45,7 @@ public final class BungeePlugin extends Plugin { long ctm = System.currentTimeMillis(); Injector injector = Guice.createInjector( - new CommonModule(getDataFolder().toPath()), + new ProxyCommonModule(getDataFolder().toPath()), new BungeePlatformModule(this) ); diff --git a/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java b/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java index 32a345f2..7acc7093 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java +++ b/bungee/src/main/java/org/geysermc/floodgate/handler/BungeeDataHandler.java @@ -45,6 +45,7 @@ import org.geysermc.floodgate.HandshakeHandler.HandshakeResult; import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.ReflectionUtils; @@ -97,7 +98,12 @@ public final class BungeeDataHandler { event.getConnection(), EXTRA_HANDSHAKE_DATA ); - HandshakeResult result = handler.handle(extraData); + Object channelWrapper = + ReflectionUtils.getValue(event.getConnection(), PLAYER_CHANNEL_WRAPPER); + + Channel channel = ReflectionUtils.getCastedValue(channelWrapper, PLAYER_CHANNEL); + + HandshakeResult result = handler.handle(channel, extraData); switch (result.getResultType()) { case EXCEPTION: event.setCancelReason(config.getDisconnect().getInvalidKey()); @@ -117,7 +123,13 @@ public final class BungeeDataHandler { } FloodgatePlayer player = result.getFloodgatePlayer(); - api.addEncryptedData(player.getCorrectUniqueId(), result.getHandshakeData()[1]); + + String encryptedData = result.getHandshakeData()[1]; + // remove skin from encrypted data if it has a skin + if (encryptedData.indexOf(0x21) != -1) { + encryptedData = encryptedData.substring(0, encryptedData.indexOf(0x21) - 1); + } + api.addEncryptedData(player.getCorrectUniqueId(), encryptedData); event.getConnection().setOnlineMode(false); event.getConnection().setUniqueId(player.getCorrectUniqueId()); @@ -126,9 +138,6 @@ public final class BungeeDataHandler { event.getConnection(), PLAYER_NAME, player.getCorrectUsername() ); - Object channelWrapper = - ReflectionUtils.getValue(event.getConnection(), PLAYER_CHANNEL_WRAPPER); - SocketAddress remoteAddress = ReflectionUtils.getCastedValue(channelWrapper, PLAYER_REMOTE_ADDRESS); @@ -137,15 +146,15 @@ public final class BungeeDataHandler { "Ignoring the player, I guess.", player.getUsername(), remoteAddress.getClass().getSimpleName() ); - } else { - int port = ((InetSocketAddress) remoteAddress).getPort(); - ReflectionUtils.setValue( - channelWrapper, PLAYER_REMOTE_ADDRESS, - new InetSocketAddress(result.getBedrockData().getIp(), port) - ); + event.setCancelled(true); + event.setCancelReason( + new TextComponent("remoteAddress is not an InetSocketAddress!")); + event.completeIntent(plugin); + return; } - Channel channel = ReflectionUtils.getCastedValue(channelWrapper, PLAYER_CHANNEL); + InetSocketAddress correctAddress = player.getProperty(PropertyKey.SOCKET_ADDRESS); + ReflectionUtils.setValue(channelWrapper, PLAYER_REMOTE_ADDRESS, correctAddress); channel.attr(playerAttribute).set(player); diff --git a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java index f9840e4b..c3b5b655 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java +++ b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java @@ -33,6 +33,7 @@ import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PreLoginEvent; import net.md_5.bungee.api.event.ServerConnectEvent; +import net.md_5.bungee.api.event.ServerConnectedEvent; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventPriority; @@ -40,7 +41,10 @@ import org.geysermc.floodgate.FloodgatePlayerImpl; import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.handler.BungeeDataHandler; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; +import org.geysermc.floodgate.skin.SkinHandler; import org.geysermc.floodgate.util.LanguageManager; public final class BungeeListener implements Listener { @@ -49,6 +53,10 @@ public final class BungeeListener implements Listener { @Inject private LanguageManager languageManager; @Inject private FloodgateLogger logger; + @Inject private ProxyFloodgateConfig config; + @Inject private PluginMessageHandler pluginMessageHandler; + @Inject private SkinHandler skinHandler; + @Inject public void init(Injector injector) { dataHandler = injector.getInstance(BungeeDataHandler.class); @@ -59,6 +67,24 @@ public final class BungeeListener implements Listener { dataHandler.handleServerConnect(event.getPlayer()); } + @EventHandler + public void onServerConnected(ServerConnectedEvent event) { + ProxiedPlayer player = event.getPlayer(); + FloodgatePlayer floodgatePlayer = api.getPlayer(player.getUniqueId()); + if (floodgatePlayer == null) { + return; + } + + // send skin request to server if data forwarding allows that + if (config.isSendFloodgateData()) { + pluginMessageHandler.sendSkinRequest(player.getUniqueId(), + floodgatePlayer.getRawSkin()); + } else { + //todo also a Proxy SkinHandler to keep stuff clean? + skinHandler.handleSkinUploadFor(floodgatePlayer, null); + } + } + @EventHandler(priority = EventPriority.LOW) public void onPreLogin(PreLoginEvent event) { dataHandler.handlePreLogin(event); diff --git a/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java b/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java index d45d4673..3f29141a 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java +++ b/bungee/src/main/java/org/geysermc/floodgate/module/BungeePlatformModule.java @@ -33,14 +33,9 @@ import lombok.RequiredArgsConstructor; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Plugin; import org.geysermc.floodgate.BungeePlugin; -import org.geysermc.floodgate.api.ProxyFloodgateApi; -import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.command.BungeeCommandRegistration; -import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.FloodgateConfigHolder; -import org.geysermc.floodgate.config.ProxyFloodgateConfig; -import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.inject.CommonPlatformInjector; import org.geysermc.floodgate.inject.bungee.BungeeInjector; import org.geysermc.floodgate.listener.BungeeListenerRegistration; @@ -50,6 +45,9 @@ import org.geysermc.floodgate.platform.command.CommandUtil; import org.geysermc.floodgate.platform.listener.ListenerRegistration; import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; import org.geysermc.floodgate.pluginmessage.BungeePluginMessageHandler; +import org.geysermc.floodgate.pluginmessage.BungeeSkinApplier; +import org.geysermc.floodgate.skin.SkinApplier; +import org.geysermc.floodgate.skin.SkinHandler; import org.geysermc.floodgate.util.BungeeCommandUtil; import org.geysermc.floodgate.util.LanguageManager; @@ -57,31 +55,12 @@ import org.geysermc.floodgate.util.LanguageManager; public final class BungeePlatformModule extends AbstractModule { private final BungeePlugin plugin; - @Override - protected void configure() { - bind(SimpleFloodgateApi.class).to(ProxyFloodgateApi.class); - } - @Provides @Singleton public Plugin bungeePlugin() { return plugin; } - @Provides - @Singleton - @Named("configClass") - public Class floodgateConfigClass() { - return ProxyFloodgateConfig.class; - } - - @Provides - @Singleton - public ProxyFloodgateApi proxyFloodgateApi(PluginMessageHandler pluginMessageHandler, - FloodgateCipher cipher) { - return new ProxyFloodgateApi(pluginMessageHandler, cipher); - } - @Provides @Singleton public FloodgateLogger floodgateLogger(LanguageManager languageManager) { @@ -117,6 +96,18 @@ public final class BungeePlatformModule extends AbstractModule { return new BungeePluginMessageHandler(configHolder); } + @Provides + @Singleton + public SkinApplier skinApplier(FloodgateLogger logger) { + return new BungeeSkinApplier(logger); + } + + @Provides + @Singleton + public SkinHandler skinHandler(SkinApplier skinApplier, FloodgateLogger logger) { + return new SkinHandler(skinApplier, logger); + } + /* DebugAddon / PlatformInjector */ diff --git a/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageHandler.java b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageHandler.java index 6c4b88f5..06453b01 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageHandler.java +++ b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeePluginMessageHandler.java @@ -25,10 +25,12 @@ package org.geysermc.floodgate.pluginmessage; -import static org.geysermc.floodgate.util.MessageFormatter.format; - +import com.google.gson.JsonObject; import com.google.inject.Inject; import com.google.inject.name.Named; +import java.io.ByteArrayInputStream; +import java.io.InputStreamReader; +import java.io.Reader; import java.util.UUID; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.chat.TextComponent; @@ -40,15 +42,22 @@ import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.event.EventHandler; import org.geysermc.cumulus.Form; +import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; +import org.geysermc.floodgate.skin.SkinApplier; +import org.geysermc.floodgate.skin.SkinUploader.UploadResult; import org.geysermc.floodgate.util.RawSkin; public final class BungeePluginMessageHandler extends PluginMessageHandler implements Listener { private ProxyServer proxy; private FloodgateLogger logger; private String formChannel; + private String skinChannel; + private FloodgateApi api; + private SkinApplier skinApplier; public BungeePluginMessageHandler(FloodgateConfigHolder configHolder) { super(configHolder); @@ -57,10 +66,14 @@ public final class BungeePluginMessageHandler extends PluginMessageHandler imple @Inject // called because this is a listener as well public void init(Plugin plugin, FloodgateLogger logger, @Named("formChannel") String formChannel, - @Named("skinChannel") String skinChannel) { + @Named("skinChannel") String skinChannel, + FloodgateApi api, SkinApplier skinApplier) { this.proxy = plugin.getProxy(); this.logger = logger; this.formChannel = formChannel; + this.skinChannel = skinChannel; + this.api = api; + this.skinApplier = skinApplier; proxy.registerChannel(formChannel); proxy.registerChannel(skinChannel); @@ -69,6 +82,7 @@ public final class BungeePluginMessageHandler extends PluginMessageHandler imple @EventHandler public void onPluginMessage(PluginMessageEvent event) { Connection source = event.getSender(); + if (event.getTag().equals(formChannel)) { if (source instanceof Server) { // send it to the client @@ -79,8 +93,7 @@ public final class BungeePluginMessageHandler extends PluginMessageHandler imple if (source instanceof ProxiedPlayer) { byte[] data = event.getData(); if (data.length < 2) { - logger.error("Invalid form response! Closing connection"); - source.disconnect(new TextComponent("Invalid form response!")); + logKick(source, "Invalid form response!"); return; } @@ -95,12 +108,73 @@ public final class BungeePluginMessageHandler extends PluginMessageHandler imple event.setCancelled(true); if (!callResponseConsumer(data)) { - logger.error(format( - "Couldn't find stored form with id {} for player {}", - formId, ((ProxiedPlayer) source).getName())); + logger.error("Couldn't find stored form with id {} for player {}", + formId, ((ProxiedPlayer) source).getName()); } } + return; } + + if (event.getTag().equals(skinChannel)) { + byte[] data = event.getData(); + + if (data.length < 1) { + logKick(source, "Got invalid Skin request/response."); + return; + } + + boolean request = data[0] == 1; + + if (!request && data.length < 2) { + logKick(source, "Got invalid Skin response."); + return; + } + + if (source instanceof Server) { + if (request) { + logKick(source, "Got Skin request from Server?"); + return; + } + + UUID playerUniqueId = ((ProxiedPlayer) event.getReceiver()).getUniqueId(); + FloodgatePlayer floodgatePlayer = api.getPlayer(playerUniqueId); + + if (floodgatePlayer == null) { + logKick(source, "Server issued Skin request for non-Floodgate player."); + return; + } + + // 1 = failed, 0 = successful. + + // we'll try it again on the next server if it failed + if (data[1] != 0) { + return; + } + + JsonObject response; + try { + Reader reader = new InputStreamReader( + new ByteArrayInputStream(event.getData())); + response = GSON.fromJson(reader, JsonObject.class); + } catch (Throwable throwable) { + logger.error("Failed to read Skin response", throwable); + return; + } + + skinApplier.applySkin(floodgatePlayer, UploadResult.success(response)); + return; + } + + // Players (Geyser) can't send requests nor responses + if (source instanceof ProxiedPlayer) { + logKick(source, "Got Skin " + (request ? "request" : "response") + " from Player?"); + } + } + } + + private void logKick(Connection source, String reason) { + logger.error(reason + " Closing connection"); + source.disconnect(new TextComponent(reason)); } @Override @@ -114,12 +188,12 @@ public final class BungeePluginMessageHandler extends PluginMessageHandler imple } @Override - public boolean sendSkinRequest(UUID player, RawSkin skin) { - return false; //todo - } - - @Override - public void sendSkinResponse(UUID player, String response) { - + public boolean sendSkinRequest(UUID uuid, RawSkin skin) { + ProxiedPlayer player = proxy.getPlayer(uuid); + if (player != null) { + player.sendData(skinChannel, createSkinRequestData(skin.encode())); + return true; + } + return false; } } diff --git a/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java new file mode 100644 index 00000000..9ae890a3 --- /dev/null +++ b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.pluginmessage; + +import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.connection.InitialHandler; +import net.md_5.bungee.connection.LoginResult; +import net.md_5.bungee.connection.LoginResult.Property; +import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.skin.SkinApplier; +import org.geysermc.floodgate.skin.SkinUploader.UploadResult; + +@RequiredArgsConstructor +public final class BungeeSkinApplier implements SkinApplier { + private final FloodgateLogger logger; + + @Override + public void applySkin(FloodgatePlayer uuid, UploadResult result) { + ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid.getCorrectUniqueId()); + + InitialHandler handler; + try { + handler = (InitialHandler) player.getPendingConnection(); + } catch (Exception exception) { + logger.error("Incompatible Bungeecord fork detected", exception); + return; + } + + LoginResult loginResult = handler.getLoginProfile(); + // expected to be null since LoginResult is the data from hasJoined, + // which Floodgate players don't have + if (loginResult == null) { + // id and name are unused and properties will be overridden + loginResult = new LoginResult(null, null, null); + } + + JsonObject response = result.getResponse(); + Property property = new Property( + "textures", + response.get("value").getAsString(), + response.get("signature").getAsString() + ); + + loginResult.setProperties(new Property[]{property}); + } +} diff --git a/common/pom.xml b/common/pom.xml index 5a95d349..8ba6cda4 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -53,6 +53,11 @@ api ${project.parent.version} + + org.geysermc.cumulus + cumulus + ${cumulus.version} + @@ -64,12 +69,6 @@ 1.27 compile - - com.google.code.gson - gson - 2.8.5 - compile - 4.0.0 diff --git a/common/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java b/common/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java index c481ccc3..2064e605 100644 --- a/common/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java +++ b/common/src/main/java/org/geysermc/floodgate/FloodgatePlatform.java @@ -41,7 +41,6 @@ import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.config.loader.ConfigLoader; import org.geysermc.floodgate.link.PlayerLinkLoader; -import org.geysermc.floodgate.module.ConfigLoadedModule; import org.geysermc.floodgate.module.PostInitializeModule; public class FloodgatePlatform { @@ -56,16 +55,16 @@ public class FloodgatePlatform { @Inject public FloodgatePlatform(FloodgateApi api, PlatformInjector platformInjector, - FloodgateLogger logger) { + FloodgateLogger logger, Injector guice) { this.api = api; this.injector = platformInjector; this.logger = logger; + this.guice = guice; } @Inject public void init(@Named("dataDirectory") Path dataDirectory, ConfigLoader configLoader, - PlayerLinkLoader playerLinkLoader, FloodgateConfigHolder configHolder, - Injector injector) { + PlayerLinkLoader playerLinkLoader, FloodgateConfigHolder configHolder) { if (!Files.isDirectory(dataDirectory)) { try { @@ -84,9 +83,6 @@ public class FloodgatePlatform { configHolder.set(config); PlayerLink link = playerLinkLoader.load(); - // make the config available for other classes (who are injected later on) - guice = injector.createChildInjector(new ConfigLoadedModule(config)); - InstanceHolder.setInstance(api, link, this.injector, KEY); } diff --git a/common/src/main/java/org/geysermc/floodgate/FloodgatePlayerImpl.java b/common/src/main/java/org/geysermc/floodgate/FloodgatePlayerImpl.java index bc42fbd5..909a4174 100644 --- a/common/src/main/java/org/geysermc/floodgate/FloodgatePlayerImpl.java +++ b/common/src/main/java/org/geysermc/floodgate/FloodgatePlayerImpl.java @@ -25,6 +25,8 @@ package org.geysermc.floodgate; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -37,6 +39,7 @@ import org.geysermc.floodgate.api.InstanceHolder; import org.geysermc.floodgate.api.ProxyFloodgateApi; import org.geysermc.floodgate.api.link.PlayerLink; import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.util.BedrockData; @@ -48,11 +51,11 @@ import org.geysermc.floodgate.util.UiProfile; @Getter @RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@SuppressWarnings("unchecked") public final class FloodgatePlayerImpl implements FloodgatePlayer { private final String version; private final String username; private final String javaUsername; - //todo maybe add a map for platform specific things private final UUID javaUniqueId; private final String xuid; private final DeviceOs deviceOs; @@ -60,10 +63,13 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer { private final UiProfile uiProfile; private final InputMode inputMode; private final String ip; - private final boolean fromProxy; //todo remove hasBungeeData + private final boolean fromProxy; private final LinkedPlayer linkedPlayer; private final RawSkin rawSkin; - + @Getter(AccessLevel.PRIVATE) + public Map propertyKeyToValue; + @Getter(AccessLevel.PRIVATE) + private Map stringToPropertyKey; /** * Returns true if the player is still logging in */ @@ -87,6 +93,12 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer { UiProfile uiProfile = UiProfile.getById(data.getUiProfile()); InputMode inputMode = InputMode.getById(data.getInputMode()); + // RawSkin must be removed from the encrypted data + if (api instanceof ProxyFloodgateApi) { + InstanceHolder.castApi(ProxyFloodgateApi.class) + .updateEncryptedData(javaUniqueId, data); + } + LinkedPlayer linkedPlayer; // we'll use the LinkedPlayer provided by Bungee or Velocity (if they included one) @@ -103,7 +115,7 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer { deviceOs, data.getLanguageCode(), uiProfile, inputMode, data.getIp(), data.isFromProxy(), linkedPlayer, skin); - // oh oh, after fetching the linkedPlayer our encrypted data is incorrect. + // encrypted data has been changed after fetching the linkedPlayer // We have to update it... if (linkedPlayer != null && api instanceof ProxyFloodgateApi) { InstanceHolder.castApi(ProxyFloodgateApi.class) @@ -112,14 +124,6 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer { return player; } - public UUID getCorrectUniqueId() { - return linkedPlayer != null ? linkedPlayer.getJavaUniqueId() : javaUniqueId; - } - - public String getCorrectUsername() { - return linkedPlayer != null ? linkedPlayer.getJavaUsername() : javaUsername; - } - /** * Fetch and return the LinkedPlayer object associated to the player if the player is linked. * Please note that this method loads the LinkedPlayer synchronously. @@ -154,9 +158,108 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer { CompletableFuture.completedFuture(null); } + public UUID getCorrectUniqueId() { + return linkedPlayer != null ? linkedPlayer.getJavaUniqueId() : javaUniqueId; + } + + public String getCorrectUsername() { + return linkedPlayer != null ? linkedPlayer.getJavaUsername() : javaUsername; + } + public BedrockData toBedrockData() { return BedrockData.of( version, username, xuid, deviceOs.ordinal(), languageCode, uiProfile.ordinal(), inputMode.ordinal(), ip, linkedPlayer, fromProxy); } + + public T getProperty(PropertyKey key) { + if (propertyKeyToValue == null) { + return null; + } + return (T) propertyKeyToValue.get(key); + } + + public T getProperty(String key) { + if (stringToPropertyKey == null) { + return null; + } + return getProperty(stringToPropertyKey.get(key)); + } + + public T removeProperty(String key) { + if (stringToPropertyKey == null) { + return null; + } + + PropertyKey propertyKey = stringToPropertyKey.get(key); + + if (propertyKey == null || !propertyKey.isRemoveable()) { + return null; + } + + return (T) propertyKeyToValue.remove(propertyKey); + } + + public T removeProperty(PropertyKey key) { + if (stringToPropertyKey == null) { + return null; + } + + PropertyKey propertyKey = stringToPropertyKey.get(key.getKey()); + + if (propertyKey == null || !propertyKey.equals(key) || !propertyKey.isRemoveable()) { + return null; + } + + return (T) propertyKeyToValue.remove(key); + } + + public T addProperty(PropertyKey key, Object value) { + if (stringToPropertyKey == null) { + stringToPropertyKey = new HashMap<>(); + propertyKeyToValue = new HashMap<>(); + + stringToPropertyKey.put(key.getKey(), key); + propertyKeyToValue.put(key, value); + return null; + } + + PropertyKey propertyKey = stringToPropertyKey.get(key.getKey()); + + if (propertyKey != null && propertyKey.equals(key) && key.isChangeable()) { + stringToPropertyKey.put(key.getKey(), key); + return (T) propertyKeyToValue.put(key, value); + } + + return (T) stringToPropertyKey.computeIfAbsent(key.getKey(), (keyString) -> { + propertyKeyToValue.put(key, value); + return key; + }); + } + + public T addProperty(String key, Object value) { + PropertyKey propertyKey = new PropertyKey(key, true, true); + + if (stringToPropertyKey == null) { + stringToPropertyKey = new HashMap<>(); + propertyKeyToValue = new HashMap<>(); + + stringToPropertyKey.put(key, propertyKey); + propertyKeyToValue.put(propertyKey, value); + return null; + } + + PropertyKey currentPropertyKey = stringToPropertyKey.get(key); + + // key is always changeable if it passes this if statement + if (currentPropertyKey != null && currentPropertyKey.equals(propertyKey)) { + stringToPropertyKey.put(key, propertyKey); + return (T) propertyKeyToValue.put(propertyKey, value); + } + + return (T) stringToPropertyKey.computeIfAbsent(key, (keyString) -> { + propertyKeyToValue.put(propertyKey, value); + return propertyKey; + }); + } } diff --git a/common/src/main/java/org/geysermc/floodgate/HandshakeHandler.java b/common/src/main/java/org/geysermc/floodgate/HandshakeHandler.java index eaac44cc..4cdd07a6 100644 --- a/common/src/main/java/org/geysermc/floodgate/HandshakeHandler.java +++ b/common/src/main/java/org/geysermc/floodgate/HandshakeHandler.java @@ -28,6 +28,8 @@ package org.geysermc.floodgate; import static org.geysermc.floodgate.util.BedrockData.EXPECTED_LENGTH; import com.google.common.base.Charsets; +import io.netty.channel.Channel; +import java.net.InetSocketAddress; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -35,6 +37,7 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.crypto.AesCipher; import org.geysermc.floodgate.crypto.FloodgateCipher; @@ -48,7 +51,7 @@ public final class HandshakeHandler { private final FloodgateCipher cipher; private final FloodgateConfigHolder configHolder; - public HandshakeResult handle(@NonNull String handshakeData) { + public HandshakeResult handle(Channel channel, @NonNull String handshakeData) { try { String[] dataArray = handshakeData.split("\0"); @@ -95,11 +98,13 @@ public final class HandshakeHandler { rawSkin = RawSkin.decode(rawSkinData); } - System.out.println(rawSkin); - FloodgatePlayer player = FloodgatePlayerImpl.from(bedrockData, rawSkin, configHolder); api.addPlayer(player.getJavaUniqueId(), player); + int port = ((InetSocketAddress) channel.remoteAddress()).getPort(); + InetSocketAddress socketAddress = new InetSocketAddress(bedrockData.getIp(), port); + player.addProperty(PropertyKey.SOCKET_ADDRESS, socketAddress); + return new HandshakeResult(ResultType.SUCCESS, dataArray, bedrockData, player); } catch (InvalidFormatException formatException) { // only header exceptions should return 'not floodgate data', diff --git a/common/src/main/java/org/geysermc/floodgate/addon/AddonManagerAddon.java b/common/src/main/java/org/geysermc/floodgate/addon/AddonManagerAddon.java index c0c21923..b652dd6a 100644 --- a/common/src/main/java/org/geysermc/floodgate/addon/AddonManagerAddon.java +++ b/common/src/main/java/org/geysermc/floodgate/addon/AddonManagerAddon.java @@ -27,10 +27,10 @@ package org.geysermc.floodgate.addon; import com.google.inject.Inject; import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; import org.geysermc.floodgate.addon.addonmanager.AddonManagerHandler; import org.geysermc.floodgate.api.inject.InjectorAddon; import org.geysermc.floodgate.inject.CommonPlatformInjector; +import org.geysermc.floodgate.util.Utils; public final class AddonManagerAddon implements InjectorAddon { @Inject private CommonPlatformInjector injector; @@ -47,10 +47,7 @@ public final class AddonManagerAddon implements InjectorAddon { @Override public void onRemoveInject(Channel channel) { - ChannelHandler handler = channel.pipeline().get("floodgate_addon"); - if (handler != null) { - channel.pipeline().remove(handler); - } + Utils.removeHandler(channel.pipeline(), "floodgate_addon"); } @Override diff --git a/common/src/main/java/org/geysermc/floodgate/addon/DebugAddon.java b/common/src/main/java/org/geysermc/floodgate/addon/DebugAddon.java index 7a3a1780..bc6fe015 100644 --- a/common/src/main/java/org/geysermc/floodgate/addon/DebugAddon.java +++ b/common/src/main/java/org/geysermc/floodgate/addon/DebugAddon.java @@ -34,6 +34,7 @@ import org.geysermc.floodgate.addon.debug.ChannelOutDebugHandler; import org.geysermc.floodgate.api.inject.InjectorAddon; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.config.FloodgateConfig; +import org.geysermc.floodgate.util.Utils; public final class DebugAddon implements InjectorAddon { @Inject private FloodgateConfig config; @@ -71,8 +72,8 @@ public final class DebugAddon implements InjectorAddon { public void onRemoveInject(Channel channel) { ChannelPipeline pipeline = channel.pipeline(); - pipeline.remove("floodgate_debug_out"); - pipeline.remove("floodgate_debug_in"); + Utils.removeHandler(pipeline, "floodgate_debug_out"); + Utils.removeHandler(pipeline, "floodgate_debug_in"); } @Override diff --git a/common/src/main/java/org/geysermc/floodgate/link/PlayerLinkLoader.java b/common/src/main/java/org/geysermc/floodgate/link/PlayerLinkLoader.java index 4eb79313..7b010c34 100644 --- a/common/src/main/java/org/geysermc/floodgate/link/PlayerLinkLoader.java +++ b/common/src/main/java/org/geysermc/floodgate/link/PlayerLinkLoader.java @@ -29,9 +29,10 @@ import static java.util.Objects.requireNonNull; import com.google.gson.Gson; import com.google.gson.JsonObject; -import com.google.inject.Inject; +import com.google.inject.AbstractModule; import com.google.inject.Injector; -import com.google.inject.name.Named; +import com.google.inject.Provides; +import com.google.inject.Singleton; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -124,8 +125,17 @@ public final class PlayerLinkLoader { return null; } + // allow the FloodgateConfig to be used directly instead of the FloodgateConfigHolder + Injector child = injector.createChildInjector(new AbstractModule() { + @Provides + @Singleton + public FloodgateConfig floodgateConfig() { + return config; + } + }); + try { - PlayerLink instance = injector.getInstance(mainClass); + PlayerLink instance = child.getInstance(mainClass); instance.load(); return instance; } catch (Exception exception) { diff --git a/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java b/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java index 025b13ef..f5b648d4 100644 --- a/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java +++ b/common/src/main/java/org/geysermc/floodgate/module/CommonModule.java @@ -54,7 +54,7 @@ import org.geysermc.floodgate.link.PlayerLinkLoader; import org.geysermc.floodgate.util.LanguageManager; @RequiredArgsConstructor -public final class CommonModule extends AbstractModule { +public class CommonModule extends AbstractModule { private final Path dataDirectory; @Override diff --git a/common/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java b/common/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java new file mode 100644 index 00000000..c09b9cfc --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/module/ProxyCommonModule.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.module; + +import com.google.inject.Provides; +import com.google.inject.Singleton; +import com.google.inject.name.Named; +import java.nio.file.Path; +import org.geysermc.floodgate.api.ProxyFloodgateApi; +import org.geysermc.floodgate.api.SimpleFloodgateApi; +import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.config.FloodgateConfig; +import org.geysermc.floodgate.config.ProxyFloodgateConfig; +import org.geysermc.floodgate.crypto.FloodgateCipher; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; +import org.geysermc.floodgate.skin.SkinApplier; +import org.geysermc.floodgate.skin.SkinHandler; + +public final class ProxyCommonModule extends CommonModule { + public ProxyCommonModule(Path dataDirectory) { + super(dataDirectory); + } + + @Override + protected void configure() { + super.configure(); + bind(SimpleFloodgateApi.class).to(ProxyFloodgateApi.class); + } + + @Provides + @Singleton + @Named("configClass") + public Class floodgateConfigClass() { + return ProxyFloodgateConfig.class; + } + + @Provides + @Singleton + public ProxyFloodgateApi proxyFloodgateApi(PluginMessageHandler pluginMessageHandler, + FloodgateCipher cipher) { + return new ProxyFloodgateApi(pluginMessageHandler, cipher); + } + + @Provides + @Singleton + public SkinHandler skinHandler(SkinApplier skinApplier, FloodgateLogger logger) { + return new SkinHandler(skinApplier, logger); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java b/common/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java new file mode 100644 index 00000000..5e6eb2a0 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/module/ServerCommonModule.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.module; + +import com.google.inject.Provides; +import com.google.inject.Singleton; +import com.google.inject.name.Named; +import java.nio.file.Path; +import org.geysermc.floodgate.api.SimpleFloodgateApi; +import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.config.FloodgateConfig; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; +import org.geysermc.floodgate.skin.ServerSkinHandler; +import org.geysermc.floodgate.skin.SkinApplier; +import org.geysermc.floodgate.skin.SkinHandler; + +public final class ServerCommonModule extends CommonModule { + public ServerCommonModule(Path dataDirectory) { + super(dataDirectory); + } + + @Override + protected void configure() { + super.configure(); + bind(SkinHandler.class).to(ServerSkinHandler.class); + } + + @Provides + @Singleton + @Named("configClass") + public Class floodgateConfigClass() { + return FloodgateConfig.class; + } + + @Provides + @Singleton + public SimpleFloodgateApi floodgateApi(PluginMessageHandler pluginMessageHandler) { + return new SimpleFloodgateApi(pluginMessageHandler); + } + + @Provides + @Singleton + public ServerSkinHandler skinHandler(SkinApplier skinApplier, FloodgateLogger logger, + PluginMessageHandler pluginMessageHandler) { + return new ServerSkinHandler(skinApplier, logger, pluginMessageHandler); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/platform/pluginmessage/PluginMessageHandler.java b/common/src/main/java/org/geysermc/floodgate/platform/pluginmessage/PluginMessageHandler.java index a3edd06d..e31ce9ee 100644 --- a/common/src/main/java/org/geysermc/floodgate/platform/pluginmessage/PluginMessageHandler.java +++ b/common/src/main/java/org/geysermc/floodgate/platform/pluginmessage/PluginMessageHandler.java @@ -26,8 +26,10 @@ package org.geysermc.floodgate.platform.pluginmessage; import com.google.common.base.Charsets; +import com.google.gson.Gson; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; +import java.nio.charset.StandardCharsets; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import org.geysermc.cumulus.Form; @@ -35,6 +37,7 @@ import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.util.RawSkin; public abstract class PluginMessageHandler { + protected static final Gson GSON = new Gson(); protected final Short2ObjectMap
storedForms = new Short2ObjectOpenHashMap<>(); private final AtomicInteger nextFormId = new AtomicInteger(0); private final FloodgateConfigHolder configHolder; @@ -45,9 +48,13 @@ public abstract class PluginMessageHandler { public abstract boolean sendForm(UUID player, Form form); - public abstract boolean sendSkinRequest(UUID player, RawSkin skin); + public boolean sendSkinRequest(UUID player, RawSkin skin) { + return false; // Non-proxy implementations don't send requests + } - public abstract void sendSkinResponse(UUID player, String response); + public void sendSkinResponse(UUID player, boolean failed, String response) { + return; // Proxy implementations don't send responses + } protected byte[] createFormData(Form form) { short formId = getNextFormId(); @@ -66,11 +73,40 @@ public abstract class PluginMessageHandler { return data; } - protected boolean callResponseConsumer(byte[] data) { + protected byte[] createSkinRequestData(byte[] data) { + // data format: + // 0 = is request + // remaining = request data + + byte[] output = new byte[data.length + 1]; + + output[0] = 1; + System.arraycopy(data, 0, output, 1, data.length); + + return output; + } + + protected byte[] createSkinResponseData(boolean failed, String data) { + // data format: + // 0 = is request + // 1 = has failed + // remaining = response data + + byte[] rawData = data.getBytes(StandardCharsets.UTF_8); + byte[] output = new byte[rawData.length + 2]; + + output[0] = 0; + output[1] = (byte) (failed ? 1 : 0); + System.arraycopy(rawData, 0, output, 2, rawData.length); + + return output; + } + + public boolean callResponseConsumer(byte[] data) { Form storedForm = storedForms.remove(getFormId(data)); if (storedForm != null) { - storedForm.getResponseHandler().accept( - new String(data, 2, data.length - 2, Charsets.UTF_8)); + String responseData = new String(data, 2, data.length -2, Charsets.UTF_8); + storedForm.getResponseHandler().accept(responseData); return true; } return false; diff --git a/common/src/main/java/org/geysermc/floodgate/skin/ServerSkinHandler.java b/common/src/main/java/org/geysermc/floodgate/skin/ServerSkinHandler.java new file mode 100644 index 00000000..f5d0c8c0 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/skin/ServerSkinHandler.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.skin; + +import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; +import org.geysermc.floodgate.util.RawSkin; + +public final class ServerSkinHandler extends SkinHandler { + private final PluginMessageHandler pluginMessageHandler; + + public ServerSkinHandler(SkinApplier skinApplier, + FloodgateLogger logger, + PluginMessageHandler pluginMessageHandler) { + super(skinApplier, logger); + this.pluginMessageHandler = pluginMessageHandler; + } + + public void handleSkinUploadFor(FloodgatePlayer player) { + handleSkinUploadFor(player, player.getRawSkin()); + } + + public void handleSkinUploadFor(FloodgatePlayer player, RawSkin rawSkin) { + handleSkinUploadFor(player, rawSkin, + (failed, response) -> { + if (player.isFromProxy()) { + pluginMessageHandler.sendSkinResponse( + player.getCorrectUniqueId(), + failed, + response + ); + } + }); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/module/ConfigLoadedModule.java b/common/src/main/java/org/geysermc/floodgate/skin/SkinApplier.java similarity index 61% rename from common/src/main/java/org/geysermc/floodgate/module/ConfigLoadedModule.java rename to common/src/main/java/org/geysermc/floodgate/skin/SkinApplier.java index b5414f0f..30e7acbe 100644 --- a/common/src/main/java/org/geysermc/floodgate/module/ConfigLoadedModule.java +++ b/common/src/main/java/org/geysermc/floodgate/skin/SkinApplier.java @@ -23,29 +23,11 @@ * @link https://github.com/GeyserMC/Floodgate */ -package org.geysermc.floodgate.module; +package org.geysermc.floodgate.skin; -import com.google.inject.AbstractModule; -import com.google.inject.Provides; -import com.google.inject.Singleton; -import lombok.RequiredArgsConstructor; -import org.geysermc.floodgate.config.FloodgateConfig; -import org.geysermc.floodgate.config.ProxyFloodgateConfig; +import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.skin.SkinUploader.UploadResult; -@RequiredArgsConstructor -public final class ConfigLoadedModule extends AbstractModule { - private final FloodgateConfig config; - - @Override - protected void configure() { - if (config instanceof ProxyFloodgateConfig) { - bind(ProxyFloodgateConfig.class).toInstance((ProxyFloodgateConfig) config); - } - } - - @Provides - @Singleton - public FloodgateConfig floodgateConfig() { - return config; - } +public interface SkinApplier { + void applySkin(FloodgatePlayer floodgatePlayer, UploadResult result); } diff --git a/common/src/main/java/org/geysermc/floodgate/skin/SkinHandler.java b/common/src/main/java/org/geysermc/floodgate/skin/SkinHandler.java index adb41ae3..db49f6d5 100644 --- a/common/src/main/java/org/geysermc/floodgate/skin/SkinHandler.java +++ b/common/src/main/java/org/geysermc/floodgate/skin/SkinHandler.java @@ -25,50 +25,66 @@ package org.geysermc.floodgate.skin; -import static org.geysermc.floodgate.util.MessageFormatter.format; - +import java.util.function.BiConsumer; import lombok.RequiredArgsConstructor; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; -import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; -import org.geysermc.floodgate.skin.SkinUploader.UploadResult; +import org.geysermc.floodgate.api.player.PropertyKey; +import org.geysermc.floodgate.util.RawSkin; @RequiredArgsConstructor -public abstract class SkinHandler { +public class SkinHandler { private final SkinUploader uploader = new SkinUploader(); - private final PluginMessageHandler messageHandler; + private final SkinApplier skinApplier; private final FloodgateLogger logger; - public final void handleSkinUploadFor(FloodgatePlayer player) { - uploader.uploadSkin(player.getRawSkin()) + public final void handleSkinUploadFor(FloodgatePlayer player, + BiConsumer consumer) { + handleSkinUploadFor(player, player.getRawSkin(), consumer); + } + + public final void handleSkinUploadFor(FloodgatePlayer player, + RawSkin rawSkin, + BiConsumer consumer) { + if (player == null || rawSkin == null) { + if (consumer != null) { + consumer.accept(true, "Skin or Player is null"); + } + return; + } + + uploader.uploadSkin(rawSkin) .whenComplete((uploadResult, throwable) -> { if (throwable != null) { logger.error( "Failed to upload player skin for " + player.getCorrectUsername(), throwable); - messageHandler.sendSkinResponse( - player.getJavaUniqueId(), throwable.getMessage()); + if (consumer != null) { + consumer.accept(true, throwable.getMessage()); + } return; } if (uploadResult.getError() != null) { - logger.error(format( - "Error while uploading player skin for {}: {}", - player.getCorrectUsername(), uploadResult.getError())); + logger.error("Error while uploading player skin for {}: {}", + player.getCorrectUsername(), uploadResult.getError()); - messageHandler.sendSkinResponse( - player.getJavaUniqueId(), uploadResult.getError()); + if (consumer != null) { + consumer.accept(true, uploadResult.getError()); + } return; } logger.info("Skin upload successful for " + player.getCorrectUsername()); logger.info(uploadResult.getResponse().toString()); - messageHandler.sendSkinResponse( - player.getJavaUniqueId(), uploadResult.getResponse().toString()); - applySkin(player, uploadResult); + + if (consumer != null) { + consumer.accept(false, uploadResult.getResponse().toString()); + } + player.addProperty(PropertyKey.SKIN_UPLOADED, uploadResult.getResponse()); + + skinApplier.applySkin(player, uploadResult); }); } - - protected abstract void applySkin(FloodgatePlayer player, UploadResult result); } diff --git a/common/src/main/java/org/geysermc/floodgate/skin/SkinUploader.java b/common/src/main/java/org/geysermc/floodgate/skin/SkinUploader.java index 7e19d4bc..7ecb8cf1 100644 --- a/common/src/main/java/org/geysermc/floodgate/skin/SkinUploader.java +++ b/common/src/main/java/org/geysermc/floodgate/skin/SkinUploader.java @@ -30,6 +30,7 @@ import java.awt.image.BufferedImage; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import javax.annotation.Nonnull; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -44,7 +45,7 @@ public final class SkinUploader { private final Executor requestExecutor = Executors.newSingleThreadExecutor(); private long nextResult = 0; - public CompletableFuture uploadSkin(RawSkin rawSkin) { + public CompletableFuture uploadSkin(@Nonnull RawSkin rawSkin) { return CompletableFuture.supplyAsync(() -> uploadSkinInner(rawSkin, 0), requestExecutor); } @@ -149,5 +150,9 @@ public final class SkinUploader { return new UploadResult(httpCode, null, false, model, skinUrl, capeUrl, response); } + + public static UploadResult success(JsonObject response) { + return new UploadResult(200, null, false, null, null, null, response); + } } } diff --git a/common/src/main/java/org/geysermc/floodgate/util/Utils.java b/common/src/main/java/org/geysermc/floodgate/util/Utils.java new file mode 100644 index 00000000..7bc40f82 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/Utils.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.util; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelPipeline; + +public class Utils { + /** + * This method is used in Addons.
Most addons can be removed once the player associated to + * the channel has been logged in, but they should also be removed once the inject is removed. + * Because of how Netty works it will throw an exception and we don't want that. This method + * removes those handlers safely. + * + * @param pipeline the pipeline + * @param handler the name of the handler to remove + */ + public static void removeHandler(ChannelPipeline pipeline, String handler) { + ChannelHandler channelHandler = pipeline.get(handler); + if (channelHandler != null) { + pipeline.remove(channelHandler); + } + } +} diff --git a/pom.xml b/pom.xml index 9cb0aa4c..bb5be621 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,7 @@ 1.2.0-SNAPSHOT + 1.0-SNAPSHOT 1.13-R0.1-SNAPSHOT 1.15-SNAPSHOT 1.1.0 diff --git a/spigot/src/main/java/org/geysermc/floodgate/SpigotPlatform.java b/spigot/src/main/java/org/geysermc/floodgate/SpigotPlatform.java index f8822218..c47d007b 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/SpigotPlatform.java +++ b/spigot/src/main/java/org/geysermc/floodgate/SpigotPlatform.java @@ -26,20 +26,23 @@ package org.geysermc.floodgate; import com.google.inject.Inject; +import com.google.inject.Injector; import com.google.inject.Module; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.inject.PlatformInjector; import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.pluginmessage.SpigotPluginMessageRegister; public final class SpigotPlatform extends FloodgatePlatform { @Inject private JavaPlugin plugin; + @Inject private Injector guice; @Inject public SpigotPlatform(FloodgateApi api, PlatformInjector platformInjector, - FloodgateLogger logger) { - super(api, platformInjector, logger); + FloodgateLogger logger, Injector injector) { + super(api, platformInjector, logger, injector); } @Override @@ -47,7 +50,9 @@ public final class SpigotPlatform extends FloodgatePlatform { boolean success = super.enable(postInitializeModules); if (!success) { Bukkit.getPluginManager().disablePlugin(plugin); + return false; } - return success; + guice.getInstance(SpigotPluginMessageRegister.class).register(); + return true; } } diff --git a/spigot/src/main/java/org/geysermc/floodgate/SpigotPlugin.java b/spigot/src/main/java/org/geysermc/floodgate/SpigotPlugin.java index 711df269..1b5b1a16 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/SpigotPlugin.java +++ b/spigot/src/main/java/org/geysermc/floodgate/SpigotPlugin.java @@ -30,7 +30,7 @@ import com.google.inject.Injector; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.module.CommandModule; -import org.geysermc.floodgate.module.CommonModule; +import org.geysermc.floodgate.module.ServerCommonModule; import org.geysermc.floodgate.module.SpigotAddonModule; import org.geysermc.floodgate.module.SpigotListenerModule; import org.geysermc.floodgate.module.SpigotPlatformModule; @@ -46,7 +46,7 @@ public final class SpigotPlugin extends JavaPlugin { long ctm = System.currentTimeMillis(); Injector injector = Guice.createInjector( - new CommonModule(getDataFolder().toPath()), + new ServerCommonModule(getDataFolder().toPath()), new SpigotPlatformModule(this) ); diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataAddon.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataAddon.java index c2ec72a7..b3a9e0b4 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataAddon.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataAddon.java @@ -34,6 +34,7 @@ import org.geysermc.floodgate.api.inject.InjectorAddon; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.FloodgateConfig; +import org.geysermc.floodgate.util.Utils; public final class SpigotDataAddon implements InjectorAddon { @Inject private HandshakeHandler handshakeHandler; @@ -63,7 +64,7 @@ public final class SpigotDataAddon implements InjectorAddon { @Override public void onRemoveInject(Channel channel) { - channel.pipeline().remove("floodgate_data_handler"); + Utils.removeHandler(channel.pipeline(), "floodgate_data_handler"); } @Override diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index e1c71aa9..86a0f63c 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -48,6 +48,7 @@ import org.geysermc.floodgate.HandshakeHandler; import org.geysermc.floodgate.HandshakeHandler.HandshakeResult; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.api.player.PropertyKey; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.ReflectionUtils; @@ -180,7 +181,7 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler networkManager = ctx.channel().pipeline().get("packet_handler"); String handshakeValue = getCastedValue(packet, HANDSHAKE_HOST); - HandshakeResult result = handshakeHandler.handle(handshakeValue); + HandshakeResult result = handshakeHandler.handle(ctx.channel(), handshakeValue); switch (result.getResultType()) { case SUCCESS: break; @@ -204,6 +205,8 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler String[] data = result.getHandshakeData(); bungeeData = isBungeeData(); + InetSocketAddress correctAddress = fPlayer.getProperty(PropertyKey.SOCKET_ADDRESS); + if (bungeeData) { setValue(packet, HANDSHAKE_HOST, data[0] + '\0' + bedrockData.getIp() + '\0' + @@ -213,12 +216,7 @@ public final class SpigotDataHandler extends SimpleChannelInboundHandler // Use a spoofedUUID for initUUID (just like Bungeecord) setValue(networkManager, "spoofedUUID", fPlayer.getCorrectUniqueId()); // Use the player his IP for stuff instead of Geyser his IP - SocketAddress newAddress = new InetSocketAddress( - bedrockData.getIp(), - ((InetSocketAddress) ctx.channel().remoteAddress()).getPort() - ); - - setValue(networkManager, SOCKET_ADDRESS, newAddress); + setValue(networkManager, SOCKET_ADDRESS, correctAddress); } } else if (isLogin) { if (!bungeeData) { diff --git a/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java b/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java index b5f792b2..6c541b95 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java +++ b/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java @@ -38,12 +38,12 @@ import org.geysermc.floodgate.FloodgatePlayerImpl; import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; -import org.geysermc.floodgate.skin.SkinHandler; +import org.geysermc.floodgate.skin.ServerSkinHandler; import org.geysermc.floodgate.util.LanguageManager; public final class SpigotListener implements Listener { @Inject private SimpleFloodgateApi api; - @Inject private SkinHandler skinHandler; + @Inject private ServerSkinHandler skinHandler; @Inject private LanguageManager languageManager; @Inject private FloodgateLogger logger; diff --git a/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java b/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java index 200a07b4..1c413dab 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java +++ b/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java @@ -33,10 +33,9 @@ import lombok.RequiredArgsConstructor; import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.floodgate.SpigotPlugin; -import org.geysermc.floodgate.api.SimpleFloodgateApi; +import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.command.SpigotCommandRegistration; -import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.inject.CommonPlatformInjector; import org.geysermc.floodgate.inject.spigot.SpigotInjector; @@ -47,8 +46,10 @@ import org.geysermc.floodgate.platform.command.CommandUtil; import org.geysermc.floodgate.platform.listener.ListenerRegistration; import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; import org.geysermc.floodgate.pluginmessage.SpigotPluginMessageHandler; -import org.geysermc.floodgate.pluginmessage.SpigotSkinHandler; -import org.geysermc.floodgate.skin.SkinHandler; +import org.geysermc.floodgate.pluginmessage.SpigotPluginMessageRegister; +import org.geysermc.floodgate.pluginmessage.SpigotSkinApplier; +import org.geysermc.floodgate.skin.ServerSkinHandler; +import org.geysermc.floodgate.skin.SkinApplier; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.SpigotCommandUtil; import org.geysermc.floodgate.util.SpigotVersionSpecificMethods; @@ -57,25 +58,17 @@ import org.geysermc.floodgate.util.SpigotVersionSpecificMethods; public final class SpigotPlatformModule extends AbstractModule { private final SpigotPlugin plugin; + @Override + protected void configure() { + bind(PluginMessageHandler.class).to(SpigotPluginMessageHandler.class); + } + @Provides @Singleton public JavaPlugin javaPlugin() { return plugin; } - @Provides - @Singleton - @Named("configClass") - public Class floodgateConfigClass() { - return FloodgateConfig.class; - } - - @Provides - @Singleton - public SimpleFloodgateApi floodgateApi(PluginMessageHandler pluginMessageHandler) { - return new SimpleFloodgateApi(pluginMessageHandler); - } - @Provides @Singleton public FloodgateLogger floodgateLogger(LanguageManager languageManager) { @@ -90,7 +83,8 @@ public final class SpigotPlatformModule extends AbstractModule { @Singleton public CommandRegistration commandRegistration( SpigotVersionSpecificMethods versionSpecificMethods, - CommandUtil commandUtil) { + CommandUtil commandUtil + ) { return new SpigotCommandRegistration(versionSpecificMethods, plugin, commandUtil); } @@ -106,24 +100,6 @@ public final class SpigotPlatformModule extends AbstractModule { return new SpigotListenerRegistration(plugin); } - @Provides - @Singleton - public PluginMessageHandler pluginMessageHandler(FloodgateConfigHolder configHolder, - @Named("formChannel") String formChannel, - @Named("skinChannel") String skinChannel) { - return new SpigotPluginMessageHandler(configHolder, plugin, formChannel, skinChannel); - } - - @Provides - @Singleton - public SkinHandler skinHandler(PluginMessageHandler messageHandler, FloodgateLogger logger, - SpigotVersionSpecificMethods versionSpecificMethods, - FloodgateConfigHolder configHolder) { - return new SpigotSkinHandler( - messageHandler, logger, versionSpecificMethods, plugin, configHolder - ); - } - /* DebugAddon / PlatformInjector */ @@ -157,4 +133,47 @@ public final class SpigotPlatformModule extends AbstractModule { public String implementationName() { return "Spigot"; } + + /* + Others + */ + + @Provides + @Singleton + public SpigotPluginMessageHandler pluginMessageHandler( + FloodgateConfigHolder configHolder, + @Named("formChannel") String formChannel, + @Named("skinChannel") String skinChannel + ) { + return new SpigotPluginMessageHandler(configHolder, plugin, formChannel, skinChannel); + } + + @Provides + @Singleton + public SpigotPluginMessageRegister pluginMessageRegister( + FloodgateApi api, + @Named("formChannel") String formChannel, + @Named("skinChannel") String skinChannel, + SpigotPluginMessageHandler pluginMessageHandler, + ServerSkinHandler skinHandler, + FloodgateLogger logger + ) { + return new SpigotPluginMessageRegister(plugin, api, formChannel, skinChannel, + pluginMessageHandler, skinHandler, logger); + } + + @Provides + @Singleton + public SkinApplier skinApplier( + SpigotVersionSpecificMethods versionSpecificMethods, + FloodgateConfigHolder configHolder + ) { + return new SpigotSkinApplier(versionSpecificMethods, plugin, configHolder); + } + + @Provides + @Singleton + public SpigotVersionSpecificMethods versionSpecificMethods() { + return new SpigotVersionSpecificMethods(plugin); + } } diff --git a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageHandler.java b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageHandler.java index ed7b2b11..53c80d78 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageHandler.java @@ -25,47 +25,28 @@ package org.geysermc.floodgate.pluginmessage; -import com.google.common.base.Charsets; import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.plugin.messaging.Messenger; import org.geysermc.cumulus.Form; -import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; -import org.geysermc.floodgate.util.RawSkin; public class SpigotPluginMessageHandler extends PluginMessageHandler { private final JavaPlugin plugin; private final String formChannel; private final String skinChannel; - public SpigotPluginMessageHandler(FloodgateConfigHolder configHolder, JavaPlugin plugin, - String formChannel, String skinChannel) { + public SpigotPluginMessageHandler( + FloodgateConfigHolder configHolder, + JavaPlugin plugin, + String formChannel, + String skinChannel + ) { super(configHolder); this.plugin = plugin; this.formChannel = formChannel; this.skinChannel = skinChannel; - - Messenger messenger = plugin.getServer().getMessenger(); - - // form - messenger.registerIncomingPluginChannel( - plugin, formChannel, - (channel, player, message) -> callResponseConsumer(message)); - messenger.registerOutgoingPluginChannel(plugin, formChannel); - - // skin - messenger.registerIncomingPluginChannel( - plugin, skinChannel, - (channel, player, message) -> { - String origin = - FloodgateApi.getInstance().getPlayer(player.getUniqueId()) != null - ? "Geyser" : "player"; - System.out.println("Got skin from " + origin + "!"); - } - ); } @Override @@ -81,22 +62,9 @@ public class SpigotPluginMessageHandler extends PluginMessageHandler { } @Override - public boolean sendSkinRequest(UUID playerId, RawSkin skin) { + public void sendSkinResponse(UUID playerId, boolean failed, String response) { try { - byte[] skinData = skin.toString().getBytes(Charsets.UTF_8); - Bukkit.getPlayer(playerId).sendPluginMessage(plugin, skinChannel, skinData); - //todo use json or something to split request and response - } catch (Exception exception) { - exception.printStackTrace(); - return false; - } - return true; - } - - @Override - public void sendSkinResponse(UUID playerId, String response) { - try { - byte[] responseData = response.getBytes(Charsets.UTF_8); + byte[] responseData = createSkinResponseData(failed, response); Bukkit.getPlayer(playerId).sendPluginMessage(plugin, skinChannel, responseData); } catch (Exception exception) { exception.printStackTrace(); diff --git a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageRegister.java b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageRegister.java new file mode 100644 index 00000000..dba19463 --- /dev/null +++ b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageRegister.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Floodgate + */ + +package org.geysermc.floodgate.pluginmessage; + +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.plugin.messaging.Messenger; +import org.geysermc.floodgate.api.FloodgateApi; +import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.skin.ServerSkinHandler; +import org.geysermc.floodgate.util.Base64Utils; +import org.geysermc.floodgate.util.RawSkin; + +@RequiredArgsConstructor +public class SpigotPluginMessageRegister { + private final JavaPlugin plugin; + private final FloodgateApi api; + private final String formChannel; + private final String skinChannel; + private final SpigotPluginMessageHandler pluginMessageHandler; + private final ServerSkinHandler skinHandler; + private final FloodgateLogger logger; + + public void register() { + Messenger messenger = plugin.getServer().getMessenger(); + + // form + messenger.registerIncomingPluginChannel( + plugin, formChannel, + (channel, player, message) -> + pluginMessageHandler.callResponseConsumer(message)); + + messenger.registerOutgoingPluginChannel(plugin, formChannel); + + // skin + messenger.registerIncomingPluginChannel( + plugin, skinChannel, + (channel, player, message) -> { + //todo make a Proxy and a Server class for this? + FloodgatePlayer floodgatePlayer = api.getPlayer(player.getUniqueId()); + if (floodgatePlayer == null) { + logKick(player, "Non-Floodgate player sent a Skin plugin message."); + return; + } + + // non-proxy servers can only handle requests (from proxies) + + if (!floodgatePlayer.isFromProxy()) { + logKick(player, "Cannot receive Skin request from Player."); + return; + } + + // 1 byte for isRequest and 9 for RawSkin itself + if (message.length < Base64Utils.getEncodedLength(9 + 1)) { + logKick(player, "Skin request data has to be at least 10 byte long."); + return; + } + + boolean request = message[0] == 1; + + if (!request) { + logKick(player, "Proxy sent a response instead of a request?"); + return; + } + + RawSkin rawSkin = null; + try { + rawSkin = RawSkin.decode(message, 1); + } catch (Exception exception) { + logger.error("Failed to decode RawSkin", exception); + } + // we let it continue since SkinHandler sends the plugin message for us + skinHandler.handleSkinUploadFor(floodgatePlayer, rawSkin); + } + ); + } + + private void logKick(Player player, String reason) { + logger.error(reason + " Closing connection for " + player.getName()); + player.kickPlayer(reason); + } +} diff --git a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinHandler.java b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java similarity index 84% rename from spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinHandler.java rename to spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java index 760b695b..adf8ab39 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java @@ -33,16 +33,14 @@ import java.lang.reflect.Method; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.geysermc.floodgate.SpigotPlugin; -import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.FloodgateConfigHolder; -import org.geysermc.floodgate.platform.pluginmessage.PluginMessageHandler; -import org.geysermc.floodgate.skin.SkinHandler; +import org.geysermc.floodgate.skin.SkinApplier; import org.geysermc.floodgate.skin.SkinUploader.UploadResult; import org.geysermc.floodgate.util.ReflectionUtils; import org.geysermc.floodgate.util.SpigotVersionSpecificMethods; -public class SpigotSkinHandler extends SkinHandler { +public final class SpigotSkinApplier implements SkinApplier { private static final Method GET_PROFILE_METHOD; static { @@ -56,17 +54,18 @@ public class SpigotSkinHandler extends SkinHandler { private final SpigotPlugin plugin; private final FloodgateConfigHolder configHolder; - public SpigotSkinHandler(PluginMessageHandler messageHandler, FloodgateLogger logger, - SpigotVersionSpecificMethods versionSpecificMethods, - SpigotPlugin plugin, FloodgateConfigHolder configHolder) { - super(messageHandler, logger); + public SpigotSkinApplier( + SpigotVersionSpecificMethods versionSpecificMethods, + SpigotPlugin plugin, + FloodgateConfigHolder configHolder + ) { this.versionSpecificMethods = versionSpecificMethods; this.plugin = plugin; this.configHolder = configHolder; } @Override - protected void applySkin(FloodgatePlayer floodgatePlayer, UploadResult result) { + public void applySkin(FloodgatePlayer floodgatePlayer, UploadResult result) { Player player = Bukkit.getPlayer(floodgatePlayer.getCorrectUniqueId()); GameProfile profile = ReflectionUtils.castedInvoke(player, GET_PROFILE_METHOD); @@ -89,7 +88,7 @@ public class SpigotSkinHandler extends SkinHandler { if (configHolder.get().isApplySkinDirectly()) { plugin.getServer().getScheduler().runTask(plugin, () -> { for (Player p : Bukkit.getOnlinePlayers()) { - if (p != player) { + if (p != player && p.canSee(player)) { versionSpecificMethods.hidePlayer(p, player); versionSpecificMethods.showPlayer(p, player); } diff --git a/velocity/src/main/java/org/geysermc/floodgate/VelocityPlugin.java b/velocity/src/main/java/org/geysermc/floodgate/VelocityPlugin.java index 261859ed..abbc1175 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/VelocityPlugin.java +++ b/velocity/src/main/java/org/geysermc/floodgate/VelocityPlugin.java @@ -33,7 +33,7 @@ import com.velocitypowered.api.plugin.annotation.DataDirectory; import java.nio.file.Path; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.module.CommandModule; -import org.geysermc.floodgate.module.CommonModule; +import org.geysermc.floodgate.module.ProxyCommonModule; import org.geysermc.floodgate.module.VelocityAddonModule; import org.geysermc.floodgate.module.VelocityListenerModule; import org.geysermc.floodgate.module.VelocityPlatformModule; @@ -48,7 +48,7 @@ public final class VelocityPlugin { long ctm = System.currentTimeMillis(); Injector injector = guice.createChildInjector( - new CommonModule(dataDirectory), + new ProxyCommonModule(dataDirectory), new VelocityPlatformModule() ); diff --git a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityDataAddon.java b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityDataAddon.java index da6a2c34..fc03613c 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityDataAddon.java +++ b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityDataAddon.java @@ -35,6 +35,7 @@ import org.geysermc.floodgate.api.inject.InjectorAddon; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.config.ProxyFloodgateConfig; +import org.geysermc.floodgate.util.Utils; public final class VelocityDataAddon implements InjectorAddon { @Inject private HandshakeHandler handshakeHandler; @@ -84,7 +85,7 @@ public final class VelocityDataAddon implements InjectorAddon { @Override public void onRemoveInject(Channel channel) { - channel.pipeline().remove("floodgate_data_handler"); + Utils.removeHandler(channel.pipeline(), "floodgate_data_handler"); } @Override diff --git a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java index efd3fb30..9742ea5c 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java +++ b/velocity/src/main/java/org/geysermc/floodgate/addon/data/VelocityProxyDataHandler.java @@ -89,7 +89,7 @@ public final class VelocityProxyDataHandler extends SimpleChannelInboundHandler< private void handleClientToProxy(ChannelHandlerContext ctx, Object packet) { String address = getCastedValue(packet, HANDSHAKE_SERVER_ADDRESS); - HandshakeResult result = handshakeHandler.handle(address); + HandshakeResult result = handshakeHandler.handle(ctx.channel(), address); switch (result.getResultType()) { case SUCCESS: break; diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityPluginMessageHandler.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityPluginMessageHandler.java index f1e5edb8..7e9187bb 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityPluginMessageHandler.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityPluginMessageHandler.java @@ -25,8 +25,6 @@ package org.geysermc.floodgate.listener; -import static org.geysermc.floodgate.util.MessageFormatter.format; - import com.google.inject.Inject; import com.google.inject.name.Named; import com.velocitypowered.api.event.Subscribe; @@ -98,9 +96,8 @@ public class VelocityPluginMessageHandler extends PluginMessageHandler { event.setResult(ForwardResult.handled()); if (!callResponseConsumer(data)) { - logger.error(format( - "Couldn't find stored form with id {} for player {}", - formId, ((Player) source).getUsername())); + logger.error("Couldn't find stored form with id {} for player {}", + formId, ((Player) source).getUsername()); } } } @@ -119,9 +116,4 @@ public class VelocityPluginMessageHandler extends PluginMessageHandler { public boolean sendSkinRequest(UUID player, RawSkin skin) { return false; //todo } - - @Override - public void sendSkinResponse(UUID player, String response) { - - } }