diff --git a/application/api/v1/blueprints/steam.py b/application/api/v1/blueprints/steam.py index a65f478..f16cf2a 100644 --- a/application/api/v1/blueprints/steam.py +++ b/application/api/v1/blueprints/steam.py @@ -36,7 +36,12 @@ def steam_app_install(): raise InvalidUsage(message, status_code=400) steam_id = payload["steam_id"] - steam_mgr = SteamManager(payload["steam_install_path"]) + + try: + steam_mgr = SteamManager(payload["steam_install_path"]) + except Exception as error: + logger.critical(error) + return "Error", 500 steam_mgr.install_steam_app( steam_id, @@ -56,15 +61,54 @@ def steam_app_install(): return "Success" -@steam.route("/steam/app/remove", methods=["POST"]) +@steam.route("/steam/app/update", methods=["POST"]) @authorization_required -def steam_app_remove(): - logger.info("Steam Application has been removed") +def steam_app_update(): + logger.info("Updating Steam Application") + + payload = request.json + + required_data = [ + "steam_install_path", + "steam_id", + "install_dir", + "user", + "password", + ] + + if not set(required_data).issubset(set(list(payload.keys()))): + message = "Error: Missing Required Data" + logger.error(message) + logger.info(payload.keys()) + raise InvalidUsage(message, status_code=400) + + steam_id = payload["steam_id"] + + try: + steam_mgr = SteamManager(payload["steam_install_path"]) + except Exception as error: + logger.critical(error) + return "Error", 500 + + steam_mgr.udpate_steam_app( + steam_id, + payload["install_dir"], + payload["user"], + payload["password"], + ) + + payload.pop("steam_install_path") + payload.pop("steam_id") + payload.pop("install_dir") + payload.pop("user") + payload.pop("password") + + logger.info("Steam Application has been updated") return "Success" -@steam.route("/steam/app/update", methods=["POST"]) +@steam.route("/steam/app/remove", methods=["POST"]) @authorization_required -def steam_app_update(): - logger.info("Steam Application has been updated") +def steam_app_remove(): + logger.info("Steam Application has been removed") return "Success" diff --git a/application/games/palworld_game.py b/application/games/palworld_game.py index 2181f67..4af634b 100644 --- a/application/games/palworld_game.py +++ b/application/games/palworld_game.py @@ -89,10 +89,6 @@ def startup(self) -> None: arguments = self._get_argument_dict() - logger.debug("************************************") - logger.debug(arguments) - logger.debug("************************************") - # Create a formatted batch file. env = Environment(loader=FileSystemLoader(get_resources_dir(__file__))) @@ -103,8 +99,6 @@ def startup(self) -> None: GAME_COMMAND=command, ) - # server_port = arguments['-serverPort'] - ini_template = env.get_template("DefaultPalWorldSettings.ini.j2") output_from_parsed_ini_template = ini_template.render( PUBLIC_PORT=arguments["-serverPort"]._value, @@ -129,15 +123,19 @@ def startup(self) -> None: game_install_dir, constants.STARTUP_BATCH_FILE_NAME ) + full_path_game_ini_config_folder = os.path.join( + game_install_dir, "Pal", "Saved", "Config", "WindowsServer" + ) + full_path_game_ini_config = os.path.join( - game_install_dir, - "Pal", - "Saved", - "Config", - "WindowsServer", + full_path_game_ini_config_folder, "PalWorldSettings.ini", ) + # The first time you run this, the config folder doesn't exist, so it has to be created. + if not os.path.exists(full_path_game_ini_config_folder): + os.makedirs(full_path_game_ini_config_folder, exist_ok=True) + # If file exists, remove it. if os.path.exists(full_path_startup_script): os.remove(full_path_startup_script) diff --git a/application/gui/widgets/game_manager_widget.py b/application/gui/widgets/game_manager_widget.py index 1a661b1..3cac3ad 100644 --- a/application/gui/widgets/game_manager_widget.py +++ b/application/gui/widgets/game_manager_widget.py @@ -13,9 +13,9 @@ ) from PyQt5.QtCore import Qt, QTimer -from application.common import toolbox, logger -from application.common.game_base import BaseGame from application import games +from application.common import constants, toolbox, logger +from application.common.game_base import BaseGame from application.gui.widgets.add_argument_widget import AddArgumentWidget from application.gui.intalled_games_menu import InstalledGameMenu from application.gui.widgets.game_arguments_widget import GameArgumentsWidget @@ -56,6 +56,7 @@ def __init__(self, client: Operator, globals, parent: QWidget) -> None: self._shutdown_btn: QPushButton = None self._restart_btn: QPushButton = None self._uninstall_btn: QPushButton = None + self._update_btn: QPushButton = None self._add_arg_btn: QPushButton = None self._game_pid_label: QLabel = None self._game_exe_found_label: QLabel = None @@ -179,6 +180,7 @@ def _refresh_on_timer(self): logger.debug("Game is Running, setting buttins accordingly") self._disable_btn(self._startup_btn) self._disable_btn(self._uninstall_btn) + self._disable_btn(self._update_btn) self._enable_btn(self._shutdown_btn) self._enable_btn(self._restart_btn) else: @@ -186,6 +188,7 @@ def _refresh_on_timer(self): logger.debug("Game is NOT Running, setting buttins accordingly") self._enable_btn(self._startup_btn) self._enable_btn(self._uninstall_btn) + self._enable_btn(self._update_btn) self._disable_btn(self._shutdown_btn) self._disable_btn(self._restart_btn) @@ -302,6 +305,12 @@ def _build_game_frame(self, game_name): self._restart_btn.clicked.connect( lambda: self._restart_game(self._current_game_name) ) + + self._update_btn = QPushButton("Update") + self._update_btn.clicked.connect( + lambda: self._update_game(self._current_game_name) + ) + self._uninstall_btn = QPushButton("Uninstall") self._uninstall_btn.clicked.connect( lambda: self._uninstall_game(self._current_game_name) @@ -310,6 +319,7 @@ def _build_game_frame(self, game_name): game_control_h_layout.addWidget(self._startup_btn) game_control_h_layout.addWidget(self._shutdown_btn) game_control_h_layout.addWidget(self._restart_btn) + game_control_h_layout.addWidget(self._update_btn) game_control_h_layout.addWidget(self._uninstall_btn) game_frame_main_layout.addLayout(game_control_h_layout) @@ -381,6 +391,18 @@ def _restart_game(self, game_name): self._client.game.game_startup(game_name, input_args=arg_dict) + def _update_game(self, game_name): + logger.info(f"Updating game: {game_name}") + steam_install_dir = self._client.app.get_setting_by_name( + constants.SETTING_NAME_STEAM_PATH + ) + game_info = self._client.game.get_game_by_name(game_name) + + steam_id = game_info["items"][0]["game_steam_id"] + install_path = game_info["items"][0]["game_install_dir"] + + self._client.steam.update_steam_app(steam_install_dir, steam_id, install_path) + def _uninstall_game(self, game_name): logger.info(f"Uninstall game: {game_name}") diff --git a/application/managers/steam_manager.py b/application/managers/steam_manager.py index 9c59d17..fc23a4d 100644 --- a/application/managers/steam_manager.py +++ b/application/managers/steam_manager.py @@ -7,7 +7,7 @@ from application import games from application.models.games import Games -from application.common import logger, toolbox +from application.common import logger, toolbox, constants from application.common.exceptions import InvalidUsage from application.extensions import DATABASE @@ -19,7 +19,7 @@ def __init__(self, steam_install_dir) -> None: toolbox.recursive_chmod(steam_install_dir) - self._steam = Steamcmd(steam_install_dir) + self._steam = Steamcmd(steam_install_dir, constants.DEFAULT_INSTALL_PATH) self._steam.install(force=True) @@ -52,7 +52,11 @@ def install_steam_app( del game_obj break - # TODO - Catch the None case. What happens if the game object is not found? + # Raise error if correct_game_object is not found. + if correct_game_object is None: + raise InvalidUsage( + "Unable to get game object that matches steam id.", status_code=500 + ) new_game = Games() new_game.game_steam_id = int(steam_id) @@ -84,6 +88,24 @@ def install_steam_app( validate=True, ) + def udpate_steam_app( + self, steam_id, installation_dir, user="anonymous", password=None + ): + return self._update_gamefiles( + gameid=steam_id, + game_install_dir=installation_dir, + user=user, + password=password, + validate=True, + ) + + def _update_gamefiles( + self, gameid, game_install_dir, user="anonymous", password=None, validate=False + ): + return self._install_gamefiles( + gameid, game_install_dir, user=user, password=password, validate=validate + ) + def _install_gamefiles( self, gameid, game_install_dir, user="anonymous", password=None, validate=False ): diff --git a/requirements.txt b/requirements.txt index 7d1b88d..4241188 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,11 +10,11 @@ pyinstaller pyjwt pyopenssl pyqt5 -pysteamcmd python-dotenv requests validators==0.20.0 vdf==3.4 -# TODO - Eventually this should be a specific version. +# TODO - Eventually these should be a specific version. +pysteamcmd @ git+https://github.com/agentsofthesystem/pysteamcmd.git@main operator @ git+https://github.com/agentsofthesystem/operator@main \ No newline at end of file