diff --git a/.gitignore b/.gitignore index f003e014..9b233578 100644 --- a/.gitignore +++ b/.gitignore @@ -224,3 +224,4 @@ nbdist/ ### Geyser ### config.yml logs/ +public-key.pem diff --git a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java index e928b9c8..8a2a7a9a 100644 --- a/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java +++ b/bootstrap/bukkit/src/main/java/org/geysermc/platform/bukkit/GeyserBukkitConfiguration.java @@ -96,6 +96,11 @@ public class GeyserBukkitConfiguration implements IGeyserConfiguration { return config.getBoolean("allow-third-party-capes", true); } + @Override + public String getFloodgateKeyFile() { + return config.getString("floodgate-key-file", "public-key.pem"); + } + @Override public IMetricsInfo getMetrics() { return metricsInfo; diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java index 931a09fa..43c77733 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java @@ -97,6 +97,11 @@ public class GeyserBungeeConfiguration implements IGeyserConfiguration { return config.getBoolean("allow-third-party-capes", true); } + @Override + public String getFloodgateKeyFile() { + return config.getString("floodgate-key-file", "public-key.pem"); + } + @Override public BungeeMetricsInfo getMetrics() { return metricsInfo; diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java index 9bb8a366..1b30c439 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java @@ -100,6 +100,11 @@ public class GeyserSpongeConfiguration implements IGeyserConfiguration { return node.getNode("allow-third-party-capes").getBoolean(true); } + @Override + public String getFloodgateKeyFile() { + return node.getNode("floodgate-key-file").getString("public-key.pem"); + } + @Override public SpongeMetricsInfo getMetrics() { return metricsInfo; diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserConfiguration.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserConfiguration.java index e1a442d7..3f7f59cf 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserConfiguration.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserConfiguration.java @@ -39,6 +39,9 @@ public class GeyserConfiguration implements IGeyserConfiguration { private BedrockConfiguration bedrock; private RemoteConfiguration remote; + @JsonProperty("floodgate-key-file") + private String floodgateKeyFile; + private Map userAuths; @JsonProperty("ping-passthrough") diff --git a/common/src/main/java/org/geysermc/common/AuthType.java b/common/src/main/java/org/geysermc/common/AuthType.java new file mode 100644 index 00000000..bec51560 --- /dev/null +++ b/common/src/main/java/org/geysermc/common/AuthType.java @@ -0,0 +1,26 @@ +package org.geysermc.common; + +import lombok.Getter; + +@Getter +public enum AuthType { + OFFLINE, + ONLINE, + FLOODGATE; + + public static final AuthType[] VALUES = values(); + + public static AuthType getById(int id) { + return id < VALUES.length ? VALUES[id] : OFFLINE; + } + + public static AuthType getByName(String name) { + String upperCase = name.toUpperCase(); + for (AuthType type : VALUES) { + if (type.name().equals(upperCase)) { + return type; + } + } + return OFFLINE; + } +} \ No newline at end of file diff --git a/common/src/main/java/org/geysermc/common/IGeyserConfiguration.java b/common/src/main/java/org/geysermc/common/IGeyserConfiguration.java index 0ea120c5..1414e6c2 100644 --- a/common/src/main/java/org/geysermc/common/IGeyserConfiguration.java +++ b/common/src/main/java/org/geysermc/common/IGeyserConfiguration.java @@ -45,6 +45,8 @@ public interface IGeyserConfiguration { boolean isAllowThirdPartyCapes(); + String getFloodgateKeyFile(); + IMetricsInfo getMetrics(); interface IBedrockConfiguration { diff --git a/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java b/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java new file mode 100644 index 00000000..8beeb998 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java @@ -0,0 +1,44 @@ +package org.geysermc.floodgate.util; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class BedrockData { + public static final int EXPECTED_LENGTH = 7; + public static final String FLOODGATE_IDENTIFIER = "Geyser-Floodgate"; + + private String version; + private String username; + private String xuid; + private int deviceId; + private String languageCode; + private int inputMode; + private String ip; + private int dataLength; + + public BedrockData(String version, String username, String xuid, int deviceId, String languageCode, int inputMode, String ip) { + this(version, username, xuid, deviceId, languageCode, inputMode, ip, EXPECTED_LENGTH); + } + + public static BedrockData fromString(String data) { + String[] split = data.split("\0"); + if (split.length != EXPECTED_LENGTH) return null; + + return new BedrockData( + split[0], split[1], split[2], Integer.parseInt(split[3]), + split[4], Integer.parseInt(split[5]), split[6], split.length + ); + } + + public static BedrockData fromRawData(byte[] data) { + return fromString(new String(data)); + } + + @Override + public String toString() { + return version +'\0'+ username +'\0'+ xuid +'\0'+ deviceId +'\0'+ languageCode +'\0'+ + inputMode +'\0'+ ip; + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/util/EncryptionUtil.java b/common/src/main/java/org/geysermc/floodgate/util/EncryptionUtil.java new file mode 100644 index 00000000..881d01ba --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/EncryptionUtil.java @@ -0,0 +1,76 @@ +package org.geysermc.floodgate.util; + +import javax.crypto.*; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.*; +import java.security.spec.EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +public class EncryptionUtil { + public static String encrypt(Key key, String data) throws IllegalBlockSizeException, + InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { + KeyGenerator generator = KeyGenerator.getInstance("AES"); + generator.init(128); + SecretKey secretKey = generator.generateKey(); + + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + byte[] encryptedText = cipher.doFinal(data.getBytes()); + + cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(key instanceof PublicKey ? Cipher.PUBLIC_KEY : Cipher.PRIVATE_KEY, key); + return Base64.getEncoder().encodeToString(cipher.doFinal(secretKey.getEncoded())) + '\0' + + Base64.getEncoder().encodeToString(encryptedText); + } + + public static String encryptBedrockData(Key key, BedrockData data) throws IllegalBlockSizeException, + InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { + return encrypt(key, data.toString()); + } + + public static byte[] decrypt(Key key, String encryptedData) throws IllegalBlockSizeException, + InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { + String[] split = encryptedData.split("\0"); + if (split.length != 2) { + throw new IllegalArgumentException("Expected two arguments, got " + split.length); + } + + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(key instanceof PublicKey ? Cipher.PUBLIC_KEY : Cipher.PRIVATE_KEY, key); + byte[] decryptedKey = cipher.doFinal(Base64.getDecoder().decode(split[0])); + + SecretKey secretKey = new SecretKeySpec(decryptedKey, 0, decryptedKey.length, "AES"); + cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return cipher.doFinal(Base64.getDecoder().decode(split[1])); + } + + public static BedrockData decryptBedrockData(Key key, String encryptedData) throws IllegalBlockSizeException, + InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { + return BedrockData.fromRawData(decrypt(key, encryptedData)); + } + + @SuppressWarnings("unchecked") + public static T getKeyFromFile(Path fileLocation, Class keyType) throws + IOException, InvalidKeySpecException, NoSuchAlgorithmException { + boolean isPublicKey = keyType == PublicKey.class; + if (!isPublicKey && keyType != PrivateKey.class) { + throw new RuntimeException("I can only read public and private keys!"); + } + + byte[] key = Files.readAllBytes(fileLocation); + + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + EncodedKeySpec keySpec = isPublicKey ? new X509EncodedKeySpec(key) : new PKCS8EncodedKeySpec(key); + return (T) (isPublicKey ? + keyFactory.generatePublic(keySpec) : + keyFactory.generatePrivate(keySpec) + ); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 0885d6d0..048b31b3 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.v389.Bedrock_v389; import lombok.Getter; +import org.geysermc.common.AuthType; import org.geysermc.common.PlatformType; import org.geysermc.common.bootstrap.IGeyserBootstrap; import org.geysermc.common.logger.IGeyserLogger; @@ -65,6 +66,7 @@ public class GeyserConnector { private static GeyserConnector instance; private RemoteServer remoteServer; + private AuthType authType; private IGeyserLogger logger; private IGeyserConfiguration config; @@ -105,6 +107,7 @@ public class GeyserConnector { commandMap = new GeyserCommandMap(this); remoteServer = new RemoteServer(config.getRemote().getAddress(), config.getRemote().getPort()); + authType = AuthType.getByName(config.getRemote().getAuthType()); passthroughThread = new PingPassthroughThread(this); if (config.isPingPassthrough()) @@ -125,7 +128,7 @@ public class GeyserConnector { metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger("")); metrics.addCustomChart(new Metrics.SingleLineChart("servers", () -> 1)); metrics.addCustomChart(new Metrics.SingleLineChart("players", players::size)); - metrics.addCustomChart(new Metrics.SimplePie("authMode", config.getRemote()::getAuthType)); + metrics.addCustomChart(new Metrics.SimplePie("authMode", authType.name()::toLowerCase)); metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName)); } diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 2e75da67..75441b79 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -126,9 +126,4 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { boolean defaultHandler(BedrockPacket packet) { return translateAndDefault(packet); } - - @Override - public boolean handle(InventoryTransactionPacket packet) { - return translateAndDefault(packet); - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 2e44a33c..73ad3a56 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -29,11 +29,9 @@ import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.auth.exception.request.RequestException; import com.github.steveice10.mc.protocol.MinecraftProtocol; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.packet.handshake.client.HandshakePacket; import com.github.steveice10.packetlib.Client; -import com.github.steveice10.packetlib.event.session.ConnectedEvent; -import com.github.steveice10.packetlib.event.session.DisconnectedEvent; -import com.github.steveice10.packetlib.event.session.PacketReceivedEvent; -import com.github.steveice10.packetlib.event.session.SessionAdapter; +import com.github.steveice10.packetlib.event.session.*; import com.github.steveice10.packetlib.packet.Packet; import com.github.steveice10.packetlib.tcp.TcpSessionFactory; import com.nukkitx.math.vector.Vector2f; @@ -49,6 +47,7 @@ import com.nukkitx.protocol.bedrock.packet.*; import lombok.Getter; import lombok.Setter; +import org.geysermc.common.AuthType; import org.geysermc.common.window.FormWindow; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; @@ -56,12 +55,20 @@ import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.inventory.PlayerInventory; import org.geysermc.connector.network.remote.RemoteServer; import org.geysermc.connector.network.session.auth.AuthData; +import org.geysermc.connector.network.session.auth.BedrockClientData; import org.geysermc.connector.network.session.cache.*; import org.geysermc.connector.network.translators.Registry; import org.geysermc.connector.network.translators.TranslatorsInit; import org.geysermc.connector.utils.Toolbox; +import org.geysermc.floodgate.util.BedrockData; +import org.geysermc.floodgate.util.EncryptionUtil; +import java.io.IOException; import java.net.InetSocketAddress; +import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; import java.util.UUID; @Getter @@ -71,7 +78,8 @@ public class GeyserSession implements CommandSender { private final UpstreamSession upstream; private RemoteServer remoteServer; private Client downstream; - private AuthData authData; + @Setter private AuthData authData; + @Setter private BedrockClientData clientData; private PlayerEntity playerEntity; private PlayerInventory inventory; @@ -123,8 +131,13 @@ public class GeyserSession implements CommandSender { public void connect(RemoteServer remoteServer) { startGame(); this.remoteServer = remoteServer; - if (!(connector.getConfig().getRemote().getAuthType().hashCode() == "online".hashCode())) { - connector.getLogger().info("Attempting to login using offline mode... authentication is disabled."); + if (connector.getAuthType() != AuthType.ONLINE) { + connector.getLogger().info( + "Attempting to login using " + connector.getAuthType().name().toLowerCase() + " mode... " + + (connector.getAuthType() == AuthType.OFFLINE ? + "authentication is disabled." : "authentication will be encrypted" + ) + ); authenticate(authData.getName()); } @@ -177,8 +190,56 @@ public class GeyserSession implements CommandSender { protocol = new MinecraftProtocol(username); } + boolean floodgate = connector.getAuthType() == AuthType.FLOODGATE; + final PublicKey publicKey; + + if (floodgate) { + PublicKey key = null; + try { + key = EncryptionUtil.getKeyFromFile( + Paths.get(connector.getConfig().getFloodgateKeyFile()), + PublicKey.class + ); + } catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException e) { + connector.getLogger().error("Error while reading Floodgate key file", e); + } + publicKey = key; + } else publicKey = null; + + if (publicKey != null) { + connector.getLogger().info("Loaded Floodgate key!"); + } + downstream = new Client(remoteServer.getAddress(), remoteServer.getPort(), protocol, new TcpSessionFactory()); downstream.getSession().addListener(new SessionAdapter() { + @Override + public void packetSending(PacketSendingEvent event) { + //todo move this somewhere else + if (event.getPacket() instanceof HandshakePacket && floodgate) { + String encrypted = ""; + try { + encrypted = EncryptionUtil.encryptBedrockData(publicKey, new BedrockData( + clientData.getGameVersion(), + authData.getName(), + authData.getXboxUUID(), + clientData.getDeviceOS().ordinal(), + clientData.getLanguageCode(), + clientData.getCurrentInputMode().ordinal(), + upstream.getSession().getAddress().getAddress().getHostAddress() + )); + } catch (Exception e) { + connector.getLogger().error("Failed to encrypt message", e); + } + + HandshakePacket handshakePacket = event.getPacket(); + event.setPacket(new HandshakePacket( + handshakePacket.getProtocolVersion(), + handshakePacket.getHostname() + '\0' + BedrockData.FLOODGATE_IDENTIFIER + '\0' + encrypted, + handshakePacket.getPort(), + handshakePacket.getIntent() + )); + } + } @Override public void connected(ConnectedEvent event) { @@ -227,10 +288,6 @@ public class GeyserSession implements CommandSender { closed = true; } - public boolean isClosed() { - return closed; - } - public void close() { disconnect("Server closed."); } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/auth/BedrockClientData.java b/connector/src/main/java/org/geysermc/connector/network/session/auth/BedrockClientData.java new file mode 100644 index 00000000..574ba544 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/session/auth/BedrockClientData.java @@ -0,0 +1,91 @@ +package org.geysermc.connector.network.session.auth; + +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +import java.util.UUID; + +@JsonIgnoreProperties(ignoreUnknown = true) +@Getter +public class BedrockClientData { + @JsonProperty(value = "GameVersion") + private String gameVersion; + @JsonProperty(value = "ServerAddress") + private String serverAddress; + @JsonProperty(value = "ThirdPartyName") + private String username; + @JsonProperty(value = "LanguageCode") + private String languageCode; + + @JsonProperty(value = "SkinId") + private String skinId; + @JsonProperty(value = "SkinData") + private String skinData; + @JsonProperty(value = "CapeId") + private String capeId; + @JsonProperty(value = "CapeData") + private byte[] capeData; + @JsonProperty(value = "CapeOnClassicSkin") + private boolean capeOnClassicSkin; + @JsonProperty(value = "SkinResourcePatch") + private String geometryName; + @JsonProperty(value = "SkinGeometryData") + private String geometryData; + @JsonProperty(value = "PremiumSkin") + private boolean premiumSkin; + + @JsonProperty(value = "DeviceId") + private String deviceId; + @JsonProperty(value = "DeviceModel") + private String deviceModel; + @JsonProperty(value = "DeviceOS") + private DeviceOS deviceOS; + @JsonProperty(value = "UIProfile") + private UIProfile uiProfile; + @JsonProperty(value = "GuiScale") + private int guiScale; + @JsonProperty(value = "CurrentInputMode") + private InputMode currentInputMode; + @JsonProperty(value = "DefaultInputMode") + private InputMode defaultInputMode; + @JsonProperty("PlatformOnlineId") + private String platformOnlineId; + @JsonProperty(value = "PlatformOfflineId") + private String platformOfflineId; + @JsonProperty(value = "SelfSignedId") + private UUID selfSignedId; + @JsonProperty(value = "ClientRandomId") + private long clientRandomId; + + public enum UIProfile { + @JsonEnumDefaultValue + CLASSIC, + POCKET + } + + public enum DeviceOS { + @JsonEnumDefaultValue + UNKOWN, + ANDROID, + IOS, + OSX, + FIREOS, + GEARVR, + HOLOLENS, + WIN10, + WIN32, + DEDICATED, + ORBIS, + NX + } + + public enum InputMode { + @JsonEnumDefaultValue + UNKNOWN, + KEYBOARD_MOUSE, + TOUCH, // I guess Touch? + CONTROLLER + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index ea799df6..e7479ee5 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -21,6 +21,7 @@ import org.geysermc.common.window.response.CustomFormResponse; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.auth.AuthData; +import org.geysermc.connector.network.session.auth.BedrockClientData; import org.geysermc.connector.network.session.cache.WindowCache; import javax.crypto.SecretKey; @@ -73,7 +74,7 @@ public class LoginEncryptionUtils { encryptConnectionWithCert(connector, session, loginPacket.getSkinData().toString(), certChainData); } - private static void encryptConnectionWithCert(GeyserConnector connector, GeyserSession session, String playerSkin, JsonNode certChainData) { + private static void encryptConnectionWithCert(GeyserConnector connector, GeyserSession session, String clientData, JsonNode certChainData) { try { boolean validChain = validateChainData(certChainData); @@ -86,17 +87,23 @@ public class LoginEncryptionUtils { throw new RuntimeException("AuthData was not found!"); } - JSONObject extraData = (JSONObject) jwt.getPayload().toJSONObject().get("extraData"); - session.setAuthenticationData(new AuthData(extraData.getAsString("displayName"), UUID.fromString(extraData.getAsString("identity")), extraData.getAsString("XUID"))); + JsonNode extraData = payload.get("extraData"); + session.setAuthenticationData(new AuthData( + extraData.get("displayName").asText(), + UUID.fromString(extraData.get("identity").asText()), + extraData.get("XUID").asText() + )); if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) { throw new RuntimeException("Identity Public Key was not found!"); } ECPublicKey identityPublicKey = EncryptionUtils.generateKey(payload.get("identityPublicKey").textValue()); - JWSObject clientJwt = JWSObject.parse(playerSkin); + JWSObject clientJwt = JWSObject.parse(clientData); EncryptionUtils.verifyJwt(clientJwt, identityPublicKey); + session.setClientData(JSON_MAPPER.convertValue(JSON_MAPPER.readTree(clientJwt.getPayload().toBytes()), BedrockClientData.class)); + if (EncryptionUtils.canUseEncryption()) { LoginEncryptionUtils.startEncryptionHandshake(session, identityPublicKey); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java index 3d3e27fe..f8827459 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -9,6 +9,7 @@ import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; import lombok.AllArgsConstructor; import lombok.Getter; +import org.geysermc.common.AuthType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.network.session.GeyserSession; @@ -20,6 +21,7 @@ import java.util.UUID; import java.util.function.Consumer; public class SkinUtils { + public static PlayerListPacket.Entry buildCachedEntry(GameProfile profile, long geyserId) { GameProfileData data = GameProfileData.from(profile); SkinProvider.Cape cape = SkinProvider.getCachedCape(data.getCapeUrl()); @@ -97,7 +99,7 @@ public class SkinUtils { return new GameProfileData(skinUrl, capeUrl, isAlex); } catch (Exception exception) { - if (!GeyserConnector.getInstance().getConfig().getRemote().getAuthType().equals("offline")) { + if (GeyserConnector.getInstance().getAuthType() != AuthType.OFFLINE) { GeyserConnector.getInstance().getLogger().debug("Got invalid texture data for " + profile.getName() + " " + exception.getMessage()); } // return default skin with default cape when texture data is invalid @@ -157,6 +159,7 @@ public class SkinUtils { if (skinAndCapeConsumer != null) skinAndCapeConsumer.accept(skinAndCape); }); + }); } diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index f92d6d42..029a4619 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -20,10 +20,14 @@ remote: address: 127.0.0.1 # The port of the remote (Java Edition) server port: 25565 - - # Authentication type. Can be offline, online, or hybrid (see the wiki). + # Authentication type. Can be offline, online, or floodgate (see the wiki). auth-type: online +# Floodgate uses encryption to ensure use from authorised sources. +# This should point to the public key generated by Floodgate (Bungee or CraftBukkit) +# You can ignore this when not using Floodgate. +floodgate-key-file: public-key.pem + ## the Xbox/MCPE username is the key for the Java server auth-info ## this allows automatic configuration/login to the remote Java server ## if you are brave/stupid enough to put your Mojang account info into