From ab6e0d1e168d46e8883477998b1acc2cd17cb5d9 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Fri, 12 Aug 2022 01:01:26 +0200 Subject: [PATCH] Some more API changes --- .../java/org/geysermc/api/GeyserApiBase.java | 30 ++++- .../geysermc/api/connection/Connection.java | 78 +++++++++--- .../geysermc/api/util/BedrockPlatform.java | 70 +++++++++++ .../java/org/geysermc/api/util/InputMode.java | 46 +++++++ .../java/org/geysermc/api/util/UiProfile.java | 42 +++++++ .../java/org/geysermc/geyser/GeyserImpl.java | 45 ++++++- .../type/player/SessionPlayerEntity.java | 2 +- .../geyser/session/GeyserSession.java | 112 ++++++++++++------ .../geyser/session/SessionManager.java | 11 ++ 9 files changed, 374 insertions(+), 62 deletions(-) create mode 100644 api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java create mode 100644 api/base/src/main/java/org/geysermc/api/util/InputMode.java create mode 100644 api/base/src/main/java/org/geysermc/api/util/UiProfile.java diff --git a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java index e9957e21c..a845e37fd 100644 --- a/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java +++ b/api/base/src/main/java/org/geysermc/api/GeyserApiBase.java @@ -28,6 +28,7 @@ package org.geysermc.api; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.common.value.qual.IntRange; import org.geysermc.api.connection.Connection; import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.util.FormBuilder; @@ -66,11 +67,34 @@ public interface GeyserApiBase { */ boolean isBedrockPlayer(@NonNull UUID uuid); - boolean sendForm(UUID uuid, Form form); + /** + * Sends a form to the given connection and opens it. + * + * @param uuid the uuid of the connection to open it on + * @param form the form to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull UUID uuid, @NonNull Form form); - boolean sendForm(UUID uuid, FormBuilder formBuilder); + /** + * Sends a form to the given connection and opens it. + * + * @param uuid the uuid of the connection to open it on + * @param formBuilder the formBuilder to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull UUID uuid, @NonNull FormBuilder formBuilder); - boolean transfer(UUID uuid, String address, int port); + /** + * Transfer the given connection to a server. A Bedrock player can successfully transfer to the same server they are + * currently playing on. + * + * @param uuid the uuid of the connection + * @param address the address of the server + * @param port the port of the server + * @return true if the transfer was a success + */ + boolean transfer(@NonNull UUID uuid, @NonNull String address, @IntRange(from = 0, to = 65535) int port); /** diff --git a/api/base/src/main/java/org/geysermc/api/connection/Connection.java b/api/base/src/main/java/org/geysermc/api/connection/Connection.java index db09149e2..1cd7a9d13 100644 --- a/api/base/src/main/java/org/geysermc/api/connection/Connection.java +++ b/api/base/src/main/java/org/geysermc/api/connection/Connection.java @@ -28,6 +28,11 @@ package org.geysermc.api.connection; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.common.value.qual.IntRange; +import org.geysermc.api.util.BedrockPlatform; +import org.geysermc.api.util.InputMode; +import org.geysermc.api.util.UiProfile; +import org.geysermc.cumulus.form.Form; +import org.geysermc.cumulus.form.util.FormBuilder; import java.util.UUID; @@ -36,43 +41,80 @@ import java.util.UUID; */ public interface Connection { /** - * Gets the bedrock name of the connection. - * - * @return the bedrock name of the connection + * Returns the bedrock name of the connection. */ - @NonNull - String bedrockUsername(); + @NonNull String bedrockUsername(); /** - * Gets the java name of the connection. - * - * @return the java name of the connection + * Returns the java name of the connection. */ @MonotonicNonNull String javaUsername(); /** - * Gets the {@link UUID} of the connection. - * - * @return the UUID of the connection + * Returns the UUID of the connection. */ @MonotonicNonNull UUID javaUuid(); /** - * Gets the XUID of the connection. - * - * @return the XUID of the connection + * Returns the XUID of the connection. */ - @NonNull - String xuid(); + @NonNull String xuid(); + + /** + * Returns the version of the Bedrock client. + */ + @NonNull String version(); + + /** + * Returns the platform that the connection is playing on. + */ + @NonNull BedrockPlatform platform(); + + /** + * Returns the language code of the connection. + */ + @NonNull String languageCode(); + + /** + * Returns the User Interface Profile of the connection. + */ + @NonNull UiProfile uiProfile(); + + /** + * Returns the Input Mode of the Bedrock client. + */ + @NonNull InputMode inputMode(); + + /** + * Returns whether the connection is linked. + * This will always return false when the auth-type isn't Floodgate. + */ + boolean isLinked(); + + /** + * Sends a form to the connection and opens it. + * + * @param form the form to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull Form form); + + /** + * Sends a form to the connection and opens it. + * + * @param formBuilder the formBuilder to send + * @return whether the form was successfully sent + */ + boolean sendForm(@NonNull FormBuilder formBuilder); /** * Transfer the connection to a server. A Bedrock player can successfully transfer to the same server they are * currently playing on. * - * @param address The address of the server - * @param port The port of the server + * @param address the address of the server + * @param port the port of the server * @return true if the transfer was a success */ boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port); diff --git a/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java b/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java new file mode 100644 index 000000000..d66077a87 --- /dev/null +++ b/api/base/src/main/java/org/geysermc/api/util/BedrockPlatform.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019-2022 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.api.util; + +public enum BedrockPlatform { + UNKNOWN("Unknown"), + GOOGLE("Android"), + IOS("iOS"), + OSX("macOS"), + AMAZON("Amazon"), + GEARVR("Gear VR"), + HOLOLENS("Hololens"), + UWP("Windows 10"), + WIN32("Windows x86"), + DEDICATED("Dedicated"), + TVOS("Apple TV"), + PS4("PS4"), + NX("Switch"), + XBOX("Xbox One"), + WINDOWS_PHONE("Windows Phone"); + + private static final BedrockPlatform[] VALUES = values(); + + private final String displayName; + + BedrockPlatform(String displayName) { + this.displayName = displayName; + } + + /** + * Get the BedrockPlatform from the identifier. + * + * @param id the BedrockPlatform identifier + * @return The BedrockPlatform or {@link #UNKNOWN} if the platform wasn't found + */ + public static BedrockPlatform fromId(int id) { + return id < VALUES.length ? VALUES[id] : VALUES[0]; + } + + /** + * @return friendly display name of platform. + */ + @Override + public String toString() { + return displayName; + } +} diff --git a/api/base/src/main/java/org/geysermc/api/util/InputMode.java b/api/base/src/main/java/org/geysermc/api/util/InputMode.java new file mode 100644 index 000000000..eadb457ab --- /dev/null +++ b/api/base/src/main/java/org/geysermc/api/util/InputMode.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019-2022 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.api.util; + +public enum InputMode { + UNKNOWN, + KEYBOARD_MOUSE, + TOUCH, + CONTROLLER, + VR; + + private static final InputMode[] VALUES = values(); + + /** + * Get the InputMode from the identifier. + * + * @param id the InputMode identifier + * @return The InputMode or {@link #UNKNOWN} if the mode wasn't found + */ + public static InputMode fromId(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } +} \ No newline at end of file diff --git a/api/base/src/main/java/org/geysermc/api/util/UiProfile.java b/api/base/src/main/java/org/geysermc/api/util/UiProfile.java new file mode 100644 index 000000000..c28ff869c --- /dev/null +++ b/api/base/src/main/java/org/geysermc/api/util/UiProfile.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019-2022 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.api.util; + +public enum UiProfile { + CLASSIC, POCKET; + + private static final UiProfile[] VALUES = values(); + + /** + * Get the UiProfile from the identifier. + * + * @param id the UiProfile identifier + * @return The UiProfile or {@link #CLASSIC} if the profile wasn't found + */ + public static UiProfile fromId(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } +} diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 67a144f36..94a2b4304 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -41,10 +41,13 @@ import io.netty.util.internal.SystemPropertyUtil; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.api.Geyser; import org.geysermc.common.PlatformType; +import org.geysermc.cumulus.form.Form; +import org.geysermc.cumulus.form.util.FormBuilder; import org.geysermc.floodgate.crypto.AesCipher; import org.geysermc.floodgate.crypto.AesKeyProducer; import org.geysermc.floodgate.crypto.Base64Topping; @@ -486,6 +489,11 @@ public class GeyserImpl implements GeyserApi { return sessionManager.size(); } + @Override + public @MonotonicNonNull String usernamePrefix() { + return null; + } + @Override public @Nullable GeyserSession connectionByUuid(@NonNull UUID uuid) { return this.sessionManager.getSessions().get(uuid); @@ -493,13 +501,38 @@ public class GeyserImpl implements GeyserApi { @Override public @Nullable GeyserSession connectionByXuid(@NonNull String xuid) { - for (GeyserSession session : sessionManager.getAllSessions()) { - if (session.xuid().equals(xuid)) { - return session; - } - } + return sessionManager.sessionByXuid(xuid); + } - return null; + @Override + public boolean isBedrockPlayer(@NonNull UUID uuid) { + return connectionByUuid(uuid) != null; + } + + @Override + public boolean sendForm(@NonNull UUID uuid, @NonNull Form form) { + Objects.requireNonNull(uuid); + Objects.requireNonNull(form); + GeyserSession session = connectionByUuid(uuid); + if (session == null) { + return false; + } + return session.sendForm(form); + } + + @Override + public boolean sendForm(@NonNull UUID uuid, @NonNull FormBuilder formBuilder) { + return sendForm(uuid, formBuilder.build()); + } + + @Override + public boolean transfer(@NonNull UUID uuid, @NonNull String address, int port) { + Objects.requireNonNull(uuid); + GeyserSession session = connectionByUuid(uuid); + if (session == null) { + return false; + } + return session.transfer(address, port); } public void shutdown() { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index f16f46e2e..7a5d34973 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -73,7 +73,7 @@ public class SessionPlayerEntity extends PlayerEntity { private int fakeTradeXp; public SessionPlayerEntity(GeyserSession session) { - super(session, -1, 1, UUID.randomUUID(), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, "unknown", null); + super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null); valid = true; } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index c8ae4792b..f67d8d92d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -91,6 +91,9 @@ import lombok.Setter; import lombok.experimental.Accessors; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.common.value.qual.IntRange; +import org.geysermc.api.util.BedrockPlatform; +import org.geysermc.api.util.InputMode; +import org.geysermc.api.util.UiProfile; import org.geysermc.common.PlatformType; import org.geysermc.cumulus.form.Form; import org.geysermc.cumulus.form.util.FormBuilder; @@ -136,7 +139,6 @@ import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.MathUtils; -import javax.annotation.Nonnull; import java.net.ConnectException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -151,13 +153,13 @@ import java.util.concurrent.atomic.AtomicInteger; @Getter public class GeyserSession implements GeyserConnection, GeyserCommandSource { - private final @Nonnull GeyserImpl geyser; - private final @Nonnull UpstreamSession upstream; + private final @NonNull GeyserImpl geyser; + private final @NonNull UpstreamSession upstream; /** * The loop where all packets and ticking is processed to prevent concurrency issues. * If this is manually called, ensure that any exceptions are properly handled. */ - private final @Nonnull EventLoop eventLoop; + private final @NonNull EventLoop eventLoop; private TcpSession downstream; @Setter private AuthData authData; @@ -1326,33 +1328,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { } @Override - public String bedrockUsername() { - return authData.name(); - } - - @Override - public UUID javaUuid() { - - } - - @Override - public String xuid() { - return authData.xuid(); - } - - @SuppressWarnings("ConstantConditions") // Need to enforce the parameter annotations - @Override - public boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port) { - if (address == null || address.isBlank()) { - throw new IllegalArgumentException("Server address cannot be null or blank"); - } else if (port < 0 || port > 65535) { - throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port); - } - TransferPacket transferPacket = new TransferPacket(); - transferPacket.setAddress(address); - transferPacket.setPort(port); - sendUpstreamPacket(transferPacket); - return true; + public String name() { + return null; } @Override @@ -1405,12 +1382,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return this.upstream.getAddress(); } - public void sendForm(Form form) { + public boolean sendForm(@NonNull Form form) { formCache.showForm(form); + return true; } - public void sendForm(FormBuilder formBuilder) { + public boolean sendForm(@NonNull FormBuilder formBuilder) { formCache.showForm(formBuilder.build()); + return true; } /** @@ -1765,7 +1744,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * * @param statistics Updated statistics values */ - public void updateStatistics(@Nonnull Object2IntMap statistics) { + public void updateStatistics(@NonNull Object2IntMap statistics) { if (this.statistics.isEmpty()) { // Initialize custom statistics to 0, so that they appear in the form for (CustomStatistic customStatistic : CustomStatistic.values()) { @@ -1851,4 +1830,69 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { public MinecraftCodecHelper getCodecHelper() { return (MinecraftCodecHelper) this.downstream.getCodecHelper(); } + + @Override + public String bedrockUsername() { + return authData.name(); + } + + @Override + public @MonotonicNonNull String javaUsername() { + return playerEntity.getUsername(); + } + + @Override + public UUID javaUuid() { + return playerEntity.getUuid(); + } + + @Override + public String xuid() { + return authData.xuid(); + } + + @Override + public @NonNull String version() { + return clientData.getGameVersion(); + } + + @Override + public @NonNull BedrockPlatform platform() { + return BedrockPlatform.values()[clientData.getDeviceOs().ordinal()]; //todo + } + + @Override + public @NonNull String languageCode() { + return locale(); + } + + @Override + public @NonNull UiProfile uiProfile() { + return UiProfile.values()[clientData.getUiProfile().ordinal()]; //todo + } + + @Override + public @NonNull InputMode inputMode() { + return InputMode.values()[clientData.getCurrentInputMode().ordinal()]; //todo + } + + @Override + public boolean isLinked() { + return false; //todo + } + + @SuppressWarnings("ConstantConditions") // Need to enforce the parameter annotations + @Override + public boolean transfer(@NonNull String address, @IntRange(from = 0, to = 65535) int port) { + if (address == null || address.isBlank()) { + throw new IllegalArgumentException("Server address cannot be null or blank"); + } else if (port < 0 || port > 65535) { + throw new IllegalArgumentException("Server port must be between 0 and 65535, was " + port); + } + TransferPacket transferPacket = new TransferPacket(); + transferPacket.setAddress(address); + transferPacket.setPort(port); + sendUpstreamPacket(transferPacket); + return true; + } } diff --git a/core/src/main/java/org/geysermc/geyser/session/SessionManager.java b/core/src/main/java/org/geysermc/geyser/session/SessionManager.java index fc6c37356..2660f54b1 100644 --- a/core/src/main/java/org/geysermc/geyser/session/SessionManager.java +++ b/core/src/main/java/org/geysermc/geyser/session/SessionManager.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.session; import com.google.common.collect.ImmutableList; import lombok.AccessLevel; import lombok.Getter; +import lombok.NonNull; import org.geysermc.geyser.text.GeyserLocale; import java.util.*; @@ -67,6 +68,16 @@ public final class SessionManager { } } + public GeyserSession sessionByXuid(@NonNull String xuid) { + Objects.requireNonNull(xuid); + for (GeyserSession session : sessions.values()) { + if (session.xuid().equals(xuid)) { + return session; + } + } + return null; + } + /** * Creates a new, immutable list containing all pending and active sessions. */