Simplify Steam Workshop UX and wire panel docs links
Agent-Logs-Url: https://github.com/GameServerPanel/GSP/sessions/1575c81b-f8a7-433a-8f3b-e068c0992c18 Co-authored-by: iaretechnician <2749183+iaretechnician@users.noreply.github.com>
This commit is contained in:
parent
5fc301e632
commit
01ad93a11a
13 changed files with 566 additions and 528 deletions
|
|
@ -1,5 +1,9 @@
|
|||
# Changelog
|
||||
|
||||
## 2026-05-08
|
||||
- **Steam Workshop reliability + UI simplification:** Removed customer CLI/update scripting instructions from Workshop user flow, reduced per-server behavior options to supported modes only, switched remaining Steam Workshop SQL references to prefix helpers (no `OGP_DB_PREFIX` strings), hardened queued-update agent processing (`queued → updating → installed/failed`) with clearer error persistence, and refreshed monitor/support documentation links to open game-specific docs (fallback to docs index) in a new tab.
|
||||
- **Billing docs routing refresh:** Updated docs browser links/icons to root-relative storefront paths (`/docs.php`, `/docs/...`) and removed stale hardcoded panel host guidance from getting-started documentation.
|
||||
|
||||
## 2026-05-07
|
||||
- **README + storefront mobile/cart pricing fixes:** Rewrote the root README for GSP positioning, hardened storefront mobile responsiveness (login, order, cart, shared header guardrails), fixed add-to-cart price persistence for low-decimal paid items, aligned cart/free-checkout math to `total_due` values, and refreshed the canonical storefront footer timestamp.
|
||||
- **Billing/cart/storefront stability pass:** Hardened `add_to_cart.php` to build schema-compatible invoice inserts dynamically (including legacy installs missing `period_start`), fixed free-checkout DB close handling so wrapper objects are never passed to `mysqli_close()`, switched cart/free-total decisions to cent-based math so low nonzero prices (e.g. $0.02) never show as FREE, improved canonical game deduplication + OS variant matching in storefront list/order pages, and aligned Steam Workshop behavior labels with the new restart/update wording.
|
||||
|
|
|
|||
|
|
@ -7,3 +7,4 @@
|
|||
- Add a side-by-side before/after diff preview panel to the config_games top-level XML section editor before section saves.
|
||||
- Add an integration smoke test that exercises paid checkout, free checkout, and add-to-cart on installs with/without `period_start` to prevent billing schema drift regressions.
|
||||
- Add a storefront visual-regression check at 375px and 430px breakpoints covering login, order, and cart pages to prevent mobile overflow regressions.
|
||||
- Complete a full pass over all `modules/billing/docs/*` game guides to standardize OS/Workshop/RCON capability statements against current XML-backed server support.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ $docsDir = __DIR__ . '/docs';
|
|||
// Get action and doc parameters
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$doc = $_GET['doc'] ?? '';
|
||||
$docsPagePath = '/docs.php';
|
||||
|
||||
/**
|
||||
* Get all documentation folders with their metadata
|
||||
|
|
@ -63,9 +64,9 @@ function getDocCategories($docsDir) {
|
|||
// Find icon file
|
||||
$icon = '';
|
||||
if (file_exists($folderPath . '/icon.png')) {
|
||||
$icon = 'docs/' . $folder . '/icon.png';
|
||||
$icon = '/docs/' . $folder . '/icon.png';
|
||||
} elseif (file_exists($folderPath . '/icon.jpg')) {
|
||||
$icon = 'docs/' . $folder . '/icon.jpg';
|
||||
$icon = '/docs/' . $folder . '/icon.jpg';
|
||||
}
|
||||
|
||||
$categories[] = [
|
||||
|
|
@ -137,7 +138,7 @@ uksort($grouped, function($a, $b) use ($categoryOrder) {
|
|||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title><?php echo $action === 'view' ? 'Documentation' : 'Documentation - GameServers.World'; ?></title>
|
||||
<title><?php echo $action === 'view' ? 'Documentation' : 'Documentation - GSP'; ?></title>
|
||||
<link rel="stylesheet" href="css/header.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<style>
|
||||
|
|
@ -351,7 +352,7 @@ uksort($grouped, function($a, $b) use ($categoryOrder) {
|
|||
<div class="container">
|
||||
<?php if ($action === 'view' && !empty($doc)): ?>
|
||||
<!-- View specific documentation -->
|
||||
<a href="docs.php" class="back-button">← Back to Documentation List</a>
|
||||
<a href="<?php echo htmlspecialchars($docsPagePath, ENT_QUOTES, 'UTF-8'); ?>" class="back-button">← Back to Documentation List</a>
|
||||
|
||||
<div class="doc-view-container">
|
||||
<?php
|
||||
|
|
@ -401,7 +402,7 @@ uksort($grouped, function($a, $b) use ($categoryOrder) {
|
|||
|
||||
<div class="docs-grid">
|
||||
<?php foreach ((array)$docs as $doc): ?>
|
||||
<a href="docs.php?action=view&doc=<?php echo urlencode($doc['folder']); ?>" class="doc-card">
|
||||
<a href="<?php echo htmlspecialchars($docsPagePath . '?action=view&doc=' . urlencode($doc['folder']), ENT_QUOTES, 'UTF-8'); ?>" class="doc-card">
|
||||
<div class="doc-icon-wrapper">
|
||||
<?php if (!empty($doc['icon'])): ?>
|
||||
<img src="<?php echo htmlspecialchars($doc['icon']); ?>" alt="" class="doc-icon">
|
||||
|
|
@ -428,4 +429,3 @@ uksort($grouped, function($a, $b) use ($categoryOrder) {
|
|||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<p>Once your payment is processed, you'll receive:</p>
|
||||
<ul>
|
||||
<li>A confirmation email with your server details</li>
|
||||
<li>Access to the control panel at <a href="http://panel.iaregamer.com" target="_blank">panel.iaregamer.com</a></li>
|
||||
<li>Access to your control panel login page</li>
|
||||
<li>FTP credentials for file management</li>
|
||||
<li>Server IP address and port</li>
|
||||
</ul>
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
<h3>Control Panel</h3>
|
||||
<ol>
|
||||
<li>Visit <a href="http://panel.iaregamer.com" target="_blank">panel.iaregamer.com</a></li>
|
||||
<li>Open your panel login URL (provided in your service welcome message)</li>
|
||||
<li>Log in with your account credentials</li>
|
||||
<li>Select your server from "My Servers"</li>
|
||||
<li>Use the control panel to start, stop, restart, and configure your server</li>
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
<ul>
|
||||
<li>Check the game-specific documentation for your server type</li>
|
||||
<li>Review the troubleshooting guides</li>
|
||||
<li>Contact support through your account dashboard</li>
|
||||
<li>Open a support request from the panel support page</li>
|
||||
<li>Check our community forums for tips and solutions</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Last Updated at 12:44pm on 2026-05-07
|
||||
Last Updated at 12:47pm on 2026-05-08
|
||||
|
|
|
|||
|
|
@ -154,6 +154,18 @@ function get_sync_name($server_xml)
|
|||
return $sync_name;
|
||||
}
|
||||
|
||||
function gsp_docs_url_for_game_key($game_key)
|
||||
{
|
||||
$game_key = trim((string)$game_key);
|
||||
if ($game_key !== '') {
|
||||
$docPath = __DIR__ . '/../billing/docs/' . $game_key . '/index.php';
|
||||
if (is_file($docPath)) {
|
||||
return '/docs.php?action=view&doc=' . rawurlencode($game_key);
|
||||
}
|
||||
}
|
||||
return '/docs.php';
|
||||
}
|
||||
|
||||
function exec_ogp_module() {
|
||||
global $db, $settings, $loggedInUserInfo;
|
||||
echo "<h2 class='gameMonitor " . ($db->isAdmin( $_SESSION['user_id'] ) ? "isAdminUser" : "") . "'>". get_lang("game_monitor") ."</h2>";
|
||||
|
|
@ -260,6 +272,15 @@ $home_info = $db->getGameHomeWithoutMods($home_id);
|
|||
$show_all = FALSE;
|
||||
}
|
||||
|
||||
$docsTarget = '/docs.php';
|
||||
if (is_array($home_info) && !empty($home_info['game_key'])) {
|
||||
$docsTarget = gsp_docs_url_for_game_key($home_info['game_key']);
|
||||
}
|
||||
echo "<div style='margin:10px 0 16px 0;padding:10px 12px;border:1px solid #2d2d2d;border-radius:6px;background:#171717;'>"
|
||||
. "<a href='" . htmlspecialchars($docsTarget, ENT_QUOTES, 'UTF-8') . "' target='_blank' rel='noopener noreferrer' style='color:#8cb9ff;text-decoration:none;font-weight:600;'>Game Documentation</a>"
|
||||
. "<span style='color:#a9a9a9;margin-left:8px;'>Opens setup and troubleshooting docs in a new tab.</span>"
|
||||
. "</div>";
|
||||
|
||||
require("protocol/lgsl/lgsl_protocol.php");
|
||||
|
||||
$info = $db->getUserById($_SESSION['user_id']);
|
||||
|
|
|
|||
|
|
@ -125,15 +125,12 @@ function sw_admin_save_profile($db)
|
|||
);
|
||||
|
||||
// Per-profile default behavior fields
|
||||
$valid_update_modes = array('manual', 'on_restart', 'before_start', 'scheduled');
|
||||
$valid_restart_behaviors = array('none', 'if_empty', 'immediate', 'next_restart');
|
||||
$valid_hot_load = array('disabled', 'attempt');
|
||||
$valid_update_modes = array('manual', 'on_restart', 'before_start');
|
||||
$valid_restart_behaviors = array('none', 'if_stopped');
|
||||
$posted_um = $_POST['default_update_mode'] ?? 'manual';
|
||||
$posted_rb = $_POST['default_restart_behavior'] ?? 'none';
|
||||
$posted_hl = $_POST['default_hot_load'] ?? 'disabled';
|
||||
$fields['default_update_mode'] = in_array($posted_um, $valid_update_modes, true) ? $posted_um : 'manual';
|
||||
$fields['default_restart_behavior'] = in_array($posted_rb, $valid_restart_behaviors, true) ? $posted_rb : 'none';
|
||||
$fields['default_hot_load'] = in_array($posted_hl, $valid_hot_load, true) ? $posted_hl : 'disabled';
|
||||
|
||||
$setParts = array();
|
||||
foreach ($fields as $col => $val) {
|
||||
|
|
@ -307,7 +304,7 @@ function sw_admin_edit_form(array $profile, array $detected = array(), $showDete
|
|||
<p class="sw-muted">
|
||||
These defaults are applied when a user enables Workshop on a server that has no saved behavior settings yet.
|
||||
Users can always override them on their own server pages.
|
||||
All defaults are intentionally set to the safest option (manual / no auto-restart / hot-load off).
|
||||
All defaults are intentionally set to the safest option (manual / no automatic restart).
|
||||
</p>
|
||||
<div class="sw-grid">
|
||||
<label>
|
||||
|
|
@ -316,23 +313,13 @@ function sw_admin_edit_form(array $profile, array $detected = array(), $showDete
|
|||
<option value="manual" <?= (($profile['default_update_mode'] ?? 'manual') === 'manual') ? 'selected' : '' ?>>Manual only (safe default)</option>
|
||||
<option value="on_restart" <?= (($profile['default_update_mode'] ?? 'manual') === 'on_restart') ? 'selected' : '' ?>>On next restart</option>
|
||||
<option value="before_start" <?= (($profile['default_update_mode'] ?? 'manual') === 'before_start') ? 'selected' : '' ?>>Before every server start</option>
|
||||
<option value="scheduled" <?= (($profile['default_update_mode'] ?? 'manual') === 'scheduled') ? 'selected' : '' ?>>Scheduled update check</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Default Restart Behavior</span>
|
||||
<select name="default_restart_behavior">
|
||||
<option value="none" <?= (($profile['default_restart_behavior'] ?? 'none') === 'none') ? 'selected' : '' ?>>Do not restart automatically (safe default)</option>
|
||||
<option value="if_empty" <?= (($profile['default_restart_behavior'] ?? 'none') === 'if_empty') ? 'selected' : '' ?>>Restart if empty</option>
|
||||
<option value="immediate" <?= (($profile['default_restart_behavior'] ?? 'none') === 'immediate') ? 'selected' : '' ?>>Restart after warning</option>
|
||||
<option value="next_restart" <?= (($profile['default_restart_behavior'] ?? 'none') === 'next_restart') ? 'selected' : '' ?>>Install on next manual restart only</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Default Hot-Load</span>
|
||||
<select name="default_hot_load">
|
||||
<option value="disabled" <?= (($profile['default_hot_load'] ?? 'disabled') === 'disabled') ? 'selected' : '' ?>>Disabled (safe default)</option>
|
||||
<option value="attempt" <?= (($profile['default_hot_load'] ?? 'disabled') === 'attempt') ? 'selected' : '' ?>>Attempt hot-load if game supports it</option>
|
||||
<option value="if_stopped" <?= (($profile['default_restart_behavior'] ?? 'none') === 'if_stopped') ? 'selected' : '' ?>>Restart only if server is stopped</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -87,12 +87,12 @@ if ($dry_run) {
|
|||
|
||||
// ── Collect home IDs to process ───────────────────────────────────────────
|
||||
if ($do_all) {
|
||||
// Find all home_ids that have at least one enabled mod with a valid enabled profile.
|
||||
// Find all home_ids that have at least one enabled queued mod with an enabled profile.
|
||||
$rows = $db->resultQuery(
|
||||
"SELECT DISTINCT m.home_id
|
||||
FROM `OGP_DB_PREFIXsteam_workshop_server_mods` m
|
||||
JOIN `OGP_DB_PREFIXsteam_workshop_game_profiles` p ON p.id = m.profile_id
|
||||
WHERE m.enabled = 1 AND p.enabled = 1"
|
||||
FROM " . sw_table('steam_workshop_server_mods') . " m
|
||||
JOIN " . sw_table('steam_workshop_game_profiles') . " p ON p.id = m.profile_id
|
||||
WHERE m.enabled = 1 AND p.enabled = 1 AND m.install_status = 'queued'"
|
||||
);
|
||||
$home_ids = $rows ? array_column($rows, 'home_id') : array();
|
||||
} else {
|
||||
|
|
@ -159,14 +159,11 @@ function sw_agent_process_home($db, $home_id, $dry_run)
|
|||
);
|
||||
$server_root = rtrim($server_root, '/');
|
||||
|
||||
// Load enabled mods, sorted by sort_order
|
||||
$mods = sw_get_server_mods($db, $home_id) ?: array();
|
||||
$mods = array_filter($mods, function ($m) {
|
||||
return !empty($m['enabled']);
|
||||
});
|
||||
// Load queued+enabled mods, sorted by sort_order
|
||||
$mods = sw_agent_get_queued_mods($db, $home_id);
|
||||
|
||||
if (empty($mods)) {
|
||||
echo " No enabled mods.\n";
|
||||
echo " No queued Workshop updates for this server.\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -185,7 +182,7 @@ function sw_agent_process_home($db, $home_id, $dry_run)
|
|||
// Mark as updating
|
||||
if (!$dry_run) {
|
||||
$db->query(
|
||||
"UPDATE `OGP_DB_PREFIXsteam_workshop_server_mods`
|
||||
"UPDATE " . sw_table('steam_workshop_server_mods') . "
|
||||
SET `install_status` = 'updating', `last_error` = NULL, `updated_at` = NOW()
|
||||
WHERE `id` = $mod_id LIMIT 1"
|
||||
);
|
||||
|
|
@ -224,7 +221,7 @@ function sw_agent_process_home($db, $home_id, $dry_run)
|
|||
if (!$dry_run) {
|
||||
$safe_err = $db->realEscapeSingle($err);
|
||||
$db->query(
|
||||
"UPDATE `OGP_DB_PREFIXsteam_workshop_server_mods`
|
||||
"UPDATE " . sw_table('steam_workshop_server_mods') . "
|
||||
SET `install_status` = 'failed', `last_error` = '$safe_err', `updated_at` = NOW()
|
||||
WHERE `id` = $mod_id LIMIT 1"
|
||||
);
|
||||
|
|
@ -246,7 +243,7 @@ function sw_agent_process_home($db, $home_id, $dry_run)
|
|||
if (!$dry_run) {
|
||||
$safe_err = $db->realEscapeSingle($err);
|
||||
$db->query(
|
||||
"UPDATE `OGP_DB_PREFIXsteam_workshop_server_mods`
|
||||
"UPDATE " . sw_table('steam_workshop_server_mods') . "
|
||||
SET `install_status` = 'failed', `last_error` = '$safe_err', `updated_at` = NOW()
|
||||
WHERE `id` = $mod_id LIMIT 1"
|
||||
);
|
||||
|
|
@ -263,7 +260,7 @@ function sw_agent_process_home($db, $home_id, $dry_run)
|
|||
// 4. Mark as installed
|
||||
if (!$dry_run) {
|
||||
$db->query(
|
||||
"UPDATE `OGP_DB_PREFIXsteam_workshop_server_mods`
|
||||
"UPDATE " . sw_table('steam_workshop_server_mods') . "
|
||||
SET `install_status` = 'installed',
|
||||
`last_installed_at` = NOW(),
|
||||
`last_updated_at` = NOW(),
|
||||
|
|
@ -276,27 +273,22 @@ function sw_agent_process_home($db, $home_id, $dry_run)
|
|||
echo " [OK] Installed → $install_path\n";
|
||||
}
|
||||
|
||||
// 5. Print launch parameters
|
||||
$enabled_mods = array_values(array_filter(
|
||||
sw_get_server_mods($db, $home_id) ?: array(),
|
||||
function ($m) { return !empty($m['enabled']); }
|
||||
));
|
||||
$params = sw_generate_launch_params($enabled_mods, $profile);
|
||||
|
||||
echo "\n Generated launch parameters:\n";
|
||||
if ($params['mod']) {
|
||||
echo " " . $params['mod'] . "\n";
|
||||
}
|
||||
if ($params['servermod']) {
|
||||
echo " " . $params['servermod'] . "\n";
|
||||
}
|
||||
if (!$params['mod'] && !$params['servermod']) {
|
||||
echo " (none)\n";
|
||||
}
|
||||
|
||||
return $all_ok;
|
||||
}
|
||||
|
||||
function sw_agent_get_queued_mods($db, $home_id)
|
||||
{
|
||||
$home_id = (int)$home_id;
|
||||
$rows = $db->resultQuery(
|
||||
"SELECT * FROM " . sw_table('steam_workshop_server_mods') . "
|
||||
WHERE `home_id` = $home_id
|
||||
AND `enabled` = 1
|
||||
AND `install_status` = 'queued'
|
||||
ORDER BY `sort_order` ASC, `id` ASC"
|
||||
);
|
||||
return $rows ? $rows : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the standard template variable map for a given home + profile.
|
||||
*
|
||||
|
|
@ -361,7 +353,7 @@ function sw_agent_steamcmd_download(array $mod, array $profile, array $tpl_vars,
|
|||
}
|
||||
|
||||
// Validate that steamcmd exists
|
||||
if (!$dry_run && !is_file($steamcmd) && !is_executable($steamcmd)) {
|
||||
if (!$dry_run && (!is_file($steamcmd) || !is_executable($steamcmd))) {
|
||||
return array('ok' => false, 'error' => "SteamCMD not found or not executable: $steamcmd");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -359,17 +359,54 @@ function sw_get_server_settings($db, $home_id)
|
|||
"SELECT * FROM " . sw_table('steam_workshop_server_settings') . "
|
||||
WHERE `home_id` = $home_id LIMIT 1"
|
||||
);
|
||||
if ($rows && isset($rows[0])) {
|
||||
return $rows[0];
|
||||
if ($rows && isset($rows[0]) && is_array($rows[0])) {
|
||||
$settings = $rows[0];
|
||||
|
||||
$legacyUpdateMap = array(
|
||||
'scheduled' => 'manual',
|
||||
);
|
||||
$legacyRestartMap = array(
|
||||
'if_empty' => 'if_stopped',
|
||||
'next_restart' => 'if_stopped',
|
||||
'immediate' => 'none',
|
||||
);
|
||||
$legacyScheduleMap = array(
|
||||
'hourly' => 'daily',
|
||||
);
|
||||
|
||||
if (isset($legacyUpdateMap[$settings['update_mode'] ?? ''])) {
|
||||
$settings['update_mode'] = $legacyUpdateMap[$settings['update_mode']];
|
||||
}
|
||||
if (isset($legacyRestartMap[$settings['restart_behavior'] ?? ''])) {
|
||||
$settings['restart_behavior'] = $legacyRestartMap[$settings['restart_behavior']];
|
||||
}
|
||||
if (isset($legacyScheduleMap[$settings['schedule_interval'] ?? ''])) {
|
||||
$settings['schedule_interval'] = $legacyScheduleMap[$settings['schedule_interval']];
|
||||
}
|
||||
|
||||
$validUpdateModes = array('manual', 'on_restart', 'before_start');
|
||||
$validRestartBehaviors = array('none', 'if_stopped');
|
||||
$validIntervals = array('disabled', 'daily', 'weekly');
|
||||
|
||||
if (!in_array($settings['update_mode'] ?? '', $validUpdateModes, true)) {
|
||||
$settings['update_mode'] = 'manual';
|
||||
}
|
||||
if (!in_array($settings['restart_behavior'] ?? '', $validRestartBehaviors, true)) {
|
||||
$settings['restart_behavior'] = 'none';
|
||||
}
|
||||
if (!in_array($settings['schedule_interval'] ?? '', $validIntervals, true)) {
|
||||
$settings['schedule_interval'] = 'disabled';
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
// Safe defaults – manual only, no automatic restarts, hot-load off
|
||||
|
||||
// Safe defaults – manual only, no automatic restarts, schedule disabled
|
||||
return array(
|
||||
'home_id' => $home_id,
|
||||
'update_mode' => 'manual',
|
||||
'restart_behavior' => 'none',
|
||||
'hot_load' => 'disabled',
|
||||
'warning_minutes' => 10,
|
||||
'schedule_interval' => 'daily',
|
||||
'schedule_interval' => 'disabled',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -378,43 +415,38 @@ function sw_get_server_settings($db, $home_id)
|
|||
*
|
||||
* @param OGPDatabase $db
|
||||
* @param int $home_id
|
||||
* @param array $data keys: update_mode, restart_behavior, hot_load,
|
||||
* warning_minutes, schedule_interval
|
||||
* @param array $data keys: update_mode, restart_behavior, schedule_interval
|
||||
* @return bool
|
||||
*/
|
||||
function sw_save_server_settings($db, $home_id, array $data)
|
||||
{
|
||||
$home_id = (int)$home_id;
|
||||
|
||||
$valid_update_modes = array('manual', 'on_restart', 'before_start', 'scheduled');
|
||||
$valid_restart_behaviors = array('none', 'if_empty', 'immediate', 'next_restart');
|
||||
$valid_hot_load = array('disabled', 'attempt');
|
||||
$valid_intervals = array('hourly', 'daily', 'weekly');
|
||||
$valid_update_modes = array('manual', 'on_restart', 'before_start');
|
||||
$valid_restart_behaviors = array('none', 'if_stopped');
|
||||
$valid_intervals = array('disabled', 'daily', 'weekly');
|
||||
|
||||
$update_mode = in_array($data['update_mode'] ?? '', $valid_update_modes, true) ? $data['update_mode'] : 'manual';
|
||||
$restart_behavior = in_array($data['restart_behavior'] ?? '', $valid_restart_behaviors, true) ? $data['restart_behavior'] : 'none';
|
||||
$hot_load = in_array($data['hot_load'] ?? '', $valid_hot_load, true) ? $data['hot_load'] : 'disabled';
|
||||
$warning_minutes = max(1, min(120, (int)($data['warning_minutes'] ?? 10)));
|
||||
$schedule_interval = in_array($data['schedule_interval'] ?? '', $valid_intervals, true) ? $data['schedule_interval'] : 'daily';
|
||||
$schedule_interval = in_array($data['schedule_interval'] ?? '', $valid_intervals, true) ? $data['schedule_interval'] : 'disabled';
|
||||
|
||||
$safe_um = $db->realEscapeSingle($update_mode);
|
||||
$safe_rb = $db->realEscapeSingle($restart_behavior);
|
||||
$safe_hl = $db->realEscapeSingle($hot_load);
|
||||
$safe_si = $db->realEscapeSingle($schedule_interval);
|
||||
|
||||
return (bool)$db->query(
|
||||
"INSERT INTO " . sw_table('steam_workshop_server_settings') . "
|
||||
(`home_id`, `update_mode`, `restart_behavior`, `hot_load`,
|
||||
`warning_minutes`, `schedule_interval`, `created_at`, `updated_at`)
|
||||
VALUES ($home_id, '$safe_um', '$safe_rb', '$safe_hl',
|
||||
$warning_minutes, '$safe_si', NOW(), NOW())
|
||||
`warning_minutes`, `schedule_interval`, `created_at`, `updated_at`)
|
||||
VALUES ($home_id, '$safe_um', '$safe_rb', 'disabled',
|
||||
0, '$safe_si', NOW(), NOW())
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`update_mode` = '$safe_um',
|
||||
`restart_behavior` = '$safe_rb',
|
||||
`hot_load` = '$safe_hl',
|
||||
`warning_minutes` = $warning_minutes,
|
||||
`schedule_interval` = '$safe_si',
|
||||
`updated_at` = NOW()"
|
||||
`update_mode` = '$safe_um',
|
||||
`restart_behavior` = '$safe_rb',
|
||||
`hot_load` = 'disabled',
|
||||
`warning_minutes` = 0,
|
||||
`schedule_interval` = '$safe_si',
|
||||
`updated_at` = NOW()"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,29 +9,55 @@
|
|||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
// ── Module metadata ──────────────────────────────────────────────────────
|
||||
$module_title = "Steam Workshop";
|
||||
$module_version = "3.1";
|
||||
$db_version = 4;
|
||||
$module_version = "3.2";
|
||||
$db_version = 5;
|
||||
$module_required = FALSE;
|
||||
$module_menus = array(
|
||||
array('subpage' => 'admin', 'name' => 'Steam Workshop', 'group' => 'admin'),
|
||||
);
|
||||
|
||||
// ── SQL helpers ──────────────────────────────────────────────────────────
|
||||
// All OGP_DB_PREFIX tokens are replaced at runtime by $db->query() /
|
||||
// $db->resultQuery() before the SQL reaches MySQL. Do not replace them
|
||||
// here with literal strings.
|
||||
if (!function_exists('sw_module_db_prefix')) {
|
||||
function sw_module_db_prefix()
|
||||
{
|
||||
if (defined('DB_PREFIX') && DB_PREFIX !== '') {
|
||||
return DB_PREFIX;
|
||||
}
|
||||
if (isset($GLOBALS['db_prefix']) && $GLOBALS['db_prefix'] !== '') {
|
||||
return $GLOBALS['db_prefix'];
|
||||
}
|
||||
if (isset($GLOBALS['table_prefix']) && $GLOBALS['table_prefix'] !== '') {
|
||||
return $GLOBALS['table_prefix'];
|
||||
}
|
||||
return 'gsp_';
|
||||
}
|
||||
}
|
||||
|
||||
$_sw_drop_old = array(
|
||||
"DROP TABLE IF EXISTS `OGP_DB_PREFIXworkshop_game_profiles`",
|
||||
"DROP TABLE IF EXISTS `OGP_DB_PREFIXworkshop_cache`",
|
||||
"DROP TABLE IF EXISTS `OGP_DB_PREFIXserver_workshop_mods`",
|
||||
"DROP TABLE IF EXISTS `OGP_DB_PREFIXserver_workshop_settings`",
|
||||
if (!function_exists('sw_module_table')) {
|
||||
function sw_module_table($table)
|
||||
{
|
||||
return '`' . sw_module_db_prefix() . $table . '`';
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('sw_module_table_name')) {
|
||||
function sw_module_table_name($table)
|
||||
{
|
||||
return sw_module_db_prefix() . $table;
|
||||
}
|
||||
}
|
||||
|
||||
$install_queries = array();
|
||||
|
||||
$legacyDrops = array(
|
||||
"DROP TABLE IF EXISTS " . sw_module_table('workshop_game_profiles'),
|
||||
"DROP TABLE IF EXISTS " . sw_module_table('workshop_cache'),
|
||||
"DROP TABLE IF EXISTS " . sw_module_table('server_workshop_mods'),
|
||||
"DROP TABLE IF EXISTS " . sw_module_table('server_workshop_settings'),
|
||||
);
|
||||
|
||||
$_sw_create_new = array(
|
||||
"CREATE TABLE IF NOT EXISTS `OGP_DB_PREFIXsteam_workshop_game_profiles` (
|
||||
$schemaCreate = array(
|
||||
"CREATE TABLE IF NOT EXISTS " . sw_module_table('steam_workshop_game_profiles') . " (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`config_name` VARCHAR(100) NOT NULL,
|
||||
`game_name` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
|
|
@ -51,8 +77,8 @@ $_sw_create_new = array(
|
|||
`update_script_template` TEXT NULL,
|
||||
`copy_bikeys_enabled` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`notes` TEXT NULL,
|
||||
`default_update_mode` ENUM('manual','on_restart','before_start','scheduled') NOT NULL DEFAULT 'manual',
|
||||
`default_restart_behavior` ENUM('none','if_empty','immediate','next_restart') NOT NULL DEFAULT 'none',
|
||||
`default_update_mode` ENUM('manual','on_restart','before_start') NOT NULL DEFAULT 'manual',
|
||||
`default_restart_behavior` ENUM('none','if_stopped') NOT NULL DEFAULT 'none',
|
||||
`default_hot_load` ENUM('disabled','attempt') NOT NULL DEFAULT 'disabled',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NULL,
|
||||
|
|
@ -60,7 +86,7 @@ $_sw_create_new = array(
|
|||
UNIQUE KEY `uniq_config_name` (`config_name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS `OGP_DB_PREFIXsteam_workshop_server_mods` (
|
||||
"CREATE TABLE IF NOT EXISTS " . sw_module_table('steam_workshop_server_mods') . " (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`home_id` INT NOT NULL,
|
||||
`profile_id` INT NOT NULL,
|
||||
|
|
@ -80,95 +106,133 @@ $_sw_create_new = array(
|
|||
UNIQUE KEY `uniq_home_workshop` (`home_id`, `workshop_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS `OGP_DB_PREFIXsteam_workshop_server_settings` (
|
||||
"CREATE TABLE IF NOT EXISTS " . sw_module_table('steam_workshop_server_settings') . " (
|
||||
`home_id` INT NOT NULL,
|
||||
`update_mode` ENUM('manual','on_restart','before_start','scheduled')
|
||||
`update_mode` ENUM('manual','on_restart','before_start')
|
||||
NOT NULL DEFAULT 'manual',
|
||||
`restart_behavior` ENUM('none','if_empty','immediate','next_restart')
|
||||
`restart_behavior` ENUM('none','if_stopped')
|
||||
NOT NULL DEFAULT 'none',
|
||||
`hot_load` ENUM('disabled','attempt')
|
||||
NOT NULL DEFAULT 'disabled',
|
||||
`warning_minutes` INT NOT NULL DEFAULT 10,
|
||||
`schedule_interval` VARCHAR(32) NOT NULL DEFAULT 'daily',
|
||||
`warning_minutes` INT NOT NULL DEFAULT 0,
|
||||
`schedule_interval` ENUM('disabled','daily','weekly') NOT NULL DEFAULT 'disabled',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NULL,
|
||||
PRIMARY KEY (`home_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
||||
);
|
||||
|
||||
// ── Install queries ──────────────────────────────────────────────────────
|
||||
//
|
||||
// $install_queries[0] – runs on fresh install (module manager iterates all keys).
|
||||
// Drops any legacy tables and creates the new schema.
|
||||
// $install_queries[3] – runs when upgrading from db_version 2 → 3.
|
||||
// Same content; idempotent because of IF [NOT] EXISTS.
|
||||
// $install_queries[4] – runs when upgrading from db_version 3 → 4.
|
||||
// Adds steam_workshop_server_settings table and default
|
||||
// behavior columns on steam_workshop_game_profiles.
|
||||
//
|
||||
// Note: the module manager loops $install_queries[$i+1] for each step from
|
||||
// current db_version up to target. Keys 1 and 2 are intentionally absent;
|
||||
// the manager safely skips undefined keys (PHP returns NULL → empty array).
|
||||
$install_queries[0] = array_merge($legacyDrops, $schemaCreate);
|
||||
$install_queries[3] = array_merge($legacyDrops, $schemaCreate);
|
||||
$install_queries[4] = array();
|
||||
|
||||
$install_queries = array();
|
||||
$install_queries[5] = array(
|
||||
function ($db) {
|
||||
$table = sw_module_table_name('steam_workshop_server_settings');
|
||||
$exists = $db->resultQuery(
|
||||
"SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '" . $db->realEscapeSingle($table) . "'"
|
||||
);
|
||||
if (!$exists || (int)($exists[0]['cnt'] ?? 0) === 0) {
|
||||
return (bool)$db->query(
|
||||
"CREATE TABLE IF NOT EXISTS " . sw_module_table('steam_workshop_server_settings') . " (
|
||||
`home_id` INT NOT NULL,
|
||||
`update_mode` ENUM('manual','on_restart','before_start') NOT NULL DEFAULT 'manual',
|
||||
`restart_behavior` ENUM('none','if_stopped') NOT NULL DEFAULT 'none',
|
||||
`hot_load` ENUM('disabled','attempt') NOT NULL DEFAULT 'disabled',
|
||||
`warning_minutes` INT NOT NULL DEFAULT 0,
|
||||
`schedule_interval` ENUM('disabled','daily','weekly') NOT NULL DEFAULT 'disabled',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NULL,
|
||||
PRIMARY KEY (`home_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
||||
);
|
||||
}
|
||||
|
||||
$install_queries[0] = array_merge($_sw_drop_old, $_sw_create_new);
|
||||
$install_queries[3] = array_merge($_sw_drop_old, $_sw_create_new);
|
||||
$ok = true;
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"UPDATE " . sw_module_table('steam_workshop_server_settings') . "
|
||||
SET `update_mode` = CASE
|
||||
WHEN `update_mode` = 'scheduled' THEN 'manual'
|
||||
ELSE `update_mode`
|
||||
END"
|
||||
);
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"UPDATE " . sw_module_table('steam_workshop_server_settings') . "
|
||||
SET `restart_behavior` = CASE
|
||||
WHEN `restart_behavior` IN ('if_empty','next_restart') THEN 'if_stopped'
|
||||
WHEN `restart_behavior` = 'immediate' THEN 'none'
|
||||
ELSE `restart_behavior`
|
||||
END"
|
||||
);
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"UPDATE " . sw_module_table('steam_workshop_server_settings') . "
|
||||
SET `schedule_interval` = CASE
|
||||
WHEN `schedule_interval` = 'hourly' THEN 'daily'
|
||||
WHEN `schedule_interval` IS NULL OR `schedule_interval` = '' THEN 'disabled'
|
||||
ELSE `schedule_interval`
|
||||
END"
|
||||
);
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"UPDATE " . sw_module_table('steam_workshop_server_settings') . "
|
||||
SET `hot_load` = 'disabled', `warning_minutes` = 0"
|
||||
);
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"ALTER TABLE " . sw_module_table('steam_workshop_server_settings') . "
|
||||
MODIFY `update_mode` ENUM('manual','on_restart','before_start') NOT NULL DEFAULT 'manual'"
|
||||
);
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"ALTER TABLE " . sw_module_table('steam_workshop_server_settings') . "
|
||||
MODIFY `restart_behavior` ENUM('none','if_stopped') NOT NULL DEFAULT 'none'"
|
||||
);
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"ALTER TABLE " . sw_module_table('steam_workshop_server_settings') . "
|
||||
MODIFY `schedule_interval` ENUM('disabled','daily','weekly') NOT NULL DEFAULT 'disabled'"
|
||||
);
|
||||
|
||||
unset($_sw_drop_old, $_sw_create_new);
|
||||
|
||||
// ── db_version 4: per-server behavior settings + per-profile defaults ────
|
||||
//
|
||||
// New table: steam_workshop_server_settings
|
||||
// Stores update/restart/hot-load preferences per server home.
|
||||
// Defaults are safe (manual-only, no auto-restart, hot-load disabled).
|
||||
//
|
||||
// Altered table: steam_workshop_game_profiles
|
||||
// Adds default_update_mode, default_restart_behavior, default_hot_load so
|
||||
// admins can configure defaults per game profile.
|
||||
//
|
||||
// All callables check INFORMATION_SCHEMA before ALTER so this is re-runnable.
|
||||
|
||||
$install_queries[4] = array(
|
||||
// Create per-server settings table
|
||||
"CREATE TABLE IF NOT EXISTS `OGP_DB_PREFIXsteam_workshop_server_settings` (
|
||||
`home_id` INT NOT NULL,
|
||||
`update_mode` ENUM('manual','on_restart','before_start','scheduled')
|
||||
NOT NULL DEFAULT 'manual',
|
||||
`restart_behavior` ENUM('none','if_empty','immediate','next_restart')
|
||||
NOT NULL DEFAULT 'none',
|
||||
`hot_load` ENUM('disabled','attempt')
|
||||
NOT NULL DEFAULT 'disabled',
|
||||
`warning_minutes` INT NOT NULL DEFAULT 10,
|
||||
`schedule_interval` VARCHAR(32) NOT NULL DEFAULT 'daily',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NULL,
|
||||
PRIMARY KEY (`home_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
||||
|
||||
// Add default_update_mode to game_profiles if missing
|
||||
function($db) {
|
||||
$r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXsteam_workshop_game_profiles' AND COLUMN_NAME = 'default_update_mode'");
|
||||
if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true;
|
||||
return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXsteam_workshop_game_profiles` ADD `default_update_mode` ENUM('manual','on_restart','before_start','scheduled') NOT NULL DEFAULT 'manual' AFTER `notes`");
|
||||
return $ok;
|
||||
},
|
||||
// Add default_restart_behavior to game_profiles if missing
|
||||
function($db) {
|
||||
$r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXsteam_workshop_game_profiles' AND COLUMN_NAME = 'default_restart_behavior'");
|
||||
if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true;
|
||||
return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXsteam_workshop_game_profiles` ADD `default_restart_behavior` ENUM('none','if_empty','immediate','next_restart') NOT NULL DEFAULT 'none' AFTER `default_update_mode`");
|
||||
},
|
||||
// Add default_hot_load to game_profiles if missing
|
||||
function($db) {
|
||||
$r = $db->resultQuery("SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'OGP_DB_PREFIXsteam_workshop_game_profiles' AND COLUMN_NAME = 'default_hot_load'");
|
||||
if ($r && isset($r[0]['cnt']) && (int)$r[0]['cnt'] > 0) return true;
|
||||
return (bool)$db->query("ALTER TABLE `OGP_DB_PREFIXsteam_workshop_game_profiles` ADD `default_hot_load` ENUM('disabled','attempt') NOT NULL DEFAULT 'disabled' AFTER `default_restart_behavior`");
|
||||
function ($db) {
|
||||
$profileTable = sw_module_table_name('steam_workshop_game_profiles');
|
||||
$exists = $db->resultQuery(
|
||||
"SELECT COUNT(*) AS cnt FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '" . $db->realEscapeSingle($profileTable) . "'"
|
||||
);
|
||||
if (!$exists || (int)($exists[0]['cnt'] ?? 0) === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$ok = true;
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"UPDATE " . sw_module_table('steam_workshop_game_profiles') . "
|
||||
SET `default_update_mode` = CASE
|
||||
WHEN `default_update_mode` = 'scheduled' THEN 'manual'
|
||||
ELSE `default_update_mode`
|
||||
END"
|
||||
);
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"UPDATE " . sw_module_table('steam_workshop_game_profiles') . "
|
||||
SET `default_restart_behavior` = CASE
|
||||
WHEN `default_restart_behavior` IN ('if_empty','next_restart') THEN 'if_stopped'
|
||||
WHEN `default_restart_behavior` = 'immediate' THEN 'none'
|
||||
ELSE `default_restart_behavior`
|
||||
END"
|
||||
);
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"ALTER TABLE " . sw_module_table('steam_workshop_game_profiles') . "
|
||||
MODIFY `default_update_mode` ENUM('manual','on_restart','before_start') NOT NULL DEFAULT 'manual'"
|
||||
);
|
||||
$ok = $ok && (bool)$db->query(
|
||||
"ALTER TABLE " . sw_module_table('steam_workshop_game_profiles') . "
|
||||
MODIFY `default_restart_behavior` ENUM('none','if_stopped') NOT NULL DEFAULT 'none'"
|
||||
);
|
||||
|
||||
return $ok;
|
||||
},
|
||||
);
|
||||
|
||||
// ── Uninstall queries ─────────────────────────────────────────────────────
|
||||
$uninstall_queries = array(
|
||||
"DROP TABLE IF EXISTS `OGP_DB_PREFIXsteam_workshop_server_settings`",
|
||||
"DROP TABLE IF EXISTS `OGP_DB_PREFIXsteam_workshop_server_mods`",
|
||||
"DROP TABLE IF EXISTS `OGP_DB_PREFIXsteam_workshop_game_profiles`",
|
||||
"DROP TABLE IF EXISTS " . sw_module_table('steam_workshop_server_settings'),
|
||||
"DROP TABLE IF EXISTS " . sw_module_table('steam_workshop_server_mods'),
|
||||
"DROP TABLE IF EXISTS " . sw_module_table('steam_workshop_game_profiles'),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -15,15 +15,38 @@
|
|||
|
||||
$module_buttons = array();
|
||||
|
||||
if (!function_exists('sw_monitor_db_prefix')) {
|
||||
function sw_monitor_db_prefix()
|
||||
{
|
||||
if (defined('DB_PREFIX') && DB_PREFIX !== '') {
|
||||
return DB_PREFIX;
|
||||
}
|
||||
if (isset($GLOBALS['db_prefix']) && $GLOBALS['db_prefix'] !== '') {
|
||||
return $GLOBALS['db_prefix'];
|
||||
}
|
||||
if (isset($GLOBALS['table_prefix']) && $GLOBALS['table_prefix'] !== '') {
|
||||
return $GLOBALS['table_prefix'];
|
||||
}
|
||||
return 'gsp_';
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('sw_monitor_table')) {
|
||||
function sw_monitor_table($name)
|
||||
{
|
||||
return '`' . sw_monitor_db_prefix() . $name . '`';
|
||||
}
|
||||
}
|
||||
|
||||
// Only show the button when a Workshop profile is enabled for this game config.
|
||||
$_sw_profile = $db->resultQuery(
|
||||
"SELECT p.id
|
||||
FROM OGP_DB_PREFIXsteam_workshop_game_profiles p
|
||||
JOIN OGP_DB_PREFIXconfig_homes c ON c.game_key = p.config_name
|
||||
JOIN OGP_DB_PREFIXserver_homes s ON s.home_cfg_id = c.home_cfg_id
|
||||
"SELECT p.`id`
|
||||
FROM " . sw_monitor_table('steam_workshop_game_profiles') . " p
|
||||
JOIN " . sw_monitor_table('config_homes') . " c ON c.`game_key` = p.`config_name`
|
||||
JOIN " . sw_monitor_table('server_homes') . " s ON s.`home_cfg_id` = c.`home_cfg_id`
|
||||
WHERE s.home_id = " . (int)$server_home['home_id'] . "
|
||||
AND p.enabled = 1
|
||||
LIMIT 1"
|
||||
AND p.enabled = 1
|
||||
LIMIT 1"
|
||||
);
|
||||
|
||||
if (!empty($_sw_profile)) {
|
||||
|
|
|
|||
|
|
@ -42,8 +42,7 @@ function exec_ogp_module()
|
|||
// Find matching Workshop profile
|
||||
$profile = sw_get_profile_for_home($db, $home_id);
|
||||
if (!$profile) {
|
||||
echo '<p>Steam Workshop is not enabled for this game type (<strong>'
|
||||
. sw_h($home['game_name']) . '</strong>).</p>';
|
||||
echo '<p>Steam Workshop is not enabled for this game.</p>';
|
||||
echo '<p>An administrator must enable Workshop support for this game under '
|
||||
. '<em>Steam Workshop › Admin</em>.</p>';
|
||||
return;
|
||||
|
|
@ -293,7 +292,7 @@ function sw_user_queue_update($db, $home_id)
|
|||
SET `install_status` = 'queued', `updated_at` = NOW()
|
||||
WHERE `home_id` = $home_id AND `enabled` = 1"
|
||||
);
|
||||
sw_success('All enabled mods queued for update. Run the agent to process downloads.');
|
||||
sw_success('All enabled mods were queued. Updates are processed automatically by the server agent.');
|
||||
}
|
||||
|
||||
function sw_user_save_settings($db, $home_id)
|
||||
|
|
@ -301,9 +300,7 @@ function sw_user_save_settings($db, $home_id)
|
|||
$ok = sw_save_server_settings($db, $home_id, array(
|
||||
'update_mode' => $_POST['update_mode'] ?? 'manual',
|
||||
'restart_behavior' => $_POST['restart_behavior'] ?? 'none',
|
||||
'hot_load' => $_POST['hot_load'] ?? 'disabled',
|
||||
'warning_minutes' => $_POST['warning_minutes'] ?? 10,
|
||||
'schedule_interval' => $_POST['schedule_interval'] ?? 'daily',
|
||||
'schedule_interval' => $_POST['schedule_interval'] ?? 'disabled',
|
||||
));
|
||||
|
||||
if ($ok) {
|
||||
|
|
@ -321,352 +318,234 @@ function sw_user_render($db, $home_id, array $home, array $profile)
|
|||
{
|
||||
$mods = sw_get_server_mods($db, $home_id) ?: array();
|
||||
$settings = sw_get_server_settings($db, $home_id);
|
||||
|
||||
// Generate launch params from enabled mods
|
||||
$enabled_mods = array_filter($mods, function ($m) {
|
||||
return !empty($m['enabled']);
|
||||
});
|
||||
$params = sw_generate_launch_params(array_values($enabled_mods), $profile);
|
||||
$queuedCount = 0;
|
||||
$failedCount = 0;
|
||||
$installedCount = 0;
|
||||
$latestUpdateAt = '';
|
||||
$latestError = '';
|
||||
foreach ($mods as $mod) {
|
||||
if (($mod['install_status'] ?? '') === 'queued') {
|
||||
$queuedCount++;
|
||||
} elseif (($mod['install_status'] ?? '') === 'failed') {
|
||||
$failedCount++;
|
||||
} elseif (($mod['install_status'] ?? '') === 'installed') {
|
||||
$installedCount++;
|
||||
}
|
||||
if (!empty($mod['last_updated_at']) && $mod['last_updated_at'] > $latestUpdateAt) {
|
||||
$latestUpdateAt = $mod['last_updated_at'];
|
||||
}
|
||||
if (($mod['install_status'] ?? '') === 'failed' && !empty($mod['last_error']) && $latestError === '') {
|
||||
$latestError = $mod['last_error'];
|
||||
}
|
||||
}
|
||||
|
||||
$base_url = 'home.php?m=steam_workshop&p=user&home_id=' . $home_id;
|
||||
?>
|
||||
<style>
|
||||
.sw-user-panel{background:#161616;border:1px solid #2f2f2f;border-radius:6px;padding:14px;margin:10px 0;color:#ececec}
|
||||
.sw-user-panel h3{margin:0 0 10px 0;color:#fff}
|
||||
.sw-user-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}
|
||||
.sw-user-grid label{display:block}
|
||||
.sw-user-grid span{display:block;font-size:12px;color:#bebebe;margin-bottom:4px}
|
||||
.sw-user-grid input[type=text],.sw-user-grid select{width:100%;box-sizing:border-box;background:#0d0d0d;border:1px solid #3a3a3a;color:#f0f0f0;padding:7px;border-radius:4px}
|
||||
.sw-user-table-wrap{overflow-x:auto}
|
||||
.sw-user-table{width:100%;border-collapse:collapse;min-width:860px}
|
||||
.sw-user-table th,.sw-user-table td{border:1px solid #353535;padding:8px;vertical-align:middle}
|
||||
.sw-user-table th{background:#202020;text-align:left;color:#fff}
|
||||
.sw-status-ok{color:#7cdc7c;font-weight:700}
|
||||
.sw-status-queued{color:#ffca63;font-weight:700}
|
||||
.sw-status-failed{color:#ff8484;font-weight:700}
|
||||
.sw-status-progress{color:#8cc7ff;font-weight:700}
|
||||
.sw-muted{color:#b4b4b4}
|
||||
</style>
|
||||
|
||||
<p>
|
||||
<strong>Server:</strong> <?= sw_h($home['home_name']) ?>
|
||||
|
||||
<strong>Game:</strong> <?= sw_h($home['game_name']) ?>
|
||||
|
||||
<strong>Workshop Profile:</strong> <?= sw_h($profile['config_name']) ?>
|
||||
</p>
|
||||
|
||||
<!-- Add Mod form -->
|
||||
<h3>Add Workshop Mod</h3>
|
||||
<form method="post" action="<?= sw_h($base_url) ?>">
|
||||
<input type="hidden" name="action" value="add_mod">
|
||||
<table>
|
||||
<tr>
|
||||
<td style="padding:4px 8px;"><label for="workshop_id">Workshop ID</label></td>
|
||||
<td style="padding:4px 8px;">
|
||||
<input type="text" id="workshop_id" name="workshop_id" value=""
|
||||
placeholder="e.g. 2863534533" style="width:180px;" required>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 8px;"><label for="add_mod_name">Display Name (optional)</label></td>
|
||||
<td style="padding:4px 8px;">
|
||||
<input type="text" id="add_mod_name" name="mod_name" value=""
|
||||
placeholder="e.g. CF" style="width:180px;">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:4px 8px;"><label for="add_mod_type">Mod Type</label></td>
|
||||
<td style="padding:4px 8px;">
|
||||
<select id="add_mod_type" name="mod_type">
|
||||
<option value="client">Client mod (-mod=)</option>
|
||||
<option value="server">Server-side only (-serverMod=)</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td style="padding:4px 8px;">
|
||||
<button type="submit" class="button">Add Mod</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Mod list -->
|
||||
<h3>Installed Mods (<?= count($mods) ?>)</h3>
|
||||
|
||||
<?php if (empty($mods)): ?>
|
||||
<p>No mods added yet. Use the form above to add Workshop IDs.</p>
|
||||
<?php else: ?>
|
||||
<form method="post" action="<?= sw_h($base_url) ?>">
|
||||
<table width="100%" style="border-collapse:collapse;">
|
||||
<thead>
|
||||
<tr style="background:#f0f0f0;">
|
||||
<th style="padding:6px 8px;text-align:center;">#</th>
|
||||
<th style="padding:6px 8px;text-align:left;">Workshop ID</th>
|
||||
<th style="padding:6px 8px;text-align:left;">Mod Name</th>
|
||||
<th style="padding:6px 8px;text-align:left;">Folder Name</th>
|
||||
<th style="padding:6px 8px;text-align:center;">Type</th>
|
||||
<th style="padding:6px 8px;text-align:center;">Enabled</th>
|
||||
<th style="padding:6px 8px;text-align:center;">Status</th>
|
||||
<th style="padding:6px 8px;text-align:center;">Order</th>
|
||||
<th style="padding:6px 8px;text-align:center;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($mods as $idx => $mod): ?>
|
||||
<tr style="border-bottom:1px solid #ddd;<?= !$mod['enabled'] ? 'opacity:0.55;' : '' ?>">
|
||||
<td style="padding:6px 8px;text-align:center;"><?= $idx + 1 ?></td>
|
||||
|
||||
<td style="padding:6px 8px;font-family:monospace;"><?= sw_h($mod['workshop_id']) ?></td>
|
||||
|
||||
<!-- Inline edit: name -->
|
||||
<td style="padding:6px 8px;">
|
||||
<form method="post" action="<?= sw_h($base_url) ?>" style="display:inline;">
|
||||
<input type="hidden" name="action" value="save_mod">
|
||||
<input type="hidden" name="mod_id" value="<?= (int)$mod['id'] ?>">
|
||||
<input type="hidden" name="folder_name" value="<?= sw_h($mod['folder_name']) ?>">
|
||||
<input type="hidden" name="mod_type" value="<?= sw_h($mod['mod_type']) ?>">
|
||||
<input type="text" name="mod_name" value="<?= sw_h($mod['mod_name']) ?>"
|
||||
style="width:120px;" title="Click Save to apply">
|
||||
</td>
|
||||
|
||||
<!-- Inline edit: folder name -->
|
||||
<td style="padding:6px 8px;">
|
||||
<input type="text" name="folder_name" value="<?= sw_h($mod['folder_name']) ?>"
|
||||
style="width:140px;" title="Folder name inside server root">
|
||||
</td>
|
||||
|
||||
<!-- Inline edit: mod type -->
|
||||
<td style="padding:6px 8px;text-align:center;">
|
||||
<select name="mod_type" style="width:100px;">
|
||||
<option value="client" <?= $mod['mod_type'] === 'client' ? 'selected' : '' ?>>-mod=</option>
|
||||
<option value="server" <?= $mod['mod_type'] === 'server' ? 'selected' : '' ?>>-serverMod=</option>
|
||||
</select>
|
||||
<button type="submit" class="button small" title="Save changes">Save</button>
|
||||
</form>
|
||||
</td>
|
||||
|
||||
<!-- Toggle enabled -->
|
||||
<td style="padding:6px 8px;text-align:center;">
|
||||
<form method="post" action="<?= sw_h($base_url) ?>" style="display:inline;">
|
||||
<input type="hidden" name="action" value="toggle_mod">
|
||||
<input type="hidden" name="mod_id" value="<?= (int)$mod['id'] ?>">
|
||||
<button type="submit" class="button small"
|
||||
style="<?= $mod['enabled'] ? 'background:#5cb85c;color:#fff;' : '' ?>"
|
||||
title="<?= $mod['enabled'] ? 'Click to disable' : 'Click to enable' ?>">
|
||||
<?= $mod['enabled'] ? 'On' : 'Off' ?>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
|
||||
<!-- Install status -->
|
||||
<td style="padding:6px 8px;text-align:center;font-size:0.85em;">
|
||||
<?php
|
||||
$s = $mod['install_status'];
|
||||
if ($s === 'installed') {
|
||||
echo '<span style="color:green;">Installed</span>';
|
||||
} elseif ($s === 'queued') {
|
||||
echo '<span style="color:orange;">Queued</span>';
|
||||
} elseif ($s === 'failed') {
|
||||
echo '<span style="color:red;" title="' . sw_h($mod['last_error']) . '">Failed</span>';
|
||||
} elseif ($s === 'updating') {
|
||||
echo '<span style="color:blue;">Updating</span>';
|
||||
} else {
|
||||
echo '<span style="color:#999;">Not installed</span>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
|
||||
<!-- Order buttons -->
|
||||
<td style="padding:6px 8px;text-align:center;white-space:nowrap;">
|
||||
<form method="post" action="<?= sw_h($base_url) ?>" style="display:inline;">
|
||||
<input type="hidden" name="action" value="move_up">
|
||||
<input type="hidden" name="mod_id" value="<?= (int)$mod['id'] ?>">
|
||||
<button type="submit" class="button small" <?= $idx === 0 ? 'disabled' : '' ?>>▲</button>
|
||||
</form>
|
||||
<form method="post" action="<?= sw_h($base_url) ?>" style="display:inline;">
|
||||
<input type="hidden" name="action" value="move_down">
|
||||
<input type="hidden" name="mod_id" value="<?= (int)$mod['id'] ?>">
|
||||
<button type="submit" class="button small"
|
||||
<?= $idx === (count($mods) - 1) ? 'disabled' : '' ?>>▼</button>
|
||||
</form>
|
||||
</td>
|
||||
|
||||
<!-- Delete -->
|
||||
<td style="padding:6px 8px;text-align:center;">
|
||||
<form method="post" action="<?= sw_h($base_url) ?>" style="display:inline;">
|
||||
<input type="hidden" name="action" value="delete_mod">
|
||||
<input type="hidden" name="mod_id" value="<?= (int)$mod['id'] ?>">
|
||||
<button type="submit" class="button small danger"
|
||||
onclick="return confirm('Remove this mod from the list?');"
|
||||
style="background:#d9534f;color:#fff;">Remove</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Launch params display -->
|
||||
<h3>Generated Launch Parameters</h3>
|
||||
<p style="color:#666;font-size:0.9em;">
|
||||
Based on the enabled mods above (sorted by order). Copy these into your server startup command.
|
||||
</p>
|
||||
|
||||
<?php if (empty($enabled_mods)): ?>
|
||||
<p>No enabled mods – launch parameters will be empty.</p>
|
||||
<?php else: ?>
|
||||
<?php if ($params['mod']): ?>
|
||||
<p>
|
||||
<strong>Client mods (<code>-mod=</code>):</strong><br>
|
||||
<input type="text" value="<?= sw_h($params['mod']) ?>"
|
||||
readonly style="width:100%;font-family:monospace;"
|
||||
onclick="this.select();">
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php if ($params['servermod']): ?>
|
||||
<p>
|
||||
<strong>Server-side mods (<code>-serverMod=</code>):</strong><br>
|
||||
<input type="text" value="<?= sw_h($params['servermod']) ?>"
|
||||
readonly style="width:100%;font-family:monospace;"
|
||||
onclick="this.select();">
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<p>
|
||||
<strong>Combined:</strong><br>
|
||||
<input type="text" value="<?= sw_h($params['combined']) ?>"
|
||||
readonly style="width:100%;font-family:monospace;"
|
||||
onclick="this.select();">
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Install/Update -->
|
||||
<h3>Install / Update Mods</h3>
|
||||
<p>
|
||||
Clicking <strong>Queue Update</strong> marks all enabled mods as <em>queued</em>.
|
||||
Then run the agent <strong>on the game server host</strong> (where SteamCMD and the game files are located)
|
||||
to download and install the mods. Adjust the path to the panel's
|
||||
<code>modules/steam_workshop/agent_update_workshop.php</code> for your server:
|
||||
</p>
|
||||
<pre style="background:#222;color:#eee;padding:10px 14px;border-radius:4px;overflow-x:auto;"
|
||||
>php /path/to/panel/modules/steam_workshop/agent_update_workshop.php --home-id=<?= $home_id ?></pre>
|
||||
|
||||
<form method="post" action="<?= sw_h($base_url) ?>">
|
||||
<input type="hidden" name="action" value="queue_update">
|
||||
<button type="submit" class="button"
|
||||
onclick="return confirm('Queue all enabled mods for update?');">
|
||||
Queue Update for All Enabled Mods
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Workshop Behavior Settings -->
|
||||
<h3>Workshop Behavior Settings</h3>
|
||||
<p style="color:#888;font-size:0.9em;">
|
||||
Configure how Workshop mods are installed and updated for this server.
|
||||
All options default to the safest setting (manual only, no automatic restarts).
|
||||
</p>
|
||||
|
||||
<form method="post" action="<?= sw_h($base_url) ?>">
|
||||
<input type="hidden" name="action" value="save_settings">
|
||||
|
||||
<table style="border-collapse:collapse;width:100%;max-width:720px;">
|
||||
<colgroup>
|
||||
<col style="width:220px;">
|
||||
<col>
|
||||
<col style="width:340px;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr style="background:#f0f0f0;">
|
||||
<th style="padding:6px 8px;text-align:left;">Setting</th>
|
||||
<th style="padding:6px 8px;text-align:left;">Value</th>
|
||||
<th style="padding:6px 8px;text-align:left;">Help</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr style="border-bottom:1px solid #ddd;">
|
||||
<td style="padding:8px;font-weight:bold;">Install / Update Mode</td>
|
||||
<td style="padding:8px;">
|
||||
<select name="update_mode" style="width:100%;">
|
||||
<option value="manual" <?= ($settings['update_mode'] === 'manual') ? 'selected' : '' ?>>Manual only</option>
|
||||
<option value="on_restart" <?= ($settings['update_mode'] === 'on_restart') ? 'selected' : '' ?>>On next restart</option>
|
||||
<option value="before_start" <?= ($settings['update_mode'] === 'before_start') ? 'selected' : '' ?>>Before every server start</option>
|
||||
<option value="scheduled" <?= ($settings['update_mode'] === 'scheduled') ? 'selected' : '' ?>>Scheduled update check</option>
|
||||
</select>
|
||||
</td>
|
||||
<td style="padding:8px;font-size:0.85em;color:#555;">
|
||||
<strong>Manual only</strong> – mods are only updated when you click “Queue Update” above.<br>
|
||||
<strong>On next restart</strong> – queued updates are applied the next time the server restarts.<br>
|
||||
<strong>Before every start</strong> – the update check runs automatically each time the server starts.<br>
|
||||
<strong>Scheduled</strong> – the update check runs on the interval set below (requires cron / agent).
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr style="border-bottom:1px solid #ddd;">
|
||||
<td style="padding:8px;font-weight:bold;">Restart Behavior</td>
|
||||
<td style="padding:8px;">
|
||||
<select name="restart_behavior" style="width:100%;">
|
||||
<option value="none" <?= ($settings['restart_behavior'] === 'none') ? 'selected' : '' ?>>Do not restart automatically</option>
|
||||
<option value="if_empty" <?= ($settings['restart_behavior'] === 'if_empty') ? 'selected' : '' ?>>Restart if empty</option>
|
||||
<option value="immediate" <?= ($settings['restart_behavior'] === 'immediate') ? 'selected' : '' ?>>Restart after warning</option>
|
||||
<option value="next_restart" <?= ($settings['restart_behavior'] === 'next_restart') ? 'selected' : '' ?>>Install on next manual restart only</option>
|
||||
</select>
|
||||
</td>
|
||||
<td style="padding:8px;font-size:0.85em;color:#555;">
|
||||
Controls what happens when new mod updates are found.<br>
|
||||
<strong>Do not restart</strong> – updates are staged but the server keeps running (safe default).<br>
|
||||
<strong>If empty</strong> – the server is restarted only when there are zero players connected.<br>
|
||||
<strong>Immediate with warning</strong> – a countdown warning is broadcast, then the server restarts.<br>
|
||||
<strong>Next manual restart</strong> – updates are installed the next time you manually stop/start the server.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr style="border-bottom:1px solid #ddd;">
|
||||
<td style="padding:8px;font-weight:bold;">Hot-Load</td>
|
||||
<td style="padding:8px;">
|
||||
<select name="hot_load" style="width:100%;">
|
||||
<option value="disabled" <?= ($settings['hot_load'] === 'disabled') ? 'selected' : '' ?>>Disabled</option>
|
||||
<option value="attempt" <?= ($settings['hot_load'] === 'attempt') ? 'selected' : '' ?>>Attempt hot-load if game supports it</option>
|
||||
</select>
|
||||
</td>
|
||||
<td style="padding:8px;font-size:0.85em;color:#555;">
|
||||
<strong>Disabled</strong> – no hot-loading; mod changes take effect only after a server restart (safe default).<br>
|
||||
<strong>Attempt</strong> – if the game supports live mod reloading (e.g. via RCON), try to hot-load instead of restarting.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr style="border-bottom:1px solid #ddd;">
|
||||
<td style="padding:8px;font-weight:bold;">Warning Countdown</td>
|
||||
<td style="padding:8px;">
|
||||
<input type="number" name="warning_minutes" min="1" max="120"
|
||||
value="<?= (int)$settings['warning_minutes'] ?>"
|
||||
style="width:80px;"> minutes
|
||||
</td>
|
||||
<td style="padding:8px;font-size:0.85em;color:#555;">
|
||||
Minutes of advance warning broadcast to players before an automatic restart.<br>
|
||||
Only used when <em>Restart Behavior</em> is set to <strong>Restart immediately after warning</strong>.<br>
|
||||
Default: 10 minutes.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr style="border-bottom:1px solid #ddd;">
|
||||
<td style="padding:8px;font-weight:bold;">Scheduled Check Interval</td>
|
||||
<td style="padding:8px;">
|
||||
<select name="schedule_interval" style="width:100%;">
|
||||
<option value="hourly" <?= ($settings['schedule_interval'] === 'hourly') ? 'selected' : '' ?>>Hourly</option>
|
||||
<option value="daily" <?= ($settings['schedule_interval'] === 'daily') ? 'selected' : '' ?>>Daily (default)</option>
|
||||
<option value="weekly" <?= ($settings['schedule_interval'] === 'weekly') ? 'selected' : '' ?>>Weekly</option>
|
||||
</select>
|
||||
</td>
|
||||
<td style="padding:8px;font-size:0.85em;color:#555;">
|
||||
How often the scheduled update check runs.<br>
|
||||
Only used when <em>Install / Update Mode</em> is set to <strong>Scheduled update check</strong>.<br>
|
||||
Requires the Workshop agent to be running via cron on the game server host.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p style="margin-top:12px;">
|
||||
<button type="submit" class="button">Save Behavior Settings</button>
|
||||
<div class="sw-user-panel">
|
||||
<p>
|
||||
<strong>Server:</strong> <?= sw_h($home['home_name']) ?>
|
||||
<strong>Game:</strong> <?= sw_h($home['game_name']) ?>
|
||||
<strong>Workshop Profile:</strong> <?= sw_h($profile['config_name']) ?>
|
||||
</p>
|
||||
</form>
|
||||
<p class="sw-muted" style="margin-bottom:0;">
|
||||
Queue updates from this page. The server agent applies queued updates automatically.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="sw-user-panel">
|
||||
<h3>Add Workshop Mod</h3>
|
||||
<form method="post" action="<?= sw_h($base_url) ?>">
|
||||
<input type="hidden" name="action" value="add_mod">
|
||||
<div class="sw-user-grid">
|
||||
<label>
|
||||
<span>Workshop ID</span>
|
||||
<input type="text" id="workshop_id" name="workshop_id" placeholder="e.g. 2863534533" required>
|
||||
</label>
|
||||
<label>
|
||||
<span>Display Name (optional)</span>
|
||||
<input type="text" id="add_mod_name" name="mod_name" placeholder="e.g. CF">
|
||||
</label>
|
||||
<label>
|
||||
<span>Mod Type</span>
|
||||
<select id="add_mod_type" name="mod_type">
|
||||
<option value="client">Client mod</option>
|
||||
<option value="server">Server-side only</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<p style="margin:12px 0 0;">
|
||||
<button type="submit" class="button">Add Mod</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="sw-user-panel">
|
||||
<h3>Update Queue & Last Result</h3>
|
||||
<p>
|
||||
<strong>Enabled mods:</strong> <?= count(array_filter($mods, function ($m) { return !empty($m['enabled']); })) ?>
|
||||
<strong>Queued:</strong> <?= $queuedCount ?>
|
||||
<strong>Installed:</strong> <?= $installedCount ?>
|
||||
<strong>Failed:</strong> <?= $failedCount ?>
|
||||
</p>
|
||||
<p>
|
||||
<strong>Last update time:</strong> <?= $latestUpdateAt ? sw_h($latestUpdateAt) : 'Never' ?>
|
||||
</p>
|
||||
<?php if ($latestError !== ''): ?>
|
||||
<p><strong>Last error:</strong> <?= sw_h($latestError) ?></p>
|
||||
<?php endif; ?>
|
||||
<form method="post" action="<?= sw_h($base_url) ?>">
|
||||
<input type="hidden" name="action" value="queue_update">
|
||||
<button type="submit" class="button" onclick="return confirm('Queue all enabled mods for update?');">Queue Update for All Enabled Mods</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="sw-user-panel">
|
||||
<h3>Workshop Behavior Settings</h3>
|
||||
<form method="post" action="<?= sw_h($base_url) ?>">
|
||||
<input type="hidden" name="action" value="save_settings">
|
||||
<div class="sw-user-grid">
|
||||
<label>
|
||||
<span>Update Mode</span>
|
||||
<select name="update_mode">
|
||||
<option value="manual" <?= ($settings['update_mode'] === 'manual') ? 'selected' : '' ?>>Manual only</option>
|
||||
<option value="on_restart" <?= ($settings['update_mode'] === 'on_restart') ? 'selected' : '' ?>>On next restart</option>
|
||||
<option value="before_start" <?= ($settings['update_mode'] === 'before_start') ? 'selected' : '' ?>>Before server start</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Restart Behavior</span>
|
||||
<select name="restart_behavior">
|
||||
<option value="none" <?= ($settings['restart_behavior'] === 'none') ? 'selected' : '' ?>>Never restart automatically</option>
|
||||
<option value="if_stopped" <?= ($settings['restart_behavior'] === 'if_stopped') ? 'selected' : '' ?>>Restart only if server is stopped</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Scheduled Checks</span>
|
||||
<select name="schedule_interval">
|
||||
<option value="disabled" <?= ($settings['schedule_interval'] === 'disabled') ? 'selected' : '' ?>>Disabled</option>
|
||||
<option value="daily" <?= ($settings['schedule_interval'] === 'daily') ? 'selected' : '' ?>>Daily</option>
|
||||
<option value="weekly" <?= ($settings['schedule_interval'] === 'weekly') ? 'selected' : '' ?>>Weekly</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<p style="margin:12px 0 0;">
|
||||
<button type="submit" class="button">Save Behavior Settings</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="sw-user-panel">
|
||||
<h3>Installed Mods (<?= count($mods) ?>)</h3>
|
||||
<?php if (empty($mods)): ?>
|
||||
<p>No mods added yet. Use the form above to add Workshop IDs.</p>
|
||||
<?php else: ?>
|
||||
<div class="sw-user-table-wrap">
|
||||
<table class="sw-user-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Workshop ID</th>
|
||||
<th>Mod Name</th>
|
||||
<th>Folder Name</th>
|
||||
<th>Type</th>
|
||||
<th>Enabled</th>
|
||||
<th>Status</th>
|
||||
<th>Last Update</th>
|
||||
<th>Last Error</th>
|
||||
<th>Order</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($mods as $idx => $mod): ?>
|
||||
<tr style="<?= !$mod['enabled'] ? 'opacity:0.55;' : '' ?>">
|
||||
<td><?= $idx + 1 ?></td>
|
||||
<td style="font-family:monospace;"><?= sw_h($mod['workshop_id']) ?></td>
|
||||
<td>
|
||||
<form method="post" action="<?= sw_h($base_url) ?>" style="display:flex;gap:6px;align-items:center;flex-wrap:wrap;">
|
||||
<input type="hidden" name="action" value="save_mod">
|
||||
<input type="hidden" name="mod_id" value="<?= (int)$mod['id'] ?>">
|
||||
<input type="text" name="mod_name" value="<?= sw_h($mod['mod_name']) ?>" style="width:120px;">
|
||||
</td>
|
||||
<td><input type="text" name="folder_name" value="<?= sw_h($mod['folder_name']) ?>" style="width:120px;"></td>
|
||||
<td>
|
||||
<select name="mod_type" style="width:120px;">
|
||||
<option value="client" <?= $mod['mod_type'] === 'client' ? 'selected' : '' ?>>Client</option>
|
||||
<option value="server" <?= $mod['mod_type'] === 'server' ? 'selected' : '' ?>>Server</option>
|
||||
</select>
|
||||
<button type="submit" class="button small">Save</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="<?= sw_h($base_url) ?>" style="display:inline;">
|
||||
<input type="hidden" name="action" value="toggle_mod">
|
||||
<input type="hidden" name="mod_id" value="<?= (int)$mod['id'] ?>">
|
||||
<button type="submit" class="button small" style="<?= $mod['enabled'] ? 'background:#5cb85c;color:#fff;' : '' ?>"><?= $mod['enabled'] ? 'On' : 'Off' ?></button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
$s = $mod['install_status'];
|
||||
if ($s === 'installed') {
|
||||
echo '<span class="sw-status-ok">Installed</span>';
|
||||
} elseif ($s === 'queued') {
|
||||
echo '<span class="sw-status-queued">Queued</span>';
|
||||
} elseif ($s === 'failed') {
|
||||
echo '<span class="sw-status-failed">Failed</span>';
|
||||
} elseif ($s === 'updating') {
|
||||
echo '<span class="sw-status-progress">Updating</span>';
|
||||
} else {
|
||||
echo '<span class="sw-muted">Not installed</span>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td><?= !empty($mod['last_updated_at']) ? sw_h($mod['last_updated_at']) : '-' ?></td>
|
||||
<?php $shortError = !empty($mod['last_error']) ? (strlen($mod['last_error']) > 70 ? (substr($mod['last_error'], 0, 67) . '...') : $mod['last_error']) : ''; ?>
|
||||
<td title="<?= sw_h($mod['last_error'] ?? '') ?>"><?= $shortError !== '' ? sw_h($shortError) : '-' ?></td>
|
||||
<td style="white-space:nowrap;">
|
||||
<form method="post" action="<?= sw_h($base_url) ?>" style="display:inline;">
|
||||
<input type="hidden" name="action" value="move_up">
|
||||
<input type="hidden" name="mod_id" value="<?= (int)$mod['id'] ?>">
|
||||
<button type="submit" class="button small" <?= $idx === 0 ? 'disabled' : '' ?>>▲</button>
|
||||
</form>
|
||||
<form method="post" action="<?= sw_h($base_url) ?>" style="display:inline;">
|
||||
<input type="hidden" name="action" value="move_down">
|
||||
<input type="hidden" name="mod_id" value="<?= (int)$mod['id'] ?>">
|
||||
<button type="submit" class="button small" <?= $idx === (count($mods) - 1) ? 'disabled' : '' ?>>▼</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="<?= sw_h($base_url) ?>" style="display:inline;">
|
||||
<input type="hidden" name="action" value="delete_mod">
|
||||
<input type="hidden" name="mod_id" value="<?= (int)$mod['id'] ?>">
|
||||
<button type="submit" class="button small danger" onclick="return confirm('Remove this mod from the list?');" style="background:#d9534f;color:#fff;">Remove</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,18 @@
|
|||
*
|
||||
*/
|
||||
|
||||
function gsp_support_docs_url_for_game_key($game_key)
|
||||
{
|
||||
$game_key = trim((string)$game_key);
|
||||
if ($game_key !== '') {
|
||||
$docPath = __DIR__ . '/../billing/docs/' . $game_key . '/index.php';
|
||||
if (is_file($docPath)) {
|
||||
return '/docs.php?action=view&doc=' . rawurlencode($game_key);
|
||||
}
|
||||
}
|
||||
return '/docs.php';
|
||||
}
|
||||
|
||||
function exec_ogp_module() {
|
||||
|
||||
global $db, $settings;
|
||||
|
|
@ -81,6 +93,19 @@ if (!empty($webhook)) {
|
|||
} // end if submit
|
||||
echo '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" crossorigin="anonymous" referrerpolicy="no-referrer" />';
|
||||
echo "<h2>".get_lang('support')."</h2>";
|
||||
$defaultDocsUrl = '/docs.php';
|
||||
if (!empty($server_homes) && is_array($server_homes)) {
|
||||
foreach ((array)$server_homes as $server_home_row) {
|
||||
if (!empty($server_home_row['game_key'])) {
|
||||
$defaultDocsUrl = gsp_support_docs_url_for_game_key($server_home_row['game_key']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
echo "<div style='margin:0 auto 16px auto;max-width:600px;padding:10px 12px;border:1px solid #2d2d2d;border-radius:6px;background:#171717;'>"
|
||||
. "<a id='support-doc-link' href='" . htmlspecialchars($defaultDocsUrl, ENT_QUOTES) . "' target='_blank' rel='noopener noreferrer' style='color:#8cb9ff;text-decoration:none;font-weight:600;'>Game Documentation</a>"
|
||||
. "<span style='color:#a9a9a9;margin-left:8px;'>Open setup and troubleshooting docs in a new tab.</span>"
|
||||
. "</div>";
|
||||
echo '
|
||||
<div style="background:#5865F2;border-radius:8px;padding:14px 20px;margin:0 auto 20px auto;max-width:600px;display:flex;align-items:center;gap:16px;box-shadow:0 2px 8px rgba(0,0,0,0.18);">
|
||||
<i class="fa-brands fa-discord" style="font-size:2.4em;color:#fff;flex-shrink:0;"></i>
|
||||
|
|
@ -97,7 +122,8 @@ if (!empty($webhook)) {
|
|||
echo get_lang('select_server').":<br /><select name='gameserver' id='gameserver'>";
|
||||
foreach ((array)$server_homes as $server_home)
|
||||
{
|
||||
echo "<option value='".$server_home['home_name']."'>".$server_home['home_name']."</option>";
|
||||
$docUrl = gsp_support_docs_url_for_game_key($server_home['game_key'] ?? '');
|
||||
echo "<option value='".htmlspecialchars($server_home['home_name'], ENT_QUOTES)."' data-doc-url='".htmlspecialchars($docUrl, ENT_QUOTES)."'>".htmlspecialchars($server_home['home_name'], ENT_QUOTES)."</option>";
|
||||
}
|
||||
echo "</select><br /><br />";
|
||||
|
||||
|
|
@ -138,6 +164,15 @@ if (!empty($webhook)) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
$(document).ready(function(){
|
||||
function updateSupportDocLink(){
|
||||
var selected = $('#gameserver option:selected');
|
||||
var url = selected.data('doc-url') || '/docs.php';
|
||||
$('#support-doc-link').attr('href', url);
|
||||
}
|
||||
$('#gameserver').on('change', updateSupportDocLink);
|
||||
updateSupportDocLink();
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
} // End function
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue