Capture current mission, AI, map and administration development

Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
2026-10-13 22:52:53 +02:00
parent a951a96edc
commit 6a667d8deb
8104 changed files with 2124945 additions and 2061 deletions
@@ -0,0 +1,13 @@
check: /tmp/err-policy-59xtcv14/check.cpp:358: int main(): Assertion `d->Detail.find(fault==0?"item=absent;":"item=present;")!=string::npos' failed.
PASS: actual mounted hierarchy sampling, ownership exclusion, deduplication, completeness/caps, quarter threshold and detached validation; no runtime policy or ammunition mutation
Traceback (most recent call last):
File "/tmp/erri-mounted-slot-diagnostic/tests/test_hq_mounted_ammo_sampler.py", line 300, in <module>
run('#define WORKBENCH\n'+program.replace('int main(){','int main(){'+cases))
~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
subprocess.run([str(p/'check')],check=True)
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.14/subprocess.py", line 578, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['/tmp/err-policy-59xtcv14/check']' died with <Signals.SIGABRT: 6>.
@@ -0,0 +1,153 @@
--- a/addons/core/Scripts/Game/ERR/10_AI/ERR_HQMountedAmmoSampler.c
+++ b/addons/core/Scripts/Game/ERR/10_AI/ERR_HQMountedAmmoSampler.c
@@ -40,6 +40,51 @@
string Detail;
string FirstPrefab;
int FirstPurpose = -1;
+ // Called before Reject, once per reason in this explicit diagnostic sample.
+ // These labels describe sampled relationships; they never authorize a weapon.
+ void SlotEvidence(int reason, IEntity entity, WeaponSlotComponent mount, InventoryStorageSlot binding, InventoryItemComponent item, array<IEntity> allowed)
+ {
+ if ((Reasons & reason) != 0) return;
+ if (reason == 64) Detail += "mount-slot-evidence:";
+ else Detail += "zero-muzzle-slot-evidence:";
+ if (item) Detail += "item=present;";
+ else Detail += "item=absent;";
+ InventoryStorageSlot parent = null;
+ if (item) parent = item.GetParentSlot();
+ if (!parent) Detail += "parent=null;";
+ else if (parent == binding) Detail += "parent=same-binding;";
+ else Detail += "parent=different-binding;";
+ if (!binding) Detail += "binding=null;";
+ else
+ {
+ if (binding.GetAttachedEntity() == entity) Detail += "binding-attached=exact;";
+ else Detail += "binding-attached=other-or-null;";
+ if (binding.GetParentContainer() == mount) Detail += "binding-container=exact-mount;";
+ else Detail += "binding-container=other-or-null;";
+ }
+ IEntity owner = null;
+ if (mount) owner = mount.GetOwner();
+ if (owner && allowed.Contains(owner)) Detail += "mount-owner=allowed;";
+ else Detail += "mount-owner=absent-or-foreign;";
+ if (owner && owner.GetPrefabData()) Detail += "mount-prefab=" + owner.GetPrefabData().GetPrefabName() + ";";
+ if (!parent) return;
+ if (parent.GetAttachedEntity() == entity) Detail += "parent-attached=exact;";
+ else Detail += "parent-attached=other-or-null;";
+ WeaponSlotComponent parentMount = WeaponSlotComponent.Cast(parent.GetParentContainer());
+ if (parent.GetParentContainer() == mount && mount) Detail += "parent-container=exact-mount;";
+ else if (parentMount) Detail += "parent-container=other-mount;";
+ else if (!parent.GetParentContainer()) Detail += "parent-container=null;";
+ else Detail += "parent-container=non-mount;";
+ if (!parentMount) return;
+ IEntity parentOwner = parentMount.GetOwner();
+ if (parentOwner && allowed.Contains(parentOwner)) Detail += "parent-mount-owner=allowed;";
+ else Detail += "parent-mount-owner=absent-or-foreign;";
+ if (parentOwner && parentOwner.GetPrefabData()) Detail += "parent-mount-prefab=" + parentOwner.GetPrefabData().GetPrefabName() + ";";
+ if (parentMount.GetSlotInfo() == parent) Detail += "parent-mount-slot=exact;";
+ else Detail += "parent-mount-slot=other-or-null;";
+ if (parentMount.GetWeaponEntity() == entity) Detail += "parent-mount-weapon=exact;";
+ else Detail += "parent-mount-weapon=other-or-null;";
+ }
void Reject(int reason, string detail, IEntity entity, int purpose = -1)
{
if ((Reasons & reason) == 0) Detail += detail + ";";
@@ -104,7 +149,11 @@
if (!mountedItem || mountedItem.GetParentSlot() != binding)
{
#ifdef WORKBENCH
- if (s_Diagnostic) s_Diagnostic.Reject(64, "mount-item-slot-mismatch", mountedEntity);
+ if (s_Diagnostic)
+ {
+ s_Diagnostic.SlotEvidence(64, mountedEntity, mount, binding, mountedItem, allowed);
+ s_Diagnostic.Reject(64, "mount-item-slot-mismatch", mountedEntity);
+ }
#endif
result.Complete = false; return;
}
@@ -272,7 +321,23 @@
if (muzzles.IsEmpty())
{
if (NonFiringPlaceholder(weapon, allowed)) result.Weapons--;
- else result.Complete = false;
+ else
+ {
+#ifdef WORKBENCH
+ if (s_Diagnostic)
+ {
+ IEntity owner = weapon.GetOwner();
+ InventoryItemComponent item = InventoryItemComponent.Cast(owner.FindComponent(InventoryItemComponent));
+ WeaponSlotComponent mount = null;
+ InventoryStorageSlot binding = null;
+ if (item && item.GetParentSlot()) mount = WeaponSlotComponent.Cast(item.GetParentSlot().GetParentContainer());
+ if (mount) binding = mount.GetSlotInfo();
+ s_Diagnostic.SlotEvidence(4096, owner, mount, binding, item, allowed);
+ s_Diagnostic.Reject(4096, "zero-muzzle-not-proven-placeholder", owner);
+ }
+#endif
+ result.Complete = false;
+ }
continue;
}
if (muzzles.Count() > 8) { result.Complete = false; continue; }
--- a/tests/test_hq_mounted_ammo_sampler.py
+++ b/tests/test_hq_mounted_ammo_sampler.py
@@ -15,6 +15,8 @@
code=code.replace('InventoryItemComponent.Cast(entity.FindComponent(InventoryItemComponent))','entity->item')
code=code.replace('InventoryItemComponent.Cast(owner.FindComponent(InventoryItemComponent))','owner->item').replace('ResourceName resource', 'string resource')
code=code.replace('WeaponSlotComponent.Cast(slot.GetParentContainer())','slot->mount').replace('BaseMuzzleComponent.Cast(slot.GetParentContainer())','slot->muzzle')
+code=code.replace('WeaponSlotComponent.Cast(parent.GetParentContainer())','parent->mount').replace('WeaponSlotComponent.Cast(item.GetParentSlot().GetParentContainer())','item->GetParentSlot()->mount')
+code=code.replace('parent.GetParentContainer()', 'parent->Container()')
code=code.replace('EStoragePurpose.','EStoragePurpose::').replace('GetParentSlot().','GetParentSlot()->')
code=code.replace('ChimeraCharacter.Cast(entity) != nullptr','entity->character').replace('ChimeraCharacter.Cast(entity) != null','entity->character')
code=code.replace('ChimeraCharacter.Cast(root)','root->character').replace('ChimeraCharacter.Cast(entity)','entity->character')
@@ -22,10 +24,11 @@
code=re.sub(r'\bERR_HQMountedAmmoDiagnostic (?=\w)', 'ERR_HQMountedAmmoDiagnostic* ',code)
code=code.replace('s_Diagnostic.', 's_Diagnostic->').replace('GetPrefabData().', 'GetPrefabData()->')
code=re.sub(r'\bERR_HQMountedAmmoSnapshot (?=\w)','ERR_HQMountedAmmoSnapshot* ',code)
-code=code.replace('array<','Array<').replace('foreach (','for (').replace('null','nullptr')
+code=code.replace('array<','Array<').replace('foreach (','for (')
+code=re.sub(r'"[^"]*"|\bnull\b',lambda m:m.group() if m.group().startswith(chr(34)) else 'nullptr',code)
code=code.replace('Array<IEntity> allowed, Array<BaseWeaponComponent> weapons,','Array<IEntity>& allowed, Array<BaseWeaponComponent>& weapons,')
code=code.replace('Array<IEntity> allowed, Array<BaseWeaponComponent> weapons)', 'Array<IEntity>& allowed, Array<BaseWeaponComponent>& weapons)')
-for name in ('copy','result','weapon','root','entity','child','part','manager','mounted','muzzle','magazine','item','storage','mount','mountedEntity','mountedItem','owner'):
+for name in ('copy','result','weapon','root','entity','child','part','manager','mounted','muzzle','magazine','item','storage','mount','mountedEntity','mountedItem','owner','parent','binding','parentMount','parentOwner'):
code=re.sub(r'\b'+name+r'\.',name+'->',code)
code=re.sub(r'\b(bool|int) (\w+);',r'\1 \2=0;',code).replace('\n}\n','\n};\n')
program=(r'''
@@ -41,7 +44,7 @@
struct Entity;struct Weapon;struct Muzzle;struct Magazine;struct Manager;struct Mount;
struct EStoragePurpose{static const int PURPOSE_UNSET=0,PURPOSE_DEPOSIT=1,PURPOSE_WEAPON_PROXY=2,PURPOSE_ATTACHMENT_PROXY=4,PURPOSE_ANY=127;};
struct Storage{int purpose=EStoragePurpose::PURPOSE_DEPOSIT;int GetPurpose(){return purpose;}};
-struct Slot{Mount* mount=nullptr;Muzzle* muzzle=nullptr;Storage* storage=nullptr;Storage* GetStorage(){return storage;}};
+struct Slot{Mount* mount=nullptr;Muzzle* muzzle=nullptr;Storage* storage=nullptr;Entity* attached=nullptr;Entity* GetAttachedEntity(){return attached;}void* Container(){return mount?static_cast<void*>(mount):muzzle?static_cast<void*>(muzzle):static_cast<void*>(storage);}Storage* GetStorage(){return storage;}};
struct Item{Slot* slot=nullptr;Slot* GetParentSlot(){return slot;}};
using InventoryStorageSlot=Slot*;using WeaponSlotComponent=Mount*;
using InventoryItemComponent=Item*;using BaseInventoryStorageComponent=Storage*;
@@ -259,6 +262,26 @@
# WORKBENCH records are opt-in. Re-run every original geometry/count assertion
# in that build too, plus exact failure witnesses from the same traversal.
cases=r'''
+ // Exact reason64 distinguishes absent item, null slot and differing slot,
+ // while actual Inspect remains incomplete and retains the same ammunition.
+ for(int fault=0;fault<3;fault++){
+ Rig r;Mount mount;Item item;Slot binding,other;Storage storage;storage.purpose=EStoragePurpose::PURPOSE_ATTACHMENT_PROXY;other.storage=&storage;
+ mount.owner=&r.root;mount.weapon=&r.gun;mount.slot=&binding;binding.mount=&mount;binding.attached=&r.gun;
+ r.manager.weapons={&mount};r.gun.item=fault==0?nullptr:&item;
+ if(fault==2)item.slot=&other;
+ other.mount=&mount;other.attached=&r.gun;
+ ERR_HQMountedAmmoSampler::BeginDiagnostic();auto snap=r.Inspect();auto*d=ERR_HQMountedAmmoSampler::EndDiagnostic();
+ assert(!snap->Complete&&snap->Weapons==1&&snap->Rounds==25&&(d->Reasons&64));
+ assert(d->Detail.find(fault==0?"item=absent;":"item=present;")!=string::npos);
+ assert(d->Detail.find(fault==2?"parent=different-binding;":"parent=null;")!=string::npos);
+ assert(d->Detail.find("binding-attached=exact;")!=string::npos&&d->Detail.find("binding-container=exact-mount;")!=string::npos);
+ assert(d->Detail.find("mount-owner=allowed;")!=string::npos&&d->Detail.find("mount-prefab=fixture;")!=string::npos);
+ if(fault==2)assert(d->Detail.find("parent-mount-slot=other-or-null;")!=string::npos);
+ }
+ {Rig r;r.weapon.muzzles={};ERR_HQMountedAmmoSampler::BeginDiagnostic();auto snap=r.Inspect();auto*d=ERR_HQMountedAmmoSampler::EndDiagnostic();
+ assert(!snap->Complete&&snap->Weapons==1&&snap->Rounds==0&&(d->Reasons&4096));
+ assert(d->Detail.find("zero-muzzle-not-proven-placeholder;")!=string::npos&&d->Detail.find("zero-muzzle-slot-evidence:item=absent;parent=null;")!=string::npos);}
+''' + r'''
{Rig r;ERR_HQMountedAmmoSampler::BeginDiagnostic();auto snap=r.Inspect();auto*d=ERR_HQMountedAmmoSampler::EndDiagnostic();assert(snap->Complete&&d->Reasons==0&&d->Nodes==4&&d->Allowed==3&&d->Managers==1);assert(!ERR_HQMountedAmmoSampler::EndDiagnostic());}
{Entity root;root.child=&root;ERR_HQMountedAmmoSampler::BeginDiagnostic();auto snap=ERR_HQMountedAmmoSampler::Inspect(&root);auto*d=ERR_HQMountedAmmoSampler::EndDiagnostic();assert(!snap->Complete&&d->Reasons==4&&d->Nodes==1&&d->Allowed==1&&root.reads==1);}
{Entity root;std::vector<Entity> children(128);root.child=&children[0];for(int i=0;i<127;i++)children[i].sibling=&children[i+1];ERR_HQMountedAmmoSampler::BeginDiagnostic();auto snap=ERR_HQMountedAmmoSampler::Inspect(&root);auto*d=ERR_HQMountedAmmoSampler::EndDiagnostic();assert(!snap->Complete&&d->Reasons==1024&&d->Nodes==129&&d->Allowed==128&&d->Structural==129&&d->Cargo==0&&children[127].reads==0);}
@@ -0,0 +1,2 @@
PASS: actual mounted hierarchy sampling, ownership exclusion, deduplication, completeness/caps, quarter threshold and detached validation; no runtime policy or ammunition mutation
PASS: WORKBENCH opt-in diagnostics distinguish cycles/cap/storage/manager/mount failures without repeated native traversal
@@ -0,0 +1,27 @@
{
"complete": true,
"approved": true,
"blockers": [],
"manifest_sha256": "e2e66871228b47738075e6d5faf1657c3c1c10fdbed2850de3c2a8d13b6883cf",
"patch_sha256": "442d71268cbdb2d3bb50fbe73bcda0203ab74e8fdd9c4469f840b2bb4413a378",
"files": {
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQMountedAmmoSampler.c": {
"baseline_sha256": "2f0db77ba999586e4290e440d112f1002da17ab5fd20bb4f5b7ff6df99f58a81",
"proposal_sha256": "e8e36110e126de921d7635b8c319f923a48d84f700fa78e018bb40136d4819d1",
"index_sha256": "88ea0fc0e13d7e3c22c7ce8b652816d1d137e4e6e37a72f48f0bdf515b936ae7"
},
"tests/test_hq_mounted_ammo_sampler.py": {
"baseline_sha256": "a4d098adcd3d1f1156d00cf0753268f286cd63640bdae5aed81828f74a8b4633",
"proposal_sha256": "f4adb94e5e688ff3843ef240c7a4232308979f232a53fe7a6a1d94d02591b960",
"index_sha256": "b1c630909e5227d0f7bd0d69d7927403fcb0f33e7297d5fa52f96001720f8442"
}
},
"scope": "Read-only source/test/log review, no compiler or native run. WORKBENCH class and s_Diagnostic gate only. SlotEvidence records once per reason, no additional loops, no state mutation or changed acceptance/count/caps. Original return and Complete=false preserved.",
"api": "InventoryStorageSlot inherits GetAttachedEntity; GetParentContainer returns component; WeaponSlot GetOwner/GetSlotInfo/GetWeaponEntity and InventoryItem.GetParentSlot signatures match installed SDK. Null item/slot/mount guarded. allowed array is the established nonnull local collection.",
"tests": "Actual whole-sampler tests add absent item/null parent/different parent distinction and zero-muzzle rejected-placeholder witness while preserving existing count assertions. Retained default and WORKBENCH green logs, baseline runtime red, and adapter failure logs hash-verified.",
"limits": [
"No native compile or observed reason64 binding state yet.",
"First occurrence per reason cannot attribute every failing entity; labels are sampled evidence, not authorization.",
"Zero-muzzle branch cannot rediscover missing reverse mount; records missing relation honestly."
]
}
@@ -0,0 +1,46 @@
{
"Prefabs/Weapons/Core/Armament_Empty.et": {
"archive": "/home/jakub/.local/share/Steam/steamapps/compatdata/1874880/pfx/drive_c/users/steamuser/Documents/My Games/ArmaReforger/addons/WCS_Armaments_629B2BA37EFFD577/data.pak",
"sha256": "73b35bd524e30fe09f147f0dc5f197d49c341b46c660ec1ce402f143dc943731",
"same_as_prior_extracted_source": true,
"bytes": 1068
},
"Prefabs/Vehicles/Tracked/BMP1/VehParts/Turret/BMP1_Turret_Base.et": {
"archive": "/home/jakub/.local/share/Steam/steamapps/compatdata/1874880/pfx/drive_c/users/steamuser/Documents/My Games/ArmaReforger/addons/BMP-1IFV_5E524E4FEECCA92B/data.pak",
"sha256": "2fc39d7c2fc4bebe027efa5607ea71dd7dd0021daffb43f65332bf2698a52a4b",
"same_as_prior_extracted_source": true,
"bytes": 16134
},
"guid_identity": {
"database": "/home/jakub/.local/share/Steam/steamapps/compatdata/1874880/pfx/drive_c/users/steamuser/Documents/My Games/ArmaReforger/addons/WCS_Armaments_629B2BA37EFFD577/resourceDatabase.rdb",
"database_sha256": "7f38b7ed578b0b47864648a7b70288fe49dae4cc2556d5754377a1c28b2b6532",
"exact_record_count": 1,
"offset": 80834,
"guid": "52120E4BA120661B"
},
"sdk_hashes": {
"/tmp/erri-vanilla-stock/scripts/Game/generated/Weapon/WeaponSlotComponent.c": "f59ed140912e7ece862b1730af57cb8dcf2c65dbf40a27bba34eb83fa0d3bc58",
"/tmp/erri-vanilla-stock/scripts/Game/generated/Components/InventorySystem/InventoryItemComponent.c": "3d812681816be5d34a578f33e208d7802b22a899be008073bf437b7a8ecb2251",
"/tmp/erri-vanilla-stock/scripts/Game/generated/InventorySystem/InventoryStorageSlot.c": "45e584eb11f53e1b7374f375d57cdc58c8e757a7922c06cf5db9328ca3294cb5",
"/tmp/erri-vanilla-stock/scripts/Game/generated/EntitySlotInfo.c": "90e41e28a14bc677d829f38faff01ad9f0568d3ec4850c496568c379f8301e50"
},
"findings": [
"Exact standalone GenericEntity declares InventoryItemComponent with Enabled 0, WeaponComponent and no muzzle component; no inherited base prefab.",
"Exact BMP turret slot index3 declares AttachType InventoryStorageSlot Gun and this WeaponTemplate.",
"SDK WeaponSlotComponent exposes GetSlotInfo and GetWeaponEntity; InventoryStorageSlot exposes GetParentContainer and inherits EntitySlotInfo.GetAttachedEntity. SDK EntitySlotInfo.GetSlotInfo(entity) recognizes structural attachment through parent WeaponSlotComponent and GetAttachedEntity equality without requiring InventoryItemComponent.GetParentSlot.",
"InventoryItemComponent.GetParentSlot explicitly returns null if none. Native construction and consequences of Enabled 0 are not specified in available script; a reciprocal inventory-item slot is not promised for this disabled component.",
"Current reason64 conflates absent InventoryItemComponent with nonmatching/null parent slot. It follows successful allowed mounted entity, mount slot, and parent-container checks, but does not log which item-state branch failed."
],
"policy_candidate": "Only if native diagnostics establish the exact empty control structural binding: consider omission of an exact allowlisted zero-muzzle control via bounded structural slot proof (same allowed hierarchy, mount.GetWeaponEntity()==entity, mount.GetSlotInfo()==slot, slot.GetParentContainer()==mount, slot.GetAttachedEntity()==entity, exact physical weapon owner). Never relax inventory custody for real weapons/cargo or accept unknown/armed resources. Handle conflicting nonnull inventory binding fail-closed. This is a future candidate, not an approved implemented fix.",
"next_observation": "Extend existing opt-in reason64 diagnostic to separate component missing / null parent slot / different parent slot, and record slot.GetAttachedEntity equality plus zero-muzzle count for exact prefab. No extra gameplay state mutation or repeated native run requested by this review.",
"limits": [
"Read-only source and installed archive/RDB examination only; no native or compiler execution.",
"Enabled 0 supports a plausible explanation but is not runtime proof that null parent slot is expected; no source-only fix recommended."
],
"required_regressions_if_policy_adopted": [
"Actual whole sampler: exact known zero-muzzle control with observed missing reverse inventory binding and full structural slot proof is omitted; real guns and ammunition remain counted exactly once.",
"Refuse wrong GUID/path, nonzero muzzle list being omitted, unknown zero-muzzle resource, foreign hierarchy, missing/mismatched mount slot or parent container, different attached entity, and conflicting nonnull inventory parent slot.",
"Manager and direct hierarchy duplicate discovery of the same control remains deduplicated without poisoning completeness; known controls alone produce zero armed weapons and cannot satisfy the armed BMP fixture.",
"Retain existing real-weapon reciprocal custody, cargo/crew exclusion, node/weapon/muzzle bounds and fail-closed unknown behavior."
]
}
@@ -0,0 +1,28 @@
{
"files": {
"addons/core/Scripts/Game/ERR/10_AI/ERR_HQMountedAmmoSampler.c": {
"baseline_sha256": "2f0db77ba999586e4290e440d112f1002da17ab5fd20bb4f5b7ff6df99f58a81",
"proposal_sha256": "e8e36110e126de921d7635b8c319f923a48d84f700fa78e018bb40136d4819d1",
"index_sha256": "88ea0fc0e13d7e3c22c7ce8b652816d1d137e4e6e37a72f48f0bdf515b936ae7"
},
"tests/test_hq_mounted_ammo_sampler.py": {
"baseline_sha256": "a4d098adcd3d1f1156d00cf0753268f286cd63640bdae5aed81828f74a8b4633",
"proposal_sha256": "f4adb94e5e688ff3843ef240c7a4232308979f232a53fe7a6a1d94d02591b960",
"index_sha256": "b1c630909e5227d0f7bd0d69d7927403fcb0f33e7297d5fa52f96001720f8442"
}
},
"patch_sha256": "442d71268cbdb2d3bb50fbe73bcda0203ab74e8fdd9c4469f840b2bb4413a378",
"production_non_workbench_token_equivalent": true,
"normalization": "Remove WORKBENCH sections, comments, whitespace and redundant braces around the single existing else assignment. All remaining tokens exactly match.",
"tests": {
"green_exit": 0,
"baseline_exit": 1,
"logs": {
"fixture-failure.log": "53be342eb93454c82e26847fa799077ecce5e2daa4869ee89ec9a27d64b3af50",
"baseline-red.log": "5719ad13bf68d29feb26bc053851949d086c1e2c22d48a879af1edda2e422c83",
"adapter-string-failure.log": "715655dfd277f7414804c7b1ee06863da9660d1c3fc15aa06a94af01e56404c5",
"adapter-failure.log": "d5b8d71945dfac7e85487ac9a5847976fbd986350112226b5097515c62d93dbf",
"green.log": "427e7758df3b8746673da188ab74c12b409011eb4f4024b98fb709b7b5aea0a4"
}
}
}