From b8a0c45bbe1fa9b2217d89e8b14ca71309e8829b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 18 May 2026 22:08:08 +0000 Subject: [PATCH] Add server content scheduler hooks and cron wiring Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/1f4380da-e76c-48d2-abe4-6f8f5f26e562 Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com> --- .github/module-map.md | 2 +- Panel/CHANGELOG.md | 1 + Panel/docs/COPILOT_TODO.md | 1 + Panel/includes/api_functions.php | 3 + .../addonsmanager/server_content_actions.php | 472 ++++++++++++++++++ Panel/modules/cron/cron.php | 19 +- Panel/modules/cron/shared_cron_functions.php | 66 ++- Panel/modules/cron/user_cron.php | 19 +- Panel/ogp_api.php | 41 ++ 9 files changed, 593 insertions(+), 31 deletions(-) create mode 100644 Panel/modules/addonsmanager/server_content_actions.php diff --git a/.github/module-map.md b/.github/module-map.md index fae19d9f..39a9b38d 100644 --- a/.github/module-map.md +++ b/.github/module-map.md @@ -40,7 +40,7 @@ This file captures how the control panel, storefront, agents, and helper scripts | `extras`, `addonsmanager` | Workshop/add-on management. | Hooks into game homes after provisioning. | | `litefm`, `ftp`, `TS3Admin` | File managers and TeamSpeak controllers. | Depend on homes and remote server credentials set during provisioning. | | `news`, `circular`, `faq` | Content modules for panel UI. | Use standard MVC wrappers, share session/auth. | -| `cron` | Scheduler UI feeding `scripts/` commands. | Maintains job metadata that OS cron reads. | +| `cron` | Scheduler UI feeding `scripts/` commands. | Maintains job metadata that OS cron reads, including scheduler-triggered Server Content actions via `ogp_api.php?server_content/run_scheduled_action` and `modules/addonsmanager/server_content_actions.php`. | ## Storefront (`Panel/modules/billing` runtime + `Website/` compatibility wrappers) diff --git a/Panel/CHANGELOG.md b/Panel/CHANGELOG.md index 921ec2a3..5a3c80b0 100644 --- a/Panel/CHANGELOG.md +++ b/Panel/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## 2026-05-18 +- **Cron ↔ Server Content action hook integration:** Added scheduler-callable Server Content hooks in `modules/addonsmanager/server_content_actions.php`, exposed API route `server_content/run_scheduled_action`, and wired cron/user-cron action builders/parsing to support server content scheduled actions (check/install/queue/restart/validate/backup flows) without embedding game-specific install logic in the scheduler. - **Server Content Workshop Phase 1 in addonsmanager:** Added a new `Workshop Content` flow under Server Content with per-home Workshop ID storage, ID validation/deduplication, install/update/remove/update-all actions, manifest-based script handoff (`gsp_server_content/workshop_manifest.json`), safe placeholder workshop scripts for Linux/Cygwin, and schema support via `server_content_workshop` plus `addons.addon_type VARCHAR(32)`. - **Updater layout hardening + pre-update patch framework:** Reworked `modules/administration/panel_update.php` to resolve explicit GSP root/Panel/Website paths, run mandatory preflight checks, self-update updater files before main sync when drift is detected, and apply ordered required patches from `modules/update/patches/` with DB/local state tracking. Backup/rollback now includes both Panel + Website archives and root `version.json`, logs moved to root `logs/update_trace.log`, and the admin Update UI now exposes preflight, patch apply, Apache path scan/fix, backup, update, and rollback actions. - **Billing runtime relocation + portable path bootstrap:** Re-homed storefront runtime to `Panel/modules/billing`, added portable runtime helpers (`billing_bootstrap.php`, `site_config.php`, `site_config.example.php`) with env/local override support for base path and panel path, normalized critical storefront redirects/links to computed billing URLs, and added `Website` compatibility wrappers for key billing entrypoints. diff --git a/Panel/docs/COPILOT_TODO.md b/Panel/docs/COPILOT_TODO.md index 038459e7..fdb3355a 100644 --- a/Panel/docs/COPILOT_TODO.md +++ b/Panel/docs/COPILOT_TODO.md @@ -18,3 +18,4 @@ - Add an automated deployment check that fails when `Website/timestamp.txt` and `modules/billing/timestamp.txt` diverge after storefront/content changes. - Add an admin preview/diff panel for Apache path repairs so staff can review exact vhost line changes before confirming `Fix Apache Paths`. - Add Phase 2 Workshop Content UX in `addonsmanager`: browse/search/select Workshop items with metadata while reusing the Phase 1 per-home saved-ID action pipeline. +- Add localized language strings/tooltips for the new cron scheduler `server_content_*` action labels across all supported panel locales. diff --git a/Panel/includes/api_functions.php b/Panel/includes/api_functions.php index 43ad0c44..0e503d8d 100644 --- a/Panel/includes/api_functions.php +++ b/Panel/includes/api_functions.php @@ -55,6 +55,9 @@ function get_function_args($main_request) //______________ Steam Workshop $functions["steam_workshop/install"] = array("token" => true, "ip" => true, "port" => true, "mod_key" => false, "mods_list" => true); + + //______________ Server Content + $functions["server_content/run_scheduled_action"] = array("token" => true, "home_id" => true, "action" => true, "options" => false); //______________ Settings $functions["setting/get"] = array("token" => true, "setting_name" => true); diff --git a/Panel/modules/addonsmanager/server_content_actions.php b/Panel/modules/addonsmanager/server_content_actions.php new file mode 100644 index 00000000..6e131f28 --- /dev/null +++ b/Panel/modules/addonsmanager/server_content_actions.php @@ -0,0 +1,472 @@ +getGameHome($home_id); +} + +function server_content_collect_workshop_ids($db, $home_id) +{ + $rows = $db->resultQuery( + "SELECT workshop_item_id FROM `".OGP_DB_PREFIX."server_content_workshop` + WHERE home_id=".(int)$home_id." AND install_state<>'removed'" + ); + $item_ids = array(); + if (is_array($rows)) { + foreach ((array)$rows as $row) { + $item_id = trim((string)$row['workshop_item_id']); + if ($item_id !== '' && preg_match('/^[0-9]+$/', $item_id)) { + $item_ids[$item_id] = $item_id; + } + } + } + return array_values($item_ids); +} + +function server_content_collect_manifest_addon_rows($db, $home_id) +{ + $rows = $db->resultQuery( + "SELECT m.addon_id, m.install_method, m.content_version, m.install_state, a.name, a.url + FROM `".OGP_DB_PREFIX."server_content_manifest` m + LEFT JOIN `".OGP_DB_PREFIX."addons` a ON a.addon_id = m.addon_id + WHERE m.home_id=".(int)$home_id." + ORDER BY m.updated_at DESC, m.installed_at DESC" + ); + return is_array($rows) ? $rows : array(); +} + +function server_content_create_remote(array $home_info) +{ + return new OGPRemoteLibrary( + $home_info['agent_ip'], + $home_info['agent_port'], + $home_info['encryption_key'], + $home_info['timeout'] + ); +} + +function server_content_result($status, $message, array $details = array()) +{ + return array( + 'status' => (string)$status, + 'success' => ((string)$status === 'success' || (string)$status === 'no_updates' || (string)$status === 'restart_required'), + 'message' => (string)$message, + 'details' => $details, + ); +} + +function server_content_record_installed_content_state(array $home_info, array $state) +{ + $home_path = rtrim(clean_path((string)$home_info['home_path']), '/'); + $file_path = clean_path($home_path . '/gsp_server_content/installed_content.json'); + if (!scm_path_is_under_home($home_path, $file_path)) { + return false; + } + $json = json_encode($state); + if ($json === false) { + return false; + } + $remote = server_content_create_remote($home_info); + $remote->exec("mkdir -p " . escapeshellarg(dirname($file_path))); + return ((int)$remote->remote_writefile($file_path, $json) === 1); +} + +function server_content_log_action($home_id, $action, $status, $message = '', $details = array()) +{ + global $db; + $payload = array( + 'home_id' => (int)$home_id, + 'action' => (string)$action, + 'status' => (string)$status, + 'message' => (string)$message, + 'details' => $details, + ); + $db->logger("server_content_action " . json_encode($payload)); + return true; +} + +function server_content_build_manifest($home_id, $content_type, $action, $items = array(), $options = array()) +{ + $home_info = server_content_get_home_info($home_id); + if ($home_info === false) { + return false; + } + $home_path = rtrim(clean_path((string)$home_info['home_path']), '/'); + $manifest_dir = clean_path($home_path . '/gsp_server_content/manifests'); + $file_stub = preg_replace('/[^a-z0-9_\-]+/i', '_', (string)$content_type . '_' . (string)$action); + if ($file_stub === '' || $file_stub === null) { + $file_stub = 'manifest'; + } + $manifest_path = clean_path($manifest_dir . '/' . $file_stub . '_' . date('Ymd_His') . '_' . mt_rand(1000, 9999) . '.json'); + if (!scm_path_is_under_home($home_path, $manifest_path)) { + return false; + } + $manifest = array( + 'manifest_version' => 1, + 'home_id' => (int)$home_info['home_id'], + 'home_cfg_id' => (int)$home_info['home_cfg_id'], + 'remote_server_id' => (int)$home_info['remote_server_id'], + 'content_type' => (string)$content_type, + 'action' => (string)$action, + 'items' => is_array($items) ? array_values($items) : array(), + 'options' => is_array($options) ? $options : array(), + 'generated_at' => date('Y-m-d H:i:s'), + ); + $manifest_json = json_encode($manifest); + if ($manifest_json === false) { + return false; + } + $remote = server_content_create_remote($home_info); + $remote->exec("mkdir -p " . escapeshellarg($manifest_dir)); + if ((int)$remote->remote_writefile($manifest_path, $manifest_json) !== 1) { + return false; + } + return $manifest_path; +} + +function server_content_resolve_script_path(array $home_info, $script_key, array $options = array()) +{ + $script_path = ''; + if (isset($options['script_path'])) { + $script_path = trim((string)$options['script_path']); + } + $server_xml = read_server_config(SERVER_CONFIG_LOCATION . "/" . $home_info['home_cfg_file']); + if ($server_xml === false) { + return array(false, false); + } + if ($script_path === '' && $script_key === 'workshop') { + $script_path = scm_get_workshop_script_path($home_info, $server_xml); + } + if ($script_path === '' && $script_key !== '' && isset($server_xml->$script_key)) { + $script_path = trim((string)$server_xml->$script_key); + } + return array($server_xml, $script_path); +} + +function server_content_execute_manifest($home_id, $manifest_path, $script_key, $options = array()) +{ + $home_info = server_content_get_home_info($home_id); + if ($home_info === false) { + return server_content_result('failed', 'Invalid server home.', array('home_id' => (int)$home_id)); + } + list($server_xml, $script_path) = server_content_resolve_script_path($home_info, $script_key, $options); + if ($server_xml === false) { + return server_content_result('failed', 'Unable to load server XML configuration.', array('home_id' => (int)$home_id)); + } + $script_path = trim((string)$script_path); + if ($script_path === '' || !preg_match('/^[^\r\n\0]+$/', $script_path)) { + return server_content_result('failed', 'Configured server content script path is invalid.', array('script_key' => (string)$script_key)); + } + $remote = server_content_create_remote($home_info); + if ($remote->status_chk() !== 1) { + return server_content_result('failed', 'Agent is offline.', array('remote_server_id' => (int)$home_info['remote_server_id'])); + } + if ((int)$remote->rfile_exists($script_path) !== 1) { + return server_content_result('failed', 'Server content script was not found on agent host.', array('script_path' => $script_path)); + } + $command = "bash " . escapeshellarg($script_path) . " " . escapeshellarg((string)$manifest_path) . " ; echo __GSP_SERVER_CONTENT_EXIT:$?"; + $output = $remote->exec($command); + if (!is_string($output) || $output === '') { + return server_content_result('failed', 'Server content script did not return output.', array('script_path' => $script_path)); + } + if (!preg_match('/__GSP_SERVER_CONTENT_EXIT:(\d+)/', $output, $matches)) { + return server_content_result('failed', 'Server content script exit marker was not found.', array('output' => trim($output))); + } + $exit_code = (int)$matches[1]; + if ($exit_code !== 0) { + return server_content_result('failed', 'Server content script failed.', array( + 'exit_code' => $exit_code, + 'output' => trim($output), + 'script_path' => $script_path, + )); + } + return server_content_result('success', 'Server content script executed successfully.', array( + 'exit_code' => $exit_code, + 'output' => trim($output), + 'script_path' => $script_path, + 'manifest_path' => (string)$manifest_path, + )); +} + +function server_content_check_updates($home_id, $options = array()) +{ + $options['check_only'] = true; + return server_content_install_updates($home_id, $options); +} + +function server_content_update_workshop($home_id, $options = array()) +{ + $options['workshop_only'] = true; + return server_content_install_updates($home_id, $options); +} + +function server_content_install_updates($home_id, $options = array()) +{ + global $db; + $home_info = server_content_get_home_info($home_id); + if ($home_info === false) { + return server_content_result('failed', 'Invalid server home.'); + } + scm_ensure_phase2_schema($db); + scm_ensure_workshop_schema($db); + + $workshop_action = isset($options['workshop_action']) ? (string)$options['workshop_action'] : ''; + if ($workshop_action === '') { + $workshop_action = !empty($options['check_only']) ? 'check_updates' : 'update'; + } + $workshop_ids = server_content_collect_workshop_ids($db, (int)$home_info['home_id']); + $manifest_rows = server_content_collect_manifest_addon_rows($db, (int)$home_info['home_id']); + if (empty($workshop_ids) && empty($manifest_rows)) { + $result = server_content_result('no_updates', 'No installed server content records were found for this home.', array( + 'home_id' => (int)$home_info['home_id'], + )); + server_content_record_installed_content_state($home_info, array( + 'home_id' => (int)$home_info['home_id'], + 'last_action' => 'no_updates', + 'last_updated_at' => date('Y-m-d H:i:s'), + )); + return $result; + } + + if (!empty($workshop_ids) && empty($options['check_only'])) { + scm_workshop_update_rows_state($db, (int)$home_info['home_id'], $workshop_ids, 'installing', null, false, false); + } + + $manifest_items = array( + 'workshop_item_ids' => $workshop_ids, + 'manifest_addons' => $manifest_rows, + ); + $manifest_path = server_content_build_manifest($home_info['home_id'], 'server_content', $workshop_action, $manifest_items, $options); + if ($manifest_path === false) { + if (!empty($workshop_ids) && empty($options['check_only'])) { + scm_workshop_update_rows_state($db, (int)$home_info['home_id'], $workshop_ids, 'failed', 'Failed to build server content manifest.', false, false); + } + return server_content_result('failed', 'Failed to build server content manifest.'); + } + + $execute = server_content_execute_manifest($home_info['home_id'], $manifest_path, 'workshop', $options); + if (empty($execute['success'])) { + if (!empty($workshop_ids) && empty($options['check_only'])) { + $error_message = isset($execute['message']) ? $execute['message'] : 'Unknown failure.'; + scm_workshop_update_rows_state($db, (int)$home_info['home_id'], $workshop_ids, 'failed', $error_message, false, false); + } + return $execute; + } + + if (!empty($workshop_ids) && empty($options['check_only'])) { + scm_workshop_update_rows_state($db, (int)$home_info['home_id'], $workshop_ids, 'installed', null, false, true); + } + server_content_record_installed_content_state($home_info, array( + 'home_id' => (int)$home_info['home_id'], + 'last_action' => (string)$workshop_action, + 'last_result' => 'success', + 'last_manifest' => $manifest_path, + 'last_updated_at' => date('Y-m-d H:i:s'), + 'installed_workshop_ids' => $workshop_ids, + )); + + if (!empty($options['check_only'])) { + return server_content_result('success', 'Server content update check completed.', array( + 'manifest_path' => $manifest_path, + 'workshop_items' => count($workshop_ids), + )); + } + return server_content_result('success', 'Server content updates were installed.', array( + 'manifest_path' => $manifest_path, + 'workshop_items' => count($workshop_ids), + 'manifest_rows' => count($manifest_rows), + )); +} + +function server_content_home_is_running(array $home_info) +{ + $remote = server_content_create_remote($home_info); + return ($remote->is_screen_running(OGP_SCREEN_TYPE_HOME, $home_info['home_id']) == 1); +} + +function server_content_install_updates_if_stopped($home_id, $options = array()) +{ + $home_info = server_content_get_home_info($home_id); + if ($home_info === false) { + return server_content_result('failed', 'Invalid server home.'); + } + if (server_content_home_is_running($home_info)) { + return server_content_result('restart_required', 'Server is running; update skipped until server is stopped.', array( + 'home_id' => (int)$home_info['home_id'], + )); + } + return server_content_install_updates($home_id, $options); +} + +function server_content_install_updates_next_restart($home_id, $options = array()) +{ + $home_info = server_content_get_home_info($home_id); + if ($home_info === false) { + return server_content_result('failed', 'Invalid server home.'); + } + $options['queued_for_restart'] = true; + $manifest_path = server_content_build_manifest($home_info['home_id'], 'server_content', 'install_next_restart', array(), $options); + if ($manifest_path === false) { + return server_content_result('failed', 'Failed to queue update manifest for next restart.'); + } + server_content_record_installed_content_state($home_info, array( + 'home_id' => (int)$home_info['home_id'], + 'last_action' => 'install_next_restart', + 'last_result' => 'queued', + 'queued_manifest' => $manifest_path, + 'last_updated_at' => date('Y-m-d H:i:s'), + )); + return server_content_result('restart_required', 'Server content updates were queued for next restart.', array( + 'manifest_path' => $manifest_path, + )); +} + +function server_content_restart_home($home_id, $options = array()) +{ + global $db, $user_info; + $home_info = server_content_get_home_info($home_id); + if ($home_info === false) { + return server_content_result('failed', 'Invalid server home.'); + } + $server_xml = read_server_config(SERVER_CONFIG_LOCATION . "/" . $home_info['home_cfg_file']); + if ($server_xml === false) { + return server_content_result('failed', 'Could not load server XML for restart.'); + } + $remote = server_content_create_remote($home_info); + $host_stat = $remote->status_chk(); + if ($host_stat !== 1) { + return server_content_result('failed', 'Agent is offline; cannot restart server.'); + } + + $ip_ports = $db->getHomeIpPorts($home_info['home_id']); + if (!is_array($ip_ports) || !isset($ip_ports[0])) { + return server_content_result('failed', 'No IP/port mapping found for server restart.'); + } + $ip = $ip_ports[0]['ip']; + $port = (int)$ip_ports[0]['port']; + + $mod_id = key($home_info['mods']); + $start_cmd = get_start_cmd($user_info, $remote, $server_xml, $home_info, $mod_id, $ip, $port, $db); + $preStart = isset($server_xml->pre_start) ? trim((string)$server_xml->pre_start) : ""; + $envVars = isset($server_xml->environment_variables) ? trim((string)$server_xml->environment_variables) : ""; + + $delay = isset($options['restart_delay_seconds']) ? (int)$options['restart_delay_seconds'] : 0; + if ($delay > 0) { + if ($delay > 300) { + $delay = 300; + } + sleep($delay); + } + + $remote_retval = $remote->remote_restart_server( + $home_info['home_id'], + $ip, + $port, + $server_xml->control_protocol, + $home_info['control_password'], + $server_xml->control_protocol_type, + $home_info['home_path'], + $server_xml->server_exec_name, + $server_xml->exe_location, + $start_cmd, + $home_info['mods'][$mod_id]['cpu_affinity'], + $home_info['mods'][$mod_id]['nice'], + $preStart, + $envVars, + $server_xml->game_key, + (isset($server_xml->console_log) ? $server_xml->console_log : "") + ); + if ($remote_retval !== 1) { + return server_content_result('restart_required', 'Update completed but automatic restart failed.', array( + 'restart_status' => $remote_retval, + )); + } + return server_content_result('success', 'Update completed and server restart was triggered.', array( + 'restart_status' => $remote_retval, + )); +} + +function server_content_install_updates_and_restart($home_id, $options = array()) +{ + $install_result = server_content_install_updates($home_id, $options); + if (empty($install_result['success']) || $install_result['status'] === 'failed') { + return $install_result; + } + $restart_result = server_content_restart_home($home_id, $options); + $install_result['details']['restart'] = $restart_result; + if ($restart_result['status'] === 'success') { + $install_result['status'] = 'success'; + $install_result['message'] = 'Server content updates installed and server restart requested.'; + return $install_result; + } + $install_result['status'] = 'restart_required'; + $install_result['success'] = true; + $install_result['message'] = 'Server content updates installed; restart is still required.'; + return $install_result; +} + +function server_content_run_scheduled_action($home_id, $action, $options = array()) +{ + $home_id = (int)$home_id; + $action = trim((string)$action); + if ($home_id <= 0) { + return server_content_result('failed', 'Invalid server home id.'); + } + $handlers = array( + 'server_content_check_updates' => 'server_content_check_updates', + 'server_content_check_workshop_updates' => 'server_content_update_workshop', + 'server_content_install_updates_if_stopped' => 'server_content_install_updates_if_stopped', + 'server_content_install_updates_next_restart' => 'server_content_install_updates_next_restart', + 'server_content_install_updates_now' => 'server_content_install_updates', + 'server_content_install_updates_and_restart' => 'server_content_install_updates_and_restart', + 'server_content_update_workshop' => 'server_content_update_workshop', + 'server_content_update_all' => 'server_content_install_updates', + 'server_content_notify_updates_only' => 'server_content_check_updates', + 'server_content_validate_files' => 'server_content_update_workshop', + 'server_content_backup_before_update' => 'server_content_install_updates', + ); + if (!isset($handlers[$action]) || !function_exists($handlers[$action])) { + $result = server_content_result('failed', 'Unsupported scheduled server content action.', array( + 'action' => $action, + )); + server_content_log_action($home_id, $action, $result['status'], $result['message'], $result['details']); + return $result; + } + + if ($action === 'server_content_check_workshop_updates' || $action === 'server_content_validate_files') { + $options['check_only'] = true; + $options['workshop_action'] = ($action === 'server_content_validate_files') ? 'validate_files' : 'check_updates'; + } + if ($action === 'server_content_backup_before_update') { + $options['backup_before_update'] = true; + } + if ($action === 'server_content_install_updates_and_restart' && !isset($options['safe_restart'])) { + $options['safe_restart'] = true; + } + if ($action === 'server_content_notify_updates_only') { + $options['notify_only'] = true; + $options['check_only'] = true; + } + + server_content_log_action($home_id, $action, 'started', 'Scheduled action started.', $options); + $handler = $handlers[$action]; + $result = $handler($home_id, $options); + server_content_log_action($home_id, $action, $result['status'], $result['message'], $result['details']); + return $result; +} + diff --git a/Panel/modules/cron/cron.php b/Panel/modules/cron/cron.php index b75c1d53..4465cbba 100644 --- a/Panel/modules/cron/cron.php +++ b/Panel/modules/cron/cron.php @@ -77,19 +77,12 @@ function exec_ogp_module() $mod_key = $game_home['mod_key']; $token = $db->getApiToken($_SESSION['user_id']); - switch ($_POST['action']) { - case "stop": - $command = "wget -qO- \"${panelURL}/ogp_api.php?gamemanager/stop&token=${token}&ip=${ip}&port=${port}&mod_key=${mod_key}\" --no-check-certificate > /dev/null 2>&1"; - break; - case "start": - $command = "wget -qO- \"${panelURL}/ogp_api.php?gamemanager/start&token=${token}&ip=${ip}&port=${port}&mod_key=${mod_key}\" --no-check-certificate > /dev/null 2>&1"; - break; - case "restart": - $command = "wget -qO- \"${panelURL}/ogp_api.php?gamemanager/restart&token=${token}&ip=${ip}&port=${port}&mod_key=${mod_key}\" --no-check-certificate > /dev/null 2>&1"; - break; - case "steam_auto_update": - $command = "wget -qO- \"${panelURL}/ogp_api.php?gamemanager/update&token=${token}&ip=${ip}&port=${port}&mod_key=${mod_key}&type=steam\" --no-check-certificate > /dev/null 2>&1"; - break; + $command = build_cron_scheduler_command($panelURL, $token, $game_home, $_POST['action']); + if($command === false) + { + print_failure(get_lang('bad_inputs')); + $view->refresh('?m=cron&p=cron',2); + return; } $remote = new OGPRemoteLibrary( $game_home['agent_ip'], $game_home['agent_port'], diff --git a/Panel/modules/cron/shared_cron_functions.php b/Panel/modules/cron/shared_cron_functions.php index e41575fa..45f7d543 100644 --- a/Panel/modules/cron/shared_cron_functions.php +++ b/Panel/modules/cron/shared_cron_functions.php @@ -47,10 +47,17 @@ function reloadJobs($server_homes, $remote_servers, $getAllJobs = true) list($wget,$wget_args,$url,$wget_nocert,$gt,$devnull,$err2out) = explode(" ", $command, 7); parse_str(parse_url(trim($url,'"'), PHP_URL_QUERY), $url_query); - - if(!isset($url_query['ip']) or !isset($url_query['port'])) + $home_info = false; + if(isset($url_query['ip']) && isset($url_query['port'])) + { + $home_info = $db->getGameHomeByIP($url_query['ip'], $url_query['port']); + } + elseif(isset($url_query['home_id'])) + { + $home_info = $db->getGameHome((int)$url_query['home_id'], true); + } + if(!$home_info) continue; - $home_info = $db->getGameHomeByIP($url_query['ip'], $url_query['port']); if(!$getAllJobs && !hasAccess($home_info)) continue; @@ -63,6 +70,8 @@ function reloadJobs($server_homes, $remote_servers, $getAllJobs = true) $action = "start"; }else if($action == "gamemanager/restart"){ $action = "restart"; + }else if($action == "server_content/run_scheduled_action" && isset($url_query['action']) && $url_query['action'] != ""){ + $action = $url_query['action']; } $jobsArray[$rhost_id][$jobId] = array( 'job' => $job, @@ -76,7 +85,7 @@ function reloadJobs($server_homes, $remote_servers, $getAllJobs = true) 'home_id' => $home_info['home_id'], 'ip' => $home_info['ip'], 'port' => $home_info['port'], - 'mod_key' => $url_query['mod_key']); + 'mod_key' => isset($url_query['mod_key']) ? $url_query['mod_key'] : ''); } else { @@ -99,6 +108,50 @@ function reloadJobs($server_homes, $remote_servers, $getAllJobs = true) return array($jobsArray, $remote_servers_offline); } +function get_server_content_scheduled_actions() { + return array( + 'server_content_check_updates', + 'server_content_check_workshop_updates', + 'server_content_install_updates_if_stopped', + 'server_content_install_updates_next_restart', + 'server_content_install_updates_now', + 'server_content_install_updates_and_restart', + 'server_content_notify_updates_only', + 'server_content_update_all', + 'server_content_validate_files', + 'server_content_backup_before_update', + ); +} + +function build_cron_scheduler_command($panelURL, $token, $game_home, $action) { + $ip = $game_home['ip']; + $port = $game_home['port']; + $mod_key = isset($game_home['mod_key']) ? $game_home['mod_key'] : ''; + $home_id = isset($game_home['home_id']) ? (int)$game_home['home_id'] : 0; + if(in_array($action, get_server_content_scheduled_actions())) + { + $options = array('triggered_by' => 'scheduler'); + if($action == 'server_content_install_updates_and_restart') + { + $options['safe_restart'] = true; + $options['restart_delay_seconds'] = 60; + } + $options_json = urlencode(json_encode($options)); + return "wget -qO- \"${panelURL}/ogp_api.php?server_content/run_scheduled_action&token=${token}&home_id=${home_id}&action=${action}&options=${options_json}\" --no-check-certificate > /dev/null 2>&1"; + } + switch ($action) { + case "stop": + return "wget -qO- \"${panelURL}/ogp_api.php?gamemanager/stop&token=${token}&ip=${ip}&port=${port}&mod_key=${mod_key}\" --no-check-certificate > /dev/null 2>&1"; + case "start": + return "wget -qO- \"${panelURL}/ogp_api.php?gamemanager/start&token=${token}&ip=${ip}&port=${port}&mod_key=${mod_key}\" --no-check-certificate > /dev/null 2>&1"; + case "restart": + return "wget -qO- \"${panelURL}/ogp_api.php?gamemanager/restart&token=${token}&ip=${ip}&port=${port}&mod_key=${mod_key}\" --no-check-certificate > /dev/null 2>&1"; + case "steam_auto_update": + return "wget -qO- \"${panelURL}/ogp_api.php?gamemanager/update&token=${token}&ip=${ip}&port=${port}&mod_key=${mod_key}&type=steam\" --no-check-certificate > /dev/null 2>&1"; + } + return false; +} + function updateCronJobTokens($old_token, $token){ global $db; $remote_servers = $db->getRemoteServers(); @@ -152,6 +205,11 @@ function get_action_selector($action = false, $server_homes = false, $homeid_ip_ if( $server_xml->installer == "steamcmd" ) $server_actions[] = 'steam_auto_update'; } + global $db; + if($db->isModuleInstalled('addonsmanager')) + { + $server_actions = array_merge($server_actions, get_server_content_scheduled_actions()); + } $select_action = '