From 516057e439960a9d27adcc677f932920a6087f96 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Tue, 16 Jun 2020 00:53:06 +0100 Subject: [PATCH] Clean files and add comments --- .../java/org/geysermc/multi/GeyserMulti.java | 8 ----- .../java/org/geysermc/multi/MasterServer.java | 12 +++---- .../org/geysermc/multi/PacketHandler.java | 30 +++++++++--------- .../multi/proxy/GeyserProxyBootstrap.java | 7 +++++ .../multi/proxy/GeyserProxyConfiguration.java | 2 +- .../multi/proxy/GeyserProxyLogger.java | 5 +-- .../multi/proxy/GeyserProxySession.java | 5 ++- .../ProxyConnectorServerEventHandler.java | 5 ++- .../java/org/geysermc/multi/ui/UIHandler.java | 31 ++++++++++++++----- .../java/org/geysermc/multi/utils/Player.java | 23 +++++++++----- .../java/org/geysermc/multi/utils/Server.java | 2 +- 11 files changed, 82 insertions(+), 48 deletions(-) diff --git a/src/main/java/org/geysermc/multi/GeyserMulti.java b/src/main/java/org/geysermc/multi/GeyserMulti.java index ea45703..edbc01b 100644 --- a/src/main/java/org/geysermc/multi/GeyserMulti.java +++ b/src/main/java/org/geysermc/multi/GeyserMulti.java @@ -1,15 +1,7 @@ package org.geysermc.multi; -import com.nukkitx.protocol.bedrock.BedrockPacketCodec; -import com.nukkitx.protocol.bedrock.v390.Bedrock_v390; - public class GeyserMulti { - public static final BedrockPacketCodec CODEC = Bedrock_v390.V390_CODEC; - - private static MasterServer masterServer; - - public static void main(String[] args) { new MasterServer(); } diff --git a/src/main/java/org/geysermc/multi/MasterServer.java b/src/main/java/org/geysermc/multi/MasterServer.java index d61daea..381942b 100644 --- a/src/main/java/org/geysermc/multi/MasterServer.java +++ b/src/main/java/org/geysermc/multi/MasterServer.java @@ -1,9 +1,7 @@ package org.geysermc.multi; -import com.nukkitx.protocol.bedrock.BedrockPong; -import com.nukkitx.protocol.bedrock.BedrockServer; -import com.nukkitx.protocol.bedrock.BedrockServerEventHandler; -import com.nukkitx.protocol.bedrock.BedrockServerSession; +import com.nukkitx.protocol.bedrock.*; +import com.nukkitx.protocol.bedrock.v390.Bedrock_v390; import lombok.Getter; import org.geysermc.multi.proxy.GeyserProxyBootstrap; import org.geysermc.multi.utils.Logger; @@ -20,6 +18,8 @@ import java.util.concurrent.ScheduledExecutorService; public class MasterServer { + public static final BedrockPacketCodec CODEC = Bedrock_v390.V390_CODEC; + private final Timer timer; private BedrockServer bdServer; private BedrockPong bdPong; @@ -72,8 +72,8 @@ public class MasterServer { bdPong.setMaximumPlayerCount(1337); bdPong.setGameType("Survival"); bdPong.setIpv4Port(port); - bdPong.setProtocolVersion(GeyserMulti.CODEC.getProtocolVersion()); - bdPong.setVersion(GeyserMulti.CODEC.getMinecraftVersion()); + bdPong.setProtocolVersion(MasterServer.CODEC.getProtocolVersion()); + bdPong.setVersion(MasterServer.CODEC.getMinecraftVersion()); bdServer.setHandler(new BedrockServerEventHandler() { @Override diff --git a/src/main/java/org/geysermc/multi/PacketHandler.java b/src/main/java/org/geysermc/multi/PacketHandler.java index a981991..3d31e85 100644 --- a/src/main/java/org/geysermc/multi/PacketHandler.java +++ b/src/main/java/org/geysermc/multi/PacketHandler.java @@ -48,9 +48,9 @@ public class PacketHandler implements BedrockPacketHandler { // Check the protocol version is correct int protocol = packet.getProtocolVersion(); - if (protocol != GeyserMulti.CODEC.getProtocolVersion()) { + if (protocol != MasterServer.CODEC.getProtocolVersion()) { PlayStatusPacket status = new PlayStatusPacket(); - if (protocol > GeyserMulti.CODEC.getProtocolVersion()) { + if (protocol > MasterServer.CODEC.getProtocolVersion()) { status.setStatus(PlayStatusPacket.Status.FAILED_SERVER); } else { status.setStatus(PlayStatusPacket.Status.FAILED_CLIENT); @@ -59,8 +59,9 @@ public class PacketHandler implements BedrockPacketHandler { } // Set the session codec - session.setPacketCodec(GeyserMulti.CODEC); + session.setPacketCodec(MasterServer.CODEC); + // Read the raw chain data JsonNode rawChainData; try { rawChainData = OBJECT_MAPPER.readTree(packet.getChainData().toByteArray()); @@ -68,6 +69,7 @@ public class PacketHandler implements BedrockPacketHandler { throw new AssertionError("Unable to read chain data!"); } + // Get the parsed chain data JsonNode chainData = rawChainData.get("chain"); if (chainData.getNodeType() != JsonNodeType.ARRAY) { throw new AssertionError("Invalid chain data!"); @@ -78,22 +80,29 @@ public class PacketHandler implements BedrockPacketHandler { JWSObject jwsObject; jwsObject = JWSObject.parse(chainData.get(chainData.size() - 1).asText()); + // Read the JWS payload JsonNode payload = OBJECT_MAPPER.readTree(jwsObject.getPayload().toBytes()); + // Check the identityPublicKey is there if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) { throw new AssertionError("Missing identity public key!"); } + // Create an ECPublicKey from the identityPublicKey ECPublicKey identityPublicKey = EncryptionUtils.generateKey(payload.get("identityPublicKey").textValue()); + // Get the skin data to validate the JWS token JWSObject skinData = JWSObject.parse(packet.getSkinData().toString()); if (skinData.verify(new DefaultJWSVerifierFactory().createJWSVerifier(skinData.getHeader(), identityPublicKey))) { + // Make sure the client sent over the username, xuid and other info if (payload.get("extraData").getNodeType() != JsonNodeType.OBJECT) { throw new AssertionError("Missing client data"); } + // Fetch the client data JsonNode extraData = payload.get("extraData"); + // Create a new player and add it to the players list player = new Player(extraData, session); masterServer.getPlayers().put(session.getAddress(), player); @@ -145,26 +154,25 @@ public class PacketHandler implements BedrockPacketHandler { player.sendWindow(FormID.MAIN, UIHandler.getServerListFormPacket(player.getServers()));; - /*TransferPacket transferPacket = new TransferPacket(); - transferPacket.setAddress("81.174.164.211"); - transferPacket.setPort(27040); - session.sendPacket(transferPacket);*/ - return false; } @Override public boolean handle(ModalFormResponsePacket packet) { + // Make sure the form is valid FormID id = FormID.fromId(packet.getFormId()); if (id != player.getCurrentWindowId()) return false; + // Fetch the form and parse the response FormWindow window = player.getCurrentWindow(); window.setResponse(packet.getFormData().trim()); + // Resend the form if they closed it if (window.getResponse() == null) { player.resendWindow(); } else { + // Send the response to the correct response function switch (id) { case MAIN: UIHandler.handleServerListResponse(player, (SimpleFormResponse) window.getResponse()); @@ -178,10 +186,4 @@ public class PacketHandler implements BedrockPacketHandler { return true; } - - @Override - public boolean handle(NetworkStackLatencyPacket packet) { - masterServer.getLogger().debug(packet.toString()); - return false; - } } diff --git a/src/main/java/org/geysermc/multi/proxy/GeyserProxyBootstrap.java b/src/main/java/org/geysermc/multi/proxy/GeyserProxyBootstrap.java index 4129ce9..e6249c7 100644 --- a/src/main/java/org/geysermc/multi/proxy/GeyserProxyBootstrap.java +++ b/src/main/java/org/geysermc/multi/proxy/GeyserProxyBootstrap.java @@ -29,8 +29,10 @@ public class GeyserProxyBootstrap implements GeyserBootstrap { @Override public void onEnable() { + // Setup a logger geyserLogger = new GeyserProxyLogger(); + // Read the static config from resources try { InputStream configFile = GeyserProxyBootstrap.class.getClassLoader().getResourceAsStream("proxy_config.yml"); ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); @@ -39,13 +41,18 @@ public class GeyserProxyBootstrap implements GeyserBootstrap { geyserLogger.severe("Failed to read proxy_config.yml! Make sure it's up to date and/or readable+writable!", ex); return; } + + // Not sure there is a point in doing this as its a static config GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); + // Create the connector and command manager connector = GeyserConnector.start(PlatformType.STANDALONE, this); geyserCommandManager = new GeyserProxyCommandManager(connector); + // Start the ping passthrough thread, again don't think there is a point geyserPingPassthrough = GeyserLegacyPingPassthrough.init(connector); + // Swap the normal handler to our custom handler so we can change some connector.getBedrockServer().setHandler(new ProxyConnectorServerEventHandler(connector)); } diff --git a/src/main/java/org/geysermc/multi/proxy/GeyserProxyConfiguration.java b/src/main/java/org/geysermc/multi/proxy/GeyserProxyConfiguration.java index 527200d..6c0aa5f 100644 --- a/src/main/java/org/geysermc/multi/proxy/GeyserProxyConfiguration.java +++ b/src/main/java/org/geysermc/multi/proxy/GeyserProxyConfiguration.java @@ -13,7 +13,7 @@ import java.nio.file.Paths; public class GeyserProxyConfiguration extends GeyserJacksonConfiguration { @JsonProperty("floodgate-key-file") - private String floodgateKeyFile = ""; + private String floodgateKeyFile; @Override public Path getFloodgateKeyFile() { diff --git a/src/main/java/org/geysermc/multi/proxy/GeyserProxyLogger.java b/src/main/java/org/geysermc/multi/proxy/GeyserProxyLogger.java index 3d3330b..14bacc3 100644 --- a/src/main/java/org/geysermc/multi/proxy/GeyserProxyLogger.java +++ b/src/main/java/org/geysermc/multi/proxy/GeyserProxyLogger.java @@ -1,11 +1,12 @@ package org.geysermc.multi.proxy; import lombok.extern.log4j.Log4j2; -import org.apache.logging.log4j.core.config.Configurator; -import org.geysermc.common.ChatColor; import org.geysermc.multi.utils.Logger; @Log4j2 public class GeyserProxyLogger extends Logger { + /** + * Disable debug messages + */ public void debug(String message) { } } diff --git a/src/main/java/org/geysermc/multi/proxy/GeyserProxySession.java b/src/main/java/org/geysermc/multi/proxy/GeyserProxySession.java index 5a57279..4a84dc9 100644 --- a/src/main/java/org/geysermc/multi/proxy/GeyserProxySession.java +++ b/src/main/java/org/geysermc/multi/proxy/GeyserProxySession.java @@ -18,12 +18,15 @@ public class GeyserProxySession extends GeyserSession { } public void authenticate(String username, String password) { + // Get the player based on the connection address Player player = MasterServer.getInstance().getPlayers().get(bedrockServerSession.getAddress()); - if (player != null) { + if (player != null && player.getCurrentServer() != null) { + // Set the remote server info for the player connector.getRemoteServer().setAddress(player.getCurrentServer().getAddress()); connector.getRemoteServer().setPort(player.getCurrentServer().getPort()); super.authenticate(username, password); }else{ + // Disconnect the player if they haven't picked a server on the master server list bedrockServerSession.disconnect("Please connect to the master server and pick a server first!"); } } diff --git a/src/main/java/org/geysermc/multi/proxy/ProxyConnectorServerEventHandler.java b/src/main/java/org/geysermc/multi/proxy/ProxyConnectorServerEventHandler.java index 03304a4..f2dfec4 100644 --- a/src/main/java/org/geysermc/multi/proxy/ProxyConnectorServerEventHandler.java +++ b/src/main/java/org/geysermc/multi/proxy/ProxyConnectorServerEventHandler.java @@ -4,7 +4,6 @@ import com.nukkitx.protocol.bedrock.BedrockServerSession; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.ConnectorServerEventHandler; import org.geysermc.connector.network.UpstreamPacketHandler; -import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.multi.MasterServer; import org.geysermc.multi.utils.Player; @@ -27,7 +26,11 @@ public class ProxyConnectorServerEventHandler extends ConnectorServerEventHandle super.onSessionCreation(bedrockServerSession); + // This doesn't clean up the old packet handler, so may cause a memory leak? bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(connector, new GeyserProxySession(connector, bedrockServerSession))); + bedrockServerSession.setPacketCodec(GeyserConnector.BEDROCK_PACKET_CODEC); // Only done here as it sometimes gets cleared + + // Add another disconnect handler to remove the player on final disconnect bedrockServerSession.addDisconnectHandler(disconnectReason -> { MasterServer.getInstance().getLogger().debug("Player disconnected from geyser proxy: " + player.getDisplayName() + " (" + disconnectReason + ")"); MasterServer.getInstance().getPlayers().remove(bedrockServerSession.getAddress()); diff --git a/src/main/java/org/geysermc/multi/ui/UIHandler.java b/src/main/java/org/geysermc/multi/ui/UIHandler.java index 732483f..f4c621d 100644 --- a/src/main/java/org/geysermc/multi/ui/UIHandler.java +++ b/src/main/java/org/geysermc/multi/ui/UIHandler.java @@ -1,6 +1,5 @@ package org.geysermc.multi.ui; -import com.nukkitx.protocol.bedrock.packet.TransferPacket; import org.geysermc.common.window.FormWindow; import org.geysermc.common.window.SimpleFormWindow; import org.geysermc.common.window.button.FormButton; @@ -13,34 +12,52 @@ import org.geysermc.multi.utils.Server; import java.util.List; public class UIHandler { + /** + * Create a list of servers for the client based on the passed servers list + * + * @param servers A list of {@link Server} objects + * @return A {@link SimpleFormWindow} object + */ public static FormWindow getServerListFormPacket(List servers) { SimpleFormWindow window = new SimpleFormWindow("Servers", ""); + // Add a button for each server with the server icon as the image for (Server server : servers) { - window.getButtons().add(new FormButton(server.getAddress(), new FormImage(FormImage.FormImageType.URL, "https://eu.mc-api.net/v3/server/favicon/" + server.getAddress() + ":" + server.getPort() + ".png"))); + window.getButtons().add(new FormButton(server.toString(), new FormImage(FormImage.FormImageType.URL, "https://eu.mc-api.net/v3/server/favicon/" + server.getAddress() + ":" + server.getPort() + ".png"))); } return window; } + /** + * Create a simple connecting message form + * + * @param server The server info to display + * @return A {@link SimpleFormWindow} object + */ public static FormWindow getWaitingScreen(Server server) { - SimpleFormWindow window = new SimpleFormWindow("Servers", "Please wait while we connect you to " + server.toString()); + SimpleFormWindow window = new SimpleFormWindow("Connecting", "Please wait while we connect you to " + server.toString()); return window; } + /** + * Handle the server list response + * + * @param player The player that submitted the response + * @param data The form response data + */ public static void handleServerListResponse(Player player, SimpleFormResponse data) { - MasterServer.getInstance().getLogger().debug(data.getClickedButton().getText()); - // Get the server Server server = player.getServers().get(data.getClickedButtonId()); // Tell the user we are connecting them + // this wont show up in alot of cases as the client connects quite quickly player.sendWindow(FormID.CONNECTING, getWaitingScreen(server)); - // Create the geyser instance if its not already running + // Create the Geyser instance if its not already running MasterServer.getInstance().createGeyserProxy(); - // Send the user over to the serverty + // Send the user over to the server player.setCurrentServer(server); player.connectToProxy(); } diff --git a/src/main/java/org/geysermc/multi/utils/Player.java b/src/main/java/org/geysermc/multi/utils/Player.java index 689727b..25125e8 100644 --- a/src/main/java/org/geysermc/multi/utils/Player.java +++ b/src/main/java/org/geysermc/multi/utils/Player.java @@ -11,10 +11,8 @@ import lombok.Getter; import lombok.Setter; import org.geysermc.common.window.FormWindow; import org.geysermc.multi.MasterServer; -import org.geysermc.multi.proxy.GeyserProxyBootstrap; import org.geysermc.multi.ui.FormID; -import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -48,7 +46,11 @@ public class Player { servers.add(new Server("81.174.164.211", 25580)); } + /** + * Send a few different packets to get the client to load in + */ public void sendStartGame() { + // A lot of this likely doesn't need to be changed StartGamePacket startGamePacket = new StartGamePacket(); startGamePacket.setUniqueEntityId(1); startGamePacket.setRuntimeEntityId(1); @@ -94,9 +96,7 @@ public class Player { startGamePacket.setEnchantmentSeed(0); startGamePacket.setMultiplayerCorrelationId(""); startGamePacket.setBlockPalette(PalleteManger.BLOCK_PALLETE); - //startGamePacket.setItemEntries(ItemRegistry.ITEMS); startGamePacket.setVanillaVersion("*"); - // startGamePacket.setMovementServerAuthoritative(true); session.sendPacket(startGamePacket); // Send an empty chunk @@ -125,7 +125,13 @@ public class Player { session.sendPacket(setEntityMotionPacket); } - + /** + * Send a window with the specified id and content + * Also cache it against the player for later use + * + * @param id The {@link FormID} to use for the form + * @param window The {@link FormWindow} to turn into json and send + */ public void sendWindow(FormID id, FormWindow window) { this.currentWindow = window; this.currentWindowId = id; @@ -133,16 +139,19 @@ public class Player { ModalFormRequestPacket modalFormRequestPacket = new ModalFormRequestPacket(); modalFormRequestPacket.setFormId(id.ordinal()); modalFormRequestPacket.setFormData(window.getJSONData()); - session.sendPacket(modalFormRequestPacket); + session.sendPacketImmediately(modalFormRequestPacket); } public void resendWindow() { sendWindow(currentWindowId, currentWindow); } + /** + * Send the player to the Geyser proxy server + */ public void connectToProxy() { TransferPacket transferPacket = new TransferPacket(); - transferPacket.setAddress("127.0.0.1"); + transferPacket.setAddress("127.0.0.1"); // Need to find a good way of getting this transferPacket.setPort(MasterServer.getInstance().getGeyserProxy().getGeyserConfig().getBedrock().getPort()); session.sendPacket(transferPacket); } diff --git a/src/main/java/org/geysermc/multi/utils/Server.java b/src/main/java/org/geysermc/multi/utils/Server.java index 8e3334d..9add4a2 100644 --- a/src/main/java/org/geysermc/multi/utils/Server.java +++ b/src/main/java/org/geysermc/multi/utils/Server.java @@ -15,6 +15,6 @@ public class Server { @Override public String toString() { - return address + ":" + port; + return address + (port != 25565 ? ":" + port : ""); } }