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
This commit is contained in:
rtm516 2021-06-16 12:18:07 +01:00 committed by GitHub
parent 9bd9c5e176
commit eeadde0550
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 178 additions and 278 deletions

View file

@ -5,7 +5,7 @@
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![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/) [![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/) [![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. GeyserConnect is an easy way for Bedrock Edition clients to connect to any Java Edition servers without having to run anything.

View file

@ -2,5 +2,4 @@ FROM openjdk:8-jre-slim
RUN mkdir /gsc RUN mkdir /gsc
WORKDIR /gsc WORKDIR /gsc
EXPOSE 19132/udp EXPOSE 19132/udp
EXPOSE 19133/udp
CMD ["java", "-Xms1G", "-jar", "GeyserConnect.jar"] CMD ["java", "-Xms1G", "-jar", "GeyserConnect.jar"]

View file

@ -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 .` 2. Build the docker file using `docker build -t geyser-connect .`
3. Start geyser using this: 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
``` ```

View file

@ -42,9 +42,6 @@ public class GeyserConnectConfig {
private String address; private String address;
@JsonProperty("remote-address")
private String remoteAddress;
private int port; private int port;
@JsonProperty("max-players") @JsonProperty("max-players")
@ -69,29 +66,14 @@ public class GeyserConnectConfig {
private VirtualHostSection vhost; 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 @Getter
public static class GeyserConfigSection { public static class GeyserConfigSection {
private int port;
@JsonProperty("cache-chunks")
private boolean cacheChunks = true;
@JsonProperty("allow-password-authentication") @JsonProperty("allow-password-authentication")
private boolean allowPasswordAuthentication = false; private boolean allowPasswordAuthentication = false;
@JsonProperty("debug-mode") @JsonProperty("debug-mode")
private boolean debugMode; private boolean debugMode;
@JsonProperty("shutdown-time")
private int shutdownTime;
} }
@Getter @Getter

View file

@ -105,8 +105,6 @@ public class MasterServer {
logger.setDebug(geyserConnectConfig.isDebugMode()); logger.setDebug(geyserConnectConfig.isDebugMode());
geyserConnectConfig.checkRemoteIP();
this.generalThreadPool = Executors.newScheduledThreadPool(32); this.generalThreadPool = Executors.newScheduledThreadPool(32);
// Start a timer to keep the thread running // Start a timer to keep the thread running
@ -131,7 +129,7 @@ public class MasterServer {
// Create the base welcome.txt file // Create the base welcome.txt file
try { 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) { } } catch (IOException ignored) { }
start(geyserConnectConfig.getPort()); start(geyserConnectConfig.getPort());
@ -184,6 +182,10 @@ public class MasterServer {
// Start server up // Start server up
bdServer.bind().join(); bdServer.bind().join();
// Create the Geyser instance
createGeyserProxy();
logger.info("Server started on " + geyserConnectConfig.getAddress() + ":" + port); logger.info("Server started on " + geyserConnectConfig.getAddress() + ":" + port);
} }
@ -200,6 +202,9 @@ public class MasterServer {
public void createGeyserProxy() { public void createGeyserProxy() {
if (geyserProxy == null) { if (geyserProxy == null) {
// Make sure Geyser doesn't start the listener
GeyserConnector.setShouldStartListener(false);
this.geyserProxy = new GeyserProxyBootstrap(); this.geyserProxy = new GeyserProxyBootstrap();
geyserProxy.onEnable(); geyserProxy.onEnable();
} }

View file

@ -44,6 +44,7 @@ import org.geysermc.connect.utils.Player;
import org.geysermc.connect.utils.Server; import org.geysermc.connect.utils.Server;
import org.geysermc.connector.entity.attribute.AttributeType; import org.geysermc.connector.entity.attribute.AttributeType;
import org.geysermc.connector.network.BedrockProtocol; 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.network.session.auth.BedrockClientData;
import org.geysermc.connector.utils.AttributeUtils; import org.geysermc.connector.utils.AttributeUtils;
import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.FileUtils;
@ -57,6 +58,7 @@ import java.io.IOException;
import java.security.interfaces.ECPublicKey; import java.security.interfaces.ECPublicKey;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class PacketHandler implements BedrockPacketHandler { public class PacketHandler implements BedrockPacketHandler {
@ -77,13 +79,13 @@ public class PacketHandler implements BedrockPacketHandler {
public void disconnect(DisconnectReason reason) { public void disconnect(DisconnectReason reason) {
if (player != null) { 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); masterServer.getStorageManager().saveServers(player);
if (player.getCurrentServer() != null && !player.getCurrentServer().isBedrock()) { 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 // Fetch the client data
JsonNode extraData = payload.get("extraData"); 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 // Create a new player and add it to the players list
player = new Player(extraData, session); player = new Player(authData, session);
masterServer.getPlayers().put(player.getXuid(), player); masterServer.getPlayers().put(player.getAuthData().getXboxUUID(), player);
// Store the full client data // Store the full client data
player.setClientData(OBJECT_MAPPER.convertValue(OBJECT_MAPPER.readTree(skinData.getPayload().toBytes()), BedrockClientData.class)); 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) { public boolean handle(ResourcePackClientResponsePacket packet) {
switch (packet.getStatus()) { switch (packet.getStatus()) {
case COMPLETED: 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(); player.sendStartGame();
break; break;
case HAVE_ALL_PACKS: case HAVE_ALL_PACKS:
@ -205,7 +214,7 @@ public class PacketHandler implements BedrockPacketHandler {
@Override @Override
public boolean handle(SetLocalPlayerAsInitializedPacket packet) { 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 // Handle the virtual host if specified
GeyserConnectConfig.VirtualHostSection vhost = MasterServer.getInstance().getGeyserConnectConfig().getVhost(); GeyserConnectConfig.VirtualHostSection vhost = MasterServer.getInstance().getGeyserConnectConfig().getVhost();
@ -236,7 +245,7 @@ public class PacketHandler implements BedrockPacketHandler {
} }
// Log the virtual host usage // 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 // Send the player to the wanted server
player.sendToServer(new Server(address, port, online, false)); player.sendToServer(new Server(address, port, online, false));

View file

@ -68,11 +68,9 @@ public class GeyserProxyBootstrap implements GeyserBootstrap {
// Grab the config as text and replace static strings to the main config variables // 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")); String text = new BufferedReader(new InputStreamReader(configFile, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("\n"));
GeyserConnectConfig multiConfig = MasterServer.getInstance().getGeyserConnectConfig(); GeyserConnectConfig multiConfig = MasterServer.getInstance().getGeyserConnectConfig();
text = text.replace("PORT", String.valueOf(multiConfig.getGeyser().getPort())); text = text.replaceAll("%MOTD%", multiConfig.getMotd());
text = text.replaceAll("MOTD", multiConfig.getMotd()); text = text.replace("%PLAYERS%", String.valueOf(multiConfig.getMaxPlayers()));
text = text.replace("PLAYERS", String.valueOf(multiConfig.getMaxPlayers())); text = text.replace("%ALLOWPASSWORDAUTHENTICATION%", String.valueOf(multiConfig.getGeyser().isAllowPasswordAuthentication()));
text = text.replace("CACHECHUNKS", String.valueOf(multiConfig.getGeyser().isCacheChunks()));
text = text.replace("ALLOWPASSWORDAUTHENTICATION", String.valueOf(multiConfig.getGeyser().isAllowPasswordAuthentication()));
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
geyserConfig = objectMapper.readValue(text, GeyserProxyConfiguration.class); geyserConfig = objectMapper.readValue(text, GeyserProxyConfiguration.class);

View file

@ -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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -26,35 +26,12 @@
package org.geysermc.connect.proxy; package org.geysermc.connect.proxy;
import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.BedrockServerSession;
import lombok.Getter;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.common.AuthType;
import org.geysermc.connector.network.session.GeyserSession; 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 { public class GeyserProxySession extends GeyserSession {
private final BedrockServerSession bedrockServerSession;
@Getter
private Player player;
public GeyserProxySession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { public GeyserProxySession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
super(connector, 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 @Override

View file

@ -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);
}
}

View file

@ -35,50 +35,13 @@ import java.util.concurrent.TimeUnit;
public class ProxyConnectorServerEventHandler extends ConnectorServerEventHandler { public class ProxyConnectorServerEventHandler extends ConnectorServerEventHandler {
private final GeyserConnector connector;
public ProxyConnectorServerEventHandler(GeyserConnector connector) { public ProxyConnectorServerEventHandler(GeyserConnector connector) {
super(connector); super(connector);
this.connector = connector;
MasterServer.getInstance().getLogger().debug("Registered custom ConnectorServerEventHandler");
} }
@Override @Override
public void onSessionCreation(BedrockServerSession bedrockServerSession) { public void onSessionCreation(BedrockServerSession bedrockServerSession) {
super.onSessionCreation(bedrockServerSession); super.onSessionCreation(bedrockServerSession);
bedrockServerSession.disconnect("Not sure how you managed it, but you shouldn't be here!");
// 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);
}
}
});
} }
} }

View file

@ -51,7 +51,7 @@ public class JsonStorageManager extends AbstractStorageManager {
@Override @Override
public void saveServers(Player player) { public void saveServers(Player player) {
try { 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) { } } catch (IOException ignored) { }
} }
@ -60,7 +60,7 @@ public class JsonStorageManager extends AbstractStorageManager {
List<Server> servers = new ArrayList<>(); List<Server> servers = new ArrayList<>();
try { try {
List<Server> loadedServers = mapper.readValue(dataFolder.resolve(player.getXuid() + ".json").toFile(), new TypeReference<List<Server>>(){}); List<Server> loadedServers = mapper.readValue(dataFolder.resolve(player.getAuthData().getXboxUUID() + ".json").toFile(), new TypeReference<List<Server>>(){});
servers.addAll(loadedServers); servers.addAll(loadedServers);
} catch (IOException ignored) { } } catch (IOException ignored) { }

View file

@ -69,7 +69,7 @@ public class MySQLStorageManager extends AbstractStorageManager {
public void saveServers(Player player) { public void saveServers(Player player) {
try { try {
Statement updatePlayersServers = connection.createStatement(); 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(); updatePlayersServers.close();
} catch (IOException | SQLException ignored) { } } catch (IOException | SQLException ignored) { }
} }
@ -80,7 +80,7 @@ public class MySQLStorageManager extends AbstractStorageManager {
try { try {
Statement getPlayersServers = connection.createStatement(); 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()) { while (rs.next()) {
List<Server> loadedServers = mapper.readValue(rs.getString("servers"), new TypeReference<List<Server>>(){}); List<Server> loadedServers = mapper.readValue(rs.getString("servers"), new TypeReference<List<Server>>(){});

View file

@ -67,7 +67,7 @@ public class SQLiteStorageManager extends AbstractStorageManager {
public void saveServers(Player player) { public void saveServers(Player player) {
try { try {
Statement updatePlayersServers = connection.createStatement(); 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(); updatePlayersServers.close();
} catch (IOException | SQLException ignored) { } } catch (IOException | SQLException ignored) { }
} }
@ -78,7 +78,7 @@ public class SQLiteStorageManager extends AbstractStorageManager {
try { try {
Statement getPlayersServers = connection.createStatement(); 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()) { while (rs.next()) {
List<Server> loadedServers = mapper.readValue(rs.getString("servers"), new TypeReference<List<Server>>() {}); List<Server> loadedServers = mapper.readValue(rs.getString("servers"), new TypeReference<List<Server>>() {});

View file

@ -29,60 +29,16 @@ import com.nukkitx.nbt.*;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/** /**
* This class is mostly copied from core Geyser * This class is mostly copied from core Geyser
*/ */
public class PaletteManger { public class PaletteManger {
public static final NbtList<NbtMap> BLOCK_PALETTE;
public static final NbtMap BIOMES_PALETTE;
public static final byte[] EMPTY_LEVEL_CHUNK_DATA; public static final byte[] EMPTY_LEVEL_CHUNK_DATA;
private static final NbtMap EMPTY_TAG = NbtMap.EMPTY; private static final NbtMap EMPTY_TAG = NbtMap.EMPTY;
static { 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<NbtMap> 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 */ /* Create empty chunk data */
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size

View file

@ -25,34 +25,39 @@
package org.geysermc.connect.utils; package org.geysermc.connect.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.nukkitx.math.vector.Vector2f; import com.nukkitx.math.vector.Vector2f;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.BedrockServerSession;
import com.nukkitx.protocol.bedrock.data.*; import com.nukkitx.protocol.bedrock.data.*;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.*; import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.geysermc.connect.MasterServer; import org.geysermc.connect.MasterServer;
import org.geysermc.connect.proxy.GeyserProxySession;
import org.geysermc.connect.ui.FormID; import org.geysermc.connect.ui.FormID;
import org.geysermc.connect.ui.UIHandler; 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.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 org.geysermc.cumulus.Form;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID;
@Getter @Getter
public class Player { public class Player {
private final String xuid; private final AuthData authData;
private final UUID identity;
private final String displayName;
private final BedrockServerSession session; private final BedrockServerSession session;
@ -71,11 +76,8 @@ public class Player {
@Setter @Setter
private ServerCategory serverCategory; private ServerCategory serverCategory;
public Player(JsonNode extraData, BedrockServerSession session) { public Player(AuthData authData, BedrockServerSession session) {
this.xuid = extraData.get("XUID").asText(); this.authData = authData;
this.identity = UUID.fromString(extraData.get("identity").asText());
this.displayName = extraData.get("displayName").asText();
this.session = session; this.session = session;
// Should fetch the servers from some form of db // Should fetch the servers from some form of db
@ -128,7 +130,7 @@ public class Player {
startGamePacket.setWorldTemplateOptionLocked(false); startGamePacket.setWorldTemplateOptionLocked(false);
startGamePacket.setLevelId(""); startGamePacket.setLevelId("");
startGamePacket.setLevelName("GeyserMulti"); startGamePacket.setLevelName("GeyserConnect");
startGamePacket.setPremiumWorldTemplateId(""); startGamePacket.setPremiumWorldTemplateId("");
startGamePacket.setCurrentTick(0); startGamePacket.setCurrentTick(0);
startGamePacket.setEnchantmentSeed(0); startGamePacket.setEnchantmentSeed(0);
@ -144,11 +146,6 @@ public class Player {
startGamePacket.setVanillaVersion("*"); startGamePacket.setVanillaVersion("*");
session.sendPacket(startGamePacket); 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 // Send an empty chunk
LevelChunkPacket data = new LevelChunkPacket(); LevelChunkPacket data = new LevelChunkPacket();
data.setChunkX(0); data.setChunkX(0);
@ -160,9 +157,18 @@ public class Player {
// Send the biomes // Send the biomes
BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket(); BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();
biomeDefinitionListPacket.setDefinitions(PaletteManger.BIOMES_PALETTE); biomeDefinitionListPacket.setDefinitions(BiomeTranslator.BIOMES);
session.sendPacket(biomeDefinitionListPacket); 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 // Let the client know the player can spawn
PlayStatusPacket playStatusPacket = new PlayStatusPacket(); PlayStatusPacket playStatusPacket = new PlayStatusPacket();
playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); 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 * Send the player to the Geyser proxy server or straight to the bedrock server if it is
*/ */
public void connectToProxy() { 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()) { if (currentServer.isBedrock()) {
address = currentServer.getAddress(); TransferPacket transferPacket = new TransferPacket();
port = currentServer.getPort(); 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(); geyserSession.getUpstream().getSession().setPacketCodec(session.getPacketCodec());
transferPacket.setAddress(address);
transferPacket.setPort(port); // Set the block translation based off of version
session.sendPacket(transferPacket); 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) { public void sendToServer(Server server) {
// Tell the user we are connecting them // Tell the user we are connecting them
// This wont show up in a lot of cases as the client connects quite quickly // This wont show up in a lot of cases as the client connects quite quickly
sendWindow(FormID.CONNECTING, UIHandler.getWaitingScreen(server)); if (!server.isOnline()) {
sendWindow(FormID.CONNECTING, UIHandler.getWaitingScreen(server));
if (!server.isBedrock()) {
// Create the Geyser instance if its not already running
MasterServer.getInstance().createGeyserProxy();
} }
// Send the user over to the server // Send the user over to the server

View file

@ -5,9 +5,6 @@
# The IP address that will listen for connections # The IP address that will listen for connections
address: 0.0.0.0 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 # The port that will listen for connections
port: 19132 port: 19132
@ -28,22 +25,12 @@ welcome-file: welcome.txt
# Config for the Geyser listener # Config for the Geyser listener
geyser: 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. # If password authentication should be allowed in online mode.
allow-password-authentication: false allow-password-authentication: false
# If debug messages should be sent through console, has to be enabled in both places to work # If debug messages should be sent through console, has to be enabled in both places to work
debug-mode: false 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 # A global list of servers sent to all clients
servers: servers:
- name: The Hive - name: The Hive

View file

@ -12,45 +12,70 @@ bedrock:
# There is no reason to change this unless you want to limit what IPs can connect to your server. # There is no reason to change this unless you want to limit what IPs can connect to your server.
address: 0.0.0.0 address: 0.0.0.0
# The port that will listen for connections # 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. # 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 makes the Bedrock port the same as the Java port every time you start the server.
# This option is for the plugin version only. # This option is for the plugin version only.
clone-remote-port: false 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 # The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients. This is irrelevant if "passthrough-motd" is set to true
motd1: "MOTD" # If either of these are empty, the respective string will default to "Geyser"
motd2: "MOTD" 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. # 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" 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: remote:
# The IP address of the remote (Java Edition) server # 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, # 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. # 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 # 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. # For plugin versions, if address has been set to "auto", the port will also follow the server's listening port.
port: 25565 port: 25565
# Authentication type. Can be offline, online, or floodgate (see https://github.com/GeyserMC/Geyser/wiki/Floodgate). # Authentication type. Can be offline, online, or floodgate (see https://github.com/GeyserMC/Geyser/wiki/Floodgate).
auth-type: online 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. # Floodgate uses encryption to ensure use from authorised sources.
# This should point to the public key generated by Floodgate (Bungee or CraftBukkit) # This should point to the public key generated by Floodgate (Bungee or CraftBukkit)
# You can ignore this when not using Floodgate. # You can ignore this when not using Floodgate.
floodgate-key-file: public-key.pem floodgate-key-file: public-key.pem
## the Xbox/MCPE username is the key for the Java server auth-info # The Xbox/Minecraft Bedrock username is the key for the Java server auth-info.
## this allows automatic configuration/login to the remote Java server # This allows automatic configuration/login to the remote Java server.
## if you are brave/stupid enough to put your Mojang account info into # If you are brave enough to put your Mojang account info into a config file.
## a config file # Uncomment the lines below to enable this feature.
#userAuths: #userAuths:
# bluerkelp2: # MCPE/Xbox username # BedrockAccountUsername: # Your Minecraft: Bedrock Edition username
# email: not_really_my_email_address_mr_minecrafter53267@gmail.com # Mojang account email address # email: javaccountemail@example.com # Your Minecraft: Java Edition email
# password: "this isn't really my password" # password: javaccountpassword123 # Your Minecraft: Java Edition password
# microsoft-account: true # Whether the account is a Mojang or Microsoft account.
# #
# herpderp40300499303040503030300500293858393589: # bluerkelp2:
# email: herpderp@derpherp.com # email: not_really_my_email_address_mr_minecrafter53267@gmail.com
# password: dooooo # 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. # 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. # 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. # Increase if you are getting BrokenPipe errors.
ping-passthrough-interval: 3 ping-passthrough-interval: 3
# Maximum amount of players that can connect # Whether to forward player ping to the server. While enabling this will allow Bedrock players to have more accurate
max-players: PLAYERS # 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 # If debug messages should be sent through console
debug-mode: false debug-mode: false
@ -89,24 +118,47 @@ allow-third-party-capes: true
allow-third-party-ears: false 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 # 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. # The default locale if we dont have the one the client requested. Uncomment to not use the default system language.
# default-locale: en_us # default-locale: en_us
# Configures if chunk caching should be enabled or not. This keeps an individual # 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 # record of each block the client loads in. This feature does allow for a few things
# things such as block break animations to show up in creative mode and among others, # such as more accurate movement that causes less problems with anticheat (meaning
# it is HIGHLY recommended you disable this on a production environment as it can eat # you're less likely to be banned) and allows block break animations to show up in
# up a lot of RAM. However, when using the Spigot version of Geyser, support for features # creative mode (and other features). Although this increases RAM usage, it likely
# or implementations this allows is automatically enabled without the additional caching as # won't have much of an effect for the vast majority of people. However, if you're
# Geyser has direct access to the server itself. # running out of RAM or are in a RAM-sensitive environment, you may want to disable
cache-chunks: CACHECHUNKS # 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. # 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) # A value of 0 is disabled. (Default: 0)
cache-images: 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 - # Bedrock prevents building and displaying blocks above Y127 in the Nether -
# enabling this config option works around that by changing the Nether dimension ID # 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 # 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 above-bedrock-nether-building: false
# Force clients to load all resource packs if there are any. # 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 # If set to false, it allows the user to connect to the server even if they don't
# want to download the resource packs # want to download the resource packs.
force-resource-packs: false 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 # 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, # 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. # 1400 is the default.
# mtu: 1400 # 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 config-version: 4