diff --git a/app/classes/minecraft/stats.py b/app/classes/minecraft/stats.py index a3f85c054de88dc3b84873520896877d3e763548..fd1c219bb0d8a60cc0d8e8f2e9fca49293d11956 100644 --- a/app/classes/minecraft/stats.py +++ b/app/classes/minecraft/stats.py @@ -245,6 +245,16 @@ class Stats: @staticmethod def parse_server_ping(ping_obj: object): + if ping_obj is bool: + return { + "online": 0, + "max": 0, + "players": 0, + "server_description": '', + "server_version": '', + "server_icon": '', + } + online_stats = {} try: diff --git a/app/classes/shared/dummy_servers.py b/app/classes/shared/dummy_servers.py index d5e047fda80eab0a5a2f4d5b49060d2c4ed93d9a..040ebdfffb746adbc5a163f2d4e0e7b93904187e 100644 --- a/app/classes/shared/dummy_servers.py +++ b/app/classes/shared/dummy_servers.py @@ -4,6 +4,7 @@ import threading import asyncio from typing import Callable +import quarry.data.packets as packets from quarry.net.server import ServerFactory, ServerProtocol from twisted.internet import asyncioreactor @@ -23,6 +24,7 @@ class DummyServers(metaclass=Singleton): ) self.reactor: asyncioreactor.AsyncioSelectorReactor | None = None self.loop: asyncio.AbstractEventLoop | None = None + self.init_quarry_protocols() def init_servers(self): self.thread.start() @@ -39,13 +41,36 @@ class DummyServers(metaclass=Singleton): self.reactor = reactor self.reactor.run(installSignalHandlers=False) + def init_quarry_protocols(self): + new_versions = [(761, '1.19.3'), (762, '1.19.4'), (763, '1.20'), (764, '1.20.2'), (765, '1.20.3')] + + for v in new_versions: + packets.minecraft_versions[v[0]] = v[1] + + max_version = packets.default_protocol_version + last_version_names = [(k, v) for k, v in packets.packet_names.items() if k[0] == max_version] + for packet_info in last_version_names: + key = packet_info[0] + name = packet_info[1] + + for version in new_versions: + packets.packet_names[(version[0], key[1], key[2], key[3])] = name + + last_version_idents = [(k, v) for k, v in packets.packet_idents.items() if k[0] == max_version] + for ident in last_version_idents: + key = ident[0] + val = ident[1] + + for version in new_versions: + packets.packet_idents[(version[0], key[1], key[2], key[3])] = val + class DummyServerProtocol(ServerProtocol): """Class that implements what to do after client connects to dummy server.""" def player_joined(self): logger.info("Player connected in server. Stopping dummy server.") - ServerProtocol.player_joined(self) + # ServerProtocol.player_joined(self) self.close(self.factory.close_message) if self.factory.player_connected_callback: diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 8011c14b75cdcfe2e0780474370f826ceef0e44d..9bd9b7bf070b0c57ea19e28094f46cad4e044a93 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -386,276 +386,281 @@ class ServerInstance: @callback def start_server(self, user_id, forge_install=False): - if not user_id: - user_lang = self.helper.get_setting("language") - else: - user_lang = HelperUsers.get_user_lang_by_id(user_id) - - # Checks if user is currently attempting to move global server - # dir - if self.helper.dir_migration: - WebSocketManager().broadcast_user( - user_id, - "send_start_error", - { - "error": self.helper.translation.translate( - "error", - "migration", - user_lang, - ) - }, - ) - return False + def do_start(): + if not user_id: + user_lang = self.helper.get_setting("language") + else: + user_lang = HelperUsers.get_user_lang_by_id(user_id) - if self.stats_helper.get_import_status() and not forge_install: - if user_id: + # Checks if user is currently attempting to move global server + # dir + if self.helper.dir_migration: WebSocketManager().broadcast_user( user_id, "send_start_error", { "error": self.helper.translation.translate( - "error", "not-downloaded", user_lang + "error", + "migration", + user_lang, ) }, ) - return False + return False - logger.info( - f"Start command detected. Reloading settings from DB for server {self.name}" - ) - self.setup_server_run_command() - # fail safe in case we try to start something already running - if self.check_running(): - logger.error("Server is already running - Cancelling Startup") - Console.error("Server is already running - Cancelling Startup") - return False - if self.check_update(): - logger.error("Server is updating. Terminating startup.") - return False + if self.stats_helper.get_import_status() and not forge_install: + if user_id: + WebSocketManager().broadcast_user( + user_id, + "send_start_error", + { + "error": self.helper.translation.translate( + "error", "not-downloaded", user_lang + ) + }, + ) + return False - logger.info(f"Launching Server {self.name} with command {self.server_command}") - Console.info(f"Launching Server {self.name} with command {self.server_command}") + logger.info( + f"Start command detected. Reloading settings from DB for server {self.name}" + ) + self.setup_server_run_command() + # fail safe in case we try to start something already running + if self.check_running(): + logger.error("Server is already running - Cancelling Startup") + Console.error("Server is already running - Cancelling Startup") + return False + if self.check_update(): + logger.error("Server is updating. Terminating startup.") + return False - # Checks for eula. Creates one if none detected. - # If EULA is detected and not set to true we offer to set it true. - e_flag = False - if Helpers.check_file_exists(os.path.join(self.settings["path"], "eula.txt")): - with open( - os.path.join(self.settings["path"], "eula.txt"), "r", encoding="utf-8" - ) as f: - line = f.readline().lower() - e_flag = line in [ - "eula=true", - "eula = true", - "eula= true", - "eula =true", - ] - # If this is a forge installer we're running we can bypass the eula checks. - if forge_install is True: - e_flag = True - if not e_flag and self.settings["type"] == "minecraft-java": - if user_id: - WebSocketManager().broadcast_user( - user_id, "send_eula_bootbox", {"id": self.server_id} - ) + logger.info(f"Launching Server {self.name} with command {self.server_command}") + Console.info(f"Launching Server {self.name} with command {self.server_command}") + + # Checks for eula. Creates one if none detected. + # If EULA is detected and not set to true we offer to set it true. + e_flag = False + if Helpers.check_file_exists(os.path.join(self.settings["path"], "eula.txt")): + with open( + os.path.join(self.settings["path"], "eula.txt"), "r", encoding="utf-8" + ) as f: + line = f.readline().lower() + e_flag = line in [ + "eula=true", + "eula = true", + "eula= true", + "eula =true", + ] + # If this is a forge installer we're running we can bypass the eula checks. + if forge_install is True: + e_flag = True + if not e_flag and self.settings["type"] == "minecraft-java": + if user_id: + WebSocketManager().broadcast_user( + user_id, "send_eula_bootbox", {"id": self.server_id} + ) + else: + logger.error( + "Autostart failed due to EULA being false. " + "Agree not sent due to auto start." + ) + return False + if Helpers.is_os_windows(): + logger.info("Windows Detected") else: - logger.error( - "Autostart failed due to EULA being false. " - "Agree not sent due to auto start." - ) - return False - if Helpers.is_os_windows(): - logger.info("Windows Detected") - else: - logger.info("Unix Detected") - - logger.info( - f"Starting server in {self.server_path} with command: {self.server_command}" - ) - - # checks to make sure file is openable (downloaded) and exists. - try: - with open( - os.path.join( - self.server_path, - HelperServers.get_server_data_by_id(self.server_id)["executable"], - ), - "r", - encoding="utf-8", - ): - # Can open the file - pass - - except: - if user_id: - WebSocketManager().broadcast_user( - user_id, - "send_start_error", - { - "error": self.helper.translation.translate( - "error", "not-downloaded", user_lang - ) - }, - ) - return + logger.info("Unix Detected") - if ( - not Helpers.is_os_windows() - and HelperServers.get_server_type_by_id(self.server_id) - == "minecraft-bedrock" - ): logger.info( - f"Bedrock and Unix detected for server {self.name}. " - f"Switching to appropriate execution string" + f"Starting server in {self.server_path} with command: {self.server_command}" ) - my_env = os.environ - my_env["LD_LIBRARY_PATH"] = self.server_path + + # checks to make sure file is openable (downloaded) and exists. try: - self.process = subprocess.Popen( - self.server_command, - cwd=self.server_path, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - env=my_env, - ) - except Exception as ex: - logger.error( - f"Server {self.name} failed to start with error code: {ex}" - ) + with open( + os.path.join( + self.server_path, + HelperServers.get_server_data_by_id(self.server_id)["executable"], + ), + "r", + encoding="utf-8", + ): + # Can open the file + pass + + except: if user_id: WebSocketManager().broadcast_user( user_id, "send_start_error", { "error": self.helper.translation.translate( - "error", "start-error", user_lang - ).format(self.name, ex) + "error", "not-downloaded", user_lang + ) }, ) - if forge_install: - # Reset import status if failed while forge installing - self.stats_helper.finish_import() - return False + return - else: - try: - self.process = subprocess.Popen( - self.server_command, - cwd=self.server_path, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + if ( + not Helpers.is_os_windows() + and HelperServers.get_server_type_by_id(self.server_id) + == "minecraft-bedrock" + ): + logger.info( + f"Bedrock and Unix detected for server {self.name}. " + f"Switching to appropriate execution string" ) - except Exception as ex: - # Checks for java on initial fail - if not self.helper.detect_java(): + my_env = os.environ + my_env["LD_LIBRARY_PATH"] = self.server_path + try: + self.process = subprocess.Popen( + self.server_command, + cwd=self.server_path, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=my_env, + ) + except Exception as ex: + logger.error( + f"Server {self.name} failed to start with error code: {ex}" + ) if user_id: WebSocketManager().broadcast_user( user_id, "send_start_error", { "error": self.helper.translation.translate( - "error", "noJava", user_lang - ).format(self.name) + "error", "start-error", user_lang + ).format(self.name, ex) }, ) + if forge_install: + # Reset import status if failed while forge installing + self.stats_helper.finish_import() return False - logger.error( - f"Server {self.name} failed to start with error code: {ex}" + + else: + try: + self.process = subprocess.Popen( + self.server_command, + cwd=self.server_path, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + except Exception as ex: + # Checks for java on initial fail + if not self.helper.detect_java(): + if user_id: + WebSocketManager().broadcast_user( + user_id, + "send_start_error", + { + "error": self.helper.translation.translate( + "error", "noJava", user_lang + ).format(self.name) + }, + ) + return False + logger.error( + f"Server {self.name} failed to start with error code: {ex}" + ) + if user_id: + WebSocketManager().broadcast_user( + user_id, + "send_start_error", + { + "error": self.helper.translation.translate( + "error", "start-error", user_lang + ).format(self.name, ex) + }, + ) + if forge_install: + # Reset import status if failed while forge installing + self.stats_helper.finish_import() + return False + + out_buf = ServerOutBuf(self.helper, self.process, self.server_id) + + logger.debug(f"Starting virtual terminal listener for server {self.name}") + threading.Thread( + target=out_buf.check, daemon=True, name=f"{self.server_id}_virtual_terminal" + ).start() + + self.is_crashed = False + self.stats_helper.server_crash_reset() + + self.start_time = str(datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")) + + if self.process.poll() is None: + logger.info(f"Server {self.name} running with PID {self.process.pid}") + Console.info(f"Server {self.name} running with PID {self.process.pid}") + self.is_crashed = False + self.stats_helper.server_crash_reset() + self.record_server_stats() + check_internet_thread = threading.Thread( + target=self.check_internet_thread, + daemon=True, + args=( + user_id, + user_lang, + ), + name=f"{self.name}_Internet", ) - if user_id: + check_internet_thread.start() + # Checks if this is the servers first run. + if self.stats_helper.get_first_run(): + self.stats_helper.set_first_run() + loc_server_port = self.stats_helper.get_server_stats()["server_port"] + # Sends port reminder message. WebSocketManager().broadcast_user( user_id, "send_start_error", { "error": self.helper.translation.translate( - "error", "start-error", user_lang - ).format(self.name, ex) + "error", "portReminder", user_lang + ).format(self.name, loc_server_port) }, ) - if forge_install: - # Reset import status if failed while forge installing - self.stats_helper.finish_import() - return False - - out_buf = ServerOutBuf(self.helper, self.process, self.server_id) - - logger.debug(f"Starting virtual terminal listener for server {self.name}") - threading.Thread( - target=out_buf.check, daemon=True, name=f"{self.server_id}_virtual_terminal" - ).start() - - self.is_crashed = False - self.stats_helper.server_crash_reset() - - self.start_time = str(datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")) - - if self.process.poll() is None: - logger.info(f"Server {self.name} running with PID {self.process.pid}") - Console.info(f"Server {self.name} running with PID {self.process.pid}") - self.is_crashed = False - self.stats_helper.server_crash_reset() - self.record_server_stats() - check_internet_thread = threading.Thread( - target=self.check_internet_thread, - daemon=True, - args=( - user_id, - user_lang, - ), - name=f"{self.name}_Internet", - ) - check_internet_thread.start() - # Checks if this is the servers first run. - if self.stats_helper.get_first_run(): - self.stats_helper.set_first_run() - loc_server_port = self.stats_helper.get_server_stats()["server_port"] - # Sends port reminder message. - WebSocketManager().broadcast_user( - user_id, - "send_start_error", - { - "error": self.helper.translation.translate( - "error", "portReminder", user_lang - ).format(self.name, loc_server_port) - }, - ) - server_users = PermissionsServers.get_server_user_list(self.server_id) - for user in server_users: - if user != user_id: + server_users = PermissionsServers.get_server_user_list(self.server_id) + for user in server_users: + if user != user_id: + WebSocketManager().broadcast_user(user, "send_start_reload", {}) + else: + server_users = PermissionsServers.get_server_user_list(self.server_id) + for user in server_users: WebSocketManager().broadcast_user(user, "send_start_reload", {}) else: - server_users = PermissionsServers.get_server_user_list(self.server_id) - for user in server_users: - WebSocketManager().broadcast_user(user, "send_start_reload", {}) - else: - logger.warning( - f"Server PID {self.process.pid} died right after starting " - f"- is this a server config issue?" - ) - Console.critical( - f"Server PID {self.process.pid} died right after starting " - f"- is this a server config issue?" - ) + logger.warning( + f"Server PID {self.process.pid} died right after starting " + f"- is this a server config issue?" + ) + Console.critical( + f"Server PID {self.process.pid} died right after starting " + f"- is this a server config issue?" + ) - if self.settings["crash_detection"]: - logger.info( - f"Server {self.name} has crash detection enabled " - f"- starting watcher task" - ) - Console.info( - f"Server {self.name} has crash detection enabled " - f"- starting watcher task" - ) + if self.settings["crash_detection"]: + logger.info( + f"Server {self.name} has crash detection enabled " + f"- starting watcher task" + ) + Console.info( + f"Server {self.name} has crash detection enabled " + f"- starting watcher task" + ) - self.server_scheduler.add_job( - self.detect_crash, "interval", seconds=30, id=f"c_{self.server_id}" - ) + self.server_scheduler.add_job( + self.detect_crash, "interval", seconds=30, id=f"c_{self.server_id}" + ) - # If this is a forge install we'll call the watcher to do the things - if forge_install: - self.forge_install_watcher() + # If this is a forge install we'll call the watcher to do the things + if forge_install: + self.forge_install_watcher() + + self.dummy_servers.stop_dummy_server( + self.server_id, port_freed_callback=do_start + ) def check_internet_thread(self, user_id, user_lang): if user_id: