Files
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

20 KiB

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.

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

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. GiveExt 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, official 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, AIAgent, AIWorld.

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: the engine calls authored decision code. Activation methods, movement and perception queries cross proto external into the engine. The generated PerceptionComponent provides counters/target access but no remote executor or complete state-transfer API. AIBehaviorTreeComponentBase 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.

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, AIPathfindingComponent.

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 and binary-string evidence record those limits and the exact inspected sources. See also the official script profiling documentation.

The existing Escape HQ measurements 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.