From eeadde0550dbe08d61cd257be8bdd02f153e5b4f Mon Sep 17 00:00:00 2001 From: rtm516 Date: Wed, 16 Jun 2021 12:18:07 +0100 Subject: [PATCH] Rework how connections work to remove the use for the transfer packets and multiple ports (#30) * Move to a more linear connection flow and remove the need for a second port * Update configs * Remove un-used remote address config options * Update docker file * Start internal Geyser on boot * Fix srv resolving * Fix inventory data when loading into the server --- README.md | 2 +- docker/Dockerfile | 1 - docker/README.md | 2 +- .../geysermc/connect/GeyserConnectConfig.java | 18 --- .../org/geysermc/connect/MasterServer.java | 11 +- .../org/geysermc/connect/PacketHandler.java | 25 ++-- .../connect/proxy/GeyserProxyBootstrap.java | 8 +- .../connect/proxy/GeyserProxySession.java | 25 +--- .../GeyserProxyUpstreamPacketHandler.java | 50 -------- .../ProxyConnectorServerEventHandler.java | 39 +----- .../connect/storage/JsonStorageManager.java | 4 +- .../connect/storage/MySQLStorageManager.java | 4 +- .../connect/storage/SQLiteStorageManager.java | 4 +- .../geysermc/connect/utils/PaletteManger.java | 44 ------- .../org/geysermc/connect/utils/Player.java | 90 ++++++++------ src/main/resources/config.yml | 13 -- src/main/resources/proxy_config.yml | 116 +++++++++++++----- 17 files changed, 178 insertions(+), 278 deletions(-) delete mode 100644 src/main/java/org/geysermc/connect/proxy/GeyserProxyUpstreamPacketHandler.java diff --git a/README.md b/README.md index f394d34..4d41909 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Build Status](https://ci.nukkitx.com/job/GeyserMC/job/GeyserConnect/job/master/badge/icon)](https://ci.nukkitx.com/job/GeyserMC/job/GeyserConnect/job/master/) [![Discord](https://img.shields.io/discord/613163671870242838.svg?color=%237289da&label=discord)](http://discord.geysermc.org/) -[![HitCount](http://hits.dwyl.io/GeyserMC/GeyserConnect.svg)](http://hits.dwyl.io/GeyserMC/GeyserConnect) +[![HitCount](http://hits.dwyl.com/GeyserMC/GeyserConnect.svg)](http://hits.dwyl.io/GeyserMC/GeyserConnect) GeyserConnect is an easy way for Bedrock Edition clients to connect to any Java Edition servers without having to run anything. diff --git a/docker/Dockerfile b/docker/Dockerfile index a078f5d..2415c64 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,5 +2,4 @@ FROM openjdk:8-jre-slim RUN mkdir /gsc WORKDIR /gsc EXPOSE 19132/udp -EXPOSE 19133/udp CMD ["java", "-Xms1G", "-jar", "GeyserConnect.jar"] \ No newline at end of file diff --git a/docker/README.md b/docker/README.md index 7f84783..7ef8aff 100644 --- a/docker/README.md +++ b/docker/README.md @@ -6,5 +6,5 @@ This contains the docker image and a basic way of running GeyserConnect 2. Build the docker file using `docker build -t geyser-connect .` 3. Start geyser using this: ``` -docker run --name "geyser-c" -d --restart always -p 19132:19132/udp -p 19133:19133/udp -v $(pwd)/data:/gsc geyser-connect +docker run --name "geyser-c" -d --restart always -p 19132:19132/udp -v $(pwd)/data:/gsc geyser-connect ``` \ No newline at end of file diff --git a/src/main/java/org/geysermc/connect/GeyserConnectConfig.java b/src/main/java/org/geysermc/connect/GeyserConnectConfig.java index 9f3f102..753a7b0 100644 --- a/src/main/java/org/geysermc/connect/GeyserConnectConfig.java +++ b/src/main/java/org/geysermc/connect/GeyserConnectConfig.java @@ -42,9 +42,6 @@ public class GeyserConnectConfig { private String address; - @JsonProperty("remote-address") - private String remoteAddress; - private int port; @JsonProperty("max-players") @@ -69,29 +66,14 @@ public class GeyserConnectConfig { private VirtualHostSection vhost; - public void checkRemoteIP() { - if ("auto".equals(remoteAddress)) { - remoteAddress = WebUtils.getBody("https://icanhazip.com/").trim(); - MasterServer.getInstance().getLogger().debug("Auto set remote IP to: " + remoteAddress); - } - } - @Getter public static class GeyserConfigSection { - private int port; - - @JsonProperty("cache-chunks") - private boolean cacheChunks = true; - @JsonProperty("allow-password-authentication") private boolean allowPasswordAuthentication = false; @JsonProperty("debug-mode") private boolean debugMode; - - @JsonProperty("shutdown-time") - private int shutdownTime; } @Getter diff --git a/src/main/java/org/geysermc/connect/MasterServer.java b/src/main/java/org/geysermc/connect/MasterServer.java index 7c01eea..55130ca 100644 --- a/src/main/java/org/geysermc/connect/MasterServer.java +++ b/src/main/java/org/geysermc/connect/MasterServer.java @@ -105,8 +105,6 @@ public class MasterServer { logger.setDebug(geyserConnectConfig.isDebugMode()); - geyserConnectConfig.checkRemoteIP(); - this.generalThreadPool = Executors.newScheduledThreadPool(32); // Start a timer to keep the thread running @@ -131,7 +129,7 @@ public class MasterServer { // Create the base welcome.txt file try { - FileUtils.fileOrCopiedFromResource(new File(MasterServer.getInstance().getGeyserConnectConfig().getWelcomeFile()), "welcome.txt", (x) -> x); + FileUtils.fileOrCopiedFromResource(new File(getGeyserConnectConfig().getWelcomeFile()), "welcome.txt", (x) -> x); } catch (IOException ignored) { } start(geyserConnectConfig.getPort()); @@ -184,6 +182,10 @@ public class MasterServer { // Start server up bdServer.bind().join(); + + // Create the Geyser instance + createGeyserProxy(); + logger.info("Server started on " + geyserConnectConfig.getAddress() + ":" + port); } @@ -200,6 +202,9 @@ public class MasterServer { public void createGeyserProxy() { if (geyserProxy == null) { + // Make sure Geyser doesn't start the listener + GeyserConnector.setShouldStartListener(false); + this.geyserProxy = new GeyserProxyBootstrap(); geyserProxy.onEnable(); } diff --git a/src/main/java/org/geysermc/connect/PacketHandler.java b/src/main/java/org/geysermc/connect/PacketHandler.java index 0ffac64..237bb69 100644 --- a/src/main/java/org/geysermc/connect/PacketHandler.java +++ b/src/main/java/org/geysermc/connect/PacketHandler.java @@ -44,6 +44,7 @@ import org.geysermc.connect.utils.Player; import org.geysermc.connect.utils.Server; import org.geysermc.connector.entity.attribute.AttributeType; import org.geysermc.connector.network.BedrockProtocol; +import org.geysermc.connector.network.session.auth.AuthData; import org.geysermc.connector.network.session.auth.BedrockClientData; import org.geysermc.connector.utils.AttributeUtils; import org.geysermc.connector.utils.FileUtils; @@ -57,6 +58,7 @@ import java.io.IOException; import java.security.interfaces.ECPublicKey; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.concurrent.TimeUnit; public class PacketHandler implements BedrockPacketHandler { @@ -77,13 +79,13 @@ public class PacketHandler implements BedrockPacketHandler { public void disconnect(DisconnectReason reason) { if (player != null) { - masterServer.getLogger().info(player.getDisplayName() + " has disconnected from the master server (" + reason + ")"); + masterServer.getLogger().info(player.getAuthData().getName() + " has disconnected from the master server (" + reason + ")"); masterServer.getStorageManager().saveServers(player); if (player.getCurrentServer() != null && !player.getCurrentServer().isBedrock()) { - masterServer.getTransferringPlayers().put(player.getXuid(), player); + masterServer.getTransferringPlayers().put(player.getAuthData().getXboxUUID(), player); } - masterServer.getPlayers().remove(player.getXuid(), player); + masterServer.getPlayers().remove(player.getAuthData().getXboxUUID(), player); } } @@ -154,9 +156,16 @@ public class PacketHandler implements BedrockPacketHandler { // Fetch the client data JsonNode extraData = payload.get("extraData"); + AuthData authData = new AuthData( + extraData.get("displayName").asText(), + UUID.fromString(extraData.get("identity").asText()), + extraData.get("XUID").asText(), + chainData, packet.getSkinData().toString() + ); + // Create a new player and add it to the players list - player = new Player(extraData, session); - masterServer.getPlayers().put(player.getXuid(), player); + player = new Player(authData, session); + masterServer.getPlayers().put(player.getAuthData().getXboxUUID(), player); // Store the full client data player.setClientData(OBJECT_MAPPER.convertValue(OBJECT_MAPPER.readTree(skinData.getPayload().toBytes()), BedrockClientData.class)); @@ -185,7 +194,7 @@ public class PacketHandler implements BedrockPacketHandler { public boolean handle(ResourcePackClientResponsePacket packet) { switch (packet.getStatus()) { case COMPLETED: - masterServer.getLogger().info("Logged in " + player.getDisplayName() + " (" + player.getXuid() + ", " + player.getIdentity() + ")"); + masterServer.getLogger().info("Logged in " + player.getAuthData().getName() + " (" + player.getAuthData().getXboxUUID() + ", " + player.getAuthData().getUUID() + ")"); player.sendStartGame(); break; case HAVE_ALL_PACKS: @@ -205,7 +214,7 @@ public class PacketHandler implements BedrockPacketHandler { @Override public boolean handle(SetLocalPlayerAsInitializedPacket packet) { - masterServer.getLogger().debug("Player initialized: " + player.getDisplayName()); + masterServer.getLogger().debug("Player initialized: " + player.getAuthData().getName()); // Handle the virtual host if specified GeyserConnectConfig.VirtualHostSection vhost = MasterServer.getInstance().getGeyserConnectConfig().getVhost(); @@ -236,7 +245,7 @@ public class PacketHandler implements BedrockPacketHandler { } // Log the virtual host usage - masterServer.getLogger().info(player.getDisplayName() + " is using virtualhost: " + address + ":" + port + (!online ? " (offline)" : "")); + masterServer.getLogger().info(player.getAuthData().getName() + " is using virtualhost: " + address + ":" + port + (!online ? " (offline)" : "")); // Send the player to the wanted server player.sendToServer(new Server(address, port, online, false)); diff --git a/src/main/java/org/geysermc/connect/proxy/GeyserProxyBootstrap.java b/src/main/java/org/geysermc/connect/proxy/GeyserProxyBootstrap.java index faef11c..8512b28 100644 --- a/src/main/java/org/geysermc/connect/proxy/GeyserProxyBootstrap.java +++ b/src/main/java/org/geysermc/connect/proxy/GeyserProxyBootstrap.java @@ -68,11 +68,9 @@ public class GeyserProxyBootstrap implements GeyserBootstrap { // Grab the config as text and replace static strings to the main config variables String text = new BufferedReader(new InputStreamReader(configFile, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n")); GeyserConnectConfig multiConfig = MasterServer.getInstance().getGeyserConnectConfig(); - text = text.replace("PORT", String.valueOf(multiConfig.getGeyser().getPort())); - text = text.replaceAll("MOTD", multiConfig.getMotd()); - text = text.replace("PLAYERS", String.valueOf(multiConfig.getMaxPlayers())); - text = text.replace("CACHECHUNKS", String.valueOf(multiConfig.getGeyser().isCacheChunks())); - text = text.replace("ALLOWPASSWORDAUTHENTICATION", String.valueOf(multiConfig.getGeyser().isAllowPasswordAuthentication())); + text = text.replaceAll("%MOTD%", multiConfig.getMotd()); + text = text.replace("%PLAYERS%", String.valueOf(multiConfig.getMaxPlayers())); + text = text.replace("%ALLOWPASSWORDAUTHENTICATION%", String.valueOf(multiConfig.getGeyser().isAllowPasswordAuthentication())); ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); geyserConfig = objectMapper.readValue(text, GeyserProxyConfiguration.class); diff --git a/src/main/java/org/geysermc/connect/proxy/GeyserProxySession.java b/src/main/java/org/geysermc/connect/proxy/GeyserProxySession.java index f6909bb..ce29ae3 100644 --- a/src/main/java/org/geysermc/connect/proxy/GeyserProxySession.java +++ b/src/main/java/org/geysermc/connect/proxy/GeyserProxySession.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org + * 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 @@ -26,35 +26,12 @@ package org.geysermc.connect.proxy; import com.nukkitx.protocol.bedrock.BedrockServerSession; -import lombok.Getter; import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.common.AuthType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connect.MasterServer; -import org.geysermc.connect.utils.Player; -import org.geysermc.connector.network.session.auth.AuthData; public class GeyserProxySession extends GeyserSession { - - private final BedrockServerSession bedrockServerSession; - @Getter - private Player player; - public GeyserProxySession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { super(connector, bedrockServerSession); - this.bedrockServerSession = bedrockServerSession; - } - - @Override - public void setAuthenticationData(AuthData authData) { - super.setAuthenticationData(authData); - - player = MasterServer.getInstance().getTransferringPlayers().getIfPresent(authData.getXboxUUID()); - if (player == null) { - bedrockServerSession.disconnect("Please connect to the master server and pick a server first!"); - } else { - MasterServer.getInstance().getTransferringPlayers().invalidate(authData.getXboxUUID()); - } } @Override diff --git a/src/main/java/org/geysermc/connect/proxy/GeyserProxyUpstreamPacketHandler.java b/src/main/java/org/geysermc/connect/proxy/GeyserProxyUpstreamPacketHandler.java deleted file mode 100644 index 3396f0a..0000000 --- a/src/main/java/org/geysermc/connect/proxy/GeyserProxyUpstreamPacketHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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/GeyserConnect - */ - -package org.geysermc.connect.proxy; - -import com.nukkitx.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket; -import org.geysermc.connect.MasterServer; -import org.geysermc.connect.utils.Player; -import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.common.AuthType; -import org.geysermc.connector.network.UpstreamPacketHandler; -import org.geysermc.connector.network.session.GeyserSession; - -public class GeyserProxyUpstreamPacketHandler extends UpstreamPacketHandler { - public GeyserProxyUpstreamPacketHandler(GeyserConnector connector, GeyserSession session) { - super(connector, session); - } - - @Override - public boolean handle(SetLocalPlayerAsInitializedPacket packet) { - Player player = ((GeyserProxySession) session).getPlayer(); - session.setRemoteAddress(player.getCurrentServer().getAddress()); - session.setRemotePort(player.getCurrentServer().getPort()); - session.setRemoteAuthType(player.getCurrentServer().isOnline() ? AuthType.ONLINE : AuthType.OFFLINE); - - return super.handle(packet); - } -} diff --git a/src/main/java/org/geysermc/connect/proxy/ProxyConnectorServerEventHandler.java b/src/main/java/org/geysermc/connect/proxy/ProxyConnectorServerEventHandler.java index ab95c05..8551d51 100644 --- a/src/main/java/org/geysermc/connect/proxy/ProxyConnectorServerEventHandler.java +++ b/src/main/java/org/geysermc/connect/proxy/ProxyConnectorServerEventHandler.java @@ -35,50 +35,13 @@ import java.util.concurrent.TimeUnit; public class ProxyConnectorServerEventHandler extends ConnectorServerEventHandler { - private final GeyserConnector connector; - public ProxyConnectorServerEventHandler(GeyserConnector connector) { super(connector); - this.connector = connector; - MasterServer.getInstance().getLogger().debug("Registered custom ConnectorServerEventHandler"); } @Override public void onSessionCreation(BedrockServerSession bedrockServerSession) { super.onSessionCreation(bedrockServerSession); - - // This doesn't clean up the old packet handler, so may cause a memory leak? - GeyserProxySession session = new GeyserProxySession(connector, bedrockServerSession); - bedrockServerSession.setPacketHandler(new GeyserProxyUpstreamPacketHandler(connector, session)); - - // Add another disconnect handler to remove the player on final disconnect - bedrockServerSession.addDisconnectHandler(disconnectReason -> { - // Make sure nothing is null before locating the player - if (MasterServer.getInstance() == null - || session.getAuthData() == null - || session.getAuthData().getXboxUUID() == null) { - return; - } - - Player player = session.getPlayer(); - if (player != null) { - MasterServer.getInstance().getLogger().debug("Player disconnected from Geyser proxy: " + player.getDisplayName() + " (" + disconnectReason + ")"); - - // Set the last disconnect time - MasterServer.getInstance().setLastDisconnectTime(System.currentTimeMillis()); - - int shutdownTime = MasterServer.getInstance().getGeyserConnectConfig().getGeyser().getShutdownTime(); - - if (shutdownTime != -1) { - MasterServer.getInstance().getGeneralThreadPool().schedule(() -> { - if (System.currentTimeMillis() - MasterServer.getInstance().getLastDisconnectTime() > shutdownTime * 1000L - && connector != null - && connector.getPlayers().size() <= 0) { - MasterServer.getInstance().shutdownGeyserProxy(); - } - }, shutdownTime, TimeUnit.SECONDS); - } - } - }); + bedrockServerSession.disconnect("Not sure how you managed it, but you shouldn't be here!"); } } diff --git a/src/main/java/org/geysermc/connect/storage/JsonStorageManager.java b/src/main/java/org/geysermc/connect/storage/JsonStorageManager.java index d51e560..6f79913 100644 --- a/src/main/java/org/geysermc/connect/storage/JsonStorageManager.java +++ b/src/main/java/org/geysermc/connect/storage/JsonStorageManager.java @@ -51,7 +51,7 @@ public class JsonStorageManager extends AbstractStorageManager { @Override public void saveServers(Player player) { try { - mapper.writeValue(dataFolder.resolve(player.getXuid() + ".json").toFile(), player.getServers()); + mapper.writeValue(dataFolder.resolve(player.getAuthData().getXboxUUID() + ".json").toFile(), player.getServers()); } catch (IOException ignored) { } } @@ -60,7 +60,7 @@ public class JsonStorageManager extends AbstractStorageManager { List servers = new ArrayList<>(); try { - List loadedServers = mapper.readValue(dataFolder.resolve(player.getXuid() + ".json").toFile(), new TypeReference>(){}); + List loadedServers = mapper.readValue(dataFolder.resolve(player.getAuthData().getXboxUUID() + ".json").toFile(), new TypeReference>(){}); servers.addAll(loadedServers); } catch (IOException ignored) { } diff --git a/src/main/java/org/geysermc/connect/storage/MySQLStorageManager.java b/src/main/java/org/geysermc/connect/storage/MySQLStorageManager.java index 68f3bb7..7d6cc31 100644 --- a/src/main/java/org/geysermc/connect/storage/MySQLStorageManager.java +++ b/src/main/java/org/geysermc/connect/storage/MySQLStorageManager.java @@ -69,7 +69,7 @@ public class MySQLStorageManager extends AbstractStorageManager { public void saveServers(Player player) { try { Statement updatePlayersServers = connection.createStatement(); - updatePlayersServers.executeUpdate("REPLACE INTO players(xuid, servers) VALUES('" + player.getXuid() + "', '" + mapper.writeValueAsString(player.getServers()) + "');"); + updatePlayersServers.executeUpdate("REPLACE INTO players(xuid, servers) VALUES('" + player.getAuthData().getXboxUUID() + "', '" + mapper.writeValueAsString(player.getServers()) + "');"); updatePlayersServers.close(); } catch (IOException | SQLException ignored) { } } @@ -80,7 +80,7 @@ public class MySQLStorageManager extends AbstractStorageManager { try { Statement getPlayersServers = connection.createStatement(); - ResultSet rs = getPlayersServers.executeQuery("SELECT servers FROM players WHERE xuid='" + player.getXuid() + "';"); + ResultSet rs = getPlayersServers.executeQuery("SELECT servers FROM players WHERE xuid='" + player.getAuthData().getXboxUUID() + "';"); while (rs.next()) { List loadedServers = mapper.readValue(rs.getString("servers"), new TypeReference>(){}); diff --git a/src/main/java/org/geysermc/connect/storage/SQLiteStorageManager.java b/src/main/java/org/geysermc/connect/storage/SQLiteStorageManager.java index b20aca5..6a3dcfd 100644 --- a/src/main/java/org/geysermc/connect/storage/SQLiteStorageManager.java +++ b/src/main/java/org/geysermc/connect/storage/SQLiteStorageManager.java @@ -67,7 +67,7 @@ public class SQLiteStorageManager extends AbstractStorageManager { public void saveServers(Player player) { try { Statement updatePlayersServers = connection.createStatement(); - updatePlayersServers.executeUpdate("INSERT OR REPLACE INTO players(xuid, servers) VALUES('" + player.getXuid() + "', '" + mapper.writeValueAsString(player.getServers()) + "');"); + updatePlayersServers.executeUpdate("INSERT OR REPLACE INTO players(xuid, servers) VALUES('" + player.getAuthData().getXboxUUID() + "', '" + mapper.writeValueAsString(player.getServers()) + "');"); updatePlayersServers.close(); } catch (IOException | SQLException ignored) { } } @@ -78,7 +78,7 @@ public class SQLiteStorageManager extends AbstractStorageManager { try { Statement getPlayersServers = connection.createStatement(); - ResultSet rs = getPlayersServers.executeQuery("SELECT servers FROM players WHERE xuid='" + player.getXuid() + "';"); + ResultSet rs = getPlayersServers.executeQuery("SELECT servers FROM players WHERE xuid='" + player.getAuthData().getXboxUUID() + "';"); while (rs.next()) { List loadedServers = mapper.readValue(rs.getString("servers"), new TypeReference>() {}); diff --git a/src/main/java/org/geysermc/connect/utils/PaletteManger.java b/src/main/java/org/geysermc/connect/utils/PaletteManger.java index 528fbc6..e7b124a 100644 --- a/src/main/java/org/geysermc/connect/utils/PaletteManger.java +++ b/src/main/java/org/geysermc/connect/utils/PaletteManger.java @@ -29,60 +29,16 @@ import com.nukkitx.nbt.*; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; /** * This class is mostly copied from core Geyser */ public class PaletteManger { - public static final NbtList BLOCK_PALETTE; - public static final NbtMap BIOMES_PALETTE; public static final byte[] EMPTY_LEVEL_CHUNK_DATA; - private static final NbtMap EMPTY_TAG = NbtMap.EMPTY; static { - /* Load block palette */ - // Build the air block entry - NbtMapBuilder mainBuilder = NbtMap.builder(); - mainBuilder.putShort("id", (short) 0); - - NbtMapBuilder blockBuilder = NbtMap.builder(); - blockBuilder.putString("name", "minecraft:air"); - blockBuilder.putInt("version", 17825806); - blockBuilder.put("states", NbtMap.EMPTY); - - mainBuilder.put("block", blockBuilder.build()); - - // Build the block list with the entry - List blocks = new ArrayList<>(); - blocks.add(mainBuilder.build()); - - BLOCK_PALETTE = new NbtList<>(NbtType.COMPOUND, blocks); - - /* Load biomes */ - // Build a fake plains biome entry - NbtMapBuilder plainsBuilder = NbtMap.builder(); - plainsBuilder.putFloat("blue_spores", 0f); - plainsBuilder.putFloat("white_ash", 0f); - plainsBuilder.putFloat("ash", 0f); - plainsBuilder.putFloat("temperature", 0f); - plainsBuilder.putFloat("red_spores", 0f); - plainsBuilder.putFloat("downfall", 0f); - - plainsBuilder.put("minecraft:overworld_generation_rules", NbtMap.EMPTY); - plainsBuilder.put("minecraft:climate", NbtMap.EMPTY); - plainsBuilder.put("tags", NbtList.EMPTY); - - // Add the fake plains to the map - NbtMapBuilder biomesBuilder = NbtMap.builder(); - biomesBuilder.put("plains", plainsBuilder.build()); - - // Build the biomes palette - BIOMES_PALETTE = biomesBuilder.build(); - /* Create empty chunk data */ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size diff --git a/src/main/java/org/geysermc/connect/utils/Player.java b/src/main/java/org/geysermc/connect/utils/Player.java index 0cdc77b..5531d36 100644 --- a/src/main/java/org/geysermc/connect/utils/Player.java +++ b/src/main/java/org/geysermc/connect/utils/Player.java @@ -25,34 +25,39 @@ package org.geysermc.connect.utils; -import com.fasterxml.jackson.databind.JsonNode; import com.nukkitx.math.vector.Vector2f; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.data.*; -import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.*; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import lombok.Getter; import lombok.Setter; import org.geysermc.connect.MasterServer; +import org.geysermc.connect.proxy.GeyserProxySession; import org.geysermc.connect.ui.FormID; import org.geysermc.connect.ui.UIHandler; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.common.AuthType; +import org.geysermc.connector.network.UpstreamPacketHandler; +import org.geysermc.connector.network.session.auth.AuthData; import org.geysermc.connector.network.session.auth.BedrockClientData; +import org.geysermc.connector.network.translators.BiomeTranslator; +import org.geysermc.connector.network.translators.EntityIdentifierRegistry; +import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.network.translators.world.block.BlockTranslator1_17_0; +import org.geysermc.connector.utils.DimensionUtils; import org.geysermc.cumulus.Form; import java.util.ArrayList; import java.util.List; -import java.util.UUID; @Getter public class Player { - private final String xuid; - private final UUID identity; - private final String displayName; + private final AuthData authData; private final BedrockServerSession session; @@ -71,11 +76,8 @@ public class Player { @Setter private ServerCategory serverCategory; - public Player(JsonNode extraData, BedrockServerSession session) { - this.xuid = extraData.get("XUID").asText(); - this.identity = UUID.fromString(extraData.get("identity").asText()); - this.displayName = extraData.get("displayName").asText(); - + public Player(AuthData authData, BedrockServerSession session) { + this.authData = authData; this.session = session; // Should fetch the servers from some form of db @@ -128,7 +130,7 @@ public class Player { startGamePacket.setWorldTemplateOptionLocked(false); startGamePacket.setLevelId(""); - startGamePacket.setLevelName("GeyserMulti"); + startGamePacket.setLevelName("GeyserConnect"); startGamePacket.setPremiumWorldTemplateId(""); startGamePacket.setCurrentTick(0); startGamePacket.setEnchantmentSeed(0); @@ -144,11 +146,6 @@ public class Player { startGamePacket.setVanillaVersion("*"); session.sendPacket(startGamePacket); - // Send a CreativeContentPacket - required for 1.16.100 - CreativeContentPacket creativeContentPacket = new CreativeContentPacket(); - creativeContentPacket.setContents(new ItemData[0]); - session.sendPacket(creativeContentPacket); - // Send an empty chunk LevelChunkPacket data = new LevelChunkPacket(); data.setChunkX(0); @@ -160,9 +157,18 @@ public class Player { // Send the biomes BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket(); - biomeDefinitionListPacket.setDefinitions(PaletteManger.BIOMES_PALETTE); + biomeDefinitionListPacket.setDefinitions(BiomeTranslator.BIOMES); session.sendPacket(biomeDefinitionListPacket); + AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); + entityPacket.setIdentifiers(EntityIdentifierRegistry.ENTITY_IDENTIFIERS); + session.sendPacket(entityPacket); + + // Send a CreativeContentPacket - required for 1.16.100 + CreativeContentPacket creativeContentPacket = new CreativeContentPacket(); + creativeContentPacket.setContents(ItemRegistry.CREATIVE_ITEMS); + session.sendPacket(creativeContentPacket); + // Let the client know the player can spawn PlayStatusPacket playStatusPacket = new PlayStatusPacket(); playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); @@ -206,33 +212,41 @@ public class Player { * Send the player to the Geyser proxy server or straight to the bedrock server if it is */ public void connectToProxy() { - // Use the clients connecting IP then fallback to the remote address from config - String address = clientData.getServerAddress().split(":")[0].trim(); - if (address.isEmpty()) { - address = MasterServer.getInstance().getGeyserConnectConfig().getRemoteAddress(); - } - - int port = MasterServer.getInstance().getGeyserConnectConfig().getGeyser().getPort(); - if (currentServer.isBedrock()) { - address = currentServer.getAddress(); - port = currentServer.getPort(); - } + TransferPacket transferPacket = new TransferPacket(); + transferPacket.setAddress(currentServer.getAddress()); + transferPacket.setPort(currentServer.getPort()); + session.sendPacket(transferPacket); + } else { + GeyserProxySession geyserSession = new GeyserProxySession(GeyserConnector.getInstance(), session); + session.setPacketHandler(new UpstreamPacketHandler(GeyserConnector.getInstance(), geyserSession)); - TransferPacket transferPacket = new TransferPacket(); - transferPacket.setAddress(address); - transferPacket.setPort(port); - session.sendPacket(transferPacket); + geyserSession.getUpstream().getSession().setPacketCodec(session.getPacketCodec()); + + // Set the block translation based off of version + geyserSession.setBlockTranslator(BlockTranslator1_17_0.INSTANCE); + + geyserSession.setAuthData(authData); + geyserSession.setClientData(clientData); + + geyserSession.setDimension(DimensionUtils.THE_END); + + geyserSession.setRemoteAddress(currentServer.getAddress()); + geyserSession.setRemotePort(currentServer.getPort()); + geyserSession.setRemoteAuthType(currentServer.isOnline() ? AuthType.ONLINE : AuthType.OFFLINE); + + // Tell Geyser to handle the login + SetLocalPlayerAsInitializedPacket initializedPacket = new SetLocalPlayerAsInitializedPacket(); + initializedPacket.setRuntimeEntityId(geyserSession.getPlayerEntity().getGeyserId()); + session.getPacketHandler().handle(initializedPacket); + } } public void sendToServer(Server server) { // Tell the user we are connecting them // This wont show up in a lot of cases as the client connects quite quickly - sendWindow(FormID.CONNECTING, UIHandler.getWaitingScreen(server)); - - if (!server.isBedrock()) { - // Create the Geyser instance if its not already running - MasterServer.getInstance().createGeyserProxy(); + if (!server.isOnline()) { + sendWindow(FormID.CONNECTING, UIHandler.getWaitingScreen(server)); } // Send the user over to the server diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index eab34c9..8d5c461 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -5,9 +5,6 @@ # The IP address that will listen for connections address: 0.0.0.0 -# The IP address remote clients use to connect, can be set to 'auto' to fetch from the web -remote-address: auto - # The port that will listen for connections port: 19132 @@ -28,22 +25,12 @@ welcome-file: welcome.txt # Config for the Geyser listener geyser: - # The port that will listen for connections - port: 19133 - - # Configures if chunk caching should be enabled or not. - cache-chunks: true - # If password authentication should be allowed in online mode. allow-password-authentication: false # If debug messages should be sent through console, has to be enabled in both places to work debug-mode: false - # The time to wait after the last player disconnects to shutdown the proxy - # In seconds, set to -1 to disable - shutdown-time: 300 - # A global list of servers sent to all clients servers: - name: The Hive diff --git a/src/main/resources/proxy_config.yml b/src/main/resources/proxy_config.yml index 72ab933..6944828 100644 --- a/src/main/resources/proxy_config.yml +++ b/src/main/resources/proxy_config.yml @@ -12,45 +12,70 @@ bedrock: # There is no reason to change this unless you want to limit what IPs can connect to your server. address: 0.0.0.0 # The port that will listen for connections - port: PORT + port: 19132 # Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock. # This option makes the Bedrock port the same as the Java port every time you start the server. # This option is for the plugin version only. clone-remote-port: false # The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients. This is irrelevant if "passthrough-motd" is set to true - motd1: "MOTD" - motd2: "MOTD" + # If either of these are empty, the respective string will default to "Geyser" + motd1: "%MOTD%" + motd2: "%MOTD%" # The Server Name that will be sent to Minecraft: Bedrock Edition clients. This is visible in both the pause menu and the settings menu. server-name: "Geyser" + # How much to compress network traffic to the Bedrock client. The higher the number, the more CPU usage used, but + # the smaller the bandwidth used. Does not have any effect below -1 or above 9. Set to -1 to disable. + compression-level: 6 + # Whether to enable PROXY protocol or not for clients. You DO NOT WANT this feature unless you run UDP reverse proxy + # in front of your Geyser instance. + enable-proxy-protocol: false + # A list of allowed PROXY protocol speaking proxy IP addresses/subnets. Only effective when "enable-proxy-protocol" is enabled, and + # should really only be used when you are not able to use a proper firewall (usually true with shared hosting providers etc.). + # Keeping this list empty means there is no IP address whitelist. + # Both IP addresses and subnets are supported. + #proxy-protocol-whitelisted-ips: [ "127.0.0.1", "172.18.0.0/16" ] remote: # The IP address of the remote (Java Edition) server # If it is "auto", for standalone version the remote address will be set to 127.0.0.1, # for plugin versions, Geyser will attempt to find the best address to connect to. - address: 127.0.0.1 + address: auto # The port of the remote (Java Edition) server # For plugin versions, if address has been set to "auto", the port will also follow the server's listening port. port: 25565 # Authentication type. Can be offline, online, or floodgate (see https://github.com/GeyserMC/Geyser/wiki/Floodgate). auth-type: online - allow-password-authentication: ALLOWPASSWORDAUTHENTICATION + # Allow for password-based authentication methods through Geyser. Only useful in online mode. + # If this is false, users must authenticate to Microsoft using a code provided by Geyser on their desktop. + allow-password-authentication: %ALLOWPASSWORDAUTHENTICATION% + # Whether to enable PROXY protocol or not while connecting to the server. + # This is useful only when: + # 1) Your server supports PROXY protocol (it probably doesn't) + # 2) You run Velocity or BungeeCord with the option enabled in the proxy's main config. + # IF YOU DON'T KNOW WHAT THIS IS, DON'T TOUCH IT! + use-proxy-protocol: false + # Forward the hostname that the Bedrock client used to connect over to the Java server + # This is designed to be used for forced hosts on proxies + forward-hostname: false # 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 -## a config file +# The Xbox/Minecraft Bedrock username is the key for the Java server auth-info. +# This allows automatic configuration/login to the remote Java server. +# If you are brave enough to put your Mojang account info into a config file. +# Uncomment the lines below to enable this feature. #userAuths: -# bluerkelp2: # MCPE/Xbox username -# email: not_really_my_email_address_mr_minecrafter53267@gmail.com # Mojang account email address -# password: "this isn't really my password" +# BedrockAccountUsername: # Your Minecraft: Bedrock Edition username +# email: javaccountemail@example.com # Your Minecraft: Java Edition email +# password: javaccountpassword123 # Your Minecraft: Java Edition password +# microsoft-account: true # Whether the account is a Mojang or Microsoft account. # -# herpderp40300499303040503030300500293858393589: -# email: herpderp@derpherp.com -# password: dooooo +# bluerkelp2: +# email: not_really_my_email_address_mr_minecrafter53267@gmail.com +# password: "this isn't really my password" +# microsoft-account: false # Bedrock clients can freeze when opening up the command prompt for the first time if given a lot of commands. # Disabling this will prevent command suggestions from being sent and solve freezing for Bedrock clients. @@ -71,8 +96,12 @@ legacy-ping-passthrough: false # Increase if you are getting BrokenPipe errors. ping-passthrough-interval: 3 -# Maximum amount of players that can connect -max-players: PLAYERS +# Whether to forward player ping to the server. While enabling this will allow Bedrock players to have more accurate +# ping, it may also cause players to time out more easily. +forward-player-ping: false + +# Maximum amount of players that can connect. This is only visual at this time and does not actually limit player count. +max-players: %PLAYERS% # If debug messages should be sent through console debug-mode: false @@ -89,24 +118,47 @@ allow-third-party-capes: true allow-third-party-ears: false # Allow a fake cooldown indicator to be sent. Bedrock players do not see a cooldown as they still use 1.8 combat -show-cooldown: true +# Can be title, actionbar or false +show-cooldown: title + +# Controls if coordinates are shown to players. +show-coordinates: true + +# If set, when a Bedrock player performs any emote, it will swap the offhand and mainhand items, just like the Java Edition keybind +# There are three options this can be set to: +# disabled - the default/fallback, which doesn't apply this workaround +# no-emotes - emotes will NOT be sent to other Bedrock clients and offhand will be swapped. This effectively disables all emotes from being seen. +# emotes-and-offhand - emotes will be sent to Bedrock clients and offhand will be swapped +emote-offhand-workaround: "disabled" # The default locale if we dont have the one the client requested. Uncomment to not use the default system language. # default-locale: en_us # Configures if chunk caching should be enabled or not. This keeps an individual -# record of each block the client loads in. While this feature does allow for a few -# things such as block break animations to show up in creative mode and among others, -# it is HIGHLY recommended you disable this on a production environment as it can eat -# up a lot of RAM. However, when using the Spigot version of Geyser, support for features -# or implementations this allows is automatically enabled without the additional caching as -# Geyser has direct access to the server itself. -cache-chunks: CACHECHUNKS +# record of each block the client loads in. This feature does allow for a few things +# such as more accurate movement that causes less problems with anticheat (meaning +# you're less likely to be banned) and allows block break animations to show up in +# creative mode (and other features). Although this increases RAM usage, it likely +# won't have much of an effect for the vast majority of people. However, if you're +# running out of RAM or are in a RAM-sensitive environment, you may want to disable +# this. When using the Spigot version of Geyser, support for features or +# implementations this allows is automatically enabled without the additional caching +# as Geyser has direct access to the server itself. +cache-chunks: true # Specify how many days images will be cached to disk to save downloading them from the internet. # A value of 0 is disabled. (Default: 0) cache-images: 0 +# Allows custom skulls to be displayed. Keeping them enabled may cause a performance decrease on older/weaker devices. +allow-custom-skulls: true + +# Whether to add (at this time, only) the furnace minecart as a separate item in the game, which normally does not exist in Bedrock Edition. +# This should only need to be disabled if using a proxy that does not use the "transfer packet" style of server switching. +# If this is disabled, furnace minecart items will be mapped to hopper minecart items. +# This option requires a restart of Geyser in order to change its setting. +add-non-bedrock-items: true + # Bedrock prevents building and displaying blocks above Y127 in the Nether - # enabling this config option works around that by changing the Nether dimension ID # to the End ID. The main downside to this is that the sky will resemble that of @@ -114,9 +166,13 @@ cache-images: 0 above-bedrock-nether-building: false # Force clients to load all resource packs if there are any. -# If set to false it allows the user to disconnect from the server if they don't -# want to download the resource packs -force-resource-packs: false +# If set to false, it allows the user to connect to the server even if they don't +# want to download the resource packs. +force-resource-packs: true + +# Allows Xbox achievements to be unlocked. +# THIS DISABLES ALL COMMANDS FROM SUCCESSFULLY RUNNING FOR BEDROCK IN-GAME, as otherwise Bedrock thinks you are cheating. +xbox-achievements-enabled: false # bStats is a stat tracker that is entirely anonymous and tracks only basic information # about Geyser, such as how many people are online, how many servers are using Geyser, @@ -144,4 +200,8 @@ enable-proxy-connections: false # 1400 is the default. # mtu: 1400 +# Whether to use direct server methods to retrieve information such as block states. +# Turning this off for Spigot will stop NMS from being used but will have a performance impact. +use-adapters: true + config-version: 4 \ No newline at end of file