Balance lab
Balance lab
The lab edits weapon statistics in game, versions every change, and can roll back. It needs the lab scope.
/gunsmithlab
Three kinds of field
Not every statistic reaches the game the same way, and the lab says so under every slider. This is the only question that matters when you move one: will this actually do anything, and when?
| Class | Badge | How it works | When it takes effect |
|---|---|---|---|
| Live | live |
Broadcast to clients as a damage modifier | Immediately |
| Scripted | scripted |
Applied by the script — recoil, sway, zone multipliers, fire rate cap | Immediately |
| Deploy | deploy |
Written into a weapon meta patch | After a server restart |
Live and scripted fields change while players are connected. Deploy fields — clip size, weapon range, spread, reload time, falloff, force, penetration — do not, and no configuration makes them.
Does it overwrite my weapon files?
No. Nothing in your game files, and nothing in any other resource, is edited.
Deploy fields are written to a separate satellite resource, aty_gunsmith_data, as a single weapons.meta registered with WEAPONINFO_FILE_PATCH. That file is generated by the deploy pipeline on every deploy — it is never hand-edited, and editing it yourself will be overwritten.
Because it is a patch resource, it does take precedence over the vanilla weapon stats it names. What it does not do is delete anything:
That caused six crashes during development. The generator now writes the entire core blob — 91 weapons and 53 ammo definitions — and replaces only the field you changed inside it. A guard in the deploy pipeline blocks any deploy whose record count has dropped, and it was tested against a deliberately sabotaged file.
This is why the patch resource is generated rather than edited. A hand-written partial weapons.meta is exactly the failure mode above.
Mk II weapons are deliberately skipped by the meta writer. Writing them breaks the 31 tint indices and loses the camo component names, so their tint and camo are preserved instead.
Deploying
A deploy writes the patch, archives the version, and applies it. The countdown before it goes live is Config.Balance.deployCountdown, 10 seconds by default.
How it is applied is Config.Balance.applyMode:
| Mode | Behaviour |
|---|---|
'safe' (default) |
Never restart while players are connected. The patch is written and the version archived; it takes effect on the server's next start |
'empty' |
Restart only when the server is empty. Same behaviour, states the intent |
'force' |
Restart under any condition |
Use 'force' only inside a closed maintenance window with nobody connected.
With the default mode, expect to see "Weapon data saved — it will take effect on the next server start". That message is the system working correctly, not a failure.
Versions and rollback
Every deploy is a row in aty_gunsmith_balance holding the full stat set, the diff against the previous version, the author and a note. The version note is what you will see in the rollback list, so write one.
Rolling back is picking an earlier version. Config.Balance.autoRollback = true does it for you if a deploy fails verification.
Deploys are rate-limited by Config.RateLimit.deploysPerHour, 3 by default.
The linter
The lab warns rather than blocks:
| Check | Default | Warns when |
|---|---|---|
ttkFloorMs |
220 |
Time-to-kill drops below this floor |
ttkClassBand |
0.35 |
TTK deviates this far from the weapon's class average |
Class averages are computed from recorded shooting range sessions, so the linter gets more useful the more you measure.
Checking it reached the game
If a change appears saved but nothing happened in game, the chain has three links and each one can be inspected:
/gunsmith:doctor bridge, tables, data resource, current balance version
The lab's own field notes cover the common cases directly — a field that is not implemented, a field that is staged for restart, a weapon with no vanilla record, or meta writing being switched off entirely (Config.Balance.metaEmitFields).
Webhooks
Config.Webhooks = { deploy = '', moderation = '', damageFlag = '' }
deploy receives every deploy and rollback. Worth setting on a server where more than one person has the lab scope.
Exports
exports.aty_gunsmith:getWeaponStats(weaponName)
exports.aty_gunsmith:setWeaponStats(weaponName, patch, actor)
exports.aty_gunsmith:deploy(actor, note)
exports.aty_gunsmith:rollback(version, actor)
And the event:
AddEventHandler('aty_gunsmith:balanceDeployed', function(data)
-- { version, diff, actor }
end)
See also
- Shooting range — measuring what a change did
- Configuration — the damage clamp
- Commands