Pārlūkot izejas kodu

Adding Game Server register/unregister commands

Zoey76 6 gadi atpakaļ
vecāks
revīzija
b72d954d39

+ 4 - 0
README.md

@@ -9,6 +9,10 @@ Existing commands
 - deploy: deploys the built source code
 - database
   - install: installs the database
+- gameserver
+  - register: register a game server in the login server
+  - list: list all game servers and it's status
+  - unregister: removes one/all game server(s)
 - account
   - create: creates an account
   - update: updates an account

+ 6 - 0
pom.xml

@@ -17,6 +17,7 @@
 		<mysql-connector-java.version>8.0.15</mysql-connector-java.version>
 		<commons-configuration2.version>2.4</commons-configuration2.version>
 		<commons-beanutils.version>1.9.3</commons-beanutils.version>
+		<asciitable.version>0.3.2</asciitable.version>
 	</properties>
 	<dependencies>
 		<dependency>
@@ -59,5 +60,10 @@
 			<artifactId>commons-beanutils</artifactId>
 			<version>${commons-beanutils.version}</version>
 		</dependency>
+		<dependency>
+			<groupId>de.vandermeer</groupId>
+			<artifactId>asciitable</artifactId>
+			<version>${asciitable.version}</version>
+		</dependency>
 	</dependencies>
 </project>

+ 2 - 0
src/main/java/com/l2jserver/cli/L2JServerCLI.java

@@ -27,6 +27,7 @@ import com.l2jserver.cli.command.BuildCommand;
 import com.l2jserver.cli.command.CodeCommand;
 import com.l2jserver.cli.command.DatabaseCommand;
 import com.l2jserver.cli.command.DeployCommand;
+import com.l2jserver.cli.command.GameServerCommand;
 import com.l2jserver.cli.command.QuitCommand;
 
 import picocli.CommandLine;
@@ -41,6 +42,7 @@ import picocli.CommandLine.Command;
 	BuildCommand.class, //
 	DeployCommand.class, //
 	DatabaseCommand.class, //
+	GameServerCommand.class, //
 	AccountCommand.class, //
 	QuitCommand.class //
 })

+ 5 - 5
src/main/java/com/l2jserver/cli/command/AccountCommand.java

@@ -30,11 +30,11 @@ import picocli.CommandLine.Command;
  * Account command.
  * @author Zoey76
  */
