Configuration

How ATG mods read their settings, and how to override any of them from a single JSON file on your server.

The three layers

Every ATG mod that supports this system builds its settings from three layers, each overriding the one before.

Layer 1 — defaults. Compiled into the mod. You get these if you change nothing.

Layer 2 — an override in your own mod. The ordinary Arma way: you cannot edit a mod’s own config file, because it arrives from the Workshop read-only and an update would overwrite you, so you make a mod of your own and override the file in it. This needs the Workbench tools.

Layer 3 — a JSON file on your server. What ATG Core adds. No tools, no mod, no rebuild.

Layer 3 is what the rest of this page is about.

The file

One file per mod, in your server’s profile directory:

<profile>/ATG/<mod-id>.json

The mod id is fixed by the mod — atg-scenario.json, atg-garmin-vitals.json. Each mod’s own configuration page names it.

A few things worth knowing before you write one:

  • An absent file is normal and means every setting keeps its layer-1/2 value. Core writes one only when a mod offers its settings in game and somebody saves from there — see Changing settings on a running server.
  • The file is read at startup, and again whenever it is reloaded. Editing it on a running server takes effect as soon as an administrator asks for it, with no restart.
  • It is read on the server only, so you do not distribute anything to your players and they cannot override it locally. Most of the resulting values are then sent on to every client; some are deliberately kept on the server — see What a client is sent.
  • Booleans are true and false, not 1 and 0. A 1 is ignored and the setting keeps the value it already had.

Confirm it was picked up: the server log carries

[ATG-Core-Config] Applied JSON overrides for 'atg-scenario' from $profile:ATG/atg-scenario.json

Two warnings can appear instead, and both mean nothing was applied:

[ATG-Core-Config] $profile:ATG/atg-scenario.json could not be parsed as JSON, so none of it was applied.
[ATG-Core-Config] $profile:ATG/atg-scenario.json was read but changed nothing.

The first is a syntax error — a trailing comma or an unquoted key. The second means the file is valid but no value in it took effect, most often a boolean written as 1 rather than true. It also appears, harmlessly, when the file just repeats the settings the server already had.

No such line means the file was not found or not parsed, and the mod is running on its built-in settings.

Single values are applied key by key

Name only the settings you want to change. Everything you leave out keeps the value it already had, so a file that changes one number stays one line long and survives mod updates that change the rest.

{
  "m_fUpkeepPerHour": 40
}

Lists are replaced whole

Some settings are lists rather than single values — a mod’s set of objective types, its tier ladder, its garrison profiles. These behave differently, and it is the one rule on this page that catches people out.

Naming a list in the JSON replaces it entirely. There is no way to change one entry, or to add an entry, and leave the rest alone. A list you touch must be written out in full, including the entries you were happy with.

That is deliberate. Changing one entry in place would mean every entry needing a permanent identity, and you addressing them by index or by id in a text file where nothing checks your arithmetic. The result is harder to get right than restating the list, and much harder to see when it goes wrong. Wholesale replacement is blunt, but what you wrote is what you get.

A list you never name is left exactly as the mod’s own config set it, the same as any other setting.

Every entry needs a $type

An entry in a list carries a $type naming its class. This is what lets one list hold entries of different kinds — an objective list mixing Destroy, Kill and Clear area objectives is three different classes in one array:

{
  "m_aObjectiveTemplates": [
    {
      "$type": "ATG_Scenario_DestroyObjective",
      "m_sName": "Destroy",
      "m_fReward": 100
    },
    {
      "$type": "ATG_Scenario_ClearAreaObjective",
      "m_sName": "Clear area",
      "m_fReward": 150
    }
  ]
}

Write it on every entry, including where they are all the same kind.

Getting a $type wrong is silent. A misspelled or missing one produces an entry that loads without complaint and then does nothing at all — an objective that never comes up, a tier that is never reached. Nothing appears in the log, because as far as the game is concerned you asked for an empty thing and got one.

Two habits make that survivable:

  • Start from the mod’s complete example, on its own configuration page, rather than writing a list from memory. That is what those examples are for.
  • Change one list at a time and confirm the server behaves as though it took, before moving on to the next.

What a client is sent

Most settings have to reach every client, because the server and the player’s own game both read them and have to arrive at the same answer. Those are sent as the player joins, and again whenever the file is reloaded.

Some are never sent. A mod marks a setting as one no client reads, and the server then keeps it to itself. There is nothing for you to do about that — you set it the same way in the same file — but it is worth knowing for two reasons:

  • A joining player’s connection is lighter. Settings withheld are bytes that never compete with the world their game is already being sent.
  • The value stays off the player’s machine. Anything a player could act on if they knew it — the threshold some automatic response fires at, say — is readable by anyone who receives it, whatever is or is not shown on screen. Withholding it is the whole of what keeps it yours.

ATG Core’s own settings are all of that kind, so nothing in atg-core.json reaches a client.

It arrives a piece at a time

What a client does get is sent in pieces rather than in one burst. The server sends a piece, waits for that player’s game to say it has been applied, and only then sends the next. Every player is paced separately, at whatever speed they can manage, so a player whose connection is struggling is given less rather than dropped. The sizes are yours to set — see Pacing what clients are sent.

A joining player’s Deploy button waits for the settings that decide how they spawn, with the reason written on the button, and goes live the moment those land. On an ordinary connection it is too quick to notice. A player genuinely stuck there is worth testing with /atg rplprobe rather than being told to rejoin — see ATG Commands.

Changing settings on a running server

Editing the file is only half of it — something has to tell the mods to read it again.

Reloading the file

