diff --git a/pom.xml b/pom.xml index 4f0fb9a..7d68023 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,11 @@ commons-cli 1.5.0 + + com.googlecode.json-simple + json-simple + 1.1.1 + diff --git a/src/main/java/com/diamante/serverlist/ClientEmulator.java b/src/main/java/com/diamante/serverlist/ClientEmulator.java index a745320..c847edc 100644 --- a/src/main/java/com/diamante/serverlist/ClientEmulator.java +++ b/src/main/java/com/diamante/serverlist/ClientEmulator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Diamante + * Copyright (C) 2023 Diamante * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -144,7 +144,7 @@ public class ClientEmulator implements Runnable { } var rawData = response.getData(); - InfoDumper.dumpServerResponse(rawData); + InfoDumper.dumpServerResponse(server, rawData); } @Override diff --git a/src/main/java/com/diamante/serverlist/InfoDumper.java b/src/main/java/com/diamante/serverlist/InfoDumper.java index 4d9a025..ffdc0e8 100644 --- a/src/main/java/com/diamante/serverlist/InfoDumper.java +++ b/src/main/java/com/diamante/serverlist/InfoDumper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Diamante + * Copyright (C) 2023 Diamante * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,15 +16,21 @@ */ package com.diamante.serverlist; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; + import java.nio.charset.StandardCharsets; +import org.json.simple.JSONObject; + /** * * @author Diamante */ public class InfoDumper { - public static void dumpServerResponse(byte[] data) { + public static void dumpServerResponse(Server server, byte[] data) { assert data.length == ClientEmulator.SERVER_INFO_SIZE; var magicLE = new byte[4]; @@ -47,5 +53,27 @@ public class InfoDumper { String infoString = new String(rawDataLE, StandardCharsets.UTF_8); System.out.println(infoString); + + // Save to JSON for easier inspection + var magicBE = Utils.longSwap(magicLE); + + var obj = new JSONObject(); + obj.put("server", server.toString()); + obj.put("magic", magicBE); + obj.put("players", playersBE); + obj.put("sv_maxClients", maxPlayersBE); + + saveJSONFile(String.format("dump\\stats_%d", server.hashCode()), obj); + } + + public static void saveJSONFile(String fileName, JSONObject obj) { + try { + var writer = new BufferedWriter(new FileWriter(fileName)); + writer.write(obj.toJSONString()); + writer.close(); + } + catch (IOException ex) { + System.err.println("saveJSONFile: IOException while writing a JSON file"); + } } } diff --git a/src/main/java/com/diamante/serverlist/Main.java b/src/main/java/com/diamante/serverlist/Main.java index 174775f..ae7457b 100644 --- a/src/main/java/com/diamante/serverlist/Main.java +++ b/src/main/java/com/diamante/serverlist/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Diamante + * Copyright (C) 2023 Diamante * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ import org.apache.commons.cli.ParseException; public class Main { private enum Mode { - Emulator, Master, Bad; + Emulator, Master, MasterPing, Bad; } private Mode mode; @@ -65,7 +65,7 @@ public class Main { var master = new Option("master", "master server mode"); var emulator = new Option("emulator", "client emulator mode"); - var magicOverride = new Option("magic_override", "master server will send all servers to the client"); + var masterPing = new Option("master_ping", "ping the master server"); var ping = Option.builder("ping") .argName("IP:Port") @@ -75,7 +75,7 @@ public class Main { options.addOption(master); options.addOption(emulator); - options.addOption(magicOverride); + options.addOption(masterPing); options.addOption(ping); return options; @@ -92,7 +92,6 @@ public class Main { var main = new Main(); var options = main.createOptions(); - var magicOverride = false; var ip = new String(); var parser = new DefaultParser(); @@ -102,10 +101,8 @@ public class Main { main.setMode(Mode.Master); } else if (line.hasOption("emulator")) { main.setMode(Mode.Emulator); - } - - if (line.hasOption("magic_override")) { - magicOverride = true; + } else if (line.hasOption("master_ping")) { + main.setMode(Mode.MasterPing); } if (line.hasOption("ping")) { @@ -126,6 +123,10 @@ public class Main { } else if (main.getMode() == Mode.Emulator) { var emulator = new ClientEmulator(); emulator.pingSingleServer(ip); + } else if (main.getMode() == Mode.MasterPing) { + var ping = new MasterServerPinger(); + ping.pingMaster(); + ping.readReplyFromMaster(); } System.out.println("Normal shutdown"); diff --git a/src/main/java/com/diamante/serverlist/MasterServer.java b/src/main/java/com/diamante/serverlist/MasterServer.java index 752050d..c476125 100644 --- a/src/main/java/com/diamante/serverlist/MasterServer.java +++ b/src/main/java/com/diamante/serverlist/MasterServer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Diamante + * Copyright (C) 2023 Diamante * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/com/diamante/serverlist/MasterServerPinger.java b/src/main/java/com/diamante/serverlist/MasterServerPinger.java new file mode 100644 index 0000000..4f0da26 --- /dev/null +++ b/src/main/java/com/diamante/serverlist/MasterServerPinger.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2023 Diamante + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.diamante.serverlist; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import java.net.Socket; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * + * @author Diamante + */ +public class MasterServerPinger { + + private static final String MASTER = "mw3.totalkillaz.ovh"; + private static final int MASTER_PORT = 27017; + + private static final int CLIENT_VERSION = 17039893; + + private Socket clientSocket; + + public void pingMaster() { + try { + clientSocket = new Socket(MASTER, MASTER_PORT); + } + catch (IOException ex) { + System.err.println("IOException: Failed to open a socket"); + return; + } + + try { + var output = clientSocket.getOutputStream(); + var data = new byte[8]; + + var magicLE = Utils.longSwap(Utils.NEW_CLIENT_MAGIC); + var versionLE = Utils.longSwap(CLIENT_VERSION); + + System.arraycopy(magicLE, 0, data, 0, 4); + System.arraycopy(versionLE, 0, data, 4, 4); + + output.write(data); + } + catch (IOException ex) { + System.err.println("IOException: Failed to write to a socket"); + } + } + + public void readReplyFromMaster() { + var out = new ByteArrayOutputStream(); + + try { + var input = clientSocket.getInputStream(); + var bytes = new byte[0x1000 * 0x6 + 0x4]; + + int count; + while ((count = input.read(bytes)) > 0) { + out.write(bytes, 0, count); + } + } + catch (IOException ex) { + System.err.println("IOException: Failed to read from a socket"); + } + + var bytes = out.toByteArray(); + + var serverCountLE = new byte[4]; + + System.arraycopy(bytes, 0, serverCountLE, 0, 4); + + var serverCountBE = Utils.longSwap(serverCountLE); + + System.out.println(String.format("readReplyFromMaster: got %d servers", serverCountBE)); + + try { + out.close(); + } + catch (IOException ex) { + System.err.println("readReplyFromMaster: IOException in out.close()"); + } + } +} diff --git a/src/main/java/com/diamante/serverlist/Server.java b/src/main/java/com/diamante/serverlist/Server.java index 1bb8819..9fcc530 100644 --- a/src/main/java/com/diamante/serverlist/Server.java +++ b/src/main/java/com/diamante/serverlist/Server.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Diamante + * Copyright (C) 2023 Diamante * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/com/diamante/serverlist/ServerList.java b/src/main/java/com/diamante/serverlist/ServerList.java index 2f7f90c..a658650 100644 --- a/src/main/java/com/diamante/serverlist/ServerList.java +++ b/src/main/java/com/diamante/serverlist/ServerList.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Diamante + * Copyright (C) 2023 Diamante * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/main/java/com/diamante/serverlist/Utils.java b/src/main/java/com/diamante/serverlist/Utils.java index fbe3681..49d7e86 100644 --- a/src/main/java/com/diamante/serverlist/Utils.java +++ b/src/main/java/com/diamante/serverlist/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Diamante + * Copyright (C) 2023 Diamante * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by