-@Command(name = "account", subcommands = { //
-	AccountCreateCommand.class, //
-	AccountUpdateCommand.class, //
-	AccountListCommand.class, //
-	AccountDeleteCommand.class //
+@Command(name = "account", aliases = "a", subcommands = {
+	AccountCreateCommand.class,
+	AccountUpdateCommand.class,
+	AccountListCommand.class,
+	AccountDeleteCommand.class
 })
 public class AccountCommand extends AbstractCommand {
 	

+ 1 - 1
src/main/java/com/l2jserver/cli/command/BuildCommand.java

@@ -24,7 +24,7 @@ import picocli.CommandLine.Command;
  * Build command.
  * @author Zoey76
  */
-@Command(name = "build")
+@Command(name = "build", aliases = "b")
 public class BuildCommand extends AbstractCommand {
 	
 	@Override

+ 1 - 1
src/main/java/com/l2jserver/cli/command/CodeCommand.java

@@ -34,7 +34,7 @@ import picocli.CommandLine.Option;
  * Code command.
  * @author Zoey76
  */
-@Command(name = "code")
+@Command(name = "code", aliases = "c")
 public class CodeCommand extends AbstractCommand {
 	
 	private static final Logger LOG = LoggerFactory.getLogger(CodeCommand.class);

+ 1 - 1
src/main/java/com/l2jserver/cli/command/DatabaseCommand.java

@@ -27,7 +27,7 @@ import picocli.CommandLine.Command;
  * Database command.
  * @author Zoey76
  */
-@Command(name = "database", subcommands = { //
+@Command(name = "database", aliases = "db", subcommands = {
 	DatabaseInstallCommand.class
 })
 public class DatabaseCommand extends AbstractCommand {

+ 1 - 1
src/main/java/com/l2jserver/cli/command/DeployCommand.java

@@ -24,7 +24,7 @@ import picocli.CommandLine.Command;
  * Deploy command.
  * @author Zoey76
  */
-@Command(name = "deploy")
+@Command(name = "deploy", aliases = "d")
 public class DeployCommand extends AbstractCommand {
 	
 	@Override

+ 44 - 0
src/main/java/com/l2jserver/cli/command/GameServerCommand.java

@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2019 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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 <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.cli.command;
+
+import com.l2jserver.cli.command.gameserver.GameServerListCommand;
+import com.l2jserver.cli.command.gameserver.GameServerRegisterCommand;
+import com.l2jserver.cli.command.gameserver.GameServerUnregisterCommand;
+
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+/**
+ * Game Server command implementation.
+ * @author Zoey76
+ */
+@Command(name = "gameserver", aliases = "gs", subcommands = {
+	GameServerRegisterCommand.class,
+	GameServerListCommand.class,
+	GameServerUnregisterCommand.class
+})
+public class GameServerCommand extends AbstractCommand {
+	
+	@Override
+	public void run() {
+		System.err.println("Please invoke a subcommand");
+		new CommandLine(new GameServerCommand()).usage(System.out);
+	}
+}

+ 1 - 1
src/main/java/com/l2jserver/cli/command/QuitCommand.java

@@ -24,7 +24,7 @@ import picocli.CommandLine.Command;
  * Quit command.
  * @author Zoey76
  */
-@Command(name = "quit")
+@Command(name = "quit", aliases = "q")
 public class QuitCommand extends AbstractCommand {
 	
 	@Override

+ 75 - 0
src/main/java/com/l2jserver/cli/command/gameserver/GameServerListCommand.java

@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2019 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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 <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.cli.command.gameserver;
+
+import java.sql.SQLException;
+
+import com.l2jserver.cli.command.AbstractCommand;
+import com.l2jserver.cli.dao.GameServerDAO;
+import com.l2jserver.cli.model.ServerNames;
+
+import de.vandermeer.asciitable.AsciiTable;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+/**
+ * Game Server list command.
+ * @author Zoey76
+ */
+@Command(name = "list", aliases = "l", description = "Lists all game servers from login server.")
+public class GameServerListCommand extends AbstractCommand {
+	
+	private static final String IN_USE = "In Use";
+	
+	private static final String FREE = "Free";
+	
+	@Option(names = {
+		"-u",
+		"--used-only"
+	}, description = "List servers in use only.", defaultValue = "false")
+	private boolean usedOnly = false;
+	
+	@Override
+	public void run() {
+		try {
+			listGameServers();
+		} catch (Exception ex) {
+			System.err.println("There has been an error listing the game servers!");
+			ex.printStackTrace();
+		}
+	}
+	
+	private void listGameServers() throws SQLException {
+		final var at = new AsciiTable();
+		at.addRule();
+		at.addRow("Id", "Name", "Status");
+		at.addRule();
+		final var gameservers = GameServerDAO.getInstance().gameServers();
+		for (var gs : ServerNames.getServers().entrySet()) {
+			final var inUse = gameservers.contains(gs.getKey());
+			if (usedOnly && !inUse) {
+				continue;
+			}
+			
+			at.addRow(gs.getKey(), gs.getValue(), (inUse ? IN_USE : FREE));
+			at.addRule();
+		}
+		System.out.println(at.render());
+	}
+}

+ 131 - 0
src/main/java/com/l2jserver/cli/command/gameserver/GameServerRegisterCommand.java

@@ -0,0 +1,131 @@
+/*
+ * Copyright © 2019 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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 <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.cli.command.gameserver;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import com.l2jserver.cli.command.AbstractCommand;
+import com.l2jserver.cli.dao.GameServerDAO;
+import com.l2jserver.cli.model.ServerNames;
+
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+/**
+ * Game Server register command.
+ * @author Zoey76
+ */
+@Command(name = "register", aliases = "r", description = "Adds a game server to the login server.")
+public class GameServerRegisterCommand extends AbstractCommand {
+	
+	@Option(names = {
+		"-fb",
+		"--fallback"
+	}, description = "If during registration the specified game server Id is in use, an attempt with the first available Id will be made.")
+	private boolean fallback;
+	
+	@Option(names = {
+		"-f",
+		"--force"
+	}, description = "Forces a game server register operation to overwrite a previous registration on the specified Id, if necessary.")
+	private boolean force = false;
+	
+	@Option(names = "-id", description = "Game Server Id.")
+	private Integer id;
+	
+	@Option(names = {
+		"-o",
+		"--output"
+	}, description = "Path where the file will be saved.", defaultValue = ".")
+	private File outputPath = new File(".");
+	
+	@Override
+	public void run() {
+		try {
+			register();
+		} catch (Exception ex) {
+			System.err.println("There has been an error registering a Game Server!");
+			ex.printStackTrace();
+		}
+	}
+	
+	private void register() throws SQLException, IOException, NoSuchAlgorithmException {
+		var gameservers = GameServerDAO.getInstance().gameServers();
+		if (force) {
+			if (id == null) {
+				System.err.println("A Game Server Id must be specified when using --force!");
+				return;
+			}
+			
+			if (gameservers.contains(id)) {
+				GameServerDAO.getInstance().unregister(id);
+				gameservers.remove(id);
+			}
+		} else if (fallback) {
+			if ((id == null) || gameservers.contains(id)) {
+				for (var gs : ServerNames.getServers().entrySet()) {
+					if (gameservers.contains(gs.getKey())) {
+						continue;
+					}
+					id = gs.getKey();
+					break;
+				}
+			}
+		}
+		
+		// Validate Id not in use.
+		if (gameservers.contains(id)) {
+			System.err.println("The Id " + id + " is in use!");
+			return;
+		}
+		
+		registerGameServer(id, outputPath);
+	}
+	
+	public static void registerGameServer(int id, File outputPath) throws IOException, SQLException, NoSuchAlgorithmException {
+		final var hexId = generateHex();
+		
+		GameServerDAO.getInstance().register(hexId, id, "");
+		
+		final var hexSetting = new Properties();
+		final var file = new File(outputPath, "hexid.txt");
+		file.createNewFile();
+		try (OutputStream out = new FileOutputStream(file)) {
+			hexSetting.setProperty("ServerID", String.valueOf(id));
+			hexSetting.setProperty("HexID", new BigInteger(hexId).toString(16));
+			hexSetting.store(out, "The HexId to Auth into LoginServer");
+		}
+		
+		System.out.println("Registered Game Server Id " + id + ".");
+	}
+	
+	private static byte[] generateHex() throws NoSuchAlgorithmException {
+		final var bytes = new byte[16];
+		SecureRandom.getInstanceStrong().nextBytes(bytes);
+		return bytes;
+	}
+}

+ 65 - 0
src/main/java/com/l2jserver/cli/command/gameserver/GameServerUnregisterCommand.java

@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2019 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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 <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.cli.command.gameserver;
+
+import com.l2jserver.cli.command.AbstractCommand;
+import com.l2jserver.cli.dao.GameServerDAO;
+
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+/**
+ * Game Server unregister command.
+ * @author Zoey76
+ */
+@Command(name = "unregister", aliases = "u", description = "Removes a game server from the login server.")
+public class GameServerUnregisterCommand extends AbstractCommand {
+	
+	@Option(names = {
+		"-a",
+		"--all"
+	}, description = "If true, all Game Servers will be unregistered.", defaultValue = "false")
+	private boolean all = false;
+	
+	@Option(names = "-id", description = "Game Server Id.")
+	private Integer id;
+	
+	@Override
+	public void run() {
+		try {
+			if (all) {
+				GameServerDAO.getInstance().unregisterAll();
+				
+				System.out.println("Unregistered all Game Servers.");
+			} else {
+				if (id == null) {
+					System.err.println("The Game Server Id is required!");
+					return;
+				}
+				
+				GameServerDAO.getInstance().unregister(id);
+				
+				System.out.println("Unregistered Game Server Id " + id + ".");
+			}
+		} catch (Exception ex) {
+			System.err.println("There has been an error unregistering " + (all ? "all servers!" : id + " game server!"));
+			ex.printStackTrace();
+		}
+	}
+}

+ 90 - 0
src/main/java/com/l2jserver/cli/dao/GameServerDAO.java

@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2019 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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 <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.cli.dao;
+
+import static com.l2jserver.cli.config.Configs.loginServer;
+
+import java.math.BigInteger;
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Game Server DAO.
+ * @author Zoey76
+ */
+public class GameServerDAO extends AbstractDAO {
+	
+	private static final String SELECT_SERVERS = "SELECT * FROM gameservers";
+	
+	private static final String INSERT_SERVER = "INSERT INTO gameservers (hexid, server_id, host) values (?,?,?)";
+	
+	private static final String DELETE_SERVER = "DELETE FROM gameservers WHERE server_id = ?";
+	
+	private static final String DELETE_ALL_SERVERS = "DELETE FROM gameservers";
+	
+	private GameServerDAO() {
+		super(loginServer().db().host(), loginServer().db().port(), loginServer().db().name(), loginServer().db().user(), loginServer().db().password());
+	}
+	
+	public void unregister(int id) throws SQLException {
+		try (var con = getConnection();
+			var ps = con.prepareStatement(DELETE_SERVER)) {
+			ps.setInt(1, id);
+			ps.executeUpdate();
+		}
+	}
+	
+	public void unregisterAll() throws SQLException {
+		try (var con = getConnection();
+			var s = con.createStatement()) {
+			s.executeUpdate(DELETE_ALL_SERVERS);
+		}
+	}
+	
+	public void register(byte[] hexId, int id, String externalHost) throws SQLException {
+		try (var con = getConnection();
+			var ps = con.prepareStatement(INSERT_SERVER)) {
+			ps.setString(1, new BigInteger(hexId).toString(16));
+			ps.setInt(2, id);
+			ps.setString(3, externalHost);
+			ps.executeUpdate();
+		}
+	}
+	
+	public Set<Integer> gameServers() throws SQLException {
+		final var result = new HashSet<Integer>();
+		try (var con = getConnection();
+			var s = con.createStatement();
+			var rs = s.executeQuery(SELECT_SERVERS)) {
+			while (rs.next()) {
+				result.add(rs.getInt("server_id"));
+			}
+		}
+		return result;
+	}
+	
+	public static GameServerDAO getInstance() {
+		return SingletonHolder.INSTANCE;
+	}
+	
+	private static class SingletonHolder {
+		protected static final GameServerDAO INSTANCE = new GameServerDAO();
+	}
+}

+ 114 - 0
src/main/java/com/l2jserver/cli/model/ServerNames.java

@@ -0,0 +1,114 @@
+/*
+ * Copyright © 2019 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server 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.
+ *
+ * L2J Server 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 <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.cli.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Server Names.
+ * @author Zoey76
+ */
+public class ServerNames {
+	
+	private static final Map<Integer, String> SERVERS = new HashMap<>();
+	
+	static {
+		SERVERS.put(0, "Bartz");
+		SERVERS.put(0, "Bartz");
+		SERVERS.put(1, "Sieghardt");
+		SERVERS.put(2, "Kain");
+		SERVERS.put(3, "Lionna");
+		SERVERS.put(4, "Erica");
+		SERVERS.put(5, "Gustin");
+		SERVERS.put(6, "Devianne");
+		SERVERS.put(7, "Hindemith");
+		SERVERS.put(8, "Teon (EURO)");
+		SERVERS.put(9, "Franz (EURO)");
+		SERVERS.put(10, "Luna (EURO)");
+		SERVERS.put(11, "Sayha");
+		SERVERS.put(12, "Aria");
+		SERVERS.put(13, "Phoenix");
+		SERVERS.put(14, "Chronos");
+		SERVERS.put(15, "Naia (EURO)");
+		SERVERS.put(16, "Elhwynna");
+		SERVERS.put(17, "Ellikia");
+		SERVERS.put(18, "Shikken");
+		SERVERS.put(19, "Scryde");
+		SERVERS.put(20, "Frikios");
+		SERVERS.put(21, "Ophylia");
+		SERVERS.put(22, "Shakdun");
+		SERVERS.put(23, "Tarziph");
+		SERVERS.put(24, "Aria");
+		SERVERS.put(25, "Esenn");
+		SERVERS.put(26, "Elcardia");
+		SERVERS.put(27, "Yiana");
+		SERVERS.put(28, "Seresin");
+		SERVERS.put(29, "Tarkai");
+		SERVERS.put(30, "Khadia");
+		SERVERS.put(31, "Roien");
+		SERVERS.put(32, "Kallint (Non-PvP)");
+		SERVERS.put(33, "Baium");
+		SERVERS.put(34, "Kamael");
+		SERVERS.put(35, "Beleth");
+		SERVERS.put(36, "Anakim");
+		SERVERS.put(37, "Lilith");
+		SERVERS.put(38, "Thifiel");
+		SERVERS.put(39, "Lithra");
+		SERVERS.put(40, "Lockirin");
+		SERVERS.put(41, "Kakai");
+		SERVERS.put(42, "Cadmus");
+		SERVERS.put(43, "Athebaldt");
+		SERVERS.put(44, "Blackbird");
+		SERVERS.put(45, "Ramsheart");
+		SERVERS.put(46, "Esthus");
+		SERVERS.put(47, "Vasper");
+		SERVERS.put(48, "Lancer");
+		SERVERS.put(49, "Ashton");
+		SERVERS.put(50, "Waytrel");
+		SERVERS.put(51, "Waltner");
+		SERVERS.put(52, "Tahnford");
+		SERVERS.put(53, "Hunter");
+		SERVERS.put(54, "Dewell");
+		SERVERS.put(55, "Rodemaye");
+		SERVERS.put(56, "Ken Rauhel");
+		SERVERS.put(57, "Ken Abigail");
+		SERVERS.put(58, "Ken Orwen");
+		SERVERS.put(59, "Van Holter");
+		SERVERS.put(60, "Desperion");
+		SERVERS.put(61, "Einhovant");
+		SERVERS.put(62, "Shunaiman");
+		SERVERS.put(63, "Faris");
+		SERVERS.put(64, "Tor");
+		SERVERS.put(65, "Carneiar");
+		SERVERS.put(66, "Dwyllios");
+		SERVERS.put(67, "Baium");
+		SERVERS.put(68, "Hallate");
+		SERVERS.put(69, "Zaken");
+		SERVERS.put(70, "Core");
+	}
+	
+	public static String getServer(int id) {
+		return SERVERS.getOrDefault(id, "Undefined");
+	}
+	
+	public static Map<Integer, String> getServers() {
+		return SERVERS;
+	}
+}