// Standalone diagnostic addon; never include in the Escape Workshop package. class AID_Probe { static IEntity Body; static SCR_AIGroup Group; static int Stage; static int Started; static int Last; static bool Ready; static bool Finished; static bool ObserverInserted; static ref map BT = new map(); static ref map Utility = new map(); static ref map GroupBT = new map(); static void Count(map counts, IEntity entity) { if (!entity) return; int count = counts.Get(entity); counts.Set(entity, count + 1); } static RplComponent Rpl(IEntity entity) { if (!entity) return null; return RplComponent.Cast(entity.FindComponent(RplComponent)); } static IEntity Resolve(RplId id) { RplComponent rpl = RplComponent.Cast(Replication.FindItem(id)); if (rpl) return rpl.GetEntity(); return null; } static void Sample(string side, IEntity entity, string kind) { RplComponent rpl = Rpl(entity); if (!rpl) { PrintFormat("AID SAMPLE side=%1 stage=%2 kind=%3 missing=1", side, Stage, kind); return; } string row = string.Format("AID SAMPLE side=%1 stage=%2 kind=%3 role=%4 owner=%5 master=%6", side, Stage, kind, rpl.Role(), rpl.IsOwner(), rpl.IsMaster()); row += string.Format(" ownerProxy=%1 remoteProxy=%2 rpl=%3", rpl.IsOwnerProxy(), rpl.IsRemoteProxy(), rpl.Id()); AIControlComponent control = AIControlComponent.Cast(entity.FindComponent(AIControlComponent)); AIAgent agent; if (control) agent = control.GetControlAIAgent(); else agent = AIAgent.Cast(entity); bool controlActive, agentActive; int lod = -1; if (control) controlActive = control.IsAIActivated(); if (agent) { agentActive = agent.IsAIActivated(); lod = agent.GetLOD(); } PerceptionComponent perception = PerceptionComponent.Cast(entity.FindComponent(PerceptionComponent)); int simulations = -1; if (perception) simulations = perception.GetSimulateCounter(); row += string.Format(" control=%1 agent=%2 controlActive=%3 agentActive=%4 lod=%5 perception=%6", control != null, agent != null, controlActive, agentActive, lod, simulations); row += string.Format(" bt=%1 utility=%2 groupBT=%3 pos=%4 wall=%5", BT.Get(entity), Utility.Get(entity), GroupBT.Get(entity), entity.GetOrigin(), System.GetTickCount()); Print(row); } static void Spawn() { vector position = "6090 0 7160"; position[1] = GetGame().GetWorld().GetSurfaceY(position[0], position[2]) + 0.2; EntitySpawnParams params = new EntitySpawnParams(); params.TransformMode = ETransformMode.WORLD; params.Transform[3] = position; SCR_AIGroup.IgnoreSpawning(true); Group = SCR_AIGroup.Cast(GetGame().SpawnEntityPrefab(Resource.Load("{EACD97CF4A702FAE}Prefabs/Groups/BLUFOR/Group_US_Base.et"), GetGame().GetWorld(), params)); SCR_AIGroup.IgnoreSpawning(false); Body = GetGame().SpawnEntityPrefab(Resource.Load("{E398E44759DA1A43}Prefabs/Characters/Factions/BLUFOR/US_Army/Character_US_TL.et"), GetGame().GetWorld(), params); if (!Body || !Group) { Print("AID FAIL spawn"); Finished = true; return; } AIControlComponent control = AIControlComponent.Cast(Body.FindComponent(AIControlComponent)); AIAgent agent; if (control) agent = control.GetControlAIAgent(); if (!agent) { Print("AID FAIL server agent absent"); Finished = true; return; } Group.AddAgent(agent); Group.SetPermanentLOD(0); Group.ActivateAI(); agent.SetPermanentLOD(0); agent.ActivateAI(); Rpl(Body).EnableStreaming(true); Rpl(Group).EnableStreaming(true); Stage = 1; Started = System.GetTickCount(); PrintFormat("AID READY groupAgents=%1 body=%2 group=%3", Group.GetAgentsCount(), Rpl(Body).Id(), Rpl(Group).Id()); } static void Tick() { if (Finished || !System.IsCLIParam("aidProbe") || !GetGame().GetGameMode()) return; int now = System.GetTickCount(); if (now - Last < 1000) return; Last = now; if (!Replication.IsServer()) { if (Body) { Sample("worker", Body, "body"); Sample("worker", Group, "group"); } return; } if (!Body) { Spawn(); return; } Sample("dedicated", Body, "body"); Sample("dedicated", Group, "group"); array players = {}; GetGame().GetPlayerManager().GetPlayers(players); foreach (int player : players) { SCR_PlayerController controller = SCR_PlayerController.Cast(GetGame().GetPlayerManager().GetPlayerController(player)); if (!controller) continue; RplIdentity identity = controller.GetRplIdentity(); if (!identity.IsValid() || identity == RplIdentity.Local()) continue; if (!ObserverInserted) { ObserversSystem observers = ObserversSystem.Cast(GetGame().GetWorld().FindSystem(ObserversSystem)); if (observers) observers.InsertObserverMP(identity, Body.GetOrigin()[0], Body.GetOrigin()[2], null); Rpl(Body).EnableStreamingConNode(identity, true); Rpl(Group).EnableStreamingConNode(identity, true); ObserverInserted = true; } controller.AID_Ping(Rpl(Body).Id(), Rpl(Group).Id(), Stage); if (!Ready || now - Started < 20000) return; if (Stage == 1) { Rpl(Body).GiveExt(identity, true); Stage = 2; } else if (Stage == 2) { Rpl(Group).GiveExt(identity, true); Stage = 3; } else if (Stage == 3) { Stage = 4; } // separate explicit worker activation trial else if (Stage == 4) { Rpl(Body).GiveExt(RplIdentity.Local(), true); Rpl(Group).GiveExt(RplIdentity.Local(), true); AIControlComponent control = AIControlComponent.Cast(Body.FindComponent(AIControlComponent)); if (control) control.ActivateAI(); Group.ActivateAI(); Stage = 5; } else if (Stage == 5) { Print("AID PROBE COMPLETE"); Finished = true; return; } Started = now; PrintFormat("AID TRANSITION stage=%1 wall=%2", Stage, now); return; } } static void Reset() { Body = null; Group = null; Stage = 0; Started = 0; Last = 0; Ready = false; Finished = false; ObserverInserted = false; BT.Clear(); Utility.Clear(); GroupBT.Clear(); } } modded class SCR_AIDecideBehavior { override ENodeResult EOnTaskSimulate(AIAgent owner, float dt) { AID_Probe.Count(AID_Probe.BT, owner.GetControlledEntity()); return super.EOnTaskSimulate(owner, dt); } } modded class SCR_AIDecideActivity { override ENodeResult EOnTaskSimulate(AIAgent owner, float dt) { AID_Probe.Count(AID_Probe.GroupBT, owner); return super.EOnTaskSimulate(owner, dt); } } modded class SCR_AIUtilityComponent { override SCR_AIBehaviorBase EvaluateBehavior(BaseTarget unknownTarget) { AID_Probe.Count(AID_Probe.Utility, m_OwnerEntity); return super.EvaluateBehavior(unknownTarget); } } modded class SCR_PlayerController { void AID_Ping(RplId body, RplId group, int stage) { Rpc(AID_RpcWorker, body, group, stage); } [RplRpc(RplChannel.Reliable, RplRcver.Owner)] void AID_RpcWorker(RplId body, RplId group, int stage) { if (Replication.IsServer()) return; AID_Probe.Body = AID_Probe.Resolve(body); AID_Probe.Group = SCR_AIGroup.Cast(AID_Probe.Resolve(group)); if (stage == 4 && AID_Probe.Stage != 4 && AID_Probe.Body) { AIControlComponent control = AIControlComponent.Cast(AID_Probe.Body.FindComponent(AIControlComponent)); bool activated; if (control) activated = control.ActivateAI(); if (AID_Probe.Group) { AID_Probe.Group.SetPermanentLOD(0); AID_Probe.Group.ActivateAI(); } PrintFormat("AID EXPLICIT WORKER ACTIVATE control=%1 result=%2", control != null, activated); } AID_Probe.Stage = stage; // Small deterministic script job proves actual worker-side computation/RPC. int result; for (int i = 1; i <= 100; i++) result += i; PrintFormat("AID RPC worker stage=%1 body=%2 group=%3 computed=%4", stage, AID_Probe.Body != null, AID_Probe.Group != null, result); Rpc(AID_RpcAck, AID_Probe.Body != null && AID_Probe.Group != null, stage, result); } [RplRpc(RplChannel.Reliable, RplRcver.Server)] void AID_RpcAck(bool ready, int stage, int result) { if (!Replication.IsServer()) return; if (ready && result == 5050 && !AID_Probe.Ready) { AID_Probe.Ready = true; AID_Probe.Started = System.GetTickCount(); } PrintFormat("AID RPC dedicated stage=%1 resolved=%2 result=%3", stage, ready, result); } } modded class ArmaReforgerScripted { override void OnUpdate(BaseWorld world, float timeslice) { super.OnUpdate(world, timeslice); if (world) AID_Probe.Tick(); } override protected void OnBeforeWorldCleanup() { AID_Probe.Reset(); super.OnBeforeWorldCleanup(); } }