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
+189
View File
@@ -0,0 +1,189 @@
"""Execute native refuel fixture policy/callback bodies; no engine qualification."""
from pathlib import Path
import os
from policy_harness import ROOT, body, run
P=Path(os.environ.get('ERR_NATIVE_MAINTENANCE_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/40_Chat/ERR_NativeMaintenanceProbe.c'))
within=body(P,'static bool WithinDeadline(')
fuel=body(P,'static bool FuelConserved(').replace('Math.AbsFloat','std::fabs')
finish=body(P,'protected void Finish(').replace('protected ','')
run(r'''
#include <cassert>
#include <cmath>
#include <limits>
'''+within+fuel+r'''
struct Callback {int Result=0;
'''+finish+r'''
};
int main(){
assert(WithinDeadline(100,100)&&WithinDeadline(100,120100));
assert(!WithinDeadline(100,99)&&!WithinDeadline(100,120101)&&!WithinDeadline(-1,0));
float nan=std::numeric_limits<float>::quiet_NaN();
assert(!WithinDeadline(nan,100)&&!WithinDeadline(0,nan));
assert(FuelConserved(5,5,20,20,40));
assert(FuelConserved(5,4.5,20,20.5,40));
assert(FuelConserved(5,0,20,25,40));
assert(!FuelConserved(5,4.5,20,21,40)); // free gain
assert(!FuelConserved(5,4,20,20.5,40)); // unaccounted loss
assert(!FuelConserved(5,6,20,19,40)); // refill/reverse transfer
assert(!FuelConserved(5,-1,20,26,40));
assert(!FuelConserved(5,0,20,25,24));
assert(!FuelConserved(0,0,20,20,40));
assert(!FuelConserved(6,0,20,26,40));
for(int field=0;field<5;field++){
float v[]={5,4,20,21,40};v[field]=nan;
assert(!FuelConserved(v[0],v[1],v[2],v[3],v[4]));
}
Callback a;a.Finish(1);a.Finish(-1);a.Finish(1);assert(a.Result==1);
Callback b;b.Finish(-1);b.Finish(1);assert(b.Result==-1);
}
''')
s=P.read_text();select=body(P,'protected bool Select(');tick=body(P,'int Tick(')
# Execute the actual Tick clock setup with fractional single-precision world times.
# Adding/subtracting 120000 loses low bits and can put the reconstructed start
# after the first tick; pure WithinDeadline tests alone cannot expose that.
clock=tick[tick.index(' if (m_'):tick.index(' if (m_State == 0)')]
clock=clock[clock.index(' if (m_Deadline < 0)' if 'm_Deadline < 0' in clock else ' if (m_Started < 0)'):]
run(r'''
#include <cassert>
'''+within+r'''
struct Probe {float m_Deadline=-1,m_Started=-1;bool failed=false;
void Check(bool, const char*) {failed=true;}
void DiagnoseReach() {}
int Tick(float now) {
'''+clock+r'''
return 0;
}
};
int main(){
for(int i=1;i<10000;i++){
float now=10000.0f+i*.013f;Probe p;
assert(p.Tick(now)==0&&!p.failed);
assert(p.Tick(now+60000)==0&&!p.failed);
assert(p.Tick(now+120016)==-1&&p.failed);
}
Probe p;assert(p.Tick(100)==0);assert(p.Tick(99)==-1&&p.failed);
}
''')
print('PASS: actual Tick clock initialization admits fractional first ticks and expires without extending the original start')
for mutation in ('SetFuel(', 'TryMove', 'Spawn(', 'SetGadgetMode(', 'PerformAction('):
assert mutation not in select,mutation
assert s.count('TryMoveItemToStorage(')==1 and s.count('m_Tank.SetFuel(')==1
assert 'm_CanFuel.Set' not in s and 'TrySpawn' not in s
assert 'm_State = 1;' in tick[:tick.index('TryMoveItemToStorage(')]
assert 'm_Move.Result == 1' in tick and 'GetParentSlot().GetStorage() == m_Destination' in tick
assert 'Check(m_Interrupted,' in tick and 'm_Calls <= 128' in tick
assert 'if (m_Mode && m_Mode == ERR_GameMode.Get()' in body(P,'protected void CancelAction(')
assert 'errMaintenanceRefuel' in body(P,'static bool Enabled(') and 'normal-escape-route' in body(P,'static bool Enabled(')
print('PASS: actual finite conservation/deadline/callback policies; read-only selection, bounded once-only inventory submission and no can refill; native transfer not executed')
# Execute exact context membership and native start sequencing. In the engine the
# action's cached point initially equals its root; SetActiveContext alone does not
# initialize it. Our double models that distinction instead of hiding the bug.
import re
methods='\n'.join(body(P,sig) for sig in ('protected bool ContextValid(', 'protected bool InteractionValid(', 'protected float ActionRange(', 'protected bool AtAction(', 'protected bool StartAction(', 'vector ApproachPosition(', 'protected void DiagnoseReach('))
methods=methods.replace('protected ','').replace('null','nullptr').replace('array<','Array<')
methods=methods.replace('Array<UserActionContext>','Array<UserActionContext*>').replace('Array<BaseUserAction>','Array<BaseUserAction*>')
for ns in ('ERR_ForceRules','ERR_Actors','vector','Math'):methods=methods.replace(ns+'.',ns+'::')
for obj in ('m_Vehicle','m_Action','m_Actions','m_Context','m_Player','m_Controller','m_Interaction'):methods=re.sub(r'\b'+obj+r'\.',obj+'->',methods)
methods=methods.replace('FindComponent(BaseActionsManagerComponent)','FindComponent()').replace('FindComponent(SCR_InteractionHandlerComponent)','FindComponent()').replace('GetGame().','GetGame()->')
run(r'''
#include <cassert>
#include <vector>
#include <algorithm>
#include <cmath>
#include <string>
struct vector {float x=0,y=0,z=0;static const vector Zero;float&operator[](int i){return i==0?x:(i==1?y:z);}float LengthSq(){return x*x+y*y+z*z;}void Normalize(){float d=std::sqrt(LengthSq());x/=d;y/=d;z/=d;}vector operator-(vector b){return {x-b.x,y-b.y,z-b.z};}vector operator+(vector b){return {x+b.x,y+b.y,z+b.z};}vector operator*(float b){return {x*b,y*b,z*b};}static float Distance(vector a,vector b){return std::sqrt((a-b).LengthSq());}};
const vector vector::Zero{};
namespace ERR_Actors {vector Ground(vector p){return p;}}
namespace Math {float Min(float a,float b){return std::min(a,b);}}
namespace ERR_ForceRules {bool Finite(float v){return std::isfinite(v);}bool FiniteVector(vector v){return Finite(v.x)&&Finite(v.y)&&Finite(v.z);}}
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;using std::vector<T>::operator=;int Count(){return this->size();}bool Contains(T t){return std::find(this->begin(),this->end(),t)!=this->end();}};
struct BaseUserAction;struct UserActionContext;struct BaseActionsManagerComponent;
struct Entity {vector pos;BaseActionsManagerComponent*manager=nullptr;vector GetOrigin(){return pos;}BaseActionsManagerComponent*FindComponent(){return manager;}};
struct UserActionContext {vector pos{2};float radius=2,visibility=0,height=0;bool visible=true,capsule=false;Array<BaseUserAction*>actions;void GetActionsList(Array<BaseUserAction*>&out){out=actions;}vector GetOrigin(){return pos;}float GetRadius(){return radius;}float GetHeight(){return height;}bool IsCapsule(){return capsule;}std::string GetContextName(){return "fuel";}float GetVisibilityRange(float global){return std::max(global,visibility);}bool IsInVisibilityAngle(vector){return visible;}};
struct BaseActionsManagerComponent {bool enabled=true;Array<UserActionContext*>contexts;bool IsEnabled(){return enabled;}void GetContextList(Array<UserActionContext*>&out){out=contexts;}};
struct BaseUserAction {Entity*owner=nullptr;UserActionContext*context=nullptr;bool progressing=false,cacheOnStart=true,disabled=false;int set=0,started=0;vector cached;
Entity*GetOwner(){return owner;}bool WasDisabledByServer(){return disabled;}bool IsInProgress(){return progressing;}void SetActiveContext(UserActionContext*c){context=c;set++;}UserActionContext*GetActiveContext(){return context;}
void OnActionStart(Entity*){assert(context&&set==1);started++;if(cacheOnStart)cached=context->GetOrigin();}vector GetWorldPositionAction(){return cached;}};
struct SCR_InteractionHandlerComponent{Entity*actor=nullptr;float range=3;Entity*GetControlledEntity(){return actor;}float GetVisibilityRange(){return range;}};
struct PlayerController{Entity*actor=nullptr;SCR_InteractionHandlerComponent*interaction=nullptr;Entity*GetControlledEntity(){return actor;}SCR_InteractionHandlerComponent*FindComponent(){return interaction;}};
struct Game{PlayerController*controller=nullptr;PlayerController*GetPlayerController(){return controller;}}game;Game*currentGame=&game;Game*GetGame(){return currentGame;}
int prints=0;template<class...Args>void PrintFormat(Args...){prints++;}
struct Probe{PlayerController*m_Controller=nullptr;SCR_InteractionHandlerComponent*m_Interaction=nullptr;bool m_ReachDiagnosed=false;int m_State=3;Entity*m_Vehicle=nullptr,*m_Player=nullptr;BaseUserAction*m_Action=nullptr;BaseActionsManagerComponent*m_Actions=nullptr;UserActionContext*m_Context=nullptr;bool m_ActionStarted=false,held=true,allowed=true;int eligibility=0;
bool Held(){return held;}bool ActionAllowed(){eligibility++;assert(m_ActionStarted&&m_Action->started==1&&m_Action->GetActiveContext()==m_Context);assert(vector::Distance(m_Action->GetWorldPositionAction(),m_Context->GetOrigin())<=.01);return allowed;}
'''+methods+r'''
};
struct Fixture{PlayerController controller;SCR_InteractionHandlerComponent interaction;Entity vehicle,player;BaseUserAction action;BaseActionsManagerComponent manager;UserActionContext context;Probe probe;
Fixture(){currentGame=&game;game.controller=&controller;controller.actor=&player;controller.interaction=&interaction;interaction.actor=&player;probe.m_Controller=&controller;probe.m_Interaction=&interaction;vehicle.manager=&manager;player.pos={2};action.owner=&vehicle;manager.contexts={&context};context.actions={&action};probe.m_Vehicle=&vehicle;probe.m_Player=&player;probe.m_Action=&action;probe.m_Actions=&manager;probe.m_Context=&context;}};
int main(){
Fixture ok;assert(ok.action.GetWorldPositionAction().x==0&&ok.context.GetOrigin().x==2);assert(ok.probe.ContextValid()&&ok.probe.AtAction());assert(std::fabs(ok.probe.ApproachPosition().x-2.75f)<.001);assert(ok.probe.StartAction());assert(ok.action.started==1&&ok.action.set==1&&ok.probe.eligibility==1);assert(!ok.probe.StartAction()&&ok.action.started==1);
for(int fault=0;fault<13;fault++){Fixture f;BaseActionsManagerComponent foreign;Entity other;UserActionContext otherContext;BaseUserAction otherAction;
switch(fault){case 0:f.vehicle.manager=&foreign;break;case 1:f.manager.enabled=false;break;case 2:f.manager.contexts={&otherContext};break;case 3:f.context.actions={&otherAction};break;case 4:f.manager.contexts.resize(65,&f.context);break;case 5:f.context.actions.resize(65,&f.action);break;case 6:f.context.pos={NAN};break;case 7:f.context.radius=0;break;case 8:f.context.radius=11;break;case 9:f.action.owner=&other;break;case 10:f.probe.m_Context=nullptr;break;case 11:f.probe.m_Vehicle=nullptr;break;case 12:f.action.disabled=true;break;}
assert(!f.probe.ContextValid()&&!f.probe.StartAction()&&!f.action.started&&!f.action.set);}
for(int fault=0;fault<4;fault++){Fixture f;if(fault==0)f.probe.held=false;if(fault==1)f.player.pos={10};if(fault==2)f.context.visible=false;if(fault==3)f.action.progressing=true;assert(!f.probe.StartAction()&&!f.action.started&&!f.action.set);}
// Small elevated sphere/capsule is a target shape, not a feet reach limit.
for(bool capsule:{false,true}){Fixture f;f.context.radius=.2;f.context.height=2;f.context.capsule=capsule;f.context.pos={2,1.2,0};f.player.pos={2.5,0,0};assert(f.probe.AtAction()&&f.probe.StartAction());}
// Use exact configured global/context visibility, with no arbitrary3m ceiling.
Fixture configured;configured.interaction.range=2;configured.context.visibility=5;configured.player.pos={6.5,0,0};assert(configured.probe.ActionRange()==5&&configured.probe.AtAction());configured.player.pos={7.001f,0,0};assert(!configured.probe.AtAction());
for(int fault=0;fault<9;fault++){Fixture f;Entity other;PlayerController foreign;SCR_InteractionHandlerComponent foreignHandler;
switch(fault){case 0:game.controller=&foreign;break;case 1:f.controller.actor=&other;break;case 2:f.controller.interaction=&foreignHandler;break;case 3:f.interaction.actor=&other;break;case 4:currentGame=nullptr;break;case 5:f.probe.m_Controller=nullptr;break;case 6:f.probe.m_Interaction=nullptr;break;case 7:f.interaction.range=-1;break;case 8:f.interaction.range=NAN;break;}
assert(!f.probe.AtAction()&&!f.probe.StartAction()&&!f.action.started);}
Fixture diagnostic;prints=0;diagnostic.probe.DiagnoseReach();assert(prints==3);diagnostic.probe.DiagnoseReach();assert(prints==3);diagnostic.probe.m_Context=nullptr;diagnostic.probe.DiagnoseReach();assert(prints==3);
Fixture missingDiagnostic;missingDiagnostic.probe.m_Context=nullptr;prints=0;missingDiagnostic.probe.DiagnoseReach();missingDiagnostic.probe.DiagnoseReach();assert(prints==1);
Fixture staleCache;staleCache.action.cacheOnStart=false;assert(!staleCache.probe.StartAction()&&staleCache.action.started==1&&staleCache.probe.eligibility==0&&staleCache.probe.m_ActionStarted);
Fixture refusal;refusal.probe.allowed=false;assert(!refusal.probe.StartAction()&&refusal.action.started==1&&refusal.probe.m_ActionStarted);
}
''')
assert 'm_Action.GetWorldPositionAction()' not in body(P,'vector ApproachPosition(')
context=body(P,'protected bool Context(')
for name in ('m_Fuel','m_CanFuel','m_SpareFuel'):assert name+'.GetOwner()' not in context
assert 'm_Tank.GetFuel() - 6' in tick and 'm_Tank.GetMaxFuel() - beforeTank >= 0.99' in tick
print('PASS: actual exact-context membership, bounded identity/reach gates, native start/cache/eligibility ordering and no duplicate/foreign action start; fuel manager identity and empty-can headroom guards checked')
# The native out-array may alias a manager-owned list. Exercise the actual
# snapshot and conservation methods with an explicitly aliasing boundary double.
nodes='\n'.join(body(P,sig) for sig in ('protected bool CaptureNodes(', 'protected bool RefuseNodeSnapshot(', 'protected bool Conservation('))
nodes=nodes.replace('protected ','').replace('array<BaseFuelNode>','Array<BaseFuelNode*>').replace('foreach (BaseFuelNode node : found)','for (BaseFuelNode* node : found)')
for ns in ('SCR_FuelNode','ERR_ForceRules','Math'):nodes=nodes.replace(ns+'.',ns+'::')
for obj in ('m_Fuel','m_CanFuel','m_Tank','node'):nodes=re.sub(r'\b'+obj+r'\.',obj+'->',nodes)
nodes=nodes.replace('nodes[i].','nodes[i]->')
run(r'''
#include <cassert>
#include <vector>
#include <memory>
#include <cmath>
#include <algorithm>
#include <limits>
template<class T>struct Array{
std::shared_ptr<std::vector<T>> data=std::make_shared<std::vector<T>>();
int Count(){return data->size();}bool IsEmpty(){return data->empty();}void Insert(T value){data->push_back(value);}T&operator[](int index){return data->at(index);}auto begin(){return data->begin();}auto end(){return data->end();}
};
struct BaseFuelNode{float fuel=10,capacity=40;bool supported=true;float GetFuel(){return fuel;}float GetMaxFuel(){return capacity;}};
namespace SCR_FuelNode{BaseFuelNode*Cast(BaseFuelNode*n){return n&&n->supported?n:nullptr;}}
namespace ERR_ForceRules{bool Finite(float v){return std::isfinite(v);}}
namespace Math{float AbsFloat(float v){return std::fabs(v);}}
struct Manager{Array<BaseFuelNode*> native;float total=5;int reads=0;void GetFuelNodesList(Array<BaseFuelNode*>&out){reads++;out.data=native.data;}float GetTotalFuel(){return total;}};
int diagnostics=0,seenCurrent=-99,seenOriginal=-99,seenValues=-99;
void PrintFormat(const char*,int current,int original,int values){diagnostics++;seenCurrent=current;seenOriginal=original;seenValues=values;}
'''+fuel+r'''
struct Probe{Manager*m_Fuel=nullptr,*m_CanFuel=nullptr;BaseFuelNode*m_Tank=nullptr;Array<BaseFuelNode*>m_Nodes;Array<float>m_InitialNodes;bool m_NodeSnapshotDiagnosed=false;float m_InitialCan=5,m_BaseTank=10;
'''+nodes+r'''
};
struct Fixture{Manager vehicle,can;BaseFuelNode tank,other;Probe probe;Fixture(){vehicle.native.Insert(&tank);vehicle.native.Insert(&other);probe.m_Fuel=&vehicle;probe.m_CanFuel=&can;probe.m_Tank=&tank;}};
int main(){
Fixture truncated;assert(truncated.probe.CaptureNodes());truncated.probe.m_InitialNodes.data->pop_back();assert(!truncated.probe.Conservation());
Fixture stable;assert(stable.probe.CaptureNodes());assert(stable.probe.m_Nodes.Count()==2&&stable.probe.m_InitialNodes.Count()==2);assert(stable.probe.Conservation());assert(!stable.probe.CaptureNodes());
stable.can.total=4;stable.tank.fuel=11;assert(stable.probe.Conservation());stable.can.total=0;stable.tank.fuel=15;assert(stable.probe.Conservation());
// Exact known native count drift cannot silently mutate the retained baseline.
Fixture alias;assert(alias.probe.CaptureNodes());BaseFuelNode added;alias.vehicle.native.Insert(&added);assert(alias.probe.m_Nodes.Count()==2&&alias.probe.m_InitialNodes.Count()==2);diagnostics=0;assert(!alias.probe.Conservation());assert(diagnostics==1&&seenCurrent==3&&seenOriginal==2&&seenValues==2);assert(!alias.probe.Conservation()&&diagnostics==1);
// Every length mismatch refuses before any checked array indexing.
for(int fault=0;fault<6;fault++){Fixture f;assert(f.probe.CaptureNodes());switch(fault){case 0:f.probe.m_InitialNodes.data->clear();break;case 1:f.probe.m_InitialNodes.data->pop_back();break;case 2:f.probe.m_InitialNodes.Insert(1);break;case 3:f.probe.m_Nodes.data->pop_back();break;case 4:f.vehicle.native.data->clear();break;case 5:f.vehicle.native.data->resize(9,&f.tank);break;}diagnostics=0;assert(!f.probe.Conservation()&&diagnostics==1);assert(!f.probe.Conservation()&&diagnostics==1);}
for(int fault=0;fault<6;fault++){Fixture f;assert(f.probe.CaptureNodes());BaseFuelNode foreign;switch(fault){case 0:f.vehicle.native[1]=&foreign;break;case 1:f.vehicle.native[1]=nullptr;break;case 2:std::swap(f.vehicle.native[0],f.vehicle.native[1]);break;case 3:f.probe.m_InitialNodes[1]=NAN;break;case 4:f.probe.m_Fuel=nullptr;break;case 5:f.probe.m_CanFuel=nullptr;break;}assert(!f.probe.Conservation());}
Fixture changed;assert(changed.probe.CaptureNodes());changed.other.fuel+=1;assert(!changed.probe.Conservation());changed.other.fuel-=1;changed.can.total=4;changed.tank.fuel=12;assert(!changed.probe.Conservation());
for(int fault=0;fault<4;fault++){Fixture f;if(fault==0)f.vehicle.native.data->clear();if(fault==1)f.vehicle.native.data->resize(9,&f.tank);if(fault==2)f.other.supported=false;if(fault==3)f.other.fuel=NAN;assert(!f.probe.CaptureNodes());}
}
''')
assert 'GetFuelNodesList(m_Nodes)' not in s
assert body(P,'protected bool Select(').count('CaptureNodes()')==1
print('PASS: actual detached node/value snapshot and conservation refuse mismatched lengths before indexed reads, retain original identities under native out-array aliasing and log bounded counts once; native cause remains unknown')