Jump to content

Grimwar/Dedicated servers

From Vellocet Developer Community

Community dedicated servers keep configuration, add-ons, state, cache, replays, and secrets in an isolated writable data directory. The game installation remains read-only and can be replaced during an update.

Community operators configure this environment. Vellocet manages official servers separately.

Create the server data directory

Assign a different absolute data directory to each server process:

GrimwarServer --data-dir /srv/grimwar/community-1 --server-preflight-only

Preflight checks the runtime, creates missing operator files, and exits. A normal first launch creates the same layout. Existing operator files are not overwritten.

Use --instance-id <id> when the default per-user server location is suitable. GRIMWAR_DATA_DIR and GRIMWAR_INSTANCE_ID provide the corresponding environment settings. Never point two running processes at the same data directory.

Run cmdlhelp in the server console for the current command-line contract. Run cfg_paths for the resolved install, data, config, secret, add-on, replay, state, and cache paths.

Common launch options

The shared name, map, player-limit, and password contract is documented in Dedicated server launch settings. Use the traditional aliases on older Grimwar releases. The options below cover Grimwar-specific setup and those existing aliases.

Switches accept -, --, /, or +. A value may follow the switch or use =.

Option Purpose
--data-dir <path> Select an absolute writable data root.
--instance-id <id> Select a named data root below the default per-user location.
--map <map-id> Start on a registered map. This does not replace the saved rotation.
--mode <mode-id> Override the startup mode, or select the first rotation entry using that mode when no map is supplied.
--hostname <name> Override the browser name for this process.
--max-players <count> Override the total playable slots.
--steam-game-port <port> Change the internal Steam game-service UDP port.
--steam-query-port <port> Change the internal Steam query UDP port.
--server-preflight-only Validate configuration and create missing operator files, then exit before platform login.
--server-startup-smoke Initialize content, Workshop, security, and platform services, then exit before publication.
+<console-variable> <value> Apply a non-secret console-variable override after autoexec.cfg.

Prefer the secret files described below for passwords. Join-password launch options are supported, but users with sufficient machine access can inspect command lines. Keep platform credentials in the secret environment or deployment files.

Network transport and ports

Community servers use Steam Relay for gameplay and Steam public discovery. The stock server does not accept direct-IP gameplay joins, so do not expose or forward a gameplay UDP port. Install the current dedicated-server build through SteamCMD and allow outbound Steam and HTTPS traffic.

The internal Steam service defaults are 27015/UDP for the game service and 27016/UDP for queries. Change them only when another local process owns one of those ports. Keep the values distinct.

RCON is separate. Its default 27015/TCP does not conflict with 27015/UDP. Expose the RCON TCP port only to administrator addresses or a private network.

Official servers use managed direct UDP. A matchmaking deployment is visible only to its assigned players. A standalone official deployment appears in the browser with an Official badge, but its endpoint is withheld until the joining Steam identity is verified. Community builds cannot enable this contract with a name, tag, or config value.

Public discovery and stable references

The in-game browser reads community servers directly from Steam. It accepts only the current Grimwar app, secure dedicated-server listings, complete compatible metadata, and a Steam FakeIP. The FakeIP is a relay address rather than the host's origin address. The client does not make separate ping, rules, player, or A2S queries and always joins by the server's numeric SteamID through Steam Relay.

The server is discoverable after it logs Steam relay identity allocated for community discovery, Steam GameServer online, advertise=True, and Dedicated server ready; publication is open. No Supabase account, directory registration, public gameplay port, or IP disclosure is required. A compatible current client only needs to refresh the Internet tab.

A game-server login token is optional for discovery. Without one, Steam assigns an anonymous server account whose SteamID may change after a restart. For a durable console reference, create a Grimwar game-server login token and set it as the secret environment variable SERVER_PLATFORM_CREDENTIAL in the hosting panel. Steam then logs the process into a persistent game-server account. Never pass the token through -gslt, --gslt, a panel note, or another command-line value; those switches are unsupported and command lines and logs may expose their contents. Rotate a token immediately if it is disclosed.

Run server_id in the server console to print the connection SteamID. Players can keep this relay-safe reference in their notes and reconnect from the game console:

connect 90292054295284751

Use the value printed by that server, not the example above. Steam's desktop Favorites feature stores an address and ports; it is separate from a Grimwar SteamID note and should not be treated as the server's durable identity.

