diff --git a/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java b/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java index dc895a79d..44544291d 100644 --- a/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java +++ b/common/src/main/java/org/geysermc/floodgate/util/BedrockData.java @@ -1,36 +1,53 @@ package org.geysermc.floodgate.util; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; -import java.util.UUID; - -@AllArgsConstructor +/** + * This class contains the raw data send by Geyser to Floodgate or from Floodgate to Floodgate. + * This class is only used internally, and you should look at FloodgatePlayer instead + * (FloodgatePlayer is present in the common module in the Floodgate repo) + */ +@AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter -public class BedrockData { - public static final int EXPECTED_LENGTH = 7; +public final class BedrockData { + public static final int EXPECTED_LENGTH = 9; 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; + private final String version; + private final String username; + private final String xuid; + private final int deviceOs; + private final String languageCode; + private final int uiProfile; + private final int inputMode; + private final String ip; + private final LinkedPlayer linkedPlayer; + private final 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 BedrockData(String version, String username, String xuid, int deviceOs, + String languageCode, int uiProfile, int inputMode, String ip, + LinkedPlayer linkedPlayer) { + this(version, username, xuid, deviceOs, languageCode, + inputMode, uiProfile, ip, linkedPlayer, EXPECTED_LENGTH); + } + + public BedrockData(String version, String username, String xuid, int deviceOs, + String languageCode, int uiProfile, int inputMode, String ip) { + this(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null); } public static BedrockData fromString(String data) { String[] split = data.split("\0"); - if (split.length != EXPECTED_LENGTH) return null; + if (split.length != EXPECTED_LENGTH) return emptyData(split.length); + LinkedPlayer linkedPlayer = LinkedPlayer.fromString(split[8]); + // The format is the same as the order of the fields in this class return new BedrockData( - split[0], split[1], split[2], Integer.parseInt(split[3]), - split[4], Integer.parseInt(split[5]), split[6], split.length + split[0], split[1], split[2], Integer.parseInt(split[3]), split[4], + Integer.parseInt(split[5]), Integer.parseInt(split[6]), split[7], + linkedPlayer, split.length ); } @@ -40,7 +57,17 @@ public class BedrockData { @Override public String toString() { - return version +'\0'+ username +'\0'+ xuid +'\0'+ deviceId +'\0'+ languageCode +'\0'+ - inputMode +'\0'+ ip; + // The format is the same as the order of the fields in this class + return version + '\0' + username + '\0' + xuid + '\0' + deviceOs + '\0' + + languageCode + '\0' + uiProfile + '\0' + inputMode + '\0' + ip + '\0' + + (linkedPlayer != null ? linkedPlayer.toString() : "null"); + } + + public boolean hasPlayerLink() { + return linkedPlayer != null; + } + + private static BedrockData emptyData(int dataLength) { + return new BedrockData(null, null, null, -1, null, -1, -1, null, null, dataLength); } } diff --git a/common/src/main/java/org/geysermc/floodgate/util/DeviceOS.java b/common/src/main/java/org/geysermc/floodgate/util/DeviceOs.java similarity index 79% rename from common/src/main/java/org/geysermc/floodgate/util/DeviceOS.java rename to common/src/main/java/org/geysermc/floodgate/util/DeviceOs.java index 93d3c121e..da783982c 100644 --- a/common/src/main/java/org/geysermc/floodgate/util/DeviceOS.java +++ b/common/src/main/java/org/geysermc/floodgate/util/DeviceOs.java @@ -26,9 +26,14 @@ package org.geysermc.floodgate.util; import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; -public enum DeviceOS { - +/** + * The Operation Systems where Bedrock players can connect with + */ +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public enum DeviceOs { @JsonEnumDefaultValue UNKNOWN("Unknown"), ANDROID("Android"), @@ -46,15 +51,16 @@ public enum DeviceOS { XBOX_ONE("Xbox One"), WIN_PHONE("Windows Phone"); - private static final DeviceOS[] VALUES = values(); + private static final DeviceOs[] VALUES = values(); private final String displayName; - DeviceOS(final String displayName) { - this.displayName = displayName; - } - - public static DeviceOS getById(int id) { + /** + * Get the DeviceOs instance from the identifier. + * @param id the DeviceOs identifier + * @return The DeviceOs or {@link #UNKNOWN} if the DeviceOs wasn't found + */ + public static DeviceOs getById(int id) { return id < VALUES.length ? VALUES[id] : VALUES[0]; } diff --git a/common/src/main/java/org/geysermc/floodgate/util/EncryptionUtil.java b/common/src/main/java/org/geysermc/floodgate/util/EncryptionUtil.java index 881d01ba9..619c7011a 100644 --- a/common/src/main/java/org/geysermc/floodgate/util/EncryptionUtil.java +++ b/common/src/main/java/org/geysermc/floodgate/util/EncryptionUtil.java @@ -12,7 +12,11 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; -public class EncryptionUtil { +/** + * The class which contains all the encryption and decryption method used in Geyser and Floodgate + * (for Floodgate data). This is only used internally and doesn't serve a purpose for anything else + */ +public final class EncryptionUtil { public static String encrypt(Key key, String data) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { KeyGenerator generator = KeyGenerator.getInstance("AES"); diff --git a/common/src/main/java/org/geysermc/floodgate/util/InputMode.java b/common/src/main/java/org/geysermc/floodgate/util/InputMode.java new file mode 100644 index 000000000..4dcaa8ab3 --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/InputMode.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.floodgate.util; + +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; + +public enum InputMode { + @JsonEnumDefaultValue + UNKNOWN, + KEYBOARD_MOUSE, + TOUCH, // I guess Touch? + CONTROLLER, + VR; + + private static final InputMode[] VALUES = values(); + + /** + * Get the InputMode instance from the identifier. + * @param id the InputMode identifier + * @return The InputMode or {@link #UNKNOWN} if the DeviceOs wasn't found + */ + public static InputMode getById(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } +} \ No newline at end of file diff --git a/common/src/main/java/org/geysermc/floodgate/util/LinkedPlayer.java b/common/src/main/java/org/geysermc/floodgate/util/LinkedPlayer.java new file mode 100644 index 000000000..a930b013a --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/LinkedPlayer.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.floodgate.util; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; + +import java.util.UUID; + +@Getter +public final class LinkedPlayer { + /** + * The Java username of the linked player + */ + private final String javaUsername; + /** + * The Java UUID of the linked player + */ + private final UUID javaUniqueId; + /** + * The UUID of the Bedrock player + */ + private final UUID bedrockId; + /** + * If the LinkedPlayer is send from a different platform. + * For example the LinkedPlayer is from Bungee but the data has been sent to the Bukkit server. + */ + @Setter(AccessLevel.PRIVATE) + private boolean fromDifferentPlatform = false; + + public LinkedPlayer(String javaUsername, UUID javaUniqueId, UUID bedrockId) { + this.javaUsername = javaUsername; + this.javaUniqueId = javaUniqueId; + this.bedrockId = bedrockId; + } + + static LinkedPlayer fromString(String data) { + if (data.length() == 4) return null; + String[] split = data.split(";"); + LinkedPlayer player = new LinkedPlayer( + split[0], UUID.fromString(split[1]), UUID.fromString(split[2]) + ); + player.setFromDifferentPlatform(true); + return player; + } + + @Override + public String toString() { + return javaUsername + ';' + javaUniqueId.toString() + ';' + bedrockId.toString(); + } +} diff --git a/common/src/main/java/org/geysermc/floodgate/util/UiProfile.java b/common/src/main/java/org/geysermc/floodgate/util/UiProfile.java new file mode 100644 index 000000000..441e9202a --- /dev/null +++ b/common/src/main/java/org/geysermc/floodgate/util/UiProfile.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + * + */ + +package org.geysermc.floodgate.util; + +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; + +public enum UiProfile { + @JsonEnumDefaultValue + CLASSIC, + POCKET; + + private static final UiProfile[] VALUES = values(); + + /** + * Get the UiProfile instance from the identifier. + * @param id the UiProfile identifier + * @return The UiProfile or {@link #CLASSIC} if the UiProfile wasn't found + */ + public static UiProfile getById(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } +} 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 b861f64c4..623bf16a9 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 @@ -327,6 +327,7 @@ public class GeyserSession implements CommandSender { authData.getXboxUUID(), clientData.getDeviceOS().ordinal(), clientData.getLanguageCode(), + clientData.getUiProfile().ordinal(), clientData.getCurrentInputMode().ordinal(), upstream.getSession().getAddress().getAddress().getHostAddress() )); 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 index 6aeebbaa3..fe8c769e9 100644 --- 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 @@ -1,16 +1,17 @@ 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 org.geysermc.floodgate.util.DeviceOS; +import org.geysermc.floodgate.util.DeviceOs; +import org.geysermc.floodgate.util.InputMode; +import org.geysermc.floodgate.util.UiProfile; import java.util.UUID; @JsonIgnoreProperties(ignoreUnknown = true) @Getter -public class BedrockClientData { +public final class BedrockClientData { @JsonProperty(value = "GameVersion") private String gameVersion; @JsonProperty(value = "ServerAddress") @@ -52,9 +53,9 @@ public class BedrockClientData { @JsonProperty(value = "DeviceModel") private String deviceModel; @JsonProperty(value = "DeviceOS") - private DeviceOS deviceOS; + private DeviceOs deviceOS; @JsonProperty(value = "UIProfile") - private UIProfile uiProfile; + private UiProfile uiProfile; @JsonProperty(value = "GuiScale") private int guiScale; @JsonProperty(value = "CurrentInputMode") @@ -78,19 +79,4 @@ public class BedrockClientData { private String skinColor; @JsonProperty(value = "ThirdPartyNameOnly") private boolean thirdPartyNameOnly; - - public enum UIProfile { - @JsonEnumDefaultValue - CLASSIC, - POCKET - } - - public enum InputMode { - @JsonEnumDefaultValue - UNKNOWN, - KEYBOARD_MOUSE, - TOUCH, // I guess Touch? - CONTROLLER, - VR - } }