From ad4603aafb8b83e682112a020aabb50b0c2e4be3 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 19 Nov 2022 13:45:18 -0500 Subject: [PATCH 1/9] Add automated forge server creation installs --- app/classes/controllers/servers_controller.py | 10 ++- app/classes/minecraft/serverjars.py | 35 +++++------ app/classes/shared/main_controller.py | 35 ++++++++--- app/classes/shared/server.py | 61 +++++++++++++++++-- 4 files changed, 106 insertions(+), 35 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index b0a9044a..934ad328 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -119,9 +119,15 @@ class ServersController(metaclass=Singleton): return srv.stats_helper.set_import() @staticmethod - def finish_import(server_id): + def finish_import(server_id, forge=False): srv = ServersController().get_server_instance_by_id(server_id) - return srv.stats_helper.finish_import() + srv.stats_helper.finish_import() + # This is where we start the forge installerr + if forge: + srv.run_threaded_server( + HelperUsers.get_user_id_by_name("system"), forge_install=True + ) + return @staticmethod def get_import_status(server_id): diff --git a/app/classes/minecraft/serverjars.py b/app/classes/minecraft/serverjars.py index 1ecfc0f1..a656944b 100644 --- a/app/classes/minecraft/serverjars.py +++ b/app/classes/minecraft/serverjars.py @@ -187,31 +187,28 @@ class ServerJars: # open a file stream with requests.get(fetch_url, timeout=2, stream=True) as r: + success = False try: with open(path, "wb") as output: shutil.copyfileobj(r.raw, output) - ServersController.finish_import(server_id) + # If this is the newer forge version we will run the installer + if server == "forge" and int(version.split(".")[1]) > 15: + ServersController.finish_import(server_id, True) + else: + ServersController.finish_import(server_id) - for user in server_users: - self.helper.websocket_helper.broadcast_user( - user, "notification", "Executable download finished" - ) - time.sleep(3) - self.helper.websocket_helper.broadcast_user( - user, "send_start_reload", {} - ) - return True + success = True except Exception as e: logger.error(f"Unable to save jar to {path} due to error:{e}") ServersController.finish_import(server_id) server_users = PermissionsServers.get_server_user_list(server_id) - for user in server_users: - self.helper.websocket_helper.broadcast_user( - user, "notification", "Executable download finished" - ) - time.sleep(3) - self.helper.websocket_helper.broadcast_user( - user, "send_start_reload", {} - ) - return False + for user in server_users: + self.helper.websocket_helper.broadcast_user( + user, "notification", "Executable download finished" + ) + time.sleep(3) + self.helper.websocket_helper.broadcast_user( + user, "send_start_reload", {} + ) + return success diff --git a/app/classes/shared/main_controller.py b/app/classes/shared/main_controller.py index eb8c3219..03c640af 100644 --- a/app/classes/shared/main_controller.py +++ b/app/classes/shared/main_controller.py @@ -484,17 +484,32 @@ class Controller: return False if Helpers.is_os_windows(): - server_command = ( - f"java -Xms{Helpers.float_to_string(min_mem)}M " - f"-Xmx{Helpers.float_to_string(max_mem)}M " - f'-jar "{server_file}" nogui' - ) + # Let's check for and setup for install server commands + if server == "forge": + server_command = ( + f"java -Xms{Helpers.float_to_string(min_mem)}M " + f"-Xmx{Helpers.float_to_string(max_mem)}M " + f'-jar "{server_file}" --installServer' + ) + else: + server_command = ( + f"java -Xms{Helpers.float_to_string(min_mem)}M " + f"-Xmx{Helpers.float_to_string(max_mem)}M " + f'-jar "{server_file}" nogui' + ) else: - server_command = ( - f"java -Xms{Helpers.float_to_string(min_mem)}M " - f"-Xmx{Helpers.float_to_string(max_mem)}M " - f"-jar {server_file} nogui" - ) + if server == "forge": + server_command = ( + f"java -Xms{Helpers.float_to_string(min_mem)}M " + f"-Xmx{Helpers.float_to_string(max_mem)}M " + f"-jar {server_file} --installServer" + ) + else: + server_command = ( + f"java -Xms{Helpers.float_to_string(min_mem)}M " + f"-Xmx{Helpers.float_to_string(max_mem)}M " + f"-jar {server_file} nogui" + ) server_log_file = "./logs/latest.log" server_stop = "stop" diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 257bf64f..4febf4ac 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -202,12 +202,15 @@ class ServerInstance: # remove the scheduled job since it's ran return self.server_scheduler.remove_job(str(self.server_id)) - def run_threaded_server(self, user_id): + def run_threaded_server(self, user_id, forge_install=False): # start the server self.server_thread = threading.Thread( target=self.start_server, daemon=True, - args=(user_id,), + args=( + user_id, + forge_install, + ), name=f"{self.server_id}_server_thread", ) self.server_thread.start() @@ -292,7 +295,7 @@ class ServerInstance: logger.critical(f"Unable to write/access {self.server_path}") Console.critical(f"Unable to write/access {self.server_path}") - def start_server(self, user_id): + def start_server(self, user_id, forge_install=False): if not user_id: user_lang = self.helper.get_setting("language") else: @@ -341,7 +344,9 @@ class ServerInstance: "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: self.helper.websocket_helper.broadcast_user( @@ -540,6 +545,10 @@ class ServerInstance: 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() + def check_internet_thread(self, user_id, user_lang): if user_id: if not Helpers.check_internet(): @@ -553,6 +562,50 @@ class ServerInstance: }, ) + def forge_install_watcher(self): + # Enter for install if that parameter is true + while True: + # We'll watch the process + if self.process.poll() is None: + # IF process still has not exited we'll keep looping + time.sleep(5) + Console.debug("Installing Forge...") + else: + # Process has exited. Lets do some work to setup the new + # run command. + # We need to grab the exact forge version number. + # We know we can find it here. + version = os.listdir( + os.path.join(self.server_path, "libraries/net/minecraftforge/forge") + )[0] + # Let's grab the server object we're going to update. + server_obj = HelperServers.get_server_obj(self.server_id) + # The forge install is done so we can delete that install file. + os.remove(os.path.join(server_obj.path, server_obj.executable)) + # Let's set the proper server executable + server_obj.executable = os.path.join( + "libraries/net/minecraftforge/forge/" + f"{version}/forge-{version}-server.jar" + ) + # Now lets set up the new run command. + # This is based off the run.sh/bat that + # Forge uses in 1.16 and < + if self.helper.is_os_windows(): + server_obj.execution_command = ( + "java @user_jvm_args.txt @libraries/net/" + f"minecraftforge/forge/{version}/win_args.txt nogui" + ) + else: + server_obj.execution_command = ( + "java @user_jvm_args.txt @libraries/net/" + f"minecraftforge/forge/{version}/unix_args.txt nogui" + ) + Console.debug("SUCCESS! Forge install completed") + + # We'll update the server with the new information now. + HelperServers.update_server(server_obj) + break + def stop_crash_detection(self): # This is only used if the crash detection settings change # while the server is running. From e42af36103f393cf65bc6d8ddb43495c0723f5bb Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 19 Nov 2022 14:41:15 -0500 Subject: [PATCH 2/9] Make sure import status persists through install --- app/classes/controllers/servers_controller.py | 3 +- app/classes/shared/server.py | 9 +++++- app/frontend/templates/panel/dashboard.html | 31 +++++++++---------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 934ad328..709ea8ce 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -121,12 +121,13 @@ class ServersController(metaclass=Singleton): @staticmethod def finish_import(server_id, forge=False): srv = ServersController().get_server_instance_by_id(server_id) - srv.stats_helper.finish_import() # This is where we start the forge installerr if forge: srv.run_threaded_server( HelperUsers.get_user_id_by_name("system"), forge_install=True ) + else: + srv.stats_helper.finish_import() return @staticmethod diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 4febf4ac..f723af33 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -301,7 +301,7 @@ class ServerInstance: else: user_lang = HelperUsers.get_user_lang_by_id(user_id) - if self.stats_helper.get_import_status(): + if self.stats_helper.get_import_status() and not forge_install: if user_id: self.helper.websocket_helper.broadcast_user( user_id, @@ -604,6 +604,13 @@ class ServerInstance: # We'll update the server with the new information now. HelperServers.update_server(server_obj) + self.stats_helper.finish_import() + server_users = PermissionsServers.get_server_user_list(self.server_id) + + for user in server_users: + self.helper.websocket_helper.broadcast_user( + user, "send_start_reload", {} + ) break def stop_crash_detection(self): diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index c89d00b2..fbeb3fca 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -168,7 +168,21 @@ {% if server['user_command_permission'] %} - {% if server['stats']['running'] %} + {% if server['stats']['updating']%} + +  {{ translate('serverTerm', 'updating', + data['lang']) }} + {% elif server['stats']['waiting_start']%} + + {{ translate('dashboard', 'starting', + data['lang']) }} + {% elif server['stats']['importing']%} + + {{ translate('serverTerm', 'importing', + data['lang']) }} + {% elif server['stats']['running'] %} @@ -183,21 +197,6 @@ title="{{ translate('dashboard', 'kill' , data['lang']) }}">   - - {% elif server['stats']['updating']%} - -  {{ translate('serverTerm', 'updating', - data['lang']) }} - {% elif server['stats']['waiting_start']%} - - {{ translate('dashboard', 'starting', - data['lang']) }} - {% elif server['stats']['importing']%} - - {{ translate('serverTerm', 'importing', - data['lang']) }} {% else %} From b95d71bfbc4f2aaafde99fe5a903e394a9ea6d77 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 19 Nov 2022 14:45:24 -0500 Subject: [PATCH 3/9] Appease the linter --- app/classes/controllers/servers_controller.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/classes/controllers/servers_controller.py b/app/classes/controllers/servers_controller.py index 709ea8ce..4c97a6c7 100644 --- a/app/classes/controllers/servers_controller.py +++ b/app/classes/controllers/servers_controller.py @@ -128,7 +128,6 @@ class ServersController(metaclass=Singleton): ) else: srv.stats_helper.finish_import() - return @staticmethod def get_import_status(server_id): From 4e17c1ea5eade9135bd41f3d18fd1a36abc037d1 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 19 Nov 2022 15:45:29 -0500 Subject: [PATCH 4/9] Add installing status --- app/frontend/templates/panel/dashboard.html | 7 ++++++- app/frontend/templates/panel/server_term.html | 16 +++++++++++++++- app/translations/en_EN.json | 6 ++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/frontend/templates/panel/dashboard.html b/app/frontend/templates/panel/dashboard.html index fbeb3fca..9d91e156 100644 --- a/app/frontend/templates/panel/dashboard.html +++ b/app/frontend/templates/panel/dashboard.html @@ -168,7 +168,12 @@ {% if server['user_command_permission'] %} - {% if server['stats']['updating']%} + {% if server['stats']['importing'] and server['stats']['running'] %} + +  {{ translate('serverTerm', 'installing', + data['lang']) }} + {% elif server['stats']['updating']%}  {{ translate('serverTerm', 'updating', diff --git a/app/frontend/templates/panel/server_term.html b/app/frontend/templates/panel/server_term.html index c4ebacb5..54ac127a 100644 --- a/app/frontend/templates/panel/server_term.html +++ b/app/frontend/templates/panel/server_term.html @@ -62,7 +62,21 @@ {% if data['permissions']['Commands'] in data['user_permissions'] %} - {% if data['server_stats']['updating']%} + {% if data['importing'] and data['server_stats']['running']%} +
+ + + +
+ {% elif data['server_stats']['updating']%}
diff --git a/app/translations/en_EN.json b/app/translations/en_EN.json index 4309b28a..e66091f5 100644 --- a/app/translations/en_EN.json +++ b/app/translations/en_EN.json @@ -97,7 +97,8 @@ "status": "Status", "stop": "Stop", "version": "Version", - "welcome": "Welcome to Crafty Controller" + "welcome": "Welcome to Crafty Controller", + "installing": "Installing..." }, "datatables": { "i18n": { @@ -481,7 +482,8 @@ "starting": "Delayed-Start", "stop": "Stop", "stopScroll": "Stop Auto Scrolling", - "updating": "Updating..." + "updating": "Updating...", + "installing": "Installing..." }, "serverMetrics": { "resetZoom": "Reset Zoom", From 32cdde061607def93137e350064708ad5d1546fb Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 19 Nov 2022 16:13:44 -0500 Subject: [PATCH 5/9] Fix failed imports --- app/classes/shared/server.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index f723af33..7f841d99 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -427,6 +427,9 @@ class ServerInstance: ).format(self.name, ex) }, ) + if forge_install: + # Reset import status if failed while forge installing + self.stats_helper.finish_import() return False else: @@ -465,6 +468,9 @@ class ServerInstance: ).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) From 004049f6daa2fa79e151c473a4d8dd87d4591e88 Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 19 Nov 2022 23:20:21 +0100 Subject: [PATCH 6/9] Using Regex to Extract Infos from Forge Scripts --- app/classes/shared/server.py | 50 ++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index f723af33..f4d19bd8 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -573,33 +573,49 @@ class ServerInstance: else: # Process has exited. Lets do some work to setup the new # run command. - # We need to grab the exact forge version number. - # We know we can find it here. - version = os.listdir( - os.path.join(self.server_path, "libraries/net/minecraftforge/forge") - )[0] # Let's grab the server object we're going to update. server_obj = HelperServers.get_server_obj(self.server_id) + # The forge install is done so we can delete that install file. os.remove(os.path.join(server_obj.path, server_obj.executable)) + + # We need to grab the exact forge version number. + # We know we can find it here in the run.sh/bat script. + run_file_path = "" + if self.helper.is_os_windows(): + run_file_path = os.path.join(server_obj.path, "run.bat") + else: + run_file_path = os.path.join(server_obj.path, "run.sh") + + if Helpers.check_file_perms(run_file_path) and os.path.isfile( + run_file_path + ): + run_file = open(run_file_path, "r") + run_file_text = run_file.read() + else: + Console.error( + "ERROR ! Forge install can't read the scripts files. Aborting ..." + ) + return + + # We get the server command parameters from forge script + server_command = re.findall( + r"java @([a-zA-Z0-9_\.]+) @([a-z.\/\-]+)([0-9.\-]+)\/\b([a-z_0-9]+\.txt)\b( .{2,4})?", + run_file_text, + )[0] + + version = server_command[2] + executable_path = f"{server_command[1]}{server_command[2]}/" + # Let's set the proper server executable server_obj.executable = os.path.join( - "libraries/net/minecraftforge/forge/" - f"{version}/forge-{version}-server.jar" + f"{executable_path}forge-{version}-server.jar" ) # Now lets set up the new run command. # This is based off the run.sh/bat that # Forge uses in 1.16 and < - if self.helper.is_os_windows(): - server_obj.execution_command = ( - "java @user_jvm_args.txt @libraries/net/" - f"minecraftforge/forge/{version}/win_args.txt nogui" - ) - else: - server_obj.execution_command = ( - "java @user_jvm_args.txt @libraries/net/" - f"minecraftforge/forge/{version}/unix_args.txt nogui" - ) + execution_command = f"java @{server_command[0]} @{executable_path}{server_command[3]} nogui {server_command[4]}" + server_obj.execution_command = execution_command Console.debug("SUCCESS! Forge install completed") # We'll update the server with the new information now. From 671a5188c276d68c2f9d80ed0e5d2a0d50d608bf Mon Sep 17 00:00:00 2001 From: Silversthorn Date: Sat, 19 Nov 2022 23:28:57 +0100 Subject: [PATCH 7/9] Adding French Translations --- app/translations/fr_FR.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/translations/fr_FR.json b/app/translations/fr_FR.json index b1706e87..4748b791 100644 --- a/app/translations/fr_FR.json +++ b/app/translations/fr_FR.json @@ -97,7 +97,8 @@ "status": "Statut", "stop": "Arrêter", "version": "Version", - "welcome": "Bienvenue sur Crafty Controller" + "welcome": "Bienvenue sur Crafty Controller", + "installing": "Installation ..." }, "datatables": { "i18n": { @@ -450,7 +451,13 @@ "starting": "Démarrage retardé", "stop": "Arrêter", "stopScroll": "Stopper l'Auto Défilement", - "updating": "Mise à Jour ..." + "updating": "Mise à Jour ...", + "installing": "Installation ..." + }, + "serverMetrics": { + "resetZoom": "Reset Zoom", + "zoomHint1": "Pour zoomer le graphique, maintenir la touche SHIFT et utiliser la molette de la souris.", + "zoomHint2": "Il est aussi possible de maintenir la touche SHIFT puis de clicker et déplacer la zone sur laquelle zoomer." }, "serverWizard": { "absoluteServerPath": "Chemin absolu de Votre Serveur", From e8564566ed04a939ce25bd0a107dc343a8f58f0b Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 19 Nov 2022 18:21:10 -0500 Subject: [PATCH 8/9] Appease the linter --- app/classes/shared/server.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/classes/shared/server.py b/app/classes/shared/server.py index 565bfc6e..ccc50f70 100644 --- a/app/classes/shared/server.py +++ b/app/classes/shared/server.py @@ -596,17 +596,19 @@ class ServerInstance: if Helpers.check_file_perms(run_file_path) and os.path.isfile( run_file_path ): - run_file = open(run_file_path, "r") + run_file = open(run_file_path, "r", encoding="utf-8") run_file_text = run_file.read() else: Console.error( - "ERROR ! Forge install can't read the scripts files. Aborting ..." + "ERROR ! Forge install can't read the scripts files." + " Aborting ..." ) return # We get the server command parameters from forge script server_command = re.findall( - r"java @([a-zA-Z0-9_\.]+) @([a-z.\/\-]+)([0-9.\-]+)\/\b([a-z_0-9]+\.txt)\b( .{2,4})?", + r"java @([a-zA-Z0-9_\.]+)" + r" @([a-z.\/\-]+)([0-9.\-]+)\/\b([a-z_0-9]+\.txt)\b( .{2,4})?", run_file_text, )[0] @@ -620,7 +622,10 @@ class ServerInstance: # Now lets set up the new run command. # This is based off the run.sh/bat that # Forge uses in 1.16 and < - execution_command = f"java @{server_command[0]} @{executable_path}{server_command[3]} nogui {server_command[4]}" + execution_command = ( + f"java @{server_command[0]}" + f" @{executable_path}{server_command[3]} nogui {server_command[4]}" + ) server_obj.execution_command = execution_command Console.debug("SUCCESS! Forge install completed") From 7ef28777c7ed86ca63b1047fc391d7edd3dc37ae Mon Sep 17 00:00:00 2001 From: Zedifus Date: Wed, 30 Nov 2022 19:54:59 +0000 Subject: [PATCH 9/9] Update changelog !495 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d294c66f..3a4e1917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog ## --- [4.0.17] - 2022/TBD ### New features -TBD +- Automate forge install process through Crafty server creation for Forge server version 1.16 and greater. ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/495)) ### Bug fixes - Fix no port on bedrock server creation ([Merge Request](https://gitlab.com/crafty-controller/crafty-4/-/merge_requests/493)) ### Tweaks