From 663f2889d48819c4d3506c4363ccd7f863ceac14 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 22 Aug 2024 21:44:51 +0200 Subject: [PATCH] Add ro_server_manager script as translation for run_server.bat --- osiris_listener_telegram.py | 167 +++++++++++++++++++++++------------- ro_server_manager.py | 107 +++++++++++++++++++++++ 2 files changed, 212 insertions(+), 62 deletions(-) create mode 100644 ro_server_manager.py diff --git a/osiris_listener_telegram.py b/osiris_listener_telegram.py index ee839e6..26b3fda 100644 --- a/osiris_listener_telegram.py +++ b/osiris_listener_telegram.py @@ -86,9 +86,19 @@ gaming_server_profiles = { 'args_DontStarveWeirdlyAtSandersOverworld': ["C:/Program Files (x86)/Steam/steamapps/common/Don't Starve Together Dedicated Server/bin/dontstarve_dedicated_server_nullrenderer.exe", '-persistent_storage_root', r"C:/Users/4lexK/Desktop/GameServers/Dont Starve/Dont Starve weirdly at Sanders", '-conf_dir', "DontStarveWeirdlyAtSandersOverworld", '-console', '-cluster', 'DontStarveWeirdlyAtSanders', '-shard', 'Master'], 'args_DontStarveWeirdlyAtSandersCave': ["C:/Program Files (x86)/Steam/steamapps/common/Don't Starve Together Dedicated Server/bin/dontstarve_dedicated_server_nullrenderer.exe", '-persistent_storage_root', r"C:/Users/4lexK/Desktop/GameServers/Dont Starve/Dont Starve weirdly at Sanders", '-conf_dir', "DontStarveWeirdlyAtSandersCave", '-console', '-cluster', 'DontStarveWeirdlyAtSanders', '-shard', 'Caves'], 'wait_time': 30 - } - + }, # Ragnarok Online + "sanderro": { + 'profile_id': 'sanderro', + 'exec_type': 'exe_ro', + 'exec_path': "C:/Users/4lexK/Desktop/GameServers/Ragnarok Online/SanderRo_rathena/2 . Emulator/rathena", # Path zur Rathena Installation + 'server_name': 'SanderRO', + 'directory': "C:/Users/4lexK/Desktop/GameServers/Ragnarok Online/SanderRo_rathena", + 'args_asgard': "", + 'args_midgar': "", + 'args_svartalfeim': "", + 'wait_time': 30 + } # World of Warcraft } @@ -336,27 +346,14 @@ def start_gameserver(profile: str) -> str: # Create logs directory if it doesn't exist os.makedirs(os.path.dirname(log_file), exist_ok=True) - if os.path.exists(pid_file): - with open(pid_file, 'r') as file: - pid = int(file.read().strip()) - if psutil.pid_exists(pid) and is_java_process(pid): - response_msg = f"Server {server['server_name']} läuft bereits." - return response_msg - else: - os.remove(pid_file) - - with open(log_file, 'w') as log: - process = subprocess.Popen( - server['command'], - cwd=server['directory'], - stdout=log, - stderr=log, - creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == 'nt' else 0 - ) - #time.sleep(wait_time) + # Prüfen, ob Prozesse bereits laufen + response = initialize_pid(pid_file, {server['server_name']}, is_java_process) + response_msg += response + + pid = start_process(log_file, command=server['command'], cur_wrk_dir=server['directory']) with open(pid_file, 'w') as file: - file.write(str(process.pid)) - response_msg = f"Server {server['server_name']} wurde mit PID '{process.pid}' gestartet." + file.write(str(pid)) + response_msg = f"Server {server['server_name']} wurde mit PID '{pid}' gestartet." case "exe_dst": # Bei exe der DST sind immer mehrere Commands abzusetzen (je nach dem wie viele Shards da sind) os.chdir(server['directory']) @@ -371,33 +368,54 @@ def start_gameserver(profile: str) -> str: os.makedirs(os.path.dirname(log_file), exist_ok=True) - # checken wie prozesse heißen - if os.path.exists(pid_file): - with open(pid_file, 'r') as file: - pid = int(file.read().strip()) - if psutil.pid_exists(pid) and is_dst_process(pid): - response_msg = f"Server {shard}{server['server_name']} läuft bereits." - return response_msg - else: - os.remove(pid_file) - - with open(log_file, 'w') as log: - try: - process = subprocess.Popen( - server[f"args_{shard}"], - cwd=server['exec_path'], - #shell=True, - stdout=log, - stderr=log, - creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == 'nt' else 0 - ) - except Exception as err: - print(f"{err = }") - #time.sleep(wait_time) + # Prüfen, ob Prozesse bereits laufen + response = initialize_pid(pid_file, f"{shard}{server['server_name']}", is_dst_process) + response_msg += response + + pid = start_process(log_file, command=server[f"args_{shard}"], cur_wrk_dir=server['exec_path']) + with open(pid_file, 'w') as file: - file.write(str(process.pid)) - response_msg += f"Server {server['server_name']} ({shard}) wurde mit PID '{process.pid}' gestartet. " + file.write(str(pid)) + response_msg += f"Server {server['server_name']} ({shard}) wurde mit PID '{pid}' gestartet. " + case "exe_ro": + # Bei exe von Ragnarok Online werden immer mehrere Server gestartet da verschiedene Server + # unterschiedliche Aufgaben haben und zusammen arbeiten (je nach dem wie viele Realms eingebaut sind | zur Zeit [08/2024] 3) + os.chdir(server['directory']) + + # Es wird immer Map- und Web-Server Basis gestartet. Hinzu kommt jeweils ein Char- und ein Login-Server pro Realm (z.Zt [08/2024] 3) + + # Map-Server + pid_file_map = os.path.join(server['directory'], f'{server['srv_map_name']}.pid') + log_file_map = os.path.join(server['directory'], 'logs', f'server_{server['srv_map_name']}_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log') + + # Web-Server + pid_file_web = os.path.join(server['directory'], f'{server['srv_web_name']}.pid') + log_file_web = os.path.join(server['directory'], 'logs', f'server_{server['srv_web_name']}_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log') + + os.makedirs(os.path.dirname(log_file_map), exist_ok=True) + + # Realm Server + realm_names = server['realm_names'] + for realm in realm_names: + + # TODO Checken, wie hier automatisiert werden kann mit realm list. + #Char Server des Realm + pid_file_realm_char = os.path.join(server['directory'], f'{realm}_char.pid') + log_file_realm_char = os.path.join(server['directory'], 'logs', f'server_{realm}_char_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log') + + #Login Server des Realm + pid_file_realm_login = os.path.join(server['directory'], f'{realm}_login.pid') + log_file_realm_login = os.path.join(server['directory'], 'logs', f'server_{realm}_login_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log') + # Prüfen, ob Prozesse bereits laufen + response = initialize_pid(pid_file_realm_char, f"{shard}{server['server_name']}", is_dst_process) + response_msg += response + + # Starten des Prozess + pid = start_process(log_file, command=server[f"args_{shard}"], cur_wrk_dir=server['exec_path']) + with open(pid_file, 'w') as file: + file.write(str(pid)) + response_msg += f"Server {server['server_name']} ({shard}) wurde mit PID '{pid}' gestartet. " else: response_msg = f"Server Informationen für Profile {profile} existieren nicht. Bitte prüfen." return response_msg @@ -490,6 +508,32 @@ def read_pid_file(pid_file): except (FileNotFoundError, ValueError): return None +def initialize_pid(path_to_pid: str, server_name: str, type_validater_fn: callable)->str: + if os.path.exists(path_to_pid): + with open(path_to_pid, 'r') as file: + pid = int(file.read().strip()) + if psutil.pid_exists(pid) and type_validater_fn(pid): + response_msg = f"Server {server_name} läuft bereits." + return response_msg + else: + os.remove(path_to_pid) + response_msg = f"Server-PID zu {server_name} als Stale identifiziert. Initialisiere." + return response_msg + +def start_process(log_file: str, command: list, cur_wrk_dir: str) -> int: + with open(log_file, 'w') as log: + try: + process = subprocess.Popen( + command, + cwd=cur_wrk_dir, + stdout=log, + stderr=log, + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if os.name == 'nt' else 0 + ) + except Exception as err: + print(f"{err = }") + return process.pid + def is_java_process(pid): try: p = psutil.Process(pid) @@ -619,17 +663,11 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> logging.info("Received request for gameserver status.") status_msg = get_status_gameservers() await query.message.reply_text(f"{status_msg}") - case "gameservers_minecraft": - await query.message.reply_text("Wähle einen Minecraft Server:", reply_markup=InlineKeyboardMarkup(minecraft_menu)) - case "gameservers_dontstarve": - await query.message.reply_text("Wähle eine Aktion für den Dont Starve Server:", reply_markup=InlineKeyboardMarkup(dont_starve_menu)) - case "gameservers_ragnarok_online": - await query.message.reply_text("Wähle eine Aktion für den Ragnarok Online Server:", reply_markup=InlineKeyboardMarkup(ragnarok_online_menu)) - case "gameservers_wow": - await query.message.reply_text("Wähle einen Aktion für den World of Warcraft Server:", reply_markup=InlineKeyboardMarkup(wow_menu)) case "gameservers_back": await send_main_menu(context, query.message.chat_id) # Minecraft Servers + case "gameservers_minecraft": + await query.message.reply_text("Wähle einen Minecraft Server:", reply_markup=InlineKeyboardMarkup(minecraft_menu)) case "minecraft_vanilla": await query.message.reply_text("Wähle einen Aktion für den Minecraft Vanilla Server:", reply_markup=InlineKeyboardMarkup(mc_vanilla_menu)) case "minecraft_sanderpack": @@ -687,6 +725,7 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> await query.message.reply_text(f"{msg}") case "mc_sandervalley_back": await query.message.reply_text("Wähle einen Minecraft Server:", reply_markup=InlineKeyboardMarkup(minecraft_menu)) + # Dont Starve Server case "gameservers_dst": await query.message.reply_text("Wähle einen Don't Starve Together Server:", reply_markup=InlineKeyboardMarkup(dont_starve_menu)) @@ -721,18 +760,22 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) -> # await query.message.reply_text(f"{msg}") #case "dstas_back": # await query.message.reply_text("Wähle einen Don't Starve Together Server:", reply_markup=InlineKeyboardMarkup(dont_starve_menu)) - # Ragnarok Online Server + + # Ragnarok Online Server + case "gameservers_ragnarok_online": + await query.message.reply_text("Wähle eine Aktion für den Ragnarok Online Server (Note: Betrifft immer Alle Realms):", reply_markup=InlineKeyboardMarkup(ragnarok_online_menu)) case "ro_start": - msg = "Not Implemented yet" - await query.message.reply_text(f"{msg}") - await send_main_menu(context, query.message.chat_id) + msg = start_gameserver("sanderro") + await query.message.reply_text(f"{msg}") case "ro_stop": - msg = "Not Implemented yet" - await query.message.reply_text(f"{msg}") - await send_main_menu(context, query.message.chat_id) + msg = stop_gameserver("sanderro") + await query.message.reply_text(f"{msg}") case "ro_back": await query.message.reply_text("Wähle einen Gameserver Typ:", reply_markup=InlineKeyboardMarkup(gameserver_menu)) + # World of Warcraft Server + case "gameservers_wow": + await query.message.reply_text("Wähle einen Aktion für den World of Warcraft Server (Note: Betrifft immer Alle Realms):", reply_markup=InlineKeyboardMarkup(wow_menu)) case "wow_start": msg = "Not Implemented yet" await query.message.reply_text(f"{msg}") diff --git a/ro_server_manager.py b/ro_server_manager.py new file mode 100644 index 0000000..fde77e8 --- /dev/null +++ b/ro_server_manager.py @@ -0,0 +1,107 @@ + +import subprocess +import sys +import argparse + +# Constants for server executable names +LOGIN_SERVER = "login-server.exe" +CHAR_SERVER = "char-server.exe" +WEB_SERVER = "web-server.exe" +MAP_SERVER = "map-server.exe" + +# Store the process IDs for each server +server_pids = { + "login": None, + "char": None, + "web": None, + "map": None, +} + +# Determine if the servers are running +server_running = { + "login": False, + "char": False, + "web": False, + "map": False, +} + +def get_server_status(server_name, exe_name): + """Check if the server is running and retrieve its PID.""" + try: + output = subprocess.check_output(f'tasklist /FI "IMAGENAME eq {exe_name}"', shell=True, text=True, errors="ignore") + lines = output.strip().splitlines() + if len(lines) > 1 and lines[1]: + server_pids[server_name] = lines[1].split()[1] + server_running[server_name] = True + else: + server_running[server_name] = False + except subprocess.CalledProcessError: + server_running[server_name] = False + +def stop_server(server_name): + """Stop the specified server if it is running.""" + if server_running[server_name]: + subprocess.call(f"taskkill /PID {server_pids[server_name]} /F", cwd=r"C:\Users\4lexK\Desktop\GameServerFiles\SanderRo_rathena\2 . Emulator\rathena", shell=True) + print(f"{server_name.capitalize()} server stopped.") + else: + print(f"{server_name.capitalize()} server is not running.") + +def start_server(server_name, script_name, restart_mode): + """Start the specified server if it is not already running.""" + if not server_running[server_name]: + subprocess.Popen(f"cmd /k {script_name} restart_mode={restart_mode}", cwd=r"C:\Users\4lexK\Desktop\GameServerFiles\SanderRo_rathena\2 . Emulator\rathena", shell=True) + print(f"{server_name.capitalize()} server started.") + else: + print(f"{server_name.capitalize()} server is already running with PID {server_pids[server_name]}.") + +def stop_all_servers(): + """Stop all servers.""" + print("Stopping all servers...") + stop_server("login") + stop_server("char") + stop_server("web") + stop_server("map") + +def start_all_servers(restart_mode="off"): + """Start all servers.""" + print("Starting all servers...") + start_server("login", 'logserv.bat', restart_mode) + start_server("char", 'charserv.bat', restart_mode) + start_server("web", 'webserv.bat', restart_mode) + start_server("map", 'mapserv.bat', restart_mode) + +def check_status(): + """ Check the status of all servers. + """ + print("Getting status of all servers...") + get_server_status("login", LOGIN_SERVER) + get_server_status("char", CHAR_SERVER) + get_server_status("web", WEB_SERVER) + get_server_status("map", MAP_SERVER) + + for server, running in server_running.items(): + if running: + print(f"{server.capitalize()} server is running with PID {server_pids[server]}") + else: + print(f"{server.capitalize()} server is not running.") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="rAthena Ragnarok Online Server Emulator Control Script") + parser.add_argument('target', choices=['status', 'watch', 'stop', 'start'], help="Choose the target action") + + # Show help if no arguments are provided + if len(sys.argv) == 1: + parser.print_help() + sys.exit(1) + + args = parser.parse_args() + + if args.target == "status": + check_status() + elif args.target == "watch": + start_all_servers(restart_mode="on") + elif args.target == "stop": + stop_all_servers() + elif args.target == "start": + start_all_servers(restart_mode="off") \ No newline at end of file