Add configurable main-road checkpoints and reconnect replacements; record Workshop 0.0.136
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -8,6 +8,7 @@ s=s.replace('protected ', '').replace('ERR_ScenarioDefinition definition', 'Defi
|
||||
s=s.replace('array<vector> positions = {};', 'Array<int> positions;').replace('array<ref ERR_RoadblockProp> covers = {};', 'Array<int> covers;')
|
||||
s=s.replace('GetGame().GetCallqueue().CallLater(BackgroundStep, 200, true)', 'queue.Schedule(200)').replace('GetGame().GetCallqueue().Remove(BackgroundStep)', 'queue.Remove()')
|
||||
s=s.replace('GetGame().GetWorldEntity().GetWorldBounds(m_Low, m_High)', 'Bounds(m_Low, m_High)').replace('GetGame().GetPlayerManager().GetPlayerCount()', 'players')
|
||||
s=s.replace('m_Mode.Settings().Get("largeRoadblockCount")', 'm_Mode->largeTarget').replace('m_Mode.Settings().Get("mainRoadWidthMetres")', '6')
|
||||
s=s.replace('m_Mode.Settings().Get("roadblockCount")', 'm_Mode->target').replace('m_Mode.Forces().Count()', 'm_Mode->ledger')
|
||||
s=s.replace('m_Mode.', 'm_Mode->').replace('System.GetTickCount()', 'Now()').replace('Replication.IsServer()', 'server').replace('ERR_GameMode.Get()', 'current')
|
||||
s=s.replace('ERR_PrepareResult.', 'Result::').replace('ERR_Phase.', 'Phase::').replace('ERR_RoadblockRules.', 'Rules::').replace('ERR_TerrainSites.Bounds', 'ValidBounds').replace('ERR_HQ.Event','Event').replace('Math.Max','std::max').replace('null','nullptr')
|
||||
@@ -28,14 +29,14 @@ int Now(){return clockMs;}bool GetGame(){return true;}void Bounds(int&,int&){}bo
|
||||
template<class...T>void PrintFormat(T...){}void Event(string,string){}
|
||||
template<class T>struct Array:std::vector<T>{int Count(){return this->size();}void Clear(){this->clear();}};
|
||||
struct Queue{int scheduled=0,removed=0;bool active=false;void Schedule(int ms){assert(ms==200);scheduled++;active=true;}void Remove(){removed++;active=false;}};
|
||||
struct Mode{int target=30,ledger=0,phase=Phase::PRISON;bool active=true;int Phase(){return phase;}bool Active(){return active;}};Mode* current=nullptr;
|
||||
struct Mode{int target=30,largeTarget=0,ledger=0,phase=Phase::PRISON;bool active=true;int Phase(){return phase;}bool Active(){return active;}};Mode* current=nullptr;
|
||||
struct Definition{};
|
||||
struct Mining{bool stopped=false;void Stop(){stopped=true;}};
|
||||
struct Scheduler{
|
||||
Mode* m_Mode;Definition* m_Definition=nullptr;Queue queue;Mining m_Mining;
|
||||
bool m_BuildGroup=false;void BuildPost(){if(m_Mode->ledger>=250)CancelPost();}void CancelPost(){m_BuildGroup=false;}
|
||||
bool m_Begun=false,m_Ready=false,m_Stopped=false,m_HaveCandidate=false,m_Background=false;
|
||||
int m_Target=0,m_Attempts=0,m_Started=0,m_CandidateStarted=0,m_Candidates=0,m_Planned=0;
|
||||
int m_SmallTarget=0,m_LargeTarget=0,m_MainRoadWidth=0,m_Target=0,m_Attempts=0,m_Started=0,m_CandidateStarted=0,m_Candidates=0,m_Planned=0;
|
||||
int m_NextSearch=0,m_NextReport=0,m_BackgroundSteps=0,m_BackgroundWork=0,m_MaxBackgroundStep=0,m_Low=0,m_High=0;
|
||||
Array<int>m_Loaded,m_Positions,m_Groups;
|
||||
int candidateCost=0,candidateCalls=0,planCalls=0,spawnCalls=0;bool accept=false,spawn=true,roster=true;int plan=2;
|
||||
@@ -110,7 +111,7 @@ s=s.replace('IEntity entity', 'Node* entity').replace('RplComponent component;',
|
||||
s=s.replace('RplComponent.Cast', 'Replica::Cast').replace('FindComponent(RplComponent)', 'FindComponent()').replace('RplId.Invalid()', '-1')
|
||||
s=s.replace('entity.FindComponent', 'entity->FindComponent').replace('m_BuildSpawned[i].FindComponent', 'm_BuildSpawned[i]->FindComponent').replace('component.', 'component->')
|
||||
for ns in ('ERR_Actors','ERR_Resources','ERR_ForceKind','ERR_RoadblockRules'):s=s.replace(ns+'.',ns+'::')
|
||||
rule=body(p,'static int PropCount(')
|
||||
rule=body(p,'static int PropCount(')+body(p,'static int GuardCount(')
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
@@ -124,13 +125,14 @@ struct Node{Replica replica;bool missing=false;Replica* FindComponent(){return m
|
||||
namespace ERR_Actors{int calls=0,failAt=0;Array<Node*> all;Node* Spawn(string,int,int=0){calls++;if(calls==failAt)return nullptr;auto*n=new Node;all.Insert(n);return n;}bool SetFaction(Node*,int){return true;}}
|
||||
namespace ERR_Resources{string DefendWaypoint(){return "hold";}}
|
||||
namespace ERR_ForceKind{int CHECKPOINT=1;}
|
||||
struct Ledger{int count=0,registered=0;bool accept=true;int Count(){return count;}bool Register(string,int,int,int strength,void*,Node*,int){assert(strength==4);if(!accept)return false;registered++;return true;}};
|
||||
struct Ledger{int count=0,registered=0;bool accept=true;int Count(){return count;}bool Register(string,int,int,int strength,void*,Node*,int){assert(strength==4||strength==8);if(!accept)return false;registered++;return true;}};
|
||||
struct Mode{Ledger ledger;int owned=0,groups=0;Ledger& Forces(){return ledger;}int EnemyFaction(){return 1;}void Own(Node*n){assert(n);owned++;}void RegisterGroup(Node*){groups++;}};
|
||||
struct Placement{bool clear=true;bool Clear(int,void*){return clear;}};
|
||||
struct Mining{int registered=0;void Register(Node*,Node*,int){registered++;}};
|
||||
struct Prop{string Prefab="cover";int Position=5,Angles=0;};
|
||||
namespace ERR_RoadblockRules{'''+rule+r'''}
|
||||
struct Build{
|
||||
bool m_Large=false;int m_SmallPlaced=0,m_LargePlaced=0;
|
||||
Mode m_Mode;Placement m_Placement;Mining m_Mining;Node* m_BuildGroup=nullptr;Node* m_BuildHold=nullptr;
|
||||
Array<int> m_BuildPositions,m_Positions;Array<Prop>m_BuildCovers;Array<Node*>m_BuildSpawned,m_Groups;Array<string>m_Roster={"one","two"};
|
||||
int m_BuildActors=0,m_BuildCoverCount=0,m_Centre=10,m_Right=0,m_NextSearch=0,rolledBack=0;string COVER="cover";bool playersClear=true,coversClear=true;
|
||||
@@ -138,31 +140,34 @@ struct Build{
|
||||
void Rollback(Array<Node*>&entities,Node*group){rolledBack+=entities.Count()+(group?1:0);}
|
||||
'''+s+r'''
|
||||
};
|
||||
int main(){Array<int> positions={1,2,3,4};Array<Prop>covers(ERR_RoadblockRules::PropCount());const int pieces=4+covers.Count()+1;
|
||||
{Build b;int start=ERR_Actors::calls;assert(b.SpawnPost(positions,covers));assert(ERR_Actors::calls==start+1);
|
||||
int main(){for(bool large:{false,true}){
|
||||
const int guards=ERR_RoadblockRules::GuardCount(large);Array<int> positions(guards,1);Array<Prop>covers(ERR_RoadblockRules::PropCount(large));const int pieces=guards+covers.Count()+1;
|
||||
{Build b;b.m_Large=large;int start=ERR_Actors::calls;assert(b.SpawnPost(positions,covers));assert(ERR_Actors::calls==start+1);
|
||||
for(int piece=0;piece<pieces;piece++){int calls=ERR_Actors::calls;b.BuildPost();assert(ERR_Actors::calls==calls+1&&b.m_Positions.Count()==0&&b.m_Mode.owned==0);}
|
||||
auto shared=b.m_BuildSpawned;for(int i=4;i<4+covers.Count();i++)assert(shared[i]->replica.streaming);
|
||||
b.BuildPost();for(int i=4;i<4+covers.Count();i++)assert(!shared[i]->replica.streaming);assert(!b.m_BuildGroup&&b.m_Positions.Count()==1&&b.m_Groups.Count()==1&&b.m_Mode.owned==pieces+1&&b.m_Mode.groups==1&&b.m_Mining.registered==1&&b.m_NextSearch==2100);
|
||||
auto shared=b.m_BuildSpawned;for(int i=guards;i<guards+covers.Count();i++)assert(shared[i]->replica.streaming);
|
||||
b.BuildPost();for(int i=guards;i<guards+covers.Count();i++)assert(!shared[i]->replica.streaming);assert(!b.m_BuildGroup&&b.m_Positions.Count()==1&&b.m_Groups.Count()==1&&b.m_Mode.owned==pieces+1&&b.m_Mode.groups==1&&b.m_Mining.registered==1&&b.m_NextSearch==2100);
|
||||
assert(b.m_SmallPlaced==(large?0:1)&&b.m_LargePlaced==(large?1:0));
|
||||
}
|
||||
// A server-only or unregistered prop must never count as a complete roadblock.
|
||||
for(int bad=4;bad<4+covers.Count();bad++)for(bool missing:{false,true}){
|
||||
Build b;b.SpawnPost(positions,covers);for(int i=0;i<pieces;i++)b.BuildPost();
|
||||
for(int bad=guards;bad<guards+covers.Count();bad++)for(bool missing:{false,true}){
|
||||
Build b;b.m_Large=large;b.SpawnPost(positions,covers);for(int i=0;i<pieces;i++)b.BuildPost();
|
||||
auto spawned=b.m_BuildSpawned;spawned[bad]->missing=missing;spawned[bad]->replica.id=-1;
|
||||
b.BuildPost();assert(b.m_Positions.Count()==0&&!b.m_BuildGroup&&b.m_Mode.owned==0&&b.rolledBack==pieces+1);
|
||||
for(int i=4;i<4+covers.Count();i++)assert(spawned[i]->replica.streaming); // reject before changing any streaming rule
|
||||
for(int i=guards;i<guards+covers.Count();i++)assert(spawned[i]->replica.streaming); // reject before changing any streaming rule
|
||||
}
|
||||
// Player approaches at any stage: no registered post and every owned piece removed.
|
||||
for(int stage=0;stage<=pieces;stage++){Build b;assert(b.SpawnPost(positions,covers));for(int i=0;i<stage;i++)b.BuildPost();b.playersClear=false;b.BuildPost();assert(!b.m_BuildGroup&&b.m_Positions.Count()==0&&b.m_Mode.owned==0&&b.rolledBack==stage+1);}
|
||||
for(int stage=0;stage<=pieces;stage++){Build b;b.m_Large=large;assert(b.SpawnPost(positions,covers));for(int i=0;i<stage;i++)b.BuildPost();b.playersClear=false;b.BuildPost();assert(!b.m_BuildGroup&&b.m_Positions.Count()==0&&b.m_Mode.owned==0&&b.rolledBack==stage+1);}
|
||||
// Any native entity creation failure rolls back rather than consuming a post.
|
||||
for(int stage=0;stage<pieces+1;stage++){Build b;ERR_Actors::failAt=ERR_Actors::calls+stage+1;bool begun=b.SpawnPost(positions,covers);
|
||||
for(int stage=0;stage<pieces+1;stage++){Build b;b.m_Large=large;ERR_Actors::failAt=ERR_Actors::calls+stage+1;bool begun=b.SpawnPost(positions,covers);
|
||||
if(stage==0)assert(!begun);else{for(int i=0;i<stage;i++)b.BuildPost();assert(b.rolledBack==stage);}
|
||||
assert(!b.m_BuildGroup&&b.m_Positions.Count()==0&&b.m_Mode.owned==0);ERR_Actors::failAt=0;
|
||||
}
|
||||
{Build b;b.SpawnPost(positions,covers);b.m_Placement.clear=false;b.BuildPost();assert(!b.m_BuildGroup&&b.rolledBack==1);}
|
||||
{Build b;b.SpawnPost(positions,covers);for(int i=0;i<4;i++)b.BuildPost();b.coversClear=false;b.BuildPost();assert(!b.m_BuildGroup&&b.rolledBack==5);}
|
||||
{Build b;b.SpawnPost(positions,covers);for(int i=0;i<pieces;i++)b.BuildPost();b.m_Mode.ledger.accept=false;b.BuildPost();assert(!b.m_BuildGroup&&b.rolledBack==pieces+1&&b.m_Positions.Count()==0);}
|
||||
{Build b;b.SpawnPost(positions,covers);b.BuildPost();b.CancelPost();assert(!b.m_BuildGroup&&b.rolledBack==2&&b.m_Mode.owned==0);}
|
||||
{Build b;b.m_Large=large;b.SpawnPost(positions,covers);b.m_Placement.clear=false;b.BuildPost();assert(!b.m_BuildGroup&&b.rolledBack==1);}
|
||||
{Build b;b.m_Large=large;b.SpawnPost(positions,covers);for(int i=0;i<guards;i++)b.BuildPost();b.coversClear=false;b.BuildPost();assert(!b.m_BuildGroup&&b.rolledBack==guards+1);}
|
||||
{Build b;b.m_Large=large;b.SpawnPost(positions,covers);for(int i=0;i<pieces;i++)b.BuildPost();b.m_Mode.ledger.accept=false;b.BuildPost();assert(!b.m_BuildGroup&&b.rolledBack==pieces+1&&b.m_Positions.Count()==0);}
|
||||
{Build b;b.m_Large=large;b.SpawnPost(positions,covers);b.BuildPost();b.CancelPost();assert(!b.m_BuildGroup&&b.rolledBack==2&&b.m_Mode.owned==0);}
|
||||
}
|
||||
for(auto*n:ERR_Actors::all)delete n;
|
||||
}
|
||||
''')
|
||||
print('PASS: actual staged post creation spawns at most one entity per callback, commits only all four guards/all planned props/waypoint, and rolls back player approach, obstructions, spawn/replication/ledger failure and cancellation; all committed props bypass distance streaming')
|
||||
print('PASS: actual staged post creation spawns at most one entity per callback, commits only all four/eight guards, all planned props and waypoint, and rolls back player approach, obstructions, spawn/replication/ledger failure and cancellation; all committed props bypass distance streaming')
|
||||
|
||||
Reference in New Issue
Block a user