Files
EscapistsReintroduced/docs/ai-delegation-1.8.md
T
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

292 lines
20 KiB
Markdown

# Reforger 1.8: native AI delegation feasibility
Investigation and paired runtime experiment, 7 October 2026.
Follow-up: [worker-local AI and native-query workaround experiments](ai-delegation-workarounds-1.8.md).
**The tested ownership-transfer route does not offload native AI.** A real dedicated
server and a separate hidden game client completed the five-phase experiment.
Giving both the soldier and its group to the worker changed ownership, but the
server kept running soldier decisions, group decisions and native perception.
The worker did not run those updates, even after explicit activation.
An RPC worker can run custom script calculations. That is a different architecture
from distributing Enfusion's native AI simulation, and this experiment does not
establish meaningful CPU savings for Escape. There is no demonstrated public-API
route to an Arma 3-style headless AI worker in the inspected 1.8 interfaces.
## Evidence and version scope
- Official script source: tag `v1.8.0.10`, commit
[`b46bdd8f4932f3a256c765f93a44417996a6da73`](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/tree/b46bdd8f4932f3a256c765f93a44417996a6da73).
- Both measured runtimes identify themselves as **1.8.0.13**, built
`2026-08-21 14:30:18 UTC`. Thus source inspection and runtime experiment cover
different patch releases of 1.8; the compiled probe confirms the interfaces it
uses in .13, not complete source/binary identity with .10.
- [Standalone addon and reproduction instructions](../tests/native/ai_delegation/README.md).
- [Server log](evidence/ai-delegation-1.8-2026-10-07/server-console.log.txt),
[worker log](evidence/ai-delegation-1.8-2026-10-07/client-console.log.txt),
[counter summary](evidence/ai-delegation-1.8-2026-10-07/summary.json),
[runtime hashes](evidence/ai-delegation-1.8-2026-10-07/runtime-sha256.json).
- [Exact executed probe](evidence/ai-delegation-1.8-2026-10-07/executed-probe.c.txt)
and [executed runner](evidence/ai-delegation-1.8-2026-10-07/executed-runner.py.txt).
The test used vanilla Game Master Everon, one ordinary US soldier, one group,
forced LOD 0, a 30 FPS cap and no Escape code. Dedicated and worker were separate
processes on the same physical Linux host, with a hidden Proton client and private
profiles. A loopback bridge connected the worker to an isolated dedicated network
namespace. This establishes execution locality, **not multi-machine speedup**.
No production server or mission configuration was modified.
The initial loose-addon run did not index/load the script. Workbench indexing fixed
that. An initial paired run streamed the group but not the soldier; explicit
observer/streaming setup fixed that. Neither preliminary attempt is treated as a
negative delegation result. The final run acknowledged both entities before its
baseline interval and ended with `AID PROBE COMPLETE`; owned-process cleanup passed.
The full logs also contain vanilla asset-loading warnings/errors and two resupply
station script errors during shutdown, after `AID PROBE COMPLETE`. This was not an
error-free engine session; no script error occurred during the measured phases.
## 1. What happens when ownership is transferred?
Measured stable flags after `GiveExt(workerIdentity, true)` on both hierarchies:
| Flag | Dedicated | Worker |
|---|---:|---:|
| `Role()` | Authority (`0`) | Proxy (`1`) |
| `IsOwner()` | false | true |
| `IsMaster()` | false | false |
| `IsOwnerProxy()` | false | true |
| `IsRemoteProxy()` | false | false |
Before transfer, the server was owner/master and the worker was a remote proxy.
Returning ownership restored those flags. Body and group were transferred in
separate phases; transferring the body alone did not transfer the separate group.
`IsMaster()` means **owner AND authority**, not merely authority. Its becoming false
on the server does not demonstrate a migrated simulation. These meanings match
[the pinned BaseRplComponent interface](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/GameLib/generated/Components/BaseRplComponent.c).
[`GiveExt`](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/Game/generated/Components/RplComponent.c)
also supports ownership-change notifications/custom data; it is not documented as
an AI-state or authority migration function.
## 2. Does any AI computation migrate with ownership?
**None of the instrumented AI computations migrated in this experiment.**
The following are actual counter increases in trimmed steady intervals, not CPU
milliseconds. Soldier BT refers to `SCR_AIDecideBehavior.EOnTaskSimulate`; group BT
to `SCR_AIDecideActivity.EOnTaskSimulate`. Soldier utility evaluation matched its
BT counter. All overrides call the stock implementation once.
| Phase | Server interval | Server perception updates | Server soldier BT calls | Server group BT calls | Worker corresponding increases |
|---|---:|---:|---:|---:|---:|
| Body owned by worker | 15.100 s | 453 | 25 | 40 | all 0 |
| Body and group owned by worker | 15.033 s | 451 | 25 | 39 | all 0 |
| Explicit worker activation | 15.033 s | 451 | 25 | 40 | all 0 |
| Ownership returned to server | 15.067 s | 452 | 25 | 40 | all 0 |
The worker-side windows are approximately the same duration, independently sampled.
The summary trims the first and last two readings to avoid phase-label RPC and
ownership notifications arriving on different frames. The server baseline also
had positive counters. Worker RPC calculations returned `5050` throughout, proving
that the worker's scripts were executing while its AI counters remained zero.
This does not measure every native subsystem. In particular, it does not establish
whether character prediction or movement networking changes cost with ownership.
It establishes that ownership did not move the tested decision/perception work.
## 3. Can clients execute behavior trees or AILib simulation?
For this replicated server-created AI, normal transfer and explicit activation did
not start the observed soldier/group BT callbacks on the client. The client had an
`AIControlComponent` but `GetControlAIAgent()` remained null. Its group object existed,
but its decision callback count stayed zero.
This is not proof that a Reforger process running as a client is intrinsically
incapable of any AI: local/offline entities or a deliberately constructed local AI
world are different experiments. Creating a parallel local simulation would still
need world synchronization and an authoritative command/result bridge; it would
not automatically remove server-native costs.
## 4. What does AI “Load balancing” balance?
The public 1.8 script tree does not expose the implementation of that diagnostic
switch, a worker registry, remote AI scheduling queues or a destination identity.
**Its exact algorithm remains unverified.** Interpreting the menu title as network
load balancing is unsupported.
Evidence points toward local AI scheduling: native diagnostics contain active/LOD
populations, postponed AI simulation and aggregate AI simulation time. Separately,
the documented `-AIPartialSim` setting batches AI processing (default four batches).
Neither the diagnostic switch's exact connection to that flag nor its thread/frame
scheduling algorithm has been established. Local batching, multithreading and
cross-machine delegation are distinct mechanisms.
[Official diagnostic menu](https://community.bistudio.com/wiki/Arma_Reforger:Diag_Menu),
[official startup parameters](https://community.bistudio.com/wiki/Arma_Reforger:Startup_Parameters).
## 5. What does `IsAIActivated` control?
It is a **query**, not a command to select an execution machine.
`ActivateAI`/`DeactivateAI` control native activation state. `AIWorld` exposes active
population limits and admission checks; agents expose LOD controls, and the pinned
comment states that maximum LOD disables agents by default.
[AIControlComponent](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/Game/generated/AI/AIControlComponent.c),
[AIAgent](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/Game/generated/AI/AIAgent.c),
[AIWorld](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/Game/generated/AI/AIWorld.c).
Crucially, the worker reported `controlActive=1` even with no local control agent
and no perception updates. Calling `ActivateAI()` returned **true**, but did not
change those observations. An activation flag or successful return alone therefore
does not prove simulation execution on that machine.
## 6. Which expensive systems are script versus native?
“Native” below describes the public call boundary, not a measured cost share.
| Subsystem | Inspectable/script portion | Native boundary / missing capability |
|---|---|---|
| Behavior trees | Scripted task callbacks, soldier utility scoring, group activities | BT scheduling/execution internals; no public migrate/serialize/resume contract |
| Navigation/pathfinding | Destination/waypoint choice, high-level route planning | Navmesh queries, path generation/following and movement internals |
| Perception | Script interpretation/reaction to targets | Perception system, target acquisition/query execution; local simulation counter |
| Physics | Script requests and collision callbacks | Physics world/integration/queries |
| Animation | Script commands and state selection | Animation graph evaluation and pose processing |
| Character simulation | Script behaviors/input decisions | Controller, movement, collision and animation integration |
| Replication | RPC handlers and authored replicated fields | Transport, relevancy, hierarchy/role handling and replication scheduling |
| AI group logic | `SCR_AIGroup`, utility activities and formation/waypoint choices | Native group/agent membership, activation, scheduling and movement support |
| Script execution | Escape VirtualHQ and stock Enforce algorithms | VM execution/native calls; engine jobs invoked by scripts may run elsewhere in time |
| Entity updates | Authored callbacks | Engine component/system update scheduling |
The key exposed boundary is [`AITaskScripted.EOnTaskSimulate`](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/Game/generated/AI/AITaskScripted.c):
the engine calls authored decision code. Activation methods, movement and perception
queries cross `proto external` into the engine. The generated
[`PerceptionComponent`](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/Game/generated/Components/PerceptionComponent.c)
provides counters/target access but no remote executor or complete state-transfer API.
[`AIBehaviorTreeComponentBase`](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/Game/generated/AI/AIBehaviorTreeComponentBase.c)
is empty at the script interface; its derived component exposes a breakpoint setter.
## 7. Can native AI simulation authority be migrated?
**No supported migration mechanism was found, and the tested mechanism did not
migrate it.** `RplNode` exposes a role query, not a public setter that moves an
existing authoritative hierarchy to another process. Creating another local
entity with authority is not transferring this entity's server authority.
[The pinned RplNode interface](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/Core/generated/Replication/RplNode.c).
The engine can, in principle, retain server authority while delegating computation,
but that requires an explicit compute/state/result protocol. Replication ownership
alone did not supply that protocol here. Dormancy, despawning and activation limits
reduce or suspend local work; they do not demonstrate execution elsewhere.
`AIGroup.GetServerAgentsCount()` mentioning remotely controlled agents is likewise
not a promise of delegated AI simulation. Our replicated group existed on the
worker without its server soldier's control agent or active decision callbacks.
## 8. Where is the best interception point for a native experiment?
There are two distinct candidates:
- **Supported script boundary:** snapshot-based custom planning immediately before
mission/group decision selection. Compute over plain values on the worker and
apply validated commands on authority. Do not serialize engine pointers,
`AIAgent` objects or behavior instances and expect them to remain valid remotely.
- **Native research candidate:** an asynchronous path-query submission/completion
boundary, if profiling shows pathfinding is a major cost and binary investigation
locates a separable job contract. Static navmesh data is more tractable to mirror
than a complete physics/perception/animation world. The public movement interface
exposes `GetCurrentPath()` and `OnPathSet()` but **not** an API for replacing the
native solver with externally supplied complete path jobs/results.
[AIBaseMovementComponent](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/Game/generated/AI/AIBaseMovementComponent.c),
[AIPathfindingComponent](https://github.com/BohemiaInteractive/Arma-Reforger-Script-Diff/blob/b46bdd8f4932f3a256c765f93a44417996a6da73/scripts/Game/generated/AI/AIPathfindingComponent.c).
The exact **script/native interface boundary** is identified. An exact **binary
hook address/symbol, replaceable job ABI or safe scheduler interception site** has
not been identified. Claiming a ready-to-implement native hook would exceed the
evidence. A whole-AI scheduler hook would also need BT stacks, agent state, sensory
memory, pending jobs, world updates and command application—not just different
ownership flags. Perception queries depend on changing world geometry and targets;
copying that state may consume much of the intended saving.
Any job experiment needs immutable inputs, entity generation IDs, a world/navmesh
revision, deadlines, cancellation, result validation, bounded queues and local
fallback. Late results must not overwrite newer decisions. Keep authoritative
character/physics state and final acceptance on the dedicated server.
For mod-level Reforger clients, use Enfusion RPC with a dedicated replicated worker
controller/mailbox. Ownership of the combat soldier is unnecessary for custom jobs;
the successful PlayerController RPC in the probe already demonstrates that.
For native extensions/external processes, persistent TCP/UDP is a separate native
integration task; local shared memory plus synchronization is another option. No
arbitrary runtime socket/shared-memory transport API was found in the inspected
script surface. Workbench's `NetApiHandler` is tooling, not evidence of a supported
dedicated-game native plugin/IPC interface. HTTP belongs only to infrequent,
latency-tolerant high-level jobs, never per-frame simulation.
## 9. What fraction of CPU load can realistically be offloaded?
**Not measured; no defensible percentage is available.** This one-soldier experiment
measures where callbacks run, not their duration or the cost of a large combat scene.
Zero migrated perception/BT calls does not mean a measured 0% change in all process
CPU, since other work could change with ownership.
The profiler review found:
- `System.GetProfileData()` exposes aggregate simulation/frame/render/GPU timing,
not exclusive AI, BT, nav, perception, physics or animation timings.
- `PerceptionComponent.GetSimulateCounter()` measures updates, not their cost.
- The pinned 1.8 Diag binary contains `AI simulation time ms`, postponed AI time
and per-LOD population display strings near `AILibrary global counts`. That is
a concrete acquisition lead, **not a captured native CPU breakdown**. The exact
aggregation/nesting semantics and availability on a dedicated capture remain
unverified. A joined client's overlay cannot stand in for server-side timing.
- Official tools document a script profiler and `-scriptExtProfiler`; the installed
Workbench's Tracy launcher references an executable absent from the installation.
No compatible complete native profiler capture was acquired. Arma 3's profiler
commands were deliberately not assumed to work in Reforger.
- Public DiagMenu functions can change numeric native menu IDs, but no AI-counter
ID/enumeration/export interface was found. Guessing an ID or a saved-state format
would not be a valid measurement.
The [profiling acquisition notes](evidence/ai-delegation-1.8-2026-10-07/profiling-findings.txt)
and [binary-string evidence](evidence/ai-delegation-1.8-2026-10-07/diag-binary-string-evidence.txt)
record those limits and the exact inspected sources. See also the
[official script profiling documentation](https://community.bistudio.com/wiki/Arma_Reforger:Script_Profiling).
The existing [Escape HQ measurements](hq-performance-measurements.md) can identify
expensive synchronous script scopes. They are wall-time measurements including
synchronous native calls, not exclusive thread CPU and not async engine-job costs.
Nested scopes must not be summed as independent work. They alone cannot establish
what percentage of total native AI simulation is removable.
Before building an offload architecture, capture a representative dedicated scene
with fixed AI count, activation/LOD distribution, player positions, terrain, movement
and combat. Acquire server-side native timings for BT, navigation, perception,
physics, animation/character, replication and remaining entity work, plus script
profiling. Preserve raw captures and distinguish nested and concurrent zones. Then
compare a matched variant that actually removes the chosen work from the server;
cap-limited FPS or changed behavior is not sufficient evidence of CPU relief.
If the removable fraction of bottleneck work is `f`, ideal speedup with infinitely
fast workers is bounded by `1/(1-f)` before communication, serialization and
validation overhead. Here `f` is unknown. Custom planning may be worthwhile only
if measurement shows it is a substantial part of the bottleneck. There is no basis
for assuming that moving VirtualHQ yields space for hundreds of additional AI.
## 10. Can multiple Reforger instances become headless AI workers?
| Architecture | 1.8 finding | What remains on authority |
|---|---|---|
| Custom script worker via RPC | Small worker computation/RPC demonstrated; useful planning offload not benchmarked | Native AI and final command application |
| Native AI worker through `GiveExt` | Tested route failed to migrate observed AI work | Soldier/group decisions and perception continued on server |
| Worker with separately reconstructed local AI world | Unimplemented research hypothesis; requires state synchronization and replacing server work | At least authoritative validation/character/physics unless much deeper integration |
| Native engine job offload | No validated hook/ABI or distributed scheduler found | Depends on a future measured, separable job boundary |
Two dedicated servers do not automatically join one shared authoritative world.
The demonstrated worker is a normal connected client running hidden for the test;
that is not a newly discovered supported headless-client mode.
**Recommendation:** do not ship `GiveExt`-based “AI offloading” or reassign Escape
combat units to workers. Keep the isolated probe for regression/investigation and
use native profiling as the next gate. Custom RPC planning is feasible as a separate
optimization if its measured cost justifies it; genuine horizontal native AI scaling
needs additional engine support or a substantial, still-unproven native integration.