From 2465883c065dd03cdb88456107b4d347bcb1159b Mon Sep 17 00:00:00 2001 From: Kurt Thiemann Date: Sun, 17 Sep 2023 13:17:04 +0200 Subject: [PATCH] add support for plugin info field, show warning when mod is loaded client-side --- build.gradle | 2 +- .../thiemann/kurt/query/QueryModClient.java | 4 + .../thiemann/kurt/query/QueryModServer.java | 1 + .../kurt/query/query/AbstractQueryServer.java | 158 +++++++++++++++++ .../kurt/query/query/QueryServer.java | 161 +++++------------- 5 files changed, 204 insertions(+), 122 deletions(-) create mode 100644 src/server/java/io/thiemann/kurt/query/query/AbstractQueryServer.java diff --git a/build.gradle b/build.gradle index 41a05bd..0ddb133 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { apply plugin: 'foxloader.dev' -version '1.1.0' +version '1.2.0' foxloader { // forceReload = true diff --git a/src/client/java/io/thiemann/kurt/query/QueryModClient.java b/src/client/java/io/thiemann/kurt/query/QueryModClient.java index aa7ebe4..91090d0 100644 --- a/src/client/java/io/thiemann/kurt/query/QueryModClient.java +++ b/src/client/java/io/thiemann/kurt/query/QueryModClient.java @@ -3,4 +3,8 @@ import com.fox2code.foxloader.loader.ClientMod; public class QueryModClient extends QueryMod implements ClientMod { + @Override + public void onInit() { + this.getLogger().warning("ReIndevQuery is only needed on the server side."); + } } diff --git a/src/server/java/io/thiemann/kurt/query/QueryModServer.java b/src/server/java/io/thiemann/kurt/query/QueryModServer.java index 675364e..ad803ea 100644 --- a/src/server/java/io/thiemann/kurt/query/QueryModServer.java +++ b/src/server/java/io/thiemann/kurt/query/QueryModServer.java @@ -1,5 +1,6 @@ package io.thiemann.kurt.query; +import com.fox2code.foxloader.loader.ModLoader; import io.thiemann.kurt.query.query.QueryServer; import com.fox2code.foxloader.loader.ServerMod; import com.fox2code.foxloader.network.NetworkPlayer; diff --git a/src/server/java/io/thiemann/kurt/query/query/AbstractQueryServer.java b/src/server/java/io/thiemann/kurt/query/query/AbstractQueryServer.java new file mode 100644 index 0000000..8155208 --- /dev/null +++ b/src/server/java/io/thiemann/kurt/query/query/AbstractQueryServer.java @@ -0,0 +1,158 @@ +package io.thiemann.kurt.query.query; + +import com.fox2code.foxloader.network.NetworkPlayer; +import io.thiemann.kurt.query.query.packet.*; +import net.minecraft.server.MinecraftServer; + +import java.io.IOException; +import java.net.*; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +public abstract class AbstractQueryServer { + private final DatagramSocket socket; + private final Thread thread; + private final Map challenges = new ConcurrentHashMap<>(); + private long lastChallengeClear = 0; + + public AbstractQueryServer(int port, InetAddress laddr) throws SocketException { + this.socket = new DatagramSocket(port, laddr); + this.thread = new Thread(this::run); + this.thread.start(); + + } + + private void run() { + while (!this.socket.isClosed()) { + if (System.currentTimeMillis() - this.lastChallengeClear > 30000) { + this.challenges.clear(); + this.lastChallengeClear = System.currentTimeMillis(); + } + + try { + byte[] buf = new byte[1024]; + DatagramPacket packet = new DatagramPacket(buf, buf.length); + this.socket.receive(packet); + this.handlePacket(packet); + } catch (IOException ignored) { + } + } + } + + /** + * Handle incoming packet + * Packet format + * + * @param msg incoming packet + */ + private void handlePacket(DatagramPacket msg) { + ServerBoundPacket packet; + try { + packet = ServerBoundPacket.fromBuffer(msg.getData(), msg.getLength()); + } catch (QueryProtocolException e) { + return; + } + + ClientBoundPacket response; + if (packet instanceof ServerBoundHandshakePacket) { + response = this.handshake((ServerBoundHandshakePacket) packet, msg.getSocketAddress()); + } else if (packet instanceof ServerBoundStatPacket) { + if (((ServerBoundStatPacket) packet).isFull()) { + response = this.fullStat((ServerBoundStatPacket) packet, msg.getSocketAddress()); + } else { + response = this.basicStat((ServerBoundStatPacket) packet, msg.getSocketAddress()); + } + } else { + return; + } + + if (response == null) { + return; + } + + byte[] payload = response.serialize(); + DatagramPacket responsePacket = new DatagramPacket(payload, payload.length, msg.getSocketAddress()); + try { + this.socket.send(responsePacket); + } catch (IOException ignored) { + } + } + + private ClientBoundHandshakePacket handshake(ServerBoundHandshakePacket packet, SocketAddress address) { + int randomToken = ((int) (Math.random() * 1000000)) & 0x0F0F0F0F; + this.challenges.put(address.toString(), randomToken); + + return new ClientBoundHandshakePacket(packet.getSessionId(), randomToken); + } + + private ClientBoundBasicStatPacket basicStat(ServerBoundStatPacket packet, SocketAddress address) { + if (!this.challenges.containsKey(address.toString())) { + return null; + } + + if (packet.getChallenge() != this.challenges.get(address.toString())) { + return null; + } + + return new ClientBoundBasicStatPacket( + packet.getSessionId(), + this.getMotd(), + "SMP", + "world", + this.getPlayersOnline(), + this.getMaxPlayers(), + this.getMinecraftServerPort(), + this.getMinecraftServerIp() + ); + } + + private ClientBoundFullStatPacket fullStat(ServerBoundStatPacket packet, SocketAddress address) { + if (!this.challenges.containsKey(address.toString())) { + return null; + } + + if (packet.getChallenge() != this.challenges.get(address.toString())) { + System.out.println("Wrong challenge: " + packet.getChallenge() + " != " + this.challenges.get(address.toString()) + " (" + address.toString() + ")"); + return null; + } + + return new ClientBoundFullStatPacket( + packet.getSessionId(), + this.getMotd(), + "SMP", + this.getGameId(), + this.getGameVersion(), + this.getPluginInfo(), + "world", + this.getPlayersOnline(), + this.getMaxPlayers(), + this.getMinecraftServerPort(), + this.getMinecraftServerIp(), + this.getPlayerList() + ); + } + + public void close() { + this.socket.close(); + this.thread.interrupt(); + } + + protected abstract int getMinecraftServerPort(); + + protected abstract String getMinecraftServerIp(); + + protected abstract String getMotd(); + + protected abstract int getPlayersOnline(); + + protected abstract int getMaxPlayers(); + + protected abstract String[] getPlayerList(); + + protected abstract String getPluginInfo(); + + protected abstract String getGameId(); + + protected abstract String getGameVersion(); +} diff --git a/src/server/java/io/thiemann/kurt/query/query/QueryServer.java b/src/server/java/io/thiemann/kurt/query/query/QueryServer.java index 8b7d0ed..d8d11f6 100644 --- a/src/server/java/io/thiemann/kurt/query/query/QueryServer.java +++ b/src/server/java/io/thiemann/kurt/query/query/QueryServer.java @@ -1,153 +1,72 @@ package io.thiemann.kurt.query.query; +import com.fox2code.foxloader.launcher.BuildConfig; +import com.fox2code.foxloader.loader.ModContainer; +import com.fox2code.foxloader.loader.ModLoader; import com.fox2code.foxloader.network.NetworkPlayer; -import io.thiemann.kurt.query.query.packet.*; import net.minecraft.server.MinecraftServer; -import java.io.IOException; import java.net.*; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Collection; -public class QueryServer { +public class QueryServer extends AbstractQueryServer { private final MinecraftServer server; - private final DatagramSocket socket; - private final Thread thread; - private final Map challenges = new ConcurrentHashMap<>(); - private long lastChallengeClear = 0; public QueryServer(MinecraftServer server, int port, InetAddress laddr) throws SocketException { + super(port, laddr); this.server = server; - this.socket = new DatagramSocket(port, laddr); - this.thread = new Thread(this::run); - this.thread.start(); } - private void run() { - while (!this.socket.isClosed()) { - if (System.currentTimeMillis() - this.lastChallengeClear > 30000) { - this.challenges.clear(); - this.lastChallengeClear = System.currentTimeMillis(); - } - - try { - byte[] buf = new byte[1024]; - DatagramPacket packet = new DatagramPacket(buf, buf.length); - this.socket.receive(packet); - this.handlePacket(packet); - } catch (IOException ignored) { - } - } + @Override + protected int getMinecraftServerPort() { + return this.server.propertyManagerObj.getIntProperty("server-port", 25565); } - /** - * Handle incoming packet - * Packet format - * - * @param msg incoming packet - */ - private void handlePacket(DatagramPacket msg) { - ServerBoundPacket packet; - try { - packet = ServerBoundPacket.fromBuffer(msg.getData(), msg.getLength()); - } catch (QueryProtocolException e) { - return; - } - - ClientBoundPacket response; - if (packet instanceof ServerBoundHandshakePacket) { - response = this.handshake((ServerBoundHandshakePacket) packet, msg.getSocketAddress()); - } else if (packet instanceof ServerBoundStatPacket) { - if (((ServerBoundStatPacket) packet).isFull()) { - response = this.fullStat((ServerBoundStatPacket) packet, msg.getSocketAddress()); - } else { - response = this.basicStat((ServerBoundStatPacket) packet, msg.getSocketAddress()); - } - } else { - return; - } - - if (response == null) { - return; - } - - byte[] payload = response.serialize(); - DatagramPacket responsePacket = new DatagramPacket(payload, payload.length, msg.getSocketAddress()); - try { - this.socket.send(responsePacket); - } catch (IOException ignored) { + @Override + protected String getMinecraftServerIp() { + String address = this.server.propertyManagerObj.getStringProperty("server-ip", ""); + if (address.length() == 0) { + address = "127.0.0.1"; } + return address; } - private ClientBoundHandshakePacket handshake(ServerBoundHandshakePacket packet, SocketAddress address) { - int randomToken = ((int) (Math.random() * 1000000)) & 0x0F0F0F0F; - this.challenges.put(address.toString(), randomToken); - - return new ClientBoundHandshakePacket(packet.getSessionId(), randomToken); + @Override + protected String getMotd() { + return this.server.getMotd(); } - private ClientBoundBasicStatPacket basicStat(ServerBoundStatPacket packet, SocketAddress address) { - if (!this.challenges.containsKey(address.toString())) { - return null; - } - - if (packet.getChallenge() != this.challenges.get(address.toString())) { - return null; - } - - return new ClientBoundBasicStatPacket( - packet.getSessionId(), - this.server.getMotd(), - "SMP", - "world", - this.server.configManager.playersOnline(), - this.server.configManager.getMaxPlayers(), - this.getMinecraftServerPort(), - this.getMinecraftServerIp() - ); + @Override + protected int getPlayersOnline() { + return this.server.configManager.playersOnline(); } - private ClientBoundFullStatPacket fullStat(ServerBoundStatPacket packet, SocketAddress address) { - if (!this.challenges.containsKey(address.toString())) { - return null; - } - - if (packet.getChallenge() != this.challenges.get(address.toString())) { - System.out.println("Wrong challenge: " + packet.getChallenge() + " != " + this.challenges.get(address.toString()) + " (" + address.toString() + ")"); - return null; - } + @Override + protected int getMaxPlayers() { + return this.server.configManager.getMaxPlayers(); + } - return new ClientBoundFullStatPacket( - packet.getSessionId(), - this.server.getMotd(), - "SMP", - "MINECRAFT", - "Beta 1.7", - "FoxLoader", - "world", - this.server.configManager.playersOnline(), - this.server.configManager.getMaxPlayers(), - this.getMinecraftServerPort(), - this.getMinecraftServerIp(), - this.server.configManager.playerEntities.stream().map(NetworkPlayer::getPlayerName).toArray(String[]::new) - ); + @Override + protected String[] getPlayerList() { + return this.server.configManager.playerEntities.stream().map(NetworkPlayer::getPlayerName).toArray(String[]::new); } - private int getMinecraftServerPort() { - return this.server.propertyManagerObj.getIntProperty("server-port", 25565); + @Override + protected String getPluginInfo() { + Collection mods = ModLoader.getModContainers(); + String[] modNames = mods.stream().map(mod -> mod.name + " " + mod.version).toArray(String[]::new); + + return "FoxLoader " + BuildConfig.FOXLOADER_VERSION + ": " + String.join("; ", modNames); } - private String getMinecraftServerIp() { - String address = this.server.propertyManagerObj.getStringProperty("server-ip", ""); - if (address.length() == 0) { - address = "127.0.0.1"; - } - return address; + @Override + protected String getGameId() { + return "MINECRAFT"; } - public void close() { - this.socket.close(); - this.thread.interrupt(); + @Override + protected String getGameVersion() { + return "Beta 1.7"; } }