/atg reload re-reads every ATG mod’s file and applies it over the settings that mod is already running. The reload is Core’s, but it is typed through ATG Commands, so the verb appears only on a server running both mods, and it needs the administrator role. /atg reload atg-scenario does one mod rather than all of them.

Each mod answers with the keys that moved, and every connected player is sent whichever of the new values reach clients at all, in the same step, so nothing waits for a rejoin.

What a reload reaches depends on the kind of setting, not on the mod:

  • A value read at the moment it is needed follows the file immediately.
  • A repeating job scheduled from an interval in the file is put back on the new interval.
  • A value a mod copied or worked something out from while starting up is redone wherever that mod knows to. Where a setting still needs a restart, that mod’s own configuration page says so.

Deleting a key does not restore its shipped default. There is no snapshot of the layers underneath to fall back to, so a key you remove — or the whole file — leaves every value where the last read put it, until the server restarts. To move a setting back, write the shipped value into the file and reload; to clear the file out entirely, delete it and restart.

Saving from in game

Where a mod offers its settings in the Game Master’s Scenario properties, saving there writes the file for you, so the running server and the file on disk agree afterwards. ATG Scenario is the mod that does this — see its configuration page for what its panel carries.

Two things about what lands on disk:

  • Only settings that differ from what layers 1 and 2 gave the mod are written. A value moved and then put back disappears from the file rather than being pinned at the default, and a key the mod does not recognise — a typo, or a setting from an older version — is dropped.
  • The previous file is kept beside it as <mod-id>.bak.json, so <profile>/ATG/atg-scenario.bak.json. If that copy cannot be made the save is refused and the old file is left alone.

The flip side of writing only what differs: a key in the file is pinned at your value across mod updates. Where a later version of a mod ships a different default for something you once changed, your file still wins. Remove that line to take the new one.

Where the complete files are

A working file names only what you are changing and leaves the rest alone:

<profile>/ATG/atg-scenario.json

{
  "m_fUpkeepPerHour": 40,
  "m_iMaxConcurrentObjectives": 4,
  "m_bCiviliansInNarrative": false
}

Each mod’s own page carries the complete file at its shipped values, which is the thing to start from rather than writing one out by hand:

Core’s own settings

Core carries settings that are meant to apply to every ATG mod at once. Anything one mod owns lives in that mod’s own file, which is also where a per-mod override of one of these goes.

<profile>/ATG/atg-core.json

SettingDefaultMeaning
m_iLogVerbosity1How much every ATG mod writes to the server log, unless that mod sets a level of its own.
m_iConfigBlockBytes4096The most settings data the server sends one player in one go.
m_iConfigClientOutstandingBytesMax16384A ceiling on settings data on its way to one player unconfirmed.
m_fConfigBrakePacketLoss0.02The packet loss at which a player is sent their settings more slowly.
m_iConfigBrakeRoundTripMs200The round-trip time at which the same happens.

The log level

ValueLevelWhat it prints
0quietWarnings and errors only.
1normalWhat each mechanic decided, once per event.
2verboseThe detail behind those decisions while a mechanic runs.
3surveyA line per object as lists are built, plus repeating position and census reports.

The levels are cumulative, so 2 also prints everything 1 does. Warnings and errors are never suppressed at any level, including 0.

Levels 2 and 3 are written for diagnosing one mechanic rather than for running a server, and cost a busy server a great deal of log volume.

Each ATG mod has its own m_iLogVerbosity which defaults to -1, meaning inherit this one. Set a mod’s own level only while investigating that mod; change it here to move everything at once.

{
  "m_iLogVerbosity": 1
}

Pacing what clients are sent

These four decide how quickly a player’s game is sent the ATG settings it needs, and every figure is per player rather than a budget for the server as a whole. What is being protected is one joining connection, which is already carrying the world and the AI while it joins.

m_iConfigBlockBytes is the size of each burst, in bytes. The server sends that much, waits for that player’s game to say it has applied it, and only then sends more. Lower it if players are dropped for replication problems while joining — this is ATG’s share of what lands on top of everything else their game is receiving.

m_iConfigClientOutstandingBytesMax is the ceiling the server will not quietly exceed: the block size is what it aims for, this is the most it will have on its way to one player unconfirmed. One mod whose settings are larger than this on their own cannot be divided, so they are sent whole anyway and the server warns once, naming the mod. That mod is the payload most likely to trouble somebody joining on a poor connection.

The other two are brakes on the state of a player’s connection. Past m_fConfigBrakePacketLoss — a fraction of packets lost, so 0.02 is two percent — or past m_iConfigBrakeRoundTripMs, that player is sent smaller bursts with a gap between them, and the further past it they are the slower it goes. Whichever of the two looks worse is the one that decides, and either set to 0 never slows anyone down for that reading.

A brake only ever slows a player down, never speeds one up. The server waits for each burst to be acknowledged before sending the next whatever a connection looks like. Raise the round-trip figure on a server with players a long way away, who have a high round-trip time without their connection being in any trouble.

{
  "m_iConfigBlockBytes": 4096,
  "m_iConfigClientOutstandingBytesMax": 16384,
  "m_fConfigBrakePacketLoss": 0.02,
  "m_iConfigBrakeRoundTripMs": 200
}

The server log says what it settled on as it starts:

[ATG-Core-Config] Settings send queue active - up to 4096 bytes to a player at a time, 16384 before they acknowledge, and anyone over 2% packet loss or 200ms round trip sent slower still.

With ATG Commands loaded, /atg status reports what the queue is doing while players are joining, and /atg rplprobe measures what one player’s connection will actually carry.