Search by

majesticdev / commandnet-s3-plugin

majestic44

S3 (Operations) tooling for Zeus/GM support, mission development, and server administration.

Package info

github.com/Spearhead-Gaming/commandnet-s3-plugin

Type:forumify-plugin

pkg:composer/majesticdev/commandnet-s3-plugin

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-09-23 14:09 UTC

This package is auto-updated.

Last update: 2026-09-23 14:20:29 UTC


README

A forumify plugin giving S3 (Operations) staff — Mission Developers, Server Admins, and Zeus/GMs — the tools they're currently missing. Sibling to commandnet-plugin, which already covers ops calendaring, rosters, promotions, LOAs, and awards; this plugin builds on it rather than duplicating it.

See Plugin Spec.md for the full scope, roadmap, and permissions matrix, and Forumify S3 Plugin — Feature Tracker.md for the brainstormed feature list the spec was drawn from.

Built for a specific MILSIM community's Forumify install; not a general-purpose skeleton.

Requirements

  • PHP 8.4 or newer
  • A Forumify 1.3.x install
  • commandnet-plugin — required. Briefings hang off its Operation, and the loadout check reads its Equipment, Position and Unit data.
  • MySQL
  • A writable var/ directory on the install: uploaded mission files are kept in var/s3-missions/
  • Optional: commandnet-discord-plugin for Discord announcements. Without it everything else works and the announcers simply aren't loaded.

Install

composer require majesticdev/commandnet-s3-plugin

Then, from the Forumify install:

bin/console forumify:plugins:refresh
bin/console forumify:plugins:activate majesticdev/commandnet-s3-plugin
bin/console doctrine:migrations:migrate

Status

All three phases of the spec are built.

Phase Module State
1 Briefing tool Built, except the map image upload
1 SOP / doctrine library Built
1 Audit log Built, S3 activity only (see below)
2 Zeus asset library Built, admin only
2 Loadout tracker Built
2 Discord announcements Built, off by default
3 Live mission notes / AAR quick-capture Built, the AAR draft is copy and paste
3 Mission upload & version repository Built, files in private local storage
3 Mission testing / feedback log Built
3 Server status / performance dashboard Built, name / map / mission / players / ping only
3 Mod / addon version tracker Built, versions entered by hand
Extra S3 dashboard and site menu item Built
Extra Operation information page Built, extra sections typed as one entry per line
Extra Mission mod lists and generated launcher preset Built, typed or from an uploaded launcher export

Nothing here has automated tests. It has been checked statically (container, Twig and syntax lint, schema drift) plus targeted runs of the riskiest logic: the server query against a local fake server, the mission file storage (name generation, refused extensions, path traversal), version comparison, the class-name parser, the operation page's text parser and template (rendered against hostile input under strict variable checking), and the mod list parser and preset generator, including against a real 70-mod Arma 3 Launcher export.

