diff --git a/api/src/main/java/org/geysermc/floodgate/api/handshake/HandshakeData.java b/api/src/main/java/org/geysermc/floodgate/api/handshake/HandshakeData.java index d152c20e..247e721a 100644 --- a/api/src/main/java/org/geysermc/floodgate/api/handshake/HandshakeData.java +++ b/api/src/main/java/org/geysermc/floodgate/api/handshake/HandshakeData.java @@ -29,7 +29,6 @@ import io.netty.channel.Channel; import java.util.UUID; import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.LinkedPlayer; -import org.geysermc.floodgate.util.RawSkin; /** * For advanced users only! You shouldn't play with this unless you know what you're doing.
@@ -76,18 +75,6 @@ public interface HandshakeData { */ void setLinkedPlayer(LinkedPlayer player); - /** - * Returns the skin of the client. Can be null even though the player is a Floodgate player. - */ - RawSkin getRawSkin(); - - /** - * Manually set the skin of the client. - * - * @param rawSkin the skin of the client - */ - void setRawSkin(RawSkin rawSkin); - /** * Returns the hostname used in the handshake packet. This is the hostname after Floodgate * removed the data. 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 ce7c6d9c..75bc5a21 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 @@ -32,7 +32,6 @@ import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.floodgate.util.InputMode; import org.geysermc.floodgate.util.LinkedPlayer; -import org.geysermc.floodgate.util.RawSkin; import org.geysermc.floodgate.util.UiProfile; public interface FloodgatePlayer { @@ -107,11 +106,6 @@ public interface FloodgatePlayer { */ LinkedPlayer getLinkedPlayer(); - /** - * Returns the raw skin of the Bedrock player - */ - RawSkin getRawSkin(); - default boolean sendForm(Form form) { return FloodgateApi.getInstance().sendForm(getCorrectUniqueId(), form); } 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 9ca9e7ae..7786b91e 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java +++ b/bungee/src/main/java/org/geysermc/floodgate/listener/BungeeListener.java @@ -37,7 +37,6 @@ import net.md_5.bungee.api.connection.PendingConnection; 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.ServerConnectedEvent; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.event.EventHandler; @@ -46,12 +45,7 @@ import net.md_5.bungee.netty.ChannelWrapper; 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.player.FloodgatePlayerImpl; -import org.geysermc.floodgate.pluginmessage.PluginMessageManager; -import org.geysermc.floodgate.pluginmessage.channel.SkinChannel; -import org.geysermc.floodgate.skin.SkinHandler; import org.geysermc.floodgate.util.BungeeCommandUtil; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.ReflectionUtils; @@ -74,10 +68,6 @@ public final class BungeeListener implements Listener { @Inject private LanguageManager languageManager; @Inject private FloodgateLogger logger; - @Inject private ProxyFloodgateConfig config; - @Inject private PluginMessageManager pluginMessageManager; - @Inject private SkinHandler skinHandler; - @Inject @Named("playerAttribute") private AttributeKey playerAttribute; @@ -86,27 +76,6 @@ public final class BungeeListener implements Listener { @Named("kickMessageAttribute") private AttributeKey kickMessageAttribute; - @EventHandler - public void onServerConnected(ServerConnectedEvent event) { - FloodgatePlayer player = api.getPlayer(event.getPlayer().getUniqueId()); - if (player == null) { - return; - } - - // only ask for skin upload if it hasn't been uploaded already - if (player.hasProperty(PropertyKey.SKIN_UPLOADED)) { - return; - } - - // send skin request to server if data forwarding allows that - if (config.isSendFloodgateData()) { - pluginMessageManager.getChannel(SkinChannel.class) - .sendSkinRequest(player.getCorrectUniqueId(), player.getRawSkin()); - } else { - skinHandler.handleSkinUploadFor(player, null); - } - } - @EventHandler(priority = EventPriority.LOWEST) public void onPreLogin(PreLoginEvent event) { // well, no reason to check if the player will be kicked anyway diff --git a/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java index 5bc3ebac..3f771b51 100644 --- a/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java +++ b/bungee/src/main/java/org/geysermc/floodgate/pluginmessage/BungeeSkinApplier.java @@ -35,14 +35,13 @@ 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) { + public void applySkin(FloodgatePlayer uuid, JsonObject skinResult) { ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid.getCorrectUniqueId()); InitialHandler handler; @@ -61,11 +60,10 @@ public final class BungeeSkinApplier implements SkinApplier { loginResult = new LoginResult(null, null, null); } - JsonObject response = result.getResponse(); Property property = new Property( "textures", - response.get("value").getAsString(), - response.get("signature").getAsString() + skinResult.get("value").getAsString(), + skinResult.get("signature").getAsString() ); loginResult.setProperties(new Property[]{property}); diff --git a/common/pom.xml b/common/pom.xml index 1576c844..f1a96bea 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -35,6 +35,16 @@ fastutil-short-object-maps 8.3.1 + + com.nukkitx.fastutil + fastutil-int-object-maps + 8.3.1 + + + org.java-websocket + Java-Websocket + 1.5.1 + org.geysermc.floodgate api diff --git a/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java b/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java index 103e29fe..605827b3 100644 --- a/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java +++ b/common/src/main/java/org/geysermc/floodgate/addon/data/HandshakeDataImpl.java @@ -33,7 +33,6 @@ import org.geysermc.floodgate.api.handshake.HandshakeData; import org.geysermc.floodgate.config.FloodgateConfig; import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.LinkedPlayer; -import org.geysermc.floodgate.util.RawSkin; import org.geysermc.floodgate.util.Utils; @Getter @@ -45,7 +44,6 @@ public class HandshakeDataImpl implements HandshakeData { private final UUID javaUniqueId; @Setter private LinkedPlayer linkedPlayer; - @Setter private RawSkin rawSkin; @Setter private String hostname; @Setter private String bedrockIp; @Setter private String disconnectReason; @@ -56,14 +54,12 @@ public class HandshakeDataImpl implements HandshakeData { BedrockData bedrockData, FloodgateConfig config, LinkedPlayer linkedPlayer, - RawSkin rawSkin, String hostname) { this.channel = channel; this.floodgatePlayer = floodgatePlayer; this.bedrockData = bedrockData; this.linkedPlayer = linkedPlayer; - this.rawSkin = rawSkin; this.hostname = hostname; String javaUsername = null; diff --git a/common/src/main/java/org/geysermc/floodgate/config/FloodgateConfig.java b/common/src/main/java/org/geysermc/floodgate/config/FloodgateConfig.java index 4370bed5..ac064ea3 100644 --- a/common/src/main/java/org/geysermc/floodgate/config/FloodgateConfig.java +++ b/common/src/main/java/org/geysermc/floodgate/config/FloodgateConfig.java @@ -37,7 +37,6 @@ public class FloodgateConfig { private String keyFileName; private String usernamePrefix; private boolean replaceSpaces; - private boolean applySkinDirectly; //todo how is this possible for proxies? private String defaultLocale; 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 d4562c22..a53c6c5e 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.inject.CommonPlatformInjector; import org.geysermc.floodgate.player.FloodgateHandshakeHandler; import org.geysermc.floodgate.pluginmessage.PluginMessageManager; import org.geysermc.floodgate.skin.SkinApplier; -import org.geysermc.floodgate.skin.SkinHandler; +import org.geysermc.floodgate.skin.SkinUploadManager; import org.geysermc.floodgate.util.LanguageManager; @RequiredArgsConstructor @@ -141,16 +141,12 @@ public class CommonModule extends AbstractModule { SimpleFloodgateApi api, FloodgateCipher cipher, FloodgateConfigHolder configHolder, + SkinUploadManager skinUploadManager, @Named("playerAttribute") AttributeKey playerAttribute, FloodgateLogger logger) { - return new FloodgateHandshakeHandler( - handshakeHandlers, - api, - cipher, - configHolder, - playerAttribute, - logger - ); + + return new FloodgateHandshakeHandler(handshakeHandlers, api, cipher, configHolder, + skinUploadManager, playerAttribute, logger); } @Provides @@ -161,11 +157,11 @@ public class CommonModule extends AbstractModule { @Provides @Singleton - public SkinHandler skinHandler( - PluginMessageManager pluginMessageManager, + public SkinUploadManager skinUploadManager( + FloodgateApi api, SkinApplier skinApplier, FloodgateLogger logger) { - return new SkinHandler(pluginMessageManager, skinApplier, logger); + return new SkinUploadManager(api, skinApplier, logger); } @Provides diff --git a/common/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java b/common/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java index 1d3d99c6..bf63be24 100644 --- a/common/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java +++ b/common/src/main/java/org/geysermc/floodgate/player/FloodgateHandshakeHandler.java @@ -46,13 +46,11 @@ 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.FloodgateConfigHolder; -import org.geysermc.floodgate.crypto.AesCipher; import org.geysermc.floodgate.crypto.FloodgateCipher; -import org.geysermc.floodgate.util.Base64Utils; +import org.geysermc.floodgate.skin.SkinUploadManager; import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.InvalidFormatException; import org.geysermc.floodgate.util.LinkedPlayer; -import org.geysermc.floodgate.util.RawSkin; import org.geysermc.floodgate.util.Utils; @RequiredArgsConstructor @@ -61,6 +59,7 @@ public final class FloodgateHandshakeHandler { private final SimpleFloodgateApi api; private final FloodgateCipher cipher; private final FloodgateConfigHolder configHolder; + private final SkinUploadManager skinUploadManager; private final AttributeKey playerAttribute; private final FloodgateLogger logger; @@ -85,22 +84,8 @@ public final class FloodgateHandshakeHandler { channel, null, hostname); } - // header + base64 iv - 0x21 - encrypted data - 0x21 - RawSkin - int expectedHeaderLength = FloodgateCipher.HEADER_LENGTH + - Base64Utils.getEncodedLength(AesCipher.IV_LENGTH); - int lastSplitIndex = data.lastIndexOf(0x21); - try { - byte[] floodgateData; - byte[] rawSkinData = null; - - // if it has a RawSkin - if (lastSplitIndex - expectedHeaderLength > 0) { - floodgateData = data.substring(0, lastSplitIndex).getBytes(Charsets.UTF_8); - rawSkinData = data.substring(lastSplitIndex + 1).getBytes(Charsets.UTF_8); - } else { - floodgateData = data.getBytes(Charsets.UTF_8); - } + byte[] floodgateData = data.getBytes(Charsets.UTF_8); // actual decryption String decrypted = cipher.decryptToString(floodgateData); @@ -112,14 +97,6 @@ public final class FloodgateHandshakeHandler { channel, bedrockData, hostname); } - RawSkin rawSkin = null; - // only decompile the skin after knowing that the floodgateData is legit - // note that we don't store a hash or anything in the BedrockData, - // so a mitm can change skins - if (rawSkinData != null) { - rawSkin = RawSkin.decode(rawSkinData); - } - LinkedPlayer linkedPlayer; // we'll use the LinkedPlayer provided by Bungee or Velocity (if they included one) @@ -133,9 +110,14 @@ public final class FloodgateHandshakeHandler { HandshakeData handshakeData = new HandshakeDataImpl( channel, true, bedrockData.clone(), configHolder.get(), - linkedPlayer != null ? linkedPlayer.clone() : null, rawSkin, hostname); + linkedPlayer != null ? linkedPlayer.clone() : null, hostname); handshakeHandlers.callHandshakeHandlers(handshakeData); + if (!handshakeData.shouldDisconnect()) { + skinUploadManager.addConnectionIfNeeded(bedrockData.getSubscribeId(), + bedrockData.getVerifyCode()); + } + UUID javaUuid = Utils.getJavaUuid(bedrockData.getXuid()); handshakeData.setHostname(correctHostname( handshakeData.getHostname(), bedrockData, javaUuid @@ -149,7 +131,8 @@ public final class FloodgateHandshakeHandler { channel.attr(playerAttribute).set(player); int port = ((InetSocketAddress) channel.remoteAddress()).getPort(); - InetSocketAddress socketAddress = new InetSocketAddress(handshakeData.getBedrockIp(), port); + InetSocketAddress socketAddress = new InetSocketAddress(handshakeData.getBedrockIp(), + port); player.addProperty(PropertyKey.SOCKET_ADDRESS, socketAddress); return new HandshakeResult(ResultType.SUCCESS, handshakeData, bedrockData, player); @@ -185,7 +168,7 @@ public final class FloodgateHandshakeHandler { String hostname) { HandshakeData handshakeData = new HandshakeDataImpl(channel, bedrockData != null, - bedrockData, configHolder.get(), null, null, hostname); + bedrockData, configHolder.get(), null, hostname); handshakeHandlers.callHandshakeHandlers(handshakeData); if (bedrockData != null) { diff --git a/common/src/main/java/org/geysermc/floodgate/player/FloodgatePlayerImpl.java b/common/src/main/java/org/geysermc/floodgate/player/FloodgatePlayerImpl.java index 19cfe698..59859867 100644 --- a/common/src/main/java/org/geysermc/floodgate/player/FloodgatePlayerImpl.java +++ b/common/src/main/java/org/geysermc/floodgate/player/FloodgatePlayerImpl.java @@ -46,7 +46,6 @@ import org.geysermc.floodgate.util.BedrockData; import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.floodgate.util.InputMode; import org.geysermc.floodgate.util.LinkedPlayer; -import org.geysermc.floodgate.util.RawSkin; import org.geysermc.floodgate.util.UiProfile; import org.geysermc.floodgate.util.Utils; @@ -67,7 +66,9 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer { private final boolean fromProxy; private final boolean proxy; // if current platform is a proxy private final LinkedPlayer linkedPlayer; - private final RawSkin rawSkin; + + private final int subscribeId; + private final String verifyCode; @Getter(AccessLevel.PRIVATE) public Map propertyKeyToValue; @@ -92,16 +93,14 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer { InputMode inputMode = InputMode.getById(data.getInputMode()); LinkedPlayer linkedPlayer = handshakeData.getLinkedPlayer(); - RawSkin skin = handshakeData.getRawSkin(); FloodgatePlayerImpl player = new FloodgatePlayerImpl( data.getVersion(), data.getUsername(), handshakeData.getJavaUsername(), javaUniqueId, data.getXuid(), deviceOs, data.getLanguageCode(), uiProfile, inputMode, data.getIp(), data.isFromProxy(), api instanceof ProxyFloodgateApi, - linkedPlayer, skin); + linkedPlayer, data.getSubscribeId(), data.getVerifyCode()); - // RawSkin should be removed, fromProxy should be changed - // and encrypted data can be changed after fetching the linkedPlayer + // fromProxy and linked player might have to be changed if (api instanceof ProxyFloodgateApi) { InstanceHolder.castApi(ProxyFloodgateApi.class) .updateEncryptedData(player.getCorrectUniqueId(), player.toBedrockData()); @@ -155,9 +154,9 @@ public final class FloodgatePlayerImpl implements FloodgatePlayer { } public BedrockData toBedrockData() { - return BedrockData.of( - version, username, xuid, deviceOs.ordinal(), languageCode, - uiProfile.ordinal(), inputMode.ordinal(), ip, linkedPlayer, proxy); + return BedrockData.of(version, username, xuid, deviceOs.ordinal(), languageCode, + uiProfile.ordinal(), inputMode.ordinal(), ip, linkedPlayer, proxy, subscribeId, + verifyCode); } @Override diff --git a/common/src/main/java/org/geysermc/floodgate/pluginmessage/channel/SkinChannel.java b/common/src/main/java/org/geysermc/floodgate/pluginmessage/channel/SkinChannel.java index 3a2057c0..b621ba48 100644 --- a/common/src/main/java/org/geysermc/floodgate/pluginmessage/channel/SkinChannel.java +++ b/common/src/main/java/org/geysermc/floodgate/pluginmessage/channel/SkinChannel.java @@ -25,36 +25,22 @@ package org.geysermc.floodgate.pluginmessage.channel; -import com.google.gson.Gson; -import com.google.gson.JsonIOException; import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; import com.google.inject.Inject; -import java.io.ByteArrayInputStream; -import java.io.InputStreamReader; -import java.io.Reader; import java.nio.charset.StandardCharsets; import java.util.UUID; import org.geysermc.floodgate.api.FloodgateApi; -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.platform.pluginmessage.PluginMessageUtils; +import org.geysermc.floodgate.config.FloodgateConfig; +import org.geysermc.floodgate.config.ProxyFloodgateConfig; import org.geysermc.floodgate.pluginmessage.PluginMessageChannel; import org.geysermc.floodgate.skin.SkinApplier; -import org.geysermc.floodgate.skin.SkinHandler; -import org.geysermc.floodgate.skin.SkinUploader.UploadResult; -import org.geysermc.floodgate.util.Base64Utils; -import org.geysermc.floodgate.util.RawSkin; public class SkinChannel implements PluginMessageChannel { - private static final Gson GSON = new Gson(); - @Inject private FloodgateApi api; - @Inject private PluginMessageUtils pluginMessageUtils; - @Inject private SkinHandler skinHandler; + @Inject private FloodgateConfig config; @Inject private SkinApplier skinApplier; - @Inject private FloodgateLogger logger; @Override public String getIdentifier() { @@ -71,58 +57,21 @@ public class SkinChannel implements PluginMessageChannel { String sourceUsername, Identity sourceIdentity) { - if (data.length < 1) { - return Result.kick("Got invalid Skin request/response"); - } - - boolean request = data[0] == 1; - - if (!request && data.length < 2) { - return Result.kick("Got invalid Skin response"); - } - - if (sourceIdentity == Identity.SERVER) { - if (request) { - return Result.kick("Got Skin request from Server?"); - } - - FloodgatePlayer floodgatePlayer = api.getPlayer(targetUuid); - - if (floodgatePlayer == null) { - return Result.kick("Server issued Skin request for non-Floodgate player"); - } - - // 1 = failed, 0 = successful. - - // we'll try it again on the next server if it failed - if (data[1] != 0) { - return Result.handled(); - } - - // we only have to continue if the player doesn't already have a skin uploaded - if (floodgatePlayer.hasProperty(PropertyKey.SKIN_UPLOADED)) { - return Result.handled(); - } - - byte[] responseData = new byte[data.length - 2]; - System.arraycopy(data, 2, responseData, 0, responseData.length); - - JsonObject response; - try { - Reader reader = new InputStreamReader(new ByteArrayInputStream(responseData)); - response = GSON.fromJson(reader, JsonObject.class); - } catch (JsonIOException | JsonSyntaxException throwable) { - logger.error("Failed to read Skin response", throwable); - return Result.handled(); - } - - floodgatePlayer.addProperty(PropertyKey.SKIN_UPLOADED, response); - skinApplier.applySkin(floodgatePlayer, UploadResult.success(response)); - } - - // Players (Geyser) can't send requests nor responses + // we can only get skins from Geyser (client) if (sourceIdentity == Identity.PLAYER) { - return Result.kick("Got Skin " + (request ? "request" : "response") + " from Player?"); + Result result = handleServerCall(data, targetUuid, targetUsername); + // aka translate 'handled' into 'forward' when send-floodgate-data is enabled + if (!result.isAllowed() && result.getReason() == null) { + if (config.isProxy() && ((ProxyFloodgateConfig) config).isSendFloodgateData()) { + return Result.forward(); + } + } + return result; + } + + // Servers can't send skin data + if (sourceIdentity == Identity.SERVER) { + return Result.kick("Got skin data from a server?"); } return Result.handled(); } @@ -131,75 +80,27 @@ public class SkinChannel implements PluginMessageChannel { public Result handleServerCall(byte[] data, UUID targetUuid, String targetUsername) { FloodgatePlayer floodgatePlayer = api.getPlayer(targetUuid); if (floodgatePlayer == null) { - return Result.kick("Non-Floodgate player sent a Skin plugin message"); + return Result.kick("Player sent skins data for a non-Floodgate player"); } - // non-proxy servers can only handle requests (from proxies) + String message = new String(data, StandardCharsets.UTF_8); - if (!floodgatePlayer.isFromProxy()) { - return Result.kick("Cannot receive Skin request from Player"); + String[] split = message.split("\0"); + // value and signature + if (split.length != 2) { + return Result.kick("Got invalid skin data"); } - // 1 byte for isRequest and 9 for RawSkin itself - if (data.length < Base64Utils.getEncodedLength(9 + 1)) { - return Result.kick("Skin request data has to be at least 10 byte long."); - } + String value = split[0]; + String signature = split[1]; - boolean request = data[0] == 1; + JsonObject result = new JsonObject(); + result.addProperty("value", value); + result.addProperty("signature", signature); - if (!request) { - return Result.kick("Proxy sent a response instead of a request?"); - } - - RawSkin rawSkin = null; - try { - rawSkin = RawSkin.decode(data, 1); - } catch (Exception exception) { - logger.error("Failed to decode RawSkin", exception); - } - - // we let it continue since SkinHandler sends the plugin message for us - skinHandler.handleServerSkinUpload(floodgatePlayer, rawSkin); + floodgatePlayer.addProperty(PropertyKey.SKIN_UPLOADED, result); + skinApplier.applySkin(floodgatePlayer, result); return Result.handled(); } - - public boolean sendSkinRequest(UUID player, RawSkin skin) { - byte[] skinRequestData = createSkinRequestData(skin.encode()); - return pluginMessageUtils.sendMessage(player, true, getIdentifier(), skinRequestData); - } - - public void sendSkinResponse(UUID player, boolean failed, String response) { - byte[] skinRequestData = createSkinResponseData(failed, response); - pluginMessageUtils.sendMessage(player, false, getIdentifier(), skinRequestData); - } - - public 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; - } - - public 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; - } } diff --git a/common/src/main/java/org/geysermc/floodgate/skin/SkinApplier.java b/common/src/main/java/org/geysermc/floodgate/skin/SkinApplier.java index 9a104a5a..a8366675 100644 --- a/common/src/main/java/org/geysermc/floodgate/skin/SkinApplier.java +++ b/common/src/main/java/org/geysermc/floodgate/skin/SkinApplier.java @@ -25,9 +25,9 @@ package org.geysermc.floodgate.skin; +import com.google.gson.JsonObject; import org.geysermc.floodgate.api.player.FloodgatePlayer; -import org.geysermc.floodgate.skin.SkinUploader.UploadResult; public interface SkinApplier { - void applySkin(FloodgatePlayer floodgatePlayer, UploadResult result); + void applySkin(FloodgatePlayer floodgatePlayer, JsonObject skinResult); } diff --git a/common/src/main/java/org/geysermc/floodgate/skin/SkinHandler.java b/common/src/main/java/org/geysermc/floodgate/skin/SkinHandler.java deleted file mode 100644 index 3015e460..00000000 --- a/common/src/main/java/org/geysermc/floodgate/skin/SkinHandler.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2019-2021 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 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.api.player.PropertyKey; -import org.geysermc.floodgate.pluginmessage.PluginMessageManager; -import org.geysermc.floodgate.pluginmessage.channel.SkinChannel; -import org.geysermc.floodgate.util.RawSkin; - -@RequiredArgsConstructor -public class SkinHandler { - private final PluginMessageManager pluginMessageManager; - private final SkinUploader uploader = new SkinUploader(); - private final SkinApplier skinApplier; - private final FloodgateLogger logger; - - 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); - - if (consumer != null) { - consumer.accept(true, throwable.getMessage()); - } - return; - } - - if (uploadResult.getError() != null) { - logger.error("Error while uploading player skin for {}: {}", - player.getCorrectUsername(), uploadResult.getError()); - - if (consumer != null) { - consumer.accept(true, uploadResult.getError()); - } - return; - } - - logger.info("Skin upload successful for " + player.getCorrectUsername()); - - if (consumer != null) { - consumer.accept(false, uploadResult.getResponse().toString()); - } - player.addProperty(PropertyKey.SKIN_UPLOADED, uploadResult.getResponse()); - - skinApplier.applySkin(player, uploadResult); - }); - } - - public void handleServerSkinUpload(FloodgatePlayer player) { - handleServerSkinUpload(player, player.getRawSkin()); - } - - public void handleServerSkinUpload(FloodgatePlayer player, RawSkin rawSkin) { - if (player == null || rawSkin == null) { - return; - } - - handleSkinUploadFor(player, rawSkin, - (failed, response) -> { - if (player.isFromProxy()) { - pluginMessageManager.getChannel(SkinChannel.class) - .sendSkinResponse(player.getCorrectUniqueId(), failed, response); - } - }); - } -} diff --git a/common/src/main/java/org/geysermc/floodgate/skin/SkinUploadManager.java b/common/src/main/java/org/geysermc/floodgate/skin/SkinUploadManager.java new file mode 100644 index 00000000..191d8dc9 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/skin/SkinUploadManager.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019-2021 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 it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import lombok.AllArgsConstructor; +import org.geysermc.floodgate.api.FloodgateApi; +import org.geysermc.floodgate.api.logger.FloodgateLogger; + +@AllArgsConstructor +public final class SkinUploadManager { + private final Int2ObjectMap connections = new Int2ObjectOpenHashMap<>(); + private final FloodgateApi api; + private final SkinApplier applier; + private final FloodgateLogger logger; + + public void addConnectionIfNeeded(int id, String verifyCode) { + connections.computeIfAbsent(id, (ignored) -> { + SkinUploadSocket socket = + new SkinUploadSocket(id, verifyCode, this, api, applier, logger); + socket.connect(); + return socket; + }); + } + + public void removeConnection(int id, SkinUploadSocket socket) { + connections.remove(id, socket); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/skin/SkinUploadSocket.java b/common/src/main/java/org/geysermc/floodgate/skin/SkinUploadSocket.java new file mode 100644 index 00000000..ac72d5f0 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/skin/SkinUploadSocket.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2019-2021 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 static org.geysermc.floodgate.util.Constants.WEBSOCKET_URL; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import java.net.URI; +import lombok.Getter; +import org.geysermc.floodgate.api.FloodgateApi; +import org.geysermc.floodgate.api.logger.FloodgateLogger; +import org.geysermc.floodgate.api.player.FloodgatePlayer; +import org.geysermc.floodgate.util.Utils; +import org.geysermc.floodgate.util.WebsocketEventType; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; + +final class SkinUploadSocket extends WebSocketClient { + private static final Gson gson = new Gson(); + + private final SkinUploadManager uploadManager; + private final FloodgateApi api; + private final SkinApplier applier; + private final FloodgateLogger logger; + + @Getter private final int id; + @Getter private final String verifyCode; + @Getter private int subscribersCount; + + public SkinUploadSocket( + int id, + String verifyCode, + SkinUploadManager uploadManager, + FloodgateApi api, + SkinApplier applier, + FloodgateLogger logger) { + + super(getWebsocketUri(id, verifyCode)); + this.id = id; + this.verifyCode = verifyCode; + this.uploadManager = uploadManager; + this.api = api; + this.applier = applier; + this.logger = logger; + } + + private static URI getWebsocketUri(int id, String verifyCode) { + try { + return new URI(WEBSOCKET_URL + "?subscribed_to=" + id + "&verify_code=" + verifyCode); + } catch (Exception exception) { + throw new RuntimeException( + "Error while creating uri. Id = " + id + ", verify_code = " + verifyCode, + exception); + } + } + + @Override + public void onOpen(ServerHandshake handshakedata) { + setConnectionLostTimeout(11); + } + + @Override + public void onMessage(String data) { + JsonObject message = gson.fromJson(data, JsonObject.class); + if (message.has("error")) { + logger.error("Skin uploader got an error: {}", message.get("error").getAsString()); + } + + int typeId = message.get("event_id").getAsInt(); + WebsocketEventType type = WebsocketEventType.getById(typeId); + if (type == null) { + logger.warn("Got unknown type {}. Ensure that Floodgate is up-to-date", typeId); + return; + } + + if (type == WebsocketEventType.SUBSCRIBERS_COUNT) { + subscribersCount = message.get("subscribers_count").getAsInt(); + } else if (type == WebsocketEventType.SKIN_UPLOADED) { + String xuid = message.get("xuid").getAsString(); + FloodgatePlayer player = api.getPlayer(Utils.getJavaUuid(xuid)); + if (player != null) { + applier.applySkin(player, message); + } + } + } + + @Override + public void onClose(int code, String reason, boolean remote) { + if (reason != null && !reason.isEmpty()) { + JsonObject message = gson.fromJson(reason, JsonObject.class); + + // info means that the uploader itself did nothing wrong + if (message.has("info")) { + String info = message.get("info").getAsString(); + logger.debug("Got disconnected from the skin uploader: {}", info); + } + + // error means that the uploader did something wrong + if (message.has("error")) { + String error = message.get("error").getAsString(); + logger.info("Got disconnected from the skin uploader: {}", error); + } + } + + uploadManager.removeConnection(id, this); + } + + @Override + public void onError(Exception exception) { + logger.error("Got an error", exception); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/skin/SkinUploader.java b/common/src/main/java/org/geysermc/floodgate/skin/SkinUploader.java deleted file mode 100644 index 52832cb7..00000000 --- a/common/src/main/java/org/geysermc/floodgate/skin/SkinUploader.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2019-2021 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 com.google.gson.JsonObject; -import java.awt.image.BufferedImage; -import java.util.Locale; -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; -import org.geysermc.floodgate.util.HttpUtils; -import org.geysermc.floodgate.util.HttpUtils.HttpResponse; -import org.geysermc.floodgate.util.RawSkin; - -public final class SkinUploader { - private static final String UPLOAD_URL = "https://api.mineskin.org/generate/upload"; - private static final int MAX_TRIES = 3; - - private final Executor requestExecutor = Executors.newSingleThreadExecutor(); - private long nextResult; - - public CompletableFuture uploadSkin(@Nonnull RawSkin rawSkin) { - return CompletableFuture.supplyAsync(() -> uploadSkinInner(rawSkin, 0), requestExecutor); - } - - private UploadResult uploadSkinInner(RawSkin rawSkin, int times) { - if (System.currentTimeMillis() < nextResult) { - try { - Thread.sleep(nextResult - System.currentTimeMillis()); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - SkinModel model = rawSkin.alex ? SkinModel.ALEX : SkinModel.STEVE; - - String url = UPLOAD_URL + getUploadUrlParameters(model); - BufferedImage image = SkinUtils.toBufferedImage(rawSkin); - - try { - UploadResult result = parseAndHandleResponse(HttpUtils.post(url, image)); - if (result.getHttpCode() == 429) { - if (times + 1 >= MAX_TRIES) { - return result; - } - uploadSkinInner(rawSkin, times + 1); - } - return result; - } catch (RuntimeException exception) { - return UploadResult.exception(exception); - } - } - - private String getUploadUrlParameters(SkinModel model) { - return "?visibility=1&model=" + model.getName(); - } - - private UploadResult parseAndHandleResponse(HttpResponse response) { - int httpCode = response.getHttpCode(); - JsonObject jsonResponse = response.getResponse(); - - if (jsonResponse == null) { - throw new IllegalStateException("Response cannot be null!"); - } - - nextResult = jsonResponse.get("nextRequest").getAsLong(); - - if (httpCode >= 200 && httpCode < 300) { - return UploadResult.success(httpCode, jsonResponse); - } else { - return UploadResult.failed(httpCode, jsonResponse.get("error").getAsString()); - } - } - - public enum SkinModel { - STEVE, ALEX; - - public static final SkinModel[] VALUES = values(); - - @Getter private final String name = name().toLowerCase(Locale.ROOT); - - public static SkinModel getByOrdinal(int ordinal) { - return VALUES.length > ordinal ? VALUES[ordinal] : STEVE; - } - - public static SkinModel getByName(String name) { - return "alex".equalsIgnoreCase(name) ? ALEX : STEVE; - } - } - - @Getter - @AllArgsConstructor(access = AccessLevel.PRIVATE) - public static final class UploadResult { - private final int httpCode; - private final String error; - private final boolean exception; - - private final SkinModel model; - private final String skinUrl; - private final String capeUrl; - private final JsonObject response; - - public static UploadResult exception(Throwable throwable) { - return new UploadResult(-1, throwable.getMessage(), true, null, null, null, null); - } - - public static UploadResult failed(int httpCode, String error) { - return new UploadResult(httpCode, error, false, SkinModel.STEVE, null, null, null); - } - - public static UploadResult success(int httpCode, JsonObject body) { - SkinModel model = SkinModel.getByName(body.get("model").getAsString()); - - JsonObject data = body.getAsJsonObject("data"); - JsonObject textureData = data.getAsJsonObject("texture"); - - JsonObject urls = textureData.getAsJsonObject("urls"); - String skinUrl = urls.get("skin").getAsString(); - String capeUrl = urls.has("cape") ? urls.get("cape").getAsString() : null; - - JsonObject response = new JsonObject(); - response.addProperty("value", textureData.get("value").getAsString()); - response.addProperty("signature", textureData.get("signature").getAsString()); - - 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/skin/SkinUtils.java b/common/src/main/java/org/geysermc/floodgate/skin/SkinUtils.java deleted file mode 100644 index e5b51290..00000000 --- a/common/src/main/java/org/geysermc/floodgate/skin/SkinUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2019-2021 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 java.awt.image.BufferedImage; -import org.geysermc.floodgate.util.RawSkin; - -public class SkinUtils { - /** - * Get the ARGB int for a given index in some image data - * - * @param index Index to get - * @param data Image data to find in - * @return An int representing ARGB - */ - private static int getARGB(int index, byte[] data) { - return (data[index + 3] & 0xFF) << 24 | (data[index] & 0xFF) << 16 | - (data[index + 1] & 0xFF) << 8 | (data[index + 2] & 0xFF); - } - - /** - * Convert a byte[] to a BufferedImage - * - * @param imageData The byte[] to convert - * @param width The width of the target image - * @param height The height of the target image - * @return The converted BufferedImage - */ - public static BufferedImage toBufferedImage(byte[] imageData, int width, int height) { - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - image.setRGB(x, y, getARGB((y * width + x) * 4, imageData)); - } - } - return image; - } - - public static BufferedImage toBufferedImage(RawSkin rawSkin) { - return toBufferedImage(rawSkin.data, rawSkin.width, rawSkin.height); - } -} diff --git a/common/src/main/java/org/geysermc/floodgate/util/Constants.java b/common/src/main/java/org/geysermc/floodgate/util/Constants.java index b6909532..f2988c63 100644 --- a/common/src/main/java/org/geysermc/floodgate/util/Constants.java +++ b/common/src/main/java/org/geysermc/floodgate/util/Constants.java @@ -28,6 +28,7 @@ package org.geysermc.floodgate.util; public final class Constants { public static final String DATABASE_NAME_FORMAT = "^floodgate-[a-zA-Z0-9_]{0,16}-database.jar$"; public static final int LOGIN_SUCCESS_PACKET_ID = 2; + public static final String WEBSOCKET_URL = "wss://api.geysermc.org/ws"; public static final boolean DEBUG_MODE = true; } diff --git a/common/src/main/resources/config.yml b/common/src/main/resources/config.yml index f9f74d5e..6b484be0 100644 --- a/common/src/main/resources/config.yml +++ b/common/src/main/resources/config.yml @@ -11,10 +11,6 @@ username-prefix: "*" # Should spaces be replaced with '_' in bedrock usernames? replace-spaces: true -# Should Floodgate apply the transferred Bedrock to Java skin directly to all Java players? -# This might cause some -apply-skin-directly: true - # The default locale for Floodgate. By default, Floodgate uses the system locale # default-locale: en_US 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 33d9d5e9..4210c959 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java +++ b/spigot/src/main/java/org/geysermc/floodgate/listener/SpigotListener.java @@ -36,13 +36,11 @@ import org.geysermc.floodgate.api.SimpleFloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.player.FloodgatePlayerImpl; -import org.geysermc.floodgate.skin.SkinHandler; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.SpigotCommandUtil; public final class SpigotListener implements Listener { @Inject private SimpleFloodgateApi api; - @Inject private SkinHandler skinHandler; @Inject private LanguageManager languageManager; @Inject private FloodgateLogger logger; @@ -62,7 +60,6 @@ public final class SpigotListener implements Listener { "floodgate.ingame.login_name", player.getCorrectUsername(), player.getCorrectUniqueId() ); - skinHandler.handleServerSkinUpload(player); languageManager.loadLocale(player.getLanguageCode()); } } 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 3c5c3b9f..c84a23b9 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java +++ b/spigot/src/main/java/org/geysermc/floodgate/module/SpigotPlatformModule.java @@ -35,7 +35,6 @@ import org.bukkit.plugin.java.JavaPlugin; import org.geysermc.floodgate.SpigotPlugin; import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.logger.FloodgateLogger; -import org.geysermc.floodgate.config.FloodgateConfigHolder; import org.geysermc.floodgate.inject.CommonPlatformInjector; import org.geysermc.floodgate.inject.spigot.SpigotInjector; import org.geysermc.floodgate.listener.SpigotListenerRegistration; @@ -141,10 +140,8 @@ public final class SpigotPlatformModule extends AbstractModule { @Provides @Singleton - public SkinApplier skinApplier( - SpigotVersionSpecificMethods versionSpecificMethods, - FloodgateConfigHolder configHolder) { - return new SpigotSkinApplier(versionSpecificMethods, plugin, configHolder); + public SkinApplier skinApplier(SpigotVersionSpecificMethods versionSpecificMethods) { + return new SpigotSkinApplier(versionSpecificMethods, plugin); } @Provides diff --git a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageRegistration.java b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageRegistration.java index 4efab165..c9197c2b 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageRegistration.java +++ b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotPluginMessageRegistration.java @@ -43,6 +43,8 @@ public class SpigotPluginMessageRegistration implements PluginMessageRegistratio (channel1, player, message) -> channel.handleServerCall(message, player.getUniqueId(), player.getName())); + //todo actually do something with the result, lol + messenger.registerOutgoingPluginChannel(plugin, channel.getIdentifier()); } } diff --git a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java index b8e9d3fc..934d228b 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java +++ b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java @@ -34,9 +34,7 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.geysermc.floodgate.SpigotPlugin; import org.geysermc.floodgate.api.player.FloodgatePlayer; -import org.geysermc.floodgate.config.FloodgateConfigHolder; 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; @@ -52,19 +50,16 @@ public final class SpigotSkinApplier implements SkinApplier { private final SpigotVersionSpecificMethods versionSpecificMethods; private final SpigotPlugin plugin; - private final FloodgateConfigHolder configHolder; public SpigotSkinApplier( SpigotVersionSpecificMethods versionSpecificMethods, - SpigotPlugin plugin, - FloodgateConfigHolder configHolder) { + SpigotPlugin plugin) { this.versionSpecificMethods = versionSpecificMethods; this.plugin = plugin; - this.configHolder = configHolder; } @Override - public void applySkin(FloodgatePlayer floodgatePlayer, UploadResult result) { + public void applySkin(FloodgatePlayer floodgatePlayer, JsonObject skinResult) { Player player = Bukkit.getPlayer(floodgatePlayer.getCorrectUniqueId()); GameProfile profile = ReflectionUtils.castedInvoke(player, GET_PROFILE_METHOD); @@ -72,28 +67,24 @@ public final class SpigotSkinApplier implements SkinApplier { throw new IllegalStateException("The GameProfile cannot be null! " + player.getName()); } - JsonObject response = result.getResponse(); - PropertyMap properties = profile.getProperties(); //todo check if removing all texture properties breaks some stuff properties.removeAll("textures"); Property property = new Property( "textures", - response.get("value").getAsString(), - response.get("signature").getAsString()); + skinResult.get("value").getAsString(), + skinResult.get("signature").getAsString()); properties.put("textures", property); - if (configHolder.get().isApplySkinDirectly()) { - // By running as a task, we don't run into async issues - plugin.getServer().getScheduler().runTask(plugin, () -> { - for (Player p : Bukkit.getOnlinePlayers()) { - if (!p.equals(player) && p.canSee(player)) { - versionSpecificMethods.hidePlayer(p, player); - versionSpecificMethods.showPlayer(p, player); - } + // By running as a task, we don't run into async issues + plugin.getServer().getScheduler().runTask(plugin, () -> { + for (Player p : Bukkit.getOnlinePlayers()) { + if (!p.equals(player) && p.canSee(player)) { + versionSpecificMethods.hidePlayer(p, player); + versionSpecificMethods.showPlayer(p, player); } - }); - } + } + }); } } diff --git a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java index 02187265..f879c4ef 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java +++ b/velocity/src/main/java/org/geysermc/floodgate/listener/VelocityListener.java @@ -40,7 +40,6 @@ import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.LoginEvent; import com.velocitypowered.api.event.connection.PreLoginEvent; import com.velocitypowered.api.event.player.GameProfileRequestEvent; -import com.velocitypowered.api.event.player.ServerPostConnectEvent; import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.util.GameProfile; import io.netty.channel.Channel; @@ -52,11 +51,6 @@ import net.kyori.adventure.text.Component; 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.pluginmessage.PluginMessageManager; -import org.geysermc.floodgate.pluginmessage.channel.SkinChannel; -import org.geysermc.floodgate.skin.SkinHandler; import org.geysermc.floodgate.util.LanguageManager; import org.geysermc.floodgate.util.VelocityCommandUtil; @@ -82,10 +76,6 @@ public final class VelocityListener { @Inject private LanguageManager languageManager; @Inject private FloodgateLogger logger; - @Inject private ProxyFloodgateConfig config; - @Inject private PluginMessageManager pluginMessageManager; - @Inject private SkinHandler skinHandler; - @Inject @Named("playerAttribute") private AttributeKey playerAttribute; @@ -142,27 +132,6 @@ public final class VelocityListener { } } - @Subscribe - public void onServerPostConnect(ServerPostConnectEvent event) { - FloodgatePlayer player = api.getPlayer(event.getPlayer().getUniqueId()); - if (player == null) { - return; - } - - // only ask for skin upload if it hasn't been uploaded already - if (player.hasProperty(PropertyKey.SKIN_UPLOADED)) { - return; - } - - // send skin request to server if data forwarding allows that - if (config.isSendFloodgateData()) { - pluginMessageManager.getChannel(SkinChannel.class) - .sendSkinRequest(player.getCorrectUniqueId(), player.getRawSkin()); - } else { - skinHandler.handleSkinUploadFor(player, null); - } - } - @Subscribe(order = PostOrder.LAST) public void onDisconnect(DisconnectEvent event) { VelocityCommandUtil.AUDIENCE_CACHE.remove(event.getPlayer().getUniqueId()); //todo diff --git a/velocity/src/main/java/org/geysermc/floodgate/util/VelocitySkinApplier.java b/velocity/src/main/java/org/geysermc/floodgate/util/VelocitySkinApplier.java index ec391700..f4a1a25e 100644 --- a/velocity/src/main/java/org/geysermc/floodgate/util/VelocitySkinApplier.java +++ b/velocity/src/main/java/org/geysermc/floodgate/util/VelocitySkinApplier.java @@ -33,22 +33,19 @@ import java.util.List; import lombok.RequiredArgsConstructor; import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.skin.SkinApplier; -import org.geysermc.floodgate.skin.SkinUploader.UploadResult; @RequiredArgsConstructor public class VelocitySkinApplier implements SkinApplier { private final ProxyServer server; @Override - public void applySkin(FloodgatePlayer floodgatePlayer, UploadResult result) { + public void applySkin(FloodgatePlayer floodgatePlayer, JsonObject skinResult) { server.getPlayer(floodgatePlayer.getCorrectUniqueId()).ifPresent(player -> { - JsonObject response = result.getResponse(); - List properties = new ArrayList<>(player.getGameProfileProperties()); properties.add(new Property( "textures", - response.get("value").getAsString(), - response.get("signature").getAsString() + skinResult.get("value").getAsString(), + skinResult.get("signature").getAsString() )); player.setGameProfileProperties(properties); });