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.
106 lines
9.2 KiB
Python
106 lines
9.2 KiB
Python
"""Actual WORKBENCH refresh-fixture observation/acceptance policy, not native proof."""
|
|
import re
|
|
from policy_harness import ROOT, body, run
|
|
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_NativeMortarSalvoProbe.c'
|
|
base=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_NativeMortarFiringProbe.c'
|
|
|
|
def adapt(s):
|
|
s=s.replace('protected ','').replace('null','nullptr').replace('string kind','std::string kind').replace('string reason, string failure','std::string reason, std::string failure')
|
|
for typ in ['ERR_HQReport','ERR_MortarBattery','ERR_NativeMortarRefreshRecord','ERR_MortarRefresh']:
|
|
s=re.sub(r'\b'+typ+r' (\w+)',typ+r'* \1',s)
|
|
for name in ['battery','report','observation','record','initial','update','m_Battery']:
|
|
s=s.replace(name+'.',name+'->')
|
|
for name in ['Report','Route','Delivered']:
|
|
s=s.replace('.'+name+'.','.'+name+'->').replace('->'+name+'.','->'+name+'->')
|
|
s=s.replace('ERR_MortarSalvo.','ERR_MortarSalvo::').replace('ERR_MortarPrediction.','ERR_MortarPrediction::').replace('ERR_NativeMortarEvidenceRules.','ERR_NativeMortarEvidenceRules::').replace('vector.Distance','vector::Distance')
|
|
s=re.sub(r'foreach \(ERR_NativeMortarRefreshRecord\* (\w+) : m_Observations\)',r'for (ERR_NativeMortarRefreshRecord* \1 : m_Observations.values)',s)
|
|
s=s.replace('].ObservedAt',']->ObservedAt')
|
|
return s
|
|
|
|
rules=adapt(body(p,'static bool QueueUpdate('))
|
|
evidence='\n'.join(adapt(body(base,s)) for s in ['static bool CommitFresh(', 'static bool AimFresh('])
|
|
matching=adapt(body(p,'bool Matches('))
|
|
methods='\n'.join(adapt(body(p,s)) for s in ['protected ERR_NativeMortarRefreshRecord FindObservation(', 'protected bool AddObservation(', 'protected bool ReportKnown(', 'protected bool AllAccepted(', 'protected bool NoReportReplay(', 'void NativeAudit(', 'void NativeCommit(', 'void NativeClosing('])
|
|
# A return type is a reference in Enforce, a pointer in this policy adapter.
|
|
methods=methods.replace('ERR_NativeMortarRefreshRecord FindObservation(', 'ERR_NativeMortarRefreshRecord* FindObservation(')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
using IEntity=void*;using SCR_AIGroup=void*;
|
|
struct vector {float x=0,y=0,z=0;float Length(){return std::sqrt(x*x+y*y+z*z);}static float Distance(vector a,vector b){return vector{a.x-b.x,a.y-b.y,a.z-b.z}.Length();}};
|
|
struct ERR_MortarSalvo {static constexpr int ROUNDS=8;};
|
|
struct ERR_MortarPrediction {static constexpr float MAX_REPORT_AGE_MS=60000;};
|
|
struct Rules {'''+rules+r'''};
|
|
struct ERR_NativeMortarEvidenceRules {'''+evidence+r'''};
|
|
template<class T>struct Items {std::vector<T> values;int Count(){return values.size();}bool IsEmpty(){return values.empty();}void Insert(T t){values.push_back(t);}T operator[](int n){return values[n];}};
|
|
struct ERR_HQReport {int MessageId=1;float ObservedAt=0;IEntity Target=nullptr;SCR_AIGroup Source=nullptr;vector Position{},Velocity{};bool HasVelocity=true,Distress=false;ERR_HQReport* Snapshot(){return new ERR_HQReport(*this);}};
|
|
struct Route {bool Downlink=true;int MessageId=3;Items<int> Nodes;Route(){Nodes.Insert(1);}};
|
|
struct ERR_MortarBattery;
|
|
struct ERR_MortarRefresh {IEntity Gun=nullptr,Group=nullptr;int OrderId=1,RadioId=3;ERR_HQReport* Report=nullptr;struct Route* Route=nullptr;bool Owns(ERR_MortarBattery*);};
|
|
struct ERR_MortarBattery {IEntity Gun=this,Group=this,Activity=this;int SourceId=1,OrderId=1,OrderShots=0;bool Released=false,CommandOpen=true,RefreshRejected=false,stopped=false;float AimedAt=1000,AimValidatedAt=24000,FlightTime=20;ERR_HQReport* Report=nullptr;ERR_MortarRefresh* Refresh=nullptr;std::set<int> claimed;bool Stopped(){return stopped;}bool CanRequest(ERR_HQReport* r,float){return !claimed.count(r->MessageId);}};
|
|
bool ERR_MortarRefresh::Owns(ERR_MortarBattery* b){return b&&Gun==b->Gun&&Group==b->Group&&OrderId==b->OrderId;}
|
|
struct ERR_NativeMortarRefreshRecord {int MessageId=0;float ObservedAt=0;int DownlinkId=0;bool Accepted=false;ERR_HQReport* Delivered=nullptr;
|
|
'''+matching+r'''};
|
|
template<class...Args>void PrintFormat(Args... args){}
|
|
struct Probe {
|
|
bool m_RefreshCase=true,m_Stopped=false,m_SawDownlink=true,m_SawActivity=false,m_BadProjectile=false,m_CommitPending=false,m_ClosureSeen=false,m_ClosureComplete=false;
|
|
ERR_MortarBattery* m_Battery=nullptr;IEntity m_Target=this,m_Group=this,m_OriginalActivity=nullptr,m_OriginalRTO=this;vector m_Impact{};
|
|
int m_Source=1,m_Order=1,m_UpdatesAccepted=0,m_UpdatedCommits=0,m_Commits=0,m_Step=1,m_CommitOrder=0,m_CommitShot=0,m_CommitSource=0,m_Events=0;float m_ObservedAt=0,m_CommittedAt=0,m_CommitObservedAt=0;std::string m_BadReason;ERR_HQReport* m_Delivered=nullptr;
|
|
Items<ERR_NativeMortarRefreshRecord*> m_Observations;
|
|
'''+methods+r'''};
|
|
struct F {
|
|
Probe p;ERR_MortarBattery b;ERR_HQReport first,next;ERR_MortarRefresh update;Route route;
|
|
F(){p.m_Battery=&b;first.Target=p.m_Target;first.Source=p.m_Group;next=first;next.MessageId=2;next.ObservedAt=20000;b.Report=&first;b.claimed.insert(1);update.Gun=b.Gun;update.Group=b.Group;update.Report=&next;update.Route=&route;}
|
|
void initial(){assert(p.AddObservation(1,0));p.FindObservation(1)->DownlinkId=2;p.NativeAudit(&b,"AIM");assert(!p.m_BadProjectile);}
|
|
void queued(){assert(p.AddObservation(2,20000));b.Refresh=&update;b.claimed.insert(2);p.NativeAudit(&b,"UPDATE QUEUED");}
|
|
void accepted(){update.Route=nullptr;b.Report=&next;p.NativeAudit(&b,"UPDATE RECEIVED");b.Refresh=nullptr;}
|
|
};
|
|
int main(){
|
|
assert(Rules::QueueUpdate(true,false,0,0,1000,20000));assert(!Rules::QueueUpdate(true,false,0,0,1000,19999));
|
|
assert(!Rules::QueueUpdate(false,false,0,0,1000,20000));assert(!Rules::QueueUpdate(true,true,0,0,1000,20000));
|
|
assert(!Rules::QueueUpdate(true,false,4,0,1000,20000));assert(!Rules::QueueUpdate(true,false,0,0,1000,91000));
|
|
assert(!Rules::QueueUpdate(true,false,0,0,22000,20000));
|
|
F f;f.initial();assert(f.p.ReportKnown(&f.first));assert(!f.p.ReportKnown(&f.next));assert(!f.p.AllAccepted());
|
|
assert(!f.p.AddObservation(1,1));assert(!f.p.AddObservation(2,0));assert(!f.p.AddObservation(0,100));
|
|
f.queued();assert(!f.p.ReportKnown(&f.next)&&f.p.ReportKnown(&f.next,false));assert(!f.p.AllAccepted());
|
|
f.accepted();assert(!f.p.m_BadProjectile&&f.p.ReportKnown(&f.next)&&f.p.m_UpdatesAccepted==1);
|
|
f.b.Released=true;f.p.NativeCommit(&f.b,25000);assert(!f.p.m_BadProjectile&&f.p.m_UpdatedCommits==1&&f.p.m_CommitSource==2&&f.p.m_CommitObservedAt==20000);
|
|
assert(f.p.AllAccepted()&&f.p.NoReportReplay(26000));f.b.claimed.erase(2);assert(!f.p.NoReportReplay(26000));f.b.claimed.insert(2);
|
|
f.b.OrderShots=8;f.p.m_Events=8;f.p.m_Commits=8;f.p.m_CommitPending=false;f.b.Released=false;
|
|
f.p.NativeClosing(&f.b,"native artillery activity COMPLETED (shot count is separate)","");assert(f.p.m_ClosureComplete&&!f.p.m_BadProjectile);
|
|
for(int fault=0;fault<7;fault++){
|
|
F a;a.initial();a.queued();
|
|
if(fault==0)a.update.OrderId=2;
|
|
if(fault==1)a.b.Activity=&a.next;
|
|
if(fault==2)a.next.ObservedAt=20001;
|
|
if(fault==3)a.next.Position.x=.02f;
|
|
if(fault==4)a.next.Target=&a.next;
|
|
if(fault==5)a.next.Source=&a.next;
|
|
if(fault==6)a.next.Velocity.x=1;
|
|
a.accepted();assert(a.p.m_BadProjectile&&!a.p.FindObservation(2)->Accepted);
|
|
}
|
|
F beforeRoute;beforeRoute.initial();beforeRoute.queued();beforeRoute.b.Report=&beforeRoute.next;beforeRoute.p.NativeAudit(&beforeRoute.b,"UPDATE RECEIVED");assert(beforeRoute.p.m_BadProjectile);
|
|
F committed;committed.initial();committed.queued();committed.b.Released=true;committed.accepted();assert(committed.p.m_BadProjectile);
|
|
F duplicate;duplicate.initial();duplicate.queued();duplicate.accepted();duplicate.b.Refresh=&duplicate.update;duplicate.p.NativeAudit(&duplicate.b,"UPDATE RECEIVED");assert(duplicate.p.m_BadProjectile&&duplicate.p.m_UpdatesAccepted==1);
|
|
F recreated;recreated.initial();recreated.p.NativeAudit(&recreated.b,"AIM");assert(recreated.p.m_BadProjectile);
|
|
F unaccepted;unaccepted.initial();unaccepted.queued();unaccepted.b.Report=&unaccepted.next;unaccepted.b.Released=true;unaccepted.p.NativeCommit(&unaccepted.b,25000);assert(unaccepted.p.m_BadProjectile&&unaccepted.p.m_Commits==0);
|
|
F extra;extra.initial();extra.queued();extra.accepted();extra.p.m_UpdatedCommits=1;assert(extra.p.AddObservation(3,40000));assert(!extra.p.AllAccepted());
|
|
F ignored;ignored.p.m_RefreshCase=false;ignored.p.NativeAudit(&ignored.b,"AIM");assert(!ignored.p.m_BadProjectile);assert(ignored.p.ReportKnown(&ignored.first));
|
|
}
|
|
''')
|
|
source=p.read_text();queue=body(p,'int ERR_QueueMortarRefreshReport(')
|
|
assert 'observedAt = GetGame().GetWorld().GetWorldTime()' in queue
|
|
assert 'm_Network.Route(radio)' in queue and 'unit.Radio != radio' in queue
|
|
assert 'unit.PendingReport = report; unit.Uplink = route;' in queue
|
|
assert 'm_Contacts.' not in queue and '.Order(' not in queue and 'BeginRefresh(' not in queue
|
|
assert 'report.ObservedAt = observedAt' in queue
|
|
assert 'outcome == "mortar-salvo" || outcome == "mortar-refresh"' in source
|
|
assert 'm_Battery.RadioOperator != m_OriginalRTO' in source
|
|
assert 'm_Battery.Report.MessageId != m_CommitSource' in body(p,'void NativeProjectile(')
|
|
assert 'ERR_NativeMortarSalvoRules.QueueUpdate(' in body(p,'int Tick(')
|
|
assert 'm_Battery.Report =' not in body(p,'int Tick(')
|
|
print('PASS: actual refresh-fixture gates pin immutable observations/actual downlinks/original activity, reject unaccepted or relabelled commits, stop queueing early and require all queued contacts claimed before replay')
|