In a browser, two flows have been run by hand. The mission flow: creating a mission, uploading three versions (200 KB, 1.5 MB and 5 MB, with the stored sizes matching exactly) and downloading one. And the mod list flow, with mock data: creating a mission with the real launcher export attached, then checking the mission page, the generated download and the operation information page; editing the list by typing it (a DLC and a local mod included); the two error paths (a bad typed line, a file that isn't a preset); an operation with no mission falling back to its server's mods; and creating an Operation Page in the admin. The other pages have not been checked with real data, so do that with some test data before relying on them.

Modules

Briefings

A structured briefing per Command Net operation: mission name, task/purpose, ordered objectives (one per line) and a status of Draft, Ready or Delivered. It extends the free-form OPORD body Command Net's Operation already has.

  • Admin: Command Net S3 → Briefings.
  • Players: /operations/{id}/briefing. A player sees it only when the status is Ready or Delivered and they have an Attending or Maybe RSVP on the operation, since Command Net has no slot model. Staff with briefing.manage see any status. Everyone else gets a 404, so Drafts aren't leaked.
  • Command Net's operation page shows a "View briefing" button when this plugin is installed and the user may view briefings.

SOP / doctrine library

Documents with versions and a changelog per version. A document's newest version is its current one, and members acknowledge a specific version, so publishing a new one asks everyone to read it again.

  • Members: /sops lists documents with their acknowledgment status, and /sops/{id} shows the current version, the changelog history and an acknowledge button.
  • Admin: SOP Library and SOP Versions. The users icon on a document shows which enlisted members (anyone not Discharged or Retired) haven't acknowledged the current version, and who has.
  • Editing a version's text does not reset its acknowledgements; publish a new version for that.

Zeus asset library

A catalog of terrain, faction, vehicle and misc asset packs with a mod link and notes. Admin only: Zeus Assets. The table's search box also matches category, so typing "terrain" filters by tag.

Loadout tracker

Command Net already holds the standing rules (the weapons each Position may use, the vehicles each Unit fields, and Equipment class names), so this adds only the missing parts:

  • Kit Approvals: equipment approved for one specific operation.
  • Loadout Check: pick an operation, paste a kit list of Arma class names, and each is marked Approved, Not authorized, or Not in equipment list, with the reason. Nothing is saved. Matching is by class name, so equipment needs its class name filled in in Command Net.

Audit log

Briefings, SOP documents and versions, Zeus assets, kit approvals, game servers, server mods, missions, mission versions, mission feedback and operation pages implement Forumify's AuditableEntityInterface, so Forumify itself records who created, changed or removed them and which fields changed. That is also the mod update history: every change to a mod's installed version is logged with who and when. Audit Log is a read-only view of just those entries, so S3 leadership can review S3 activity without access to the site-wide log.

It does not record a reason, and kicks, bans and other game-server actions never touch the forum database, so they aren't logged. That would need an RCON integration, which isn't built. Acknowledgements and live notes are not audited.

Discord announcements

Built on the Discord plugin's BotService::postAnnouncement(), which posts through its bot to each server's announcements channel. The spec's webhook design is superseded by that: the bot already exists, and role and rank changes already reach Discord through its role mappings. This plugin adds two announcements, each behind a toggle that is off by default under Discord Announcements:

  • A briefing being marked Ready.
  • A new SOP version being published.

A failed bot call is logged and never blocks saving, and @ is neutralized in staff-entered text so a title can't ping a whole server.

Missions and playtest feedback

Missions with uploaded, versioned files, so the exact build used in an operation stays retrievable.

  • Admin: Missions. A mission can be tied to an operation. Each version is a .pbo, .vt or .zip (up to 256 MB, and also capped by your server's PHP upload limits) with a label and notes, and stays downloadable.
  • Files are stored under var/s3-missions/ with generated names, outside the web root, and served only through a permission-checked download. Nothing a user types becomes a path. MissionStorage is the only class that touches the disk, so moving to S3 or another Flysystem storage is a swap of that one class.
  • Each version has an unguessable feedback link that a Mission Dev copies and shares. Any member with mission.feedback can report a bug, balance issue or general feedback through it. Missions are deliberately not listed for members, so upcoming ones can't be browsed. The owner or a lead can mark feedback resolved or reopen it.
  • Ownership: any Mission Dev with mission.manage can create a mission and becomes its owner. Only the owner, or someone with mission.manage_all (leads), can upload versions or resolve feedback. Editing or deleting the mission record itself is leads only.
  • Mod list: every mission has one list of mods and DLC, set two ways. Type it, one line per mod as Name | Workshop link or ID (start a DLC line with DLC:; a line with only a name is a local mod that players can't download), or upload the preset exported from the Arma 3 Launcher (Mods, Preset, Export), whose mods and DLC are read out of the HTML. Saving replaces the list, on the mission form or on Edit mod list (open to the owner and leads), where the text box starts from the stored list. A bad line, or a file with no mods in it, is a form error and leaves the stored list alone.
  • Generated download: players get a launcher preset built fresh from the list, from the mission page and the operation page. The uploaded file is only read for names and Steam ids, with external entities and network access off; it is never stored or served back, so an uploaded page can't be redistributed. Limits: 2 MB and 500 mods.

Live notes

Live Notes lets a GM pick an operation and jot timestamped notes during it. Notes can only be added, so the record stays honest. Afterwards the page builds an after-action report draft from them, to copy into Command Net's AAR form. It is not a real prefill of that form, which would mean changing commandnet-plugin.

Server status and mod tracker

  • Game Servers: a host and Steam query port per server.
  • Server Status: asks each server live, over the Steam query protocol (A2S_INFO), whether it is answering, its name, map, mission, player count and ping, plus how many of its tracked mods are out of date. It cannot show uptime or the loaded mod list, and it isn't RCON. Servers are queried one after another with a short timeout, so many offline servers make the page slow.
  • Server Mods: the version installed on each server against the version in the client modpack, flagged Out of date when the server is behind. Versions are entered by hand and compared with version_compare, so 1.2.0 is correctly behind 1.10.0.

Admin menu

The Command Net S3 admin menu is grouped the way the spec groups the modules, with a Dashboard link at the top:

  • Zeus / GM: Briefings, Zeus Assets, Operation Pages, Live Notes.
  • Mission Development: SOP Library, SOP Versions, Missions, Loadout Check, Kit Approvals.
  • Server Administration: Server Status, Game Servers, Server Mods, Discord Announcements, Audit Log.

A category only appears when the user can see something in it, because Forumify's own menu filter would otherwise leave an emptied category behind as a blank flyout.

S3 dashboard and the site menu

/s3 is a home page for S3 staff, gated by dashboard.view. Each section appears, and runs its queries, only when the viewer has the permission for what it shows: upcoming operations with their briefing state, your SOP documents awaiting acknowledgment, open playtest feedback, out-of-date mods, recent S3 audit activity, and a list of tools.

For Forumify's site Menu Builder there is an S3 item type. Tick which pages it covers (Dashboard, Current Operation, SOP Library): one page renders as a plain link, several as a dropdown, and pages the viewer can't access are left out. Use the item's own permissions in the Menu Builder to limit it to S3 staff roles. Nothing is added to the site menu by default.

Operation information page

A live page for an operation, ported from the community's static operations-page design.

  • /operations/current shows the operation in progress, otherwise the next scheduled one (or says there isn't one). /operations/{id}/info shows any operation. Needs operation_info.view.
  • Read live on every view, so it never goes stale: title, status, dates, location, unit, RSVP counts, the OPORD body, the briefing's mission and objectives, the countdown, and the chosen server's online state, player count and map.
  • The mod list is the one on the mission tied to the operation (the newest, if several), and its "Download Mod-Pack" button gives the generated launcher preset. With no mission list, it falls back to the mods tracked on the chosen server, shown with their versions. DLC and local mods are marked, and the Version column appears only when a row has one. A preset link typed on the Operation Page overrides the generated download.
  • Stored on an Operation Page, edited under Zeus / GM → Operation Pages: order number, one-line summary, server, preset and Steam collection links, and text boxes for the timeline, task organization, mission data, comms plan, ROE, pre-op checklist and quick links. Each is one entry per line with columns split by |, for example 1900 | Platoon Briefing | Full OPORD brief, and the form shows an example for each.
  • Until an Operation Page is Published, only staff (operation_page.manage) see its sections; everyone else still gets the operation's own details. A draft briefing is hidden from non-staff too.
  • All text is escaped, and a link must start with http(s):// or /, so a typed javascript: link is dropped.

Permissions

Checked as command-net-s3.<area>.<action> (the prefix is slugged from the plugin's display name, "Command Net S3"), declared in CommandNetS3Plugin::getPermissions().

Permission Grants
admin.briefing.view / .manage See / create, edit and delete briefings; staff can view Drafts on the player page
admin.sop.view / .manage See / manage SOP documents and versions; view acknowledgement status
admin.zeus_asset.view / .manage See / manage the Zeus asset library
admin.loadout.view / .manage Use the Loadout Check and see / manage kit approvals
admin.audit_log.view The S3 audit log view
admin.discord.manage Turn Discord announcements on and off
admin.server.view / .manage See / manage game servers and mods; view Server Status
admin.mission.view See missions, versions and feedback; download files
admin.mission.manage Create missions; upload versions and resolve feedback on missions you own
admin.mission.manage_all Leads: edit or delete any mission, upload to or resolve on any
admin.live_notes.view / .manage See / add live mission notes
admin.operation_page.view / .manage See / edit Operation Pages; manage also previews unpublished ones
briefing.view The player-facing briefing page
sop.view / sop.acknowledge Read the SOP library / acknowledge a version
mission.feedback Submit playtest feedback through a shared feedback link
dashboard.view Open the S3 dashboard
operation_info.view Open the operation information page

Tables

s3_briefing, s3_sop, s3_sop_version, s3_sop_acknowledgement, s3_zeus_asset, s3_mission_kit_approval, s3_game_server, s3_server_mod, s3_mission, s3_mission_version, s3_mission_feedback, s3_mission_note, s3_operation_page, s3_mission_mod. The audit log uses Forumify's own audit_log table, and Discord settings are stored under the command_net_s3.discord setting. Uploaded mission files are on disk in var/s3-missions/, not in the database.

Known gaps

  • The generated preset is unverified in the real launcher. It follows the format of a real export and reads back to the same list, but nobody has confirmed the Arma 3 Launcher imports it. Try importing a downloaded preset before relying on it.
  • The mod list belongs to the mission, not to one version, and changes to it are not audited (the list is replaced as a whole). Mods have no per-mod version there; versions only appear on the server-mod fallback.
  • Times on the operation page follow the site's timezone, but the timeline and other typed sections are literal text. Type the timeline in the timezone the page shows.
  • Mostly not exercised in a browser. Only the mission flow and the mod list flow above have been run by hand. Not checked with real data: submitting and resolving playtest feedback, the browser-side refusal of a disallowed upload (the storage layer's refusals are tested), the non-owner permission checks, Live Notes, Server Status, briefings, the SOP library, the dashboard, the categorized admin menu, the Menu Builder "S3" item type, the operation page as a normal member without staff rights, and the rest. The loadout check's Approved path hasn't been tried against real equipment, and no Discord message has been sent.
  • Briefing map image upload from the spec isn't built.
  • "Slotted" means an Attending or Maybe RSVP. Change BriefingController::isSlotted() if you gain a real slotting concept.
  • Nothing is in the site menu until an admin adds it. Use the "S3" item type in Forumify's Menu Builder for the dashboard, the current operation and the SOP library.
  • The Zeus library has no player- or GM-facing page, so Zeus/GM staff need admin-panel access plus the view permission.
  • The audit log view lists its entities explicitly in S3AuditLogTable::AUDITED_ENTITIES; add new auditable entities there.
  • Promotions aren't announced on Discord. That belongs to personnel management, outside this plugin's scope in the spec.
  • No automated tests.
  • Deleting a mission leaves its files on disk. The records go, the files in var/s3-missions/ stay, which also means an accidental delete doesn't lose a build.
  • Mission uploads are limited by PHP (upload_max_filesize and post_max_size), which may be lower than the 256 MB the form allows.
  • Back up var/s3-missions/ along with the database, since mission files aren't in it.
  • The server query is basic: no uptime, no loaded mod list, not RCON, and servers are queried sequentially.
  • Mod versions are entered by hand; nothing reads them from the server.
  • The AAR draft is copy and paste, not a prefill of Command Net's AAR form.
  • Live notes have no "assigned GM" per operation, because Command Net doesn't model one. Anyone with live_notes.manage can add notes to any operation.
  • On the operation page the OPORD is one rich-text block, not five collapsible paragraphs, because Command Net stores it as a single body. Mods have no Required / Optional / Client tag, so the design's filter chips are gone (the search box remains).
  • The operation page queries the game server on every view, which adds a moment when it is down. Cache the result briefly if the page gets busy.
  • The operation page design imports its fonts from Google Fonts, so viewers' browsers make that request. Remove the @import in _page.html.twig to avoid it.

Development

make quality       # phpcs + phpstan (needs composer install)
make quality-fix   # phpcbf

The dev server config in .claude/launch.json starts PHP with 4 workers and 300 MB upload limits. Plain php -S is single-threaded, and in dev mode a page takes seconds, so its own health checks can starve it; PHP's default 2 MB upload limit also rejects real missions.

Migrations are generated with doctrine:migrations:diff --namespace=CommandNetS3PluginMigrations. If several Claude or dev sessions share one Forumify install, generate and run migrations from one place: diff and migrate pick up every plugin's uncommitted entities.