Merge owned systems into one addon while preserving resource identities. Gate player admission on the replicated prison scene, restore the original framed door, and hold enemy AI until escape. Correct scenario branding and author metadata, include publishing assets, and retain regression and native verification evidence.
109 lines
6.8 KiB
Python
109 lines
6.8 KiB
Python
"""Narrow Workbench body-fixture contracts; native LOS/radio proof requires an engine run."""
|
|
from pathlib import Path
|
|
import os, re
|
|
from policy_harness import ROOT, body, run
|
|
stage=os.environ.get('ERR_NATIVE_BODY_STAGE')
|
|
p=Path(stage)/'ERR_NativeHQBodyProbe.c' if stage else ROOT/'addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeHQBodyProbe.c'
|
|
s=p.read_text()
|
|
smoke=(Path(stage)/'ERR_NativeSmoke.c' if stage else ROOT/'addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeSmoke.c').read_text()
|
|
runner=(Path(stage)/'headless_workbench.py' if stage else ROOT/'tools/headless_workbench.py').read_text()
|
|
assert s.startswith('#ifdef WORKBENCH') and s.rstrip().endswith('#endif')
|
|
for forbidden in ('Spawn(', 'SpawnEntity', 'TrySpawn', 'new ERR_HQReport', 'new ERR_HQStatusReport',
|
|
'.Ingest(', 'Accept(', '.Advance(', 'SetHealth(', 'DeleteEntity', 'm_Contacts.Insert'):
|
|
assert forbidden not in s, forbidden
|
|
# One clearly disclosed scene mutation; no repeated movement to keep LOS artificially true.
|
|
assert s.count('.SetOrigin(')==1 and s.count('.Kill(')==1
|
|
assert 'actor == victim.Radio' in s and 'victim == observer' in s
|
|
prepare=body(p,'ERR_NativeHQBodySetup ERR_BodyFixturePrepare(')
|
|
assert 'InitialMember(slot)' in prepare and 'CanAttribute(slot, actor)' in prepare
|
|
assert 'victim.NextStatus = now + 180000' in prepare
|
|
pending=body(p,'ERR_HQUnit ERR_BodyFixturePending(')
|
|
assert 'ObserveBody(fixture.ObserverUnit, now)' in pending
|
|
assert 'PendingReport.Target == fixture.Actor' in pending and 'PendingReport.TravelMode == "body"' in pending
|
|
assert 'PendingReport =' not in pending and 'Uplink =' not in pending
|
|
assert 'reporter.PendingReport.Snapshot()' in s and 'm_Route = reporter.Uplink' in s
|
|
for proof in ('m_Route.MessageId == m_Report.MessageId','m_Report.Source == reporter.Group',
|
|
'm_Report.CasualtySlot == m_Setup.InitialSlot','m_Route.Cursor < m_Route.Nodes.Count()',
|
|
'status.MessageId == m_Setup.StatusMessageId','status.Position == m_Setup.StatusPosition',
|
|
'status.ConfirmedLosses == 1','m_Status.DeathSlots[0] == m_Setup.InitialSlot',
|
|
'm_StatusRoute.Cursor < m_StatusRoute.Nodes.Count()', 'm_StableUntil = now + 20000',
|
|
'belief.Kind == ERR_HQMemoryKind.BODY', '!hq.ERR_BodyFixtureContact(m_Setup.Actor)'):
|
|
assert proof in s,proof
|
|
assert 'now + 150000' in s and 'void Stop()' in s and 'NextStatus = m_Setup.SavedNextStatus' in s
|
|
assert 'm_ERRTestHQBody = outcome == "hq-body"' in smoke and 'm_ERRSmokeStep = 107' in smoke
|
|
branch=smoke.split('else if (m_ERRSmokeStep == 107)',1)[1].split('else if (m_ERRSmokeStep == 106)',1)[0]
|
|
assert 'm_ERRHQBodyProbe.Tick(ERR_HQ.Cast(Command()), now)' in branch
|
|
assert 'm_ERRHQBodyProbe.Stop()' in branch and 'm_ERRSmokeStep = 9' in branch
|
|
assert "'hq-body'" in runner and "a.project != 'chat' or a.rounds != 1" in runner
|
|
print('PASS: native body fixture uses existing immutable actors, real production LOS/queued uplink, pre-delivery isolation, actual status overlap, bounded cleanup and no fabricated reports/spawns')
|
|
|
|
# Execute the actual bounded lookup: the public Get(actor) API is sighting-only.
|
|
assert 'Memory().Get(' not in p.read_text()
|
|
lookup=body(p,'protected ERR_HQBelief BodyBelief(')
|
|
lookup=lookup.replace('protected ', '').replace('ERR_HQMemoryKind.', 'ERR_HQMemoryKind::')
|
|
lookup=lookup.replace('ERR_HQBelief ', 'ERR_HQBelief* ').replace('ERR_HQ hq','ERR_HQ* hq')
|
|
lookup=lookup.replace('hq.Memory().', 'hq->Memory()->').replace('belief.', 'belief->').replace('null','nullptr')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <vector>
|
|
using IEntity=void*;
|
|
struct ERR_HQMemoryKind{static constexpr int SIGHTING=0,BODY=2;};
|
|
struct ERR_HQBelief{int Kind;IEntity Target;int MessageId;};
|
|
struct Memory{std::vector<ERR_HQBelief*> entries;int Count(){return entries.size();}
|
|
ERR_HQBelief* At(int i){return entries.at(i);}};
|
|
struct ERR_HQ{struct Memory memory;struct Memory* Memory(){return &memory;}};
|
|
struct Probe{
|
|
'''+lookup+r'''
|
|
};
|
|
int main(){
|
|
ERR_HQ hq;Probe probe;int actor=0,other=0;
|
|
ERR_HQBelief sighting{0,&actor,101},differentBody{2,&other,101},oldBody{2,&actor,100},exact{2,&actor,101};
|
|
hq.memory.entries={nullptr,&sighting,&differentBody,&oldBody};
|
|
assert(!probe.BodyBelief(&hq,&actor,101));
|
|
hq.memory.entries.push_back(&exact);
|
|
assert(probe.BodyBelief(&hq,&actor,101)==&exact);
|
|
assert(!probe.BodyBelief(&hq,&actor,102));
|
|
}
|
|
''')
|
|
print('PASS: actual native fixture lookup selects exact BODY kind/identity/message from bounded evaluations, never sighting-only Get')
|
|
|
|
# Native ammo proof extends the already-real status delivery; it never changes gear or sender payloads.
|
|
for forbidden in ('SetAmmoCount(', 'ClearChamber(', 'TryMoveItem', 'TryDelete', 'TrySpawn'):
|
|
assert forbidden not in p.read_text(),forbidden
|
|
native_ammo=body(p,'protected bool NativeAmmo(')
|
|
for evidence in ('InitialMember(slot)', 'ERR_Actors.Alive(actor)', 'ERR_Actors.Group(actor) != unit.Group',
|
|
'CanAttribute(slot, actor)', 'ERR_HQAmmoSampler.Inspect(actor, unit.Group, inspected)',
|
|
'manager.GetWeaponsList(weapons)', 'loaded.GetAmmoCount()',
|
|
'loadedIdentities.Contains(loaded.GetOwner())', 'queued.SmallArmsRounds >= nativeLoadedRounds',
|
|
'SameAmmo(queued, inspected)', 'nativeLoadedRounds > 0'):
|
|
assert evidence in native_ammo,evidence
|
|
assert 'PendingStatus =' not in native_ammo and 'AmmoState =' not in native_ammo
|
|
for frozen in ('m_AmmoAtQueue = m_Status.AmmoState.Snapshot()',
|
|
'status.AmmoState != m_Status.AmmoState', 'status.AmmoState != m_AmmoAtQueue',
|
|
'SameAmmo(status.AmmoState, m_AmmoAtQueue)', 'SameAmmo(stable.AmmoState, m_AmmoAtQueue)'):
|
|
assert frozen in p.read_text(),frozen
|
|
compare=body(p,'protected bool SameAmmo(').replace('protected ', '').replace('ERR_HQAmmoSnapshot ', 'ERR_HQAmmoSnapshot* ')
|
|
compare=compare.replace('a.', 'a->').replace('b.', 'b->')
|
|
run(r'''
|
|
#include <cassert>
|
|
struct ERR_HQAmmoSnapshot{
|
|
bool Complete=true,LauncherComplete=false;
|
|
int Members=5,SmallArmsMuzzles=5,SmallArmsRounds=150,SmallArmsMagazines=10,SmallArmsChambers=5;
|
|
int LauncherRounds=2,LauncherMagazines=2,LauncherChambers=0;
|
|
};
|
|
struct Probe{
|
|
'''+compare+r'''
|
|
};
|
|
int main(){
|
|
Probe probe;ERR_HQAmmoSnapshot a,b;
|
|
assert(probe.SameAmmo(&a,&b)&&!probe.SameAmmo(nullptr,&b)&&!probe.SameAmmo(&a,nullptr));
|
|
int* fields[]={&b.Members,&b.SmallArmsMuzzles,&b.SmallArmsRounds,&b.SmallArmsMagazines,&b.SmallArmsChambers,
|
|
&b.LauncherRounds,&b.LauncherMagazines,&b.LauncherChambers};
|
|
for(auto* field:fields){(*field)++;assert(!probe.SameAmmo(&a,&b));(*field)--;}
|
|
b.Complete=false;assert(!probe.SameAmmo(&a,&b));b.Complete=true;
|
|
b.LauncherComplete=true;assert(!probe.SameAmmo(&a,&b));b.LauncherComplete=false;
|
|
assert(probe.SameAmmo(&a,&b));
|
|
}
|
|
''')
|
|
print('PASS: actual ammo snapshot comparison checks every scalar/category flag; native probe requires real roster rescan, loaded-round lower bound and separate immutable pending/Watch objects')
|