# Workshop System Workspace reference: [`GSP-WORKSPACE.md`](../../../GSP-WORKSPACE.md) ## Current State The current Workshop/content work is split across two module lines: - `Panel/modules/steam_workshop` - deprecated compatibility layer - `Panel/modules/addonsmanager` - the active Server Content Manager path Important files: - `Panel/modules/addonsmanager/module.php` - `Panel/modules/addonsmanager/user_addons.php` - `Panel/modules/addonsmanager/addons_manager.php` - `Panel/modules/addonsmanager/workshop_content.php` - `Panel/modules/addonsmanager/workshop_action.php` - `Panel/modules/addonsmanager/scripts/workshop/generic_steam_workshop_linux.sh` - `Panel/modules/addonsmanager/scripts/workshop/generic_steam_workshop_windows_cygwin.sh` - `Panel/modules/steam_workshop/module.php` - `Panel/modules/steam_workshop/agent_update_workshop.php` ## Current Implemented Behavior Workshop is Panel-side orchestration. The active workflow lives in Server Content Manager and uses existing agent primitives only: - Panel writes manifests with validated numeric Workshop IDs. - Panel generates a per-job shell script under the server home. - The generated job script writes a temporary SteamCMD runscript and runs `steamcmd +runscript `. - Panel invokes the job script through the existing authenticated agent `exec` RPC. - Agents do not need new Workshop-specific business logic. - Legacy agent RPCs from the old `steam_workshop` module remain compatibility-only and are not the primary path. The active user workflow is now `addonsmanager` -> `workshop_content`. Users can enter either: - a numeric Workshop item ID - a full Steam Workshop URL containing `id=` The Panel extracts and stores only numeric Workshop IDs. Invalid text is rejected before any manifest or shell command is built. The Panel writes a manifest under the server home: - `{SERVER_HOME}/gsp_server_content/workshop_manifest.json` The Panel writes a generated per-job script to: - `{SERVER_HOME}/gsp_server_content/jobs/workshop/workshop_job__.sh` The agent executes the generated script with the manifest path by using the existing generic command execution path. Customers do not need to place scripts manually on the agent. Script/job rules: 1. Server Content Manager always generates the primary Workshop job script per action. 2. Old XML/static script settings are logged as deprecated and ignored by the primary path. 3. The default script filename must never be treated as a pre-existing agent path. 4. The agent does not require `generic_steam_workshop_linux.sh` or `generic_steam_workshop_windows_cygwin.sh` to exist on disk. The generated job script uses this SteamCMD runscript pattern: ```text @ShutdownOnFailedCommand 0 @NoPromptForPassword 1 force_install_dir login anonymous workshop_download_item validate quit ``` The manifest includes: - `home_id` - server/game path - `workshop_app_id` - Workshop item IDs - per-item target paths - install strategy - key-copy settings - content template metadata Default install paths: - Generic Workshop installs default to `{SERVER_ROOT}/workshop/{MOD_FOLDER}`. - DayZ/Arma-style installs default to `dayz_mod_folder` or `arma_mod_folder` based on the game key/name/config file. Those strategies install to `{SERVER_ROOT}/{MOD_FOLDER}` so `@` folders remain compatible with existing `-mod=` workflows. - DayZ/Arma key-copy behavior copies `.bikey` files into the server `keys` folder when found. Missing key files are logged but do not fail the install. App ID rules: - `workshop_app_id` must come from a Server Content template, Steam Workshop profile, or game XML. - Game XML should declare Workshop support in the canonical `workshop_support` block. - Do not silently use the dedicated server Steam app ID as the Workshop app ID unless a legacy profile explicitly does so. - Arma 3 XML declares Workshop app ID `107410`; its dedicated server Steam app ID remains `233780`. Canonical XML: ```xml 1 steam 107410 107410 steamcmd arma_mod_folder {SERVER_ROOT}/{MOD_FOLDER} -mod={MOD_LIST} ; @ {MOD_PATH}/keys/*.bikey {SERVER_ROOT}/keys ``` The Panel helper parser reads `workshop_support` first. Older direct tags are tolerated only as a compatibility fallback in helper code; they are not the canonical XML format. ## Database State `server_content_workshop` tracks: - `content_id` - `home_id` - `workshop_app_id` - `workshop_item_id` - `title` - `install_path` - `install_strategy` - `enabled` - `load_order` - `update_policy` - `pending_action` - `install_state` - `last_installed_at` - `last_updated_at` - `last_error` Current install states used by Phase 1: - `queued` - `installing` - `installed` - `downloaded` - `failed` - `removed` `server_content_workshop_catalog` tracks known/common Workshop items seen through Server Content Manager: - `workshop_id` - `app_id` - `title` - `install_count` - `first_seen` - `last_installed` - `last_updated` - `published_date` - `tags` - `game_key` - `local_cache_path` The catalog is Panel-side and does not require Steam Web API metadata. Metadata can be added later. ## What Exists Today The current direction already supports: - content records in the Panel database - Workshop item IDs - installation metadata - install history tables - game compatibility fields - launch parameter additions - post-install behavior fields ## Main Limitations - Workshop metadata is still incomplete. - Load order is tracked but not yet a full drag-and-drop or startup-param UX concept. - Enable/disable is exposed and stored but does not yet regenerate startup parameters. - update/remove are synchronous and should become background jobs. - caching and cleanup policy need product-level design, not just ad hoc scripts. - `-mod=` / `-serverMod=` generation still needs a safe structured implementation. ## Scheduler Integration Workshop updates use the existing `cron` / Scheduler system. No second Workshop scheduler should be created. Supported scheduler action keys: - `workshop_update` - `workshop_update_and_restart` - `workshop_download_only` - `workshop_install_pending_on_restart` Compatibility Server Content keys remain available: - `server_content_check_workshop_updates` - `server_content_update_workshop` - `server_content_install_updates_next_restart` - `server_content_install_updates_and_restart` Per-item update policy values stored on `server_content_workshop.update_policy`: - `manual` - `scheduled` - `update_now` - `update_and_restart` - `download_only` - `install_on_restart` ## Troubleshooting | Symptom | Meaning | Fix | |---|---|---| | `Configured workshop script not found on agent host: generic_steam_workshop_windows_cygwin.sh` | Old Panel logic treated the default script filename as an agent path. | Update the Panel. Current logic generates a per-job script under `gsp_server_content/jobs/workshop/`. | | `SteamCMD is missing on the agent host.` | The handler could not find SteamCMD at the configured path, `STEAMCMD_PATH`, or common locations. | Install SteamCMD on the agent and/or set the SteamCMD path in the Workshop profile/template. | | `Workshop App ID is missing` | No template/profile/XML provided an app ID. | Add `workshop_app_id` to the Server Content template or game XML. | | Download succeeds but mod does not load | Startup parameters are not yet regenerated from installed Workshop rows. | Manually add the installed `@...` folders to the game startup params until Phase 2 startup integration is complete. | ## Recommended Mental Model Use `addonsmanager` as the main future home for: - mods - add-ons - Workshop items - scripts - config packs - server content manifests - install history Treat `steam_workshop` as a legacy bridge for migration only. ## References Reviewed - `reference/Module-Steam_Workshop` is the local legacy OGP module. It confirms that the Panel historically owned Workshop state/configuration and used the agent for execution. - The uploaded `steam-workshop-downloader` reference was not present in this workspace, but its reviewed behavior is reflected in the active design: generate a SteamCMD runscript, call `workshop_download_item validate`, copy or link the downloaded `steamapps/workshop/content//` folder into the server's mod location, optionally lowercase files for Linux, copy Arma/DayZ keys, and generate future `-mod=` data from installed items. ## Panel-Agent Contract Phase 1 does not use the legacy `steam_workshop` XML-RPC method for the primary user workflow. Instead: 1. Panel parses customer input into numeric Workshop IDs. 2. Panel writes `{SERVER_HOME}/gsp_server_content/workshop_manifest.json`. 3. Panel writes a generated per-job script under `{SERVER_HOME}/gsp_server_content/jobs/workshop/`. 4. The job script writes a temporary SteamCMD runscript. 5. Panel invokes the job script through the existing authenticated agent `exec` RPC. 6. The job writes `workshop_install.log` or `workshop_install_windows.log` under `gsp_server_content`. 7. Panel updates `server_content_workshop.install_state` from queued/installing to installed/failed/removed. Important manifest fields: - `home_id` - `home_cfg_id` - `game_path` - `server_path` - `workshop_app_id` - `steam_app_id` - `items` - `item_details` - `install_strategy` - `target_path` - `extra.copy_keys` - `extra.keys_target_path` Generated Workshop jobs validate numeric item IDs, keep writes under the server home, use SteamCMD through a temporary runscript, copy files into the resolved target path, and copy `.bikey` files for DayZ/Arma strategies when enabled. Bundled handler actions: - `install` - download with SteamCMD, copy/install into target path. - `update` - validate/download with SteamCMD, copy/install into target path. - `check_updates` - validate/download only; does not alter live mod folders. - `download_only` - download/cache only and leave install pending. - `validate_files` - SteamCMD validate/download only. - `remove` - move the installed target folder into `gsp_server_content/workshop/removed/`; this does not require SteamCMD.