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
trueandfalse, not1and0. A1is 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:
- ATG Scenario — the large one, and the one with lists in it.
- ATG Garmin Vitals — a short one, and no lists.
- Server messages — Core’s own rotating and daily chat messages.
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
| Setting | Default | Meaning |
|---|---|---|
m_iLogVerbosity | 1 | How much every ATG mod writes to the server log, unless that mod sets a level of its own. |
m_iConfigBlockBytes | 4096 | The most settings data the server sends one player in one go. |
m_iConfigClientOutstandingBytesMax | 16384 | A ceiling on settings data on its way to one player unconfirmed. |
m_fConfigBrakePacketLoss | 0.02 | The packet loss at which a player is sent their settings more slowly. |
m_iConfigBrakeRoundTripMs | 200 | The round-trip time at which the same happens. |
The log level
| Value | Level | What it prints |
|---|---|---|
| 0 | quiet | Warnings and errors only. |
| 1 | normal | What each mechanic decided, once per event. |
| 2 | verbose | The detail behind those decisions while a mechanic runs. |
| 3 | survey | A 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.