Add custom servers base and direct connect

This commit is contained in:
rtm516 2020-06-16 18:30:01 +01:00
parent fc3b792e84
commit 3679fd5034
13 changed files with 194 additions and 37 deletions

View file

@ -4,11 +4,14 @@ package org.geysermc.multi;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter; import lombok.Getter;
import org.geysermc.multi.utils.PlayerStorageManager;
import org.geysermc.multi.utils.Server;
import java.util.List;
@Getter @Getter
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class GeyserMultiConfig { public class GeyserMultiConfig {
private GeyserConfigSection geyser;
private int port; private int port;
@ -20,11 +23,29 @@ public class GeyserMultiConfig {
@JsonProperty("debug-mode") @JsonProperty("debug-mode")
private boolean debugMode; private boolean debugMode;
private GeyserConfigSection geyser;
private List<Server> servers;
@JsonProperty("custom-servers")
private CustomServersSection customServers;
@Getter @Getter
public static class GeyserConfigSection { public static class GeyserConfigSection {
private int port; private int port;
@JsonProperty("debug-mode") @JsonProperty("debug-mode")
private boolean debugMode; private boolean debugMode;
} }
@Getter
public static class CustomServersSection {
private boolean enabled;
private int max;
@JsonProperty("storage-type")
private PlayerStorageManager.StorageType storageType;
}
} }

View file

@ -7,11 +7,15 @@ import org.geysermc.connector.utils.FileUtils;
import org.geysermc.multi.proxy.GeyserProxyBootstrap; import org.geysermc.multi.proxy.GeyserProxyBootstrap;
import org.geysermc.multi.utils.Logger; import org.geysermc.multi.utils.Logger;
import org.geysermc.multi.utils.Player; import org.geysermc.multi.utils.Player;
import org.geysermc.multi.utils.PlayerStorageManager;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.*; import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -45,6 +49,8 @@ public class MasterServer {
private GeyserMultiConfig geyserMultiConfig; private GeyserMultiConfig geyserMultiConfig;
public MasterServer() { public MasterServer() {
this.instance = this;
logger = new Logger(); logger = new Logger();
try { try {
@ -57,7 +63,6 @@ public class MasterServer {
logger.setDebug(geyserMultiConfig.isDebugMode()); logger.setDebug(geyserMultiConfig.isDebugMode());
this.instance = this;
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
@ -65,6 +70,8 @@ public class MasterServer {
TimerTask task = new TimerTask() { public void run() { } }; TimerTask task = new TimerTask() { public void run() { } };
timer.scheduleAtFixedRate(task, 0L, 1000L); timer.scheduleAtFixedRate(task, 0L, 1000L);
PlayerStorageManager.setupStorage();
start(geyserMultiConfig.getPort()); start(geyserMultiConfig.getPort());
logger.start(); logger.start();

View file

@ -12,6 +12,7 @@ import com.nukkitx.protocol.bedrock.handler.BedrockPacketHandler;
import com.nukkitx.protocol.bedrock.packet.*; import com.nukkitx.protocol.bedrock.packet.*;
import com.nukkitx.protocol.bedrock.util.EncryptionUtils; import com.nukkitx.protocol.bedrock.util.EncryptionUtils;
import org.geysermc.common.window.FormWindow; import org.geysermc.common.window.FormWindow;
import org.geysermc.common.window.response.CustomFormResponse;
import org.geysermc.common.window.response.SimpleFormResponse; import org.geysermc.common.window.response.SimpleFormResponse;
import org.geysermc.multi.ui.FormID; import org.geysermc.multi.ui.FormID;
import org.geysermc.multi.ui.UIHandler; import org.geysermc.multi.ui.UIHandler;
@ -130,7 +131,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() + ")"); masterServer.getLogger().info("Logged in " + player.getDisplayName() + " (" + player.getXuid() + ", " + player.getIdentity() + ")");
player.sendStartGame(); player.sendStartGame();
break; break;
case HAVE_ALL_PACKS: case HAVE_ALL_PACKS:
@ -152,7 +153,7 @@ public class PacketHandler implements BedrockPacketHandler {
public boolean handle(SetLocalPlayerAsInitializedPacket packet) { public boolean handle(SetLocalPlayerAsInitializedPacket packet) {
masterServer.getLogger().debug("Player initialized: " + player.getDisplayName()); masterServer.getLogger().debug("Player initialized: " + player.getDisplayName());
player.sendWindow(FormID.MAIN, UIHandler.getServerListFormPacket(player.getServers()));; player.sendWindow(FormID.MAIN, UIHandler.getServerList(player.getServers()));;
return false; return false;
} }
@ -169,7 +170,7 @@ public class PacketHandler implements BedrockPacketHandler {
window.setResponse(packet.getFormData().trim()); window.setResponse(packet.getFormData().trim());
// Resend the form if they closed it // Resend the form if they closed it
if (window.getResponse() == null) { if (window.getResponse() == null && id != FormID.DIRECT_CONNECT) {
player.resendWindow(); player.resendWindow();
} else { } else {
// Send the response to the correct response function // Send the response to the correct response function
@ -178,6 +179,10 @@ public class PacketHandler implements BedrockPacketHandler {
UIHandler.handleServerListResponse(player, (SimpleFormResponse) window.getResponse()); UIHandler.handleServerListResponse(player, (SimpleFormResponse) window.getResponse());
break; break;
case DIRECT_CONNECT:
UIHandler.handleDirectConnectResponse(player, (CustomFormResponse) window.getResponse());
break;
default: default:
player.resendWindow(); player.resendWindow();
break; break;

View file

@ -2,21 +2,20 @@ package org.geysermc.multi.proxy;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.apache.logging.log4j.core.util.IOUtils;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.bootstrap.GeyserBootstrap;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.command.CommandManager;
import org.geysermc.connector.ping.IGeyserPingPassthrough; import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.ping.IGeyserPingPassthrough;
import org.geysermc.multi.GeyserMultiConfig; import org.geysermc.multi.GeyserMultiConfig;
import org.geysermc.multi.MasterServer; import org.geysermc.multi.MasterServer;
import org.geysermc.multi.utils.Logger;
import org.geysermc.multi.utils.Server;
import java.io.*; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors; import java.util.stream.Collectors;

View file

@ -3,7 +3,6 @@ package org.geysermc.multi.proxy;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.configuration.GeyserJacksonConfiguration; import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
import java.nio.file.Path; import java.nio.file.Path;

View file

@ -18,6 +18,8 @@ public class ProxyConnectorServerEventHandler extends ConnectorServerEventHandle
@Override @Override
public void onSessionCreation(BedrockServerSession bedrockServerSession) { public void onSessionCreation(BedrockServerSession bedrockServerSession) {
bedrockServerSession.setPacketCodec(GeyserConnector.BEDROCK_PACKET_CODEC); // Only done here as it allows us to disconnect the player
Player player = MasterServer.getInstance().getPlayers().get(bedrockServerSession.getAddress()); Player player = MasterServer.getInstance().getPlayers().get(bedrockServerSession.getAddress());
if (player == null) { if (player == null) {
bedrockServerSession.disconnect("Please connect to the master server and pick a server first!"); bedrockServerSession.disconnect("Please connect to the master server and pick a server first!");
@ -28,7 +30,6 @@ public class ProxyConnectorServerEventHandler extends ConnectorServerEventHandle
// This doesn't clean up the old packet handler, so may cause a memory leak? // 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.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 // Add another disconnect handler to remove the player on final disconnect
bedrockServerSession.addDisconnectHandler(disconnectReason -> { bedrockServerSession.addDisconnectHandler(disconnectReason -> {

View file

@ -1,14 +1,19 @@
package org.geysermc.multi.ui; package org.geysermc.multi.ui;
import org.geysermc.common.window.CustomFormBuilder;
import org.geysermc.common.window.CustomFormWindow;
import org.geysermc.common.window.FormWindow; import org.geysermc.common.window.FormWindow;
import org.geysermc.common.window.SimpleFormWindow; import org.geysermc.common.window.SimpleFormWindow;
import org.geysermc.common.window.button.FormButton; import org.geysermc.common.window.button.FormButton;
import org.geysermc.common.window.button.FormImage; import org.geysermc.common.window.button.FormImage;
import org.geysermc.common.window.component.InputComponent;
import org.geysermc.common.window.response.CustomFormResponse;
import org.geysermc.common.window.response.SimpleFormResponse; import org.geysermc.common.window.response.SimpleFormResponse;
import org.geysermc.multi.MasterServer; import org.geysermc.multi.MasterServer;
import org.geysermc.multi.utils.Player; import org.geysermc.multi.utils.Player;
import org.geysermc.multi.utils.Server; import org.geysermc.multi.utils.Server;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class UIHandler { public class UIHandler {
@ -18,13 +23,23 @@ public class UIHandler {
* @param servers A list of {@link Server} objects * @param servers A list of {@link Server} objects
* @return A {@link SimpleFormWindow} object * @return A {@link SimpleFormWindow} object
*/ */
public static FormWindow getServerListFormPacket(List<Server> servers) { public static FormWindow getServerList(List<Server> servers) {
SimpleFormWindow window = new SimpleFormWindow("Servers", ""); SimpleFormWindow window = new SimpleFormWindow("Servers", "");
// Add a button for each server with the server icon as the image window.getButtons().add(new FormButton("Direct connect"));
window.getButtons().add(new FormButton("Edit servers"));
// Add a button for each global server
for (Server server : MasterServer.getInstance().getGeyserMultiConfig().getServers()) {
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")));
}
// Add a button for each personal server
if (MasterServer.getInstance().getGeyserMultiConfig().getCustomServers().isEnabled()) {
for (Server server : servers) { for (Server server : servers) {
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"))); 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; return window;
} }
@ -40,6 +55,19 @@ public class UIHandler {
return window; return window;
} }
/**
* Create a direct connect form
*
* @return A {@link SimpleFormWindow} object
*/
public static FormWindow getDirectConnect() {
CustomFormWindow window = new CustomFormBuilder("Direct Connect")
.addComponent(new InputComponent("IP", "play.cubecraft.net", ""))
.addComponent(new InputComponent("Port", "25565", "25565"))
.build();
return window;
}
/** /**
* Handle the server list response * Handle the server list response
* *
@ -47,18 +75,30 @@ public class UIHandler {
* @param data The form response data * @param data The form response data
*/ */
public static void handleServerListResponse(Player player, SimpleFormResponse data) { public static void handleServerListResponse(Player player, SimpleFormResponse data) {
switch (data.getClickedButtonId()) {
case 0:
player.sendWindow(FormID.DIRECT_CONNECT, getDirectConnect());
break;
case 1:
break;
default:
// Get the server // Get the server
Server server = player.getServers().get(data.getClickedButtonId()); List<Server> servers = new ArrayList<>(MasterServer.getInstance().getGeyserMultiConfig().getServers());
servers.addAll(player.getServers());
Server server = servers.get(data.getClickedButtonId() - 2);
// Tell the user we are connecting them player.sendToServer(server);
// this wont show up in alot of cases as the client connects quite quickly break;
player.sendWindow(FormID.CONNECTING, getWaitingScreen(server)); }
}
// Create the Geyser instance if its not already running public static void handleDirectConnectResponse(Player player, CustomFormResponse data) {
MasterServer.getInstance().createGeyserProxy(); // Take them back to the main menu if they close the direct connect window
if (data == null) {
player.sendWindow(FormID.MAIN, getServerList(player.getServers()));;
return;
}
// Send the user over to the server player.sendToServer(new Server(data.getInputResponses().get(0), Integer.valueOf(data.getInputResponses().get(1))));
player.setCurrentServer(server);
player.connectToProxy();
} }
} }

View file

@ -3,14 +3,10 @@ package org.geysermc.multi.utils;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import net.minecrell.terminalconsole.SimpleTerminalConsole; import net.minecrell.terminalconsole.SimpleTerminalConsole;
import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.message.Message;
import org.geysermc.common.ChatColor; import org.geysermc.common.ChatColor;
import org.geysermc.connector.GeyserLogger; import org.geysermc.connector.GeyserLogger;
import org.geysermc.multi.MasterServer; import org.geysermc.multi.MasterServer;
import java.io.IOException;
import java.util.logging.Level;
@Log4j2 @Log4j2
public class Logger extends SimpleTerminalConsole implements GeyserLogger { public class Logger extends SimpleTerminalConsole implements GeyserLogger {

View file

@ -6,7 +6,6 @@ import com.nukkitx.nbt.stream.NBTInputStream;
import com.nukkitx.nbt.stream.NBTOutputStream; import com.nukkitx.nbt.stream.NBTOutputStream;
import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.nbt.tag.CompoundTag;
import com.nukkitx.nbt.tag.ListTag; import com.nukkitx.nbt.tag.ListTag;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.FileUtils;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;

View file

@ -5,13 +5,16 @@ 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.GamePublishSetting;
import com.nukkitx.protocol.bedrock.data.GameRuleData;
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
import com.nukkitx.protocol.bedrock.packet.*; import com.nukkitx.protocol.bedrock.packet.*;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.geysermc.common.window.FormWindow; import org.geysermc.common.window.FormWindow;
import org.geysermc.multi.MasterServer; import org.geysermc.multi.MasterServer;
import org.geysermc.multi.ui.FormID; import org.geysermc.multi.ui.FormID;
import org.geysermc.multi.ui.UIHandler;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -42,8 +45,9 @@ public class Player {
this.session = session; this.session = session;
// Should fetch the servers from some form of db // Should fetch the servers from some form of db
servers.add(new Server("play.cubecraft.net")); if (MasterServer.getInstance().getGeyserMultiConfig().getCustomServers().isEnabled()) {
servers.add(new Server("81.174.164.211", 25580)); servers.addAll(PlayerStorageManager.loadServers(this));
}
} }
/** /**
@ -155,4 +159,17 @@ public class Player {
transferPacket.setPort(MasterServer.getInstance().getGeyserProxy().getGeyserConfig().getBedrock().getPort()); transferPacket.setPort(MasterServer.getInstance().getGeyserProxy().getGeyserConfig().getBedrock().getPort());
session.sendPacket(transferPacket); session.sendPacket(transferPacket);
} }
public void sendToServer(Server server) {
// Tell the user we are connecting them
// this wont show up in alot of cases as the client connects quite quickly
sendWindow(FormID.CONNECTING, UIHandler.getWaitingScreen(server));
// Create the Geyser instance if its not already running
MasterServer.getInstance().createGeyserProxy();
// Send the user over to the server
setCurrentServer(server);
connectToProxy();
}
} }

View file

@ -0,0 +1,51 @@
package org.geysermc.multi.utils;
import com.fasterxml.jackson.annotation.JsonValue;
import org.geysermc.multi.MasterServer;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class PlayerStorageManager {
public static void setupStorage() {
if (!MasterServer.getInstance().getGeyserMultiConfig().getCustomServers().isEnabled()) {
return;
}
switch (MasterServer.getInstance().getGeyserMultiConfig().getCustomServers().getStorageType()) {
case JSON:
File playersFolder = new File("players/");
if (!playersFolder.exists()) {
playersFolder.mkdirs();
}
break;
case SQLITE:
throw new NotImplementedException();
}
}
public static void saveServers(Player player, List<Server> servers) {
}
public static List<Server> loadServers(Player player) {
List<Server> servers = new ArrayList<>();
servers.add(new Server("81.174.164.211", 25580));
return servers;
}
public enum StorageType {
JSON("json"),
SQLITE("sqlite");
@JsonValue
private String name;
StorageType(String name) {
this.name = name;
}
}
}

View file

@ -7,7 +7,12 @@ import lombok.Getter;
@AllArgsConstructor @AllArgsConstructor
public class Server { public class Server {
private String address; private String address;
private int port; private int port = 25565;
// Added so we can load from config
public Server() {
super();
}
public Server(String address) { public Server(String address) {
this(address, 25565); this(address, 25565);

View file

@ -21,3 +21,20 @@ geyser:
# 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
# A global list of servers sent to all clients
servers:
- address: "play.cubecraft.net"
- address: "127.0.0.1"
port: 25565
custom-servers:
# Should custom servers be enabled for users
enabled: false
# Max amount of custom servers per user
max: 10
# Storage engine for custom servers
# Can be json, sqlite
storage-type: json