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);
});