Operator files

The first normal or preflight launch creates the following files and directories below the data root:

Path Purpose
config/server.cfg Base non-secret settings.
config/server.d/ Split non-secret .cfg files.
config/rulesets/ Operator-defined rulesets.
config/modes/ Per-mode overrides named <mode-id>.cfg.
config/maps/ Per-map overrides named <map-id>.cfg.
config/autoexec.cfg Final non-secret overrides.
config/motd.html Optional message-of-the-day HTML fragment.
config/bots.cfg Optional bot display names, one per line.
config/banned_user.cfg Persistent SteamID bans.
config/rotation.json Ordered map/mode rotation or map blacklist.
config/server-addons.json Workshop items, manual bindings, and collections.
config/mods/ VMod Admin users, groups, capability grants, and other mod configuration.
secrets/server.secrets.cfg Sensitive settings such as the RCON password.
secrets/server.secrets.d/ Split sensitive .cfg files.

secrets/deployment.cfg is not an operator file. A hosting environment may generate it as the final, owner-restricted override layer.

Config precedence

The server applies configuration in this order. A later assignment to the same console variable wins.

Order Source
1 config/server.cfg
2 config/server.d/*.cfg, sorted by filename without regard to case
3 The ruleset selected by sv_ruleset
4 config/modes/<active-mode-id>.cfg
5 config/maps/<active-map-id>.cfg
6 config/autoexec.cfg
7 Command-line +variable value overrides
8 secrets/server.secrets.d/*.cfg, sorted by filename without regard to case
9 secrets/server.secrets.cfg
10 Generated secrets/deployment.cfg

Each non-comment line in a normal CFG file is submitted as a console command. Quote values that contain spaces:

hostname "Toronto Community Server"
bot_quota 4
mp_timelimit 30

Use numeric prefixes when the order inside server.d matters, such as 10-identity.cfg and 20-gameplay.cfg. Lines beginning with // or # are comments.

Sensitive identifiers containing terms such as password, secret, token, or apikey are ignored in non-secret files and command-line overrides. Secret layers suppress their command output.

Run authenticated cfg_reload to reapply the CFG stack and selected ruleset for the current map and mode. It does not synchronize Workshop items, reload manually staged packages, or load server-addons.json. Restart after changing those inputs. rotation.json is read by rotation selection rather than by cfg_reload.

Basic server settings

Uncomment or add only the values the server needs in config/server.cfg. For example:

hostname "Toronto Community Server"
sv_ruleset casual

bot_quota 4
bot_quota_mode 0
bot_difficulty 2

mp_timelimit 30
mp_roundtime 0
mp_winlimit 0
mp_maxrounds 0
sv_match_end_policy 1

0 lets the active mode supply its default where supported. sv_match_end_policy accepts 0 for a next-map vote, 1 for automatic travel to the next rotation entry, or 2 to quit after the match.

Use the console's variable help for the current value contract. The generated server.cfg also lists common bot, match, security, inventory, and movement settings.

Rulesets

Community and listen servers default to the bundled casual ruleset. Put a custom ruleset in config/rulesets/<name>.cfg:

// config/rulesets/community_plus.cfg
exec rulesets/casual.cfg
mp_allowspectators 0

Select it with sv_ruleset community_plus. A ruleset supports one console-variable assignment per line, exec rulesets/<name>.cfg inheritance, blank lines, and comments. It rejects arbitrary commands, nested paths, unknown or sensitive variables, include cycles, and invalid values.

During a live or overtime round, a console change to sv_ruleset applies at the next round boundary unless the command includes now.

Per-mode and per-map overrides

Name each override file with the canonical runtime ID. Map overrides run after mode overrides, so a map can refine a shared mode configuration:

// config/modes/arena.cfg
mp_roundtime 12
bot_quota 6
// config/maps/as_compound.cfg
mp_roundtime 10

When as_compound runs in arena, the round time is 10 because the map file is later in the stack. A map played in another mode still receives its map file. Put an override in autoexec.cfg only when it must win for every map and mode.

Message of the day

Enable the generated config/motd.html fragment from server.cfg:

sv_motd_enabled 1
sv_motd_title "Server Rules"
sv_motd_file "motd.html"

The file is shown after a player joins and before deployment. It may contain inline HTML, CSS, and data: images or fonts. Scripts, forms, frames, plugins, media, navigation, and network requests are blocked. The title is limited to 128 characters and the HTML payload to 64 KiB. File lookup stays inside the server config root and accepts only .html files.

Bot names and bans

Leave config/bots.cfg blank to use the built-in bot names. To replace them, add one display name per line:

Mara
"Copper Jack"
Ash

config/banned_user.cfg stores one SteamID64 per line. An optional second value is a Unix expiry time; 0 means permanent:

# permanent
76561198000000000 0

Authenticated console and RCON commands update the file:

banid <steamid|userid|name> [minutes] [kick]
removeid <steamid>

A missing or zero duration creates a permanent ban. Use a positive duration for a temporary ban.

Configure RCON

Put RCON settings in secrets/server.secrets.cfg:

rcon_enable 1
rcon_password "use-a-long-unique-password"
rcon_port 27015
rcon_maxclients 3
rcon_timeout 30
rcon_failures 3
rcon_ban_time 300
rcon_log 1

Restrict the secret directory at the operating-system level. Expose the RCON TCP port only to administrator addresses or a private network. Do not reuse the server password or a Steam credential as the RCON password.

Configure map and mode rotation

config/rotation.json stores map/mode pairs. listMode defaults to whitelist, which keeps the declared order:

{
  "listMode": "whitelist",
  "entries": [
    "as_compound",
    {
      "map": "bz_ordainment",
      "mode": "blitz"
    },
    {
      "map": "community_castle",
      "mode": "arena"
    },
    {
      "map": "community_castle",
      "mode": "koth"
    }
  ]
}

Use internal map IDs from the installed package manifests. A string is shorthand for an object with a map field and no mode field. The same map can appear more than once when each entry has a different mode. Duplicate map/mode pairs are removed.

How a mode is selected

For each whitelist entry, the server resolves the mode in this order:

  1. The entry's explicit mode value.
  2. The map entry's defaultModeId in its installed addon.json or built-in map data.
  3. sandbox when the map declares no default.

Map ID prefixes are not part of mode selection. A map named bz_example does not select blitz unless its map metadata declares that default or the rotation entry supplies "mode": "blitz".

An explicit mode must be built in or registered by an active VMod before the map starts. Otherwise the mode cannot be activated.

Blacklist rotation

Set listMode to blacklist to rotate every registered map except the listed defaults:

{
  "listMode": "blacklist",
  "entries": [
    "as_compound",
    "community_ruins"
  ]
}

Blacklist expansion runs after Workshop synchronization. Each registered map contributes one pair using its default mode, and the remaining maps are sorted by internal ID without regard to case. An empty blacklist rotates every registered map. Excluding every map stops startup with an error.

An object entry can exclude an exact map/default-mode pair, but a blacklist cannot assign an alternative mode or add a second pairing. Use a whitelist when a map must run with an override or in several modes.

Startup and match transitions

Workshop synchronization and add-on loading finish before the final startup map is selected. Missing or unavailable whitelist entries are skipped in file order. Startup fails when no loadable entry remains.

The launch options interact with the saved rotation as follows:

Launch input Startup result Next rotation result
No map or mode First loadable entry Following entry
--mode <mode> only First loadable entry with that mode; startup fails if none exists Following entry
--map <map> Requested registered map, even when it is outside the rotation First entry when the active map/mode pair is outside the rotation
--map <map> --mode <mode> Requested pair Following entry when that exact pair exists; otherwise the first entry

Use authenticated console or RCON for an immediate manual change:

changelevel <map-id> [mode-id]

Omitting the mode uses the map default, then sandbox. The saved rotation remains unchanged.

Install Workshop add-ons

config/server-addons.json controls Workshop acquisition. Store every Steam published-file or collection ID as a JSON string so the 64-bit value cannot lose precision.

Field Use it when
items Steam should download a direct Map or Content item for the server.
collections Steam should expand a collection and install its child items.
manualItems A complete Release package is already installed below <data-root>/addons and must be bound to its client-visible Workshop item.

A combined configuration looks like this:

{
  "schemaVersion": 1,
  "workshop": {
    "depotId": "",
    "items": [
      "1234567890"
    ],
    "manualItems": [
      {
        "publishedFileId": "2345678901",
        "addonId": "community_castle"
      }
    ],
    "collections": [
      "3456789012"
    ]
  }
}

Leave depotId empty unless Vellocet support directs you to change it. Grimwar supplies the dedicated-server depot.

Steam-managed items and collections

items contains direct Workshop published-file IDs. The server downloads and updates them through Steam under <data-root>/cache/steam-workshop-server.

collections contains Workshop collection IDs. The server resolves the collection and installs its children. A joining client acquires only the package for the active map and any session-wide requirements, not the full server collection.

For container deployments, --workshop-items or GRIMWAR_WORKSHOP_ITEMS adds direct item IDs to the file configuration:

GrimwarServer --workshop-items=1234567890,2345678901
GRIMWAR_WORKSHOP_ITEMS=1234567890,2345678901

Comma, semicolon, and whitespace separators are accepted. Collections remain in server-addons.json. --workshop-depot-id and GRIMWAR_WORKSHOP_DEPOT_ID are advanced depot overrides, not normal setup values.

Manually staged Workshop package

manualItems binds a local server package to a Workshop item that clients can download. Use it for a mixed package that contains a map or reusable audio plus a VMod, or when the operator manages server files outside Steam.

  1. Build or obtain the complete, unmodified Release package.
  2. Put the directory below <data-root>/addons. The package root itself must contain addon.json.
  3. Read the package id and buildId from addon.json.
  4. Add its Workshop published-file ID and manifest add-on ID to manualItems.
  5. Remove the same published-file ID from items.
  6. Restart the server and inspect [ServerWorkshop] logs.

Example layout:

/srv/grimwar/community-1/
  addons/
    community_castle/
      addon.json
      mod.json
      CommunityCastle.csx
      maps/
  config/
    server-addons.json

The local server package and client-visible Workshop package must have the same add-on ID and build ID. Map and VMod uploads include declared server source; map-bound snapshots remain inside the compiled scene. Clients do not execute it. Replace the staged server package every time the Workshop item is rebuilt.

One published-file ID may appear in items or manualItems, never both. Duplicate published-file IDs or add-on IDs in manualItems make the configuration invalid.

Clients do not download files from <data-root>/addons. They receive the sanitized Map or Content item from Steam and verify its published-file ID, add-on ID, and exact build ID. The current Workshop visibility must allow those clients to access it.

When every configured identity uses manualItems, the server does not request a Workshop download. A collection still requires Steam so the server can query its children.

See How Grimwar identifies maps and content for the manifest fields that distinguish a map from reusable content. Put every installed map ID used by the server in a whitelist rotation. Blacklist rotation considers installed maps automatically.

Install VMods

  1. Run vmod_runtime_paths and use the printed add-on directory.
  2. Copy the complete VMod add-on folder below <data-root>/addons.
  3. Restart, or run vmod_runtime_reload while no scripted mode is active.
  4. Confirm the package with vmod_runtime_list.

VMod-only packages are server-only and are installed manually. A mixed package can publish its map or audio to Workshop, but the server still needs the complete package. Admin users, groups, and external capability grants live below <data-root>/config/mods. See VMod scripting and administration.

Startup and diagnostics

Before opening the server publicly:

  1. Run preflight with the production data directory.
  2. Start the server and confirm it reaches its ready/listening state.
  3. Run status -v and verify the community role, steam-relay transport, build, protocol, map, mode, ruleset, data path, and player limit.
  4. Run cfg_paths and confirm the data, config, secret, and add-on roots.
  5. Confirm each Workshop or manual item loaded and every rotation map registered.
  6. Join with a separate Steam account and test authentication, required-content delivery, round transition, and map change.
  7. Test RCON from an allowed administrator address.

Useful log prefixes are:

Prefix Check
[ServerWorkshop] Server configuration, manual bindings, collection expansion, downloads, validation, and activation.
[AddonCatalog] / [AddonLoader] Package selection, validation, dependencies, and runtime loading.
[ServerRotation] Unknown entries, selected map/mode pairs, and next-map resolution.
[WorkshopDL] Exact client/server published-file, add-on, and build matching.
[SteamWorkshop] Steam installation and update state.
[ContentDL] Content provider selection and completion.
[Net] Pre-login requirements; set net_log 2 for per-add-on identities.

Do not add a public gameplay mapping when preflight or Steam startup fails. Fix the reported Steam login, credential, port conflict, package, or network error first.

Back up the data directory before game or add-on updates. It contains operator configuration, VMod state, source selections, bans, and other mutable server data. The game installation is not a substitute for that backup.