try to fix roadblocks
This commit is contained in:
@@ -1,4 +1,13 @@
|
|||||||
#ifdef WORKBENCH
|
#ifdef WORKBENCH
|
||||||
|
class ERR_RoadblockProbeRules
|
||||||
|
{
|
||||||
|
static int Target()
|
||||||
|
{
|
||||||
|
string value;
|
||||||
|
if (System.GetCLIParam("errRoadblockProbeCount", value)) return Math.Clamp(value.ToInt(), 1, 30);
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
modded class ERR_HQ
|
modded class ERR_HQ
|
||||||
{
|
{
|
||||||
bool ERR_ProbeRoadblockGroup(SCR_AIGroup group)
|
bool ERR_ProbeRoadblockGroup(SCR_AIGroup group)
|
||||||
@@ -14,7 +23,7 @@ modded class ERR_Roadblocks
|
|||||||
{
|
{
|
||||||
string outcome;
|
string outcome;
|
||||||
if (!m_Begun && System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "roadblocks")
|
if (!m_Begun && System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "roadblocks")
|
||||||
m_Mode.Settings().Set("roadblockCount", 30);
|
m_Mode.Settings().Set("roadblockCount", ERR_RoadblockProbeRules.Target());
|
||||||
return super.Prepare(definition);
|
return super.Prepare(definition);
|
||||||
}
|
}
|
||||||
protected int m_ERRLayoutPosts;
|
protected int m_ERRLayoutPosts;
|
||||||
@@ -27,7 +36,7 @@ modded class ERR_Roadblocks
|
|||||||
// Inspect the actual spawned models before the transaction clears its refs.
|
// Inspect the actual spawned models before the transaction clears its refs.
|
||||||
if (m_BuildHold)
|
if (m_BuildHold)
|
||||||
{
|
{
|
||||||
bool valid = m_BuildCovers.Count() == 10 && m_BuildSpawned.Count() == 15;
|
bool valid = ShareProps();
|
||||||
for (int i = 0; i < m_BuildCovers.Count() && valid; i++)
|
for (int i = 0; i < m_BuildCovers.Count() && valid; i++)
|
||||||
{
|
{
|
||||||
ERR_RoadblockProp prop = m_BuildCovers[i];
|
ERR_RoadblockProp prop = m_BuildCovers[i];
|
||||||
@@ -89,7 +98,7 @@ modded class ERR_Roadblocks
|
|||||||
bool ERR_ProbeBackgroundPending() { return m_Background; }
|
bool ERR_ProbeBackgroundPending() { return m_Background; }
|
||||||
bool ERR_ProbeCompleteRoadblocks()
|
bool ERR_ProbeCompleteRoadblocks()
|
||||||
{
|
{
|
||||||
if (m_ERRLayoutFailed || m_ERRLayoutPosts < 30 || m_Target != 30 || m_Positions.Count() != 30 || m_Background || !m_Ready) return false;
|
if (m_ERRLayoutFailed || m_ERRLayoutPosts < ERR_RoadblockProbeRules.Target() || m_Target != ERR_RoadblockProbeRules.Target() || m_Positions.Count() != m_Target || m_Background || !m_Ready) return false;
|
||||||
foreach (SCR_AIGroup group : m_Groups)
|
foreach (SCR_AIGroup group : m_Groups)
|
||||||
{
|
{
|
||||||
ERR_ForceRecord record = m_Mode.Forces().FindGroup(group);
|
ERR_ForceRecord record = m_Mode.Forces().FindGroup(group);
|
||||||
@@ -147,13 +156,13 @@ modded class ERR_GameMode
|
|||||||
IEntity player; if (controller) player = controller.GetControlledEntity();
|
IEntity player; if (controller) player = controller.GetControlledEntity();
|
||||||
if (!player || !Players().CurrentActor(player)) return;
|
if (!player || !Players().CurrentActor(player)) return;
|
||||||
DamageManagerComponent.Cast(player.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
DamageManagerComponent.Cast(player.FindComponent(DamageManagerComponent)).EnableDamageHandling(false);
|
||||||
if (!posts.ERR_ProbeBackgroundPending() || posts.ERR_ProbePostCount() >= 30 || !UnlockPrison(player)) { ERR_RoadblockProbeFail("did not enter background search with prison unlocked"); return; }
|
if (!posts.ERR_ProbeBackgroundPending() || posts.ERR_ProbePostCount() >= ERR_RoadblockProbeRules.Target() || !UnlockPrison(player)) { ERR_RoadblockProbeFail("did not enter background search with prison unlocked"); return; }
|
||||||
m_ERRRoadblockInitial = posts.ERR_ProbePostCount();
|
m_ERRRoadblockInitial = posts.ERR_ProbePostCount();
|
||||||
PrintFormat("ERR SMOKE PASS: roadblocks prison unlocked with %1/30 posts; background still pending", m_ERRRoadblockInitial);
|
PrintFormat("ERR SMOKE PASS: roadblocks prison unlocked with %1/%2 posts; background still pending", m_ERRRoadblockInitial, ERR_RoadblockProbeRules.Target());
|
||||||
}
|
}
|
||||||
if (posts.ERR_ProbeBackgroundPending()) return;
|
if (posts.ERR_ProbeBackgroundPending()) return;
|
||||||
if (!posts.ERR_ProbeCompleteRoadblocks() || posts.ERR_ProbePostCount() <= m_ERRRoadblockInitial) { ERR_RoadblockProbeFail("30 physical four-guard posts, ledger and HQ admission"); return; }
|
if (!posts.ERR_ProbeCompleteRoadblocks() || posts.ERR_ProbePostCount() <= m_ERRRoadblockInitial) { ERR_RoadblockProbeFail("physical four-guard posts, replicated props, ledger and HQ admission"); return; }
|
||||||
Print("ERR SMOKE PASS: roadblocks 30 physical posts / 120 guards / 300 checkpoint props; verified model footprints and centre lane; background completed after prison unlock, no extra posts on repeat tick");
|
PrintFormat("ERR SMOKE PASS: roadblocks %1 physical posts / %2 guards / %3 replicated checkpoint props with distance streaming disabled; verified model footprints and centre lane; background completed after prison unlock, no extra posts on repeat tick", posts.ERR_ProbePostCount(), posts.ERR_ProbePostCount() * 4, posts.ERR_ProbePostCount() * 10);
|
||||||
ERR_StopRoadblockProbe();
|
ERR_StopRoadblockProbe();
|
||||||
Transition(ERR_Phase.DEFEAT);
|
Transition(ERR_Phase.DEFEAT);
|
||||||
Print("ERR SMOKE COMPLETE: roadblocks background completion on Everon; scripted local fixture");
|
Print("ERR SMOKE COMPLETE: roadblocks background completion on Everon; scripted local fixture");
|
||||||
|
|||||||
@@ -224,6 +224,27 @@ class ERR_Roadblocks
|
|||||||
m_BuildSpawned.Clear(); m_BuildGroup = null; m_BuildHold = null;
|
m_BuildSpawned.Clear(); m_BuildGroup = null; m_BuildHold = null;
|
||||||
m_BuildPositions.Clear(); m_BuildCovers.Clear();
|
m_BuildPositions.Clear(); m_BuildCovers.Clear();
|
||||||
}
|
}
|
||||||
|
// A free Game Master camera does not force-stream bare, non-editable props
|
||||||
|
// with the nearby editable guards. Keep this bounded bank (<=300 objects) on
|
||||||
|
// clients, including late joiners, rather than tying it to the player's body.
|
||||||
|
// Do not report a completed post if any physical prop lacks a network node.
|
||||||
|
protected bool ShareProps()
|
||||||
|
{
|
||||||
|
if (m_BuildCovers.Count() != 10 || m_BuildSpawned.Count() != 15) return false;
|
||||||
|
for (int i = 4; i < 14; i++)
|
||||||
|
{
|
||||||
|
IEntity entity = m_BuildSpawned[i];
|
||||||
|
RplComponent component;
|
||||||
|
if (entity) component = RplComponent.Cast(entity.FindComponent(RplComponent));
|
||||||
|
if (!component || component.Id() == RplId.Invalid()) return false;
|
||||||
|
}
|
||||||
|
for (int i = 4; i < 14; i++)
|
||||||
|
{
|
||||||
|
RplComponent component = RplComponent.Cast(m_BuildSpawned[i].FindComponent(RplComponent));
|
||||||
|
component.EnableStreaming(false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
protected void BuildPost()
|
protected void BuildPost()
|
||||||
{
|
{
|
||||||
if (!PlayersClear() || m_Mode.Forces().Count() >= 250) { CancelPost(); return; }
|
if (!PlayersClear() || m_Mode.Forces().Count() >= 250) { CancelPost(); return; }
|
||||||
@@ -256,6 +277,11 @@ class ERR_Roadblocks
|
|||||||
m_BuildSpawned.Insert(m_BuildHold); m_BuildHold.SetCompletionRadius(25);
|
m_BuildSpawned.Insert(m_BuildHold); m_BuildHold.SetCompletionRadius(25);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!ShareProps())
|
||||||
|
{
|
||||||
|
PrintFormat("Reintroduced [roadblocks]: rejected post at %1; checkpoint props missing replication nodes", m_Centre);
|
||||||
|
CancelPost(); return;
|
||||||
|
}
|
||||||
string id = "roadblock-" + m_Positions.Count().ToString();
|
string id = "roadblock-" + m_Positions.Count().ToString();
|
||||||
if (!m_Mode.Forces().Register(id, ERR_ForceKind.CHECKPOINT, m_Centre, 4, null, m_BuildGroup, GetGame().GetWorld().GetWorldTime()))
|
if (!m_Mode.Forces().Register(id, ERR_ForceKind.CHECKPOINT, m_Centre, 4, null, m_BuildGroup, GetGame().GetWorld().GetWorldTime()))
|
||||||
{ CancelPost(); return; }
|
{ CancelPost(); return; }
|
||||||
@@ -264,7 +290,7 @@ class ERR_Roadblocks
|
|||||||
m_BuildGroup.AddWaypoint(m_BuildHold);
|
m_BuildGroup.AddWaypoint(m_BuildHold);
|
||||||
m_Mode.RegisterGroup(m_BuildGroup); m_BuildGroup.ActivateAI();
|
m_Mode.RegisterGroup(m_BuildGroup); m_BuildGroup.ActivateAI();
|
||||||
m_Mining.Register(m_BuildGroup, m_BuildHold, m_Right);
|
m_Mining.Register(m_BuildGroup, m_BuildHold, m_Right);
|
||||||
PrintFormat("Reintroduced [roadblocks]: %1 at %2 guards=4 props=10 roadPassage=open HQ=registered", id, m_Centre);
|
PrintFormat("Reintroduced [roadblocks]: %1 at %2 guards=4 props=10 networkedProps=10 propStreaming=always roadPassage=open HQ=registered", id, m_Centre);
|
||||||
m_BuildSpawned.Clear(); m_BuildGroup = null; m_BuildHold = null;
|
m_BuildSpawned.Clear(); m_BuildGroup = null; m_BuildHold = null;
|
||||||
m_BuildPositions.Clear(); m_BuildCovers.Clear();
|
m_BuildPositions.Clear(); m_BuildCovers.Clear();
|
||||||
m_NextSearch = System.GetTickCount() + 2000;
|
m_NextSearch = System.GetTickCount() + 2000;
|
||||||
|
|||||||
@@ -118,3 +118,10 @@ Before requesting `System.MakeScreenshot("$logs:err-ordinary-prison")`, the help
|
|||||||
[121730](evidence/game-2026-09-19-121730-prison-view/report.json) passes ordinary Chat startup and the optional capture. The [unaltered player view](evidence/game-2026-09-19-121730-prison-view/ordinary-prison-view.png) shows the prisoner inside the authored walls with the correct escape objective and instructions. The exact native BMP is retained beside a documented lossless PNG conversion. The image also exposed tall wheat inside the yard; the first bounded decal candidate in [122353](evidence/game-2026-09-19-122353-yard-candidate/report.json) clears only a small central patch and is not yet full-yard treatment. Neither capture supplies player-input or full-route acceptance.
|
[121730](evidence/game-2026-09-19-121730-prison-view/report.json) passes ordinary Chat startup and the optional capture. The [unaltered player view](evidence/game-2026-09-19-121730-prison-view/ordinary-prison-view.png) shows the prisoner inside the authored walls with the correct escape objective and instructions. The exact native BMP is retained beside a documented lossless PNG conversion. The image also exposed tall wheat inside the yard; the first bounded decal candidate in [122353](evidence/game-2026-09-19-122353-yard-candidate/report.json) clears only a small central patch and is not yet full-yard treatment. Neither capture supplies player-input or full-route acceptance.
|
||||||
|
|
||||||
[125906 integrated startup](evidence/game-2026-09-19-125906-integrated-prison/report.json) passes after the admission-budget, interaction-feedback, optional aircraft-target and stock-scale yard changes. The native player view remains correct. The scale-ten vanilla decal clears a larger irregular patch while wheat remains near and within parts of the enclosure; complete yard clearance is not claimed. This is ordinary-game startup and initial-view evidence only.
|
[125906 integrated startup](evidence/game-2026-09-19-125906-integrated-prison/report.json) passes after the admission-budget, interaction-feedback, optional aircraft-target and stock-scale yard changes. The native player view remains correct. The scale-ten vanilla decal clears a larger irregular patch while wheat remains near and within parts of the enclosure; complete yard clearance is not claimed. This is ordinary-game startup and initial-view evidence only.
|
||||||
|
|
||||||
|
Roadblock props remain replicated independently of player distance so an admin
|
||||||
|
viewing the island through Game Master can see the same barriers as nearby
|
||||||
|
players. This is limited to the ten props per post (at most 300); guard and
|
||||||
|
vehicle streaming is unchanged. A post is counted only after all ten props have
|
||||||
|
valid replication nodes. Local Workbench validation does not by itself prove
|
||||||
|
remote-client visibility.
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ assert 'if (!PlayersClear() || m_Mode.Forces().Count() >= 250) { CancelPost(); r
|
|||||||
print('PASS: actual roadblock scheduler yields initial deadline/attempt cap to background, budgets samples, pauses empty/full worlds, completes 30 after unlock, preserves safety and never replenishes losses; teardown cancels work')
|
print('PASS: actual roadblock scheduler yields initial deadline/attempt cap to background, budgets samples, pauses empty/full worlds, completes 30 after unlock, preserves safety and never replenishes losses; teardown cancels work')
|
||||||
|
|
||||||
# Run actual staged physical creation and rollback, with native entities doubled.
|
# Run actual staged physical creation and rollback, with native entities doubled.
|
||||||
s='\n'.join(body(p,sig) for sig in ('protected bool SpawnPost(', 'protected void CancelPost()', 'protected void BuildPost()'))
|
s='\n'.join(body(p,sig) for sig in ('protected bool SpawnPost(', 'protected void CancelPost()', 'protected bool ShareProps()', 'protected void BuildPost()'))
|
||||||
s=s.replace('protected ', '').replace('array<vector> positions','Array<int>& positions').replace('array<ref ERR_RoadblockProp> covers','Array<Prop>& covers').replace('ERR_RoadblockProp prop', 'Prop prop')
|
s=s.replace('protected ', '').replace('array<vector> positions','Array<int>& positions').replace('array<ref ERR_RoadblockProp> covers','Array<Prop>& covers').replace('ERR_RoadblockProp prop', 'Prop prop')
|
||||||
s=s.replace('SCR_AIGroup.Cast','Node::Cast').replace('AIWaypoint.Cast','Node::Cast').replace('IEntity actor','Node* actor').replace('IEntity cover','Node* cover')
|
s=s.replace('SCR_AIGroup.Cast','Node::Cast').replace('AIWaypoint.Cast','Node::Cast').replace('IEntity actor','Node* actor').replace('IEntity cover','Node* cover')
|
||||||
s=s.replace('foreach (IEntity ownedEntity : m_BuildSpawned)','for (Node* ownedEntity : m_BuildSpawned)').replace('vector point','int point').replace('vector coverPosition','int coverPosition')
|
s=s.replace('foreach (IEntity ownedEntity : m_BuildSpawned)','for (Node* ownedEntity : m_BuildSpawned)').replace('vector point','int point').replace('vector coverPosition','int coverPosition')
|
||||||
@@ -106,6 +106,9 @@ s=s.replace('foreach (ERR_RoadblockProp plannedProp : covers)', 'for (Prop plann
|
|||||||
s=s.replace('m_Forward.VectorToAngles()', '0').replace('m_Positions.Count().ToString()', 'std::to_string(m_Positions.Count())')
|
s=s.replace('m_Forward.VectorToAngles()', '0').replace('m_Positions.Count().ToString()', 'std::to_string(m_Positions.Count())')
|
||||||
s=s.replace('GetGame().GetWorld().GetWorldTime()', 'Now()').replace('System.GetTickCount()', 'Now()').replace('null','nullptr')
|
s=s.replace('GetGame().GetWorld().GetWorldTime()', 'Now()').replace('System.GetTickCount()', 'Now()').replace('null','nullptr')
|
||||||
s=s.replace('m_BuildGroup.', 'm_BuildGroup->').replace('m_BuildHold.', 'm_BuildHold->')
|
s=s.replace('m_BuildGroup.', 'm_BuildGroup->').replace('m_BuildHold.', 'm_BuildHold->')
|
||||||
|
s=s.replace('IEntity entity', 'Node* entity').replace('RplComponent component;', 'Replica* component=nullptr;').replace('RplComponent component =', 'Replica* 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'):s=s.replace(ns+'.',ns+'::')
|
for ns in ('ERR_Actors','ERR_Resources','ERR_ForceKind'):s=s.replace(ns+'.',ns+'::')
|
||||||
run(r'''
|
run(r'''
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@@ -115,7 +118,8 @@ using string=std::string;
|
|||||||
int Now(){return 100;}
|
int Now(){return 100;}
|
||||||
template<class...T>void PrintFormat(T...){}
|
template<class...T>void PrintFormat(T...){}
|
||||||
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;using std::vector<T>::operator=;int Count(){return this->size();}void Clear(){this->clear();}void Copy(Array<T>&other){*this=other;}void Insert(T value){this->push_back(value);}};
|
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;using std::vector<T>::operator=;int Count(){return this->size();}void Clear(){this->clear();}void Copy(Array<T>&other){*this=other;}void Insert(T value){this->push_back(value);}};
|
||||||
struct Node{static Node* Cast(Node*n){return n;}void SetFaction(int){}void SetSpawnImmediately(bool){}void SetDeleteWhenEmpty(bool){}void SetPermanentLOD(int){}bool AddAIEntityToGroup(Node*){return true;}void SetCompletionRadius(int r){assert(r==25);}void AddWaypoint(Node*){}void ActivateAI(){}};
|
struct Replica{int id=1;bool streaming=true;static Replica* Cast(Replica*r){return r;}int Id(){return id;}void EnableStreaming(bool value){streaming=value;}};
|
||||||
|
struct Node{Replica replica;bool missing=false;Replica* FindComponent(){return missing?nullptr:&replica;}static Node* Cast(Node*n){return n;}void SetFaction(int){}void SetSpawnImmediately(bool){}void SetDeleteWhenEmpty(bool){}void SetPermanentLOD(int){}bool AddAIEntityToGroup(Node*){return true;}void SetCompletionRadius(int r){assert(r==25);}void AddWaypoint(Node*){}void ActivateAI(){}};
|
||||||
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_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_Resources{string DefendWaypoint(){return "hold";}}
|
||||||
namespace ERR_ForceKind{int CHECKPOINT=1;}
|
namespace ERR_ForceKind{int CHECKPOINT=1;}
|
||||||
@@ -135,7 +139,15 @@ struct Build{
|
|||||||
int main(){Array<int> positions={1,2,3,4};Array<Prop>covers(10);
|
int main(){Array<int> positions={1,2,3,4};Array<Prop>covers(10);
|
||||||
{Build b;int start=ERR_Actors::calls;assert(b.SpawnPost(positions,covers));assert(ERR_Actors::calls==start+1);
|
{Build b;int start=ERR_Actors::calls;assert(b.SpawnPost(positions,covers));assert(ERR_Actors::calls==start+1);
|
||||||
for(int piece=0;piece<15;piece++){int calls=ERR_Actors::calls;b.BuildPost();assert(ERR_Actors::calls==calls+1&&b.m_Positions.Count()==0&&b.m_Mode.owned==0);}
|
for(int piece=0;piece<15;piece++){int calls=ERR_Actors::calls;b.BuildPost();assert(ERR_Actors::calls==calls+1&&b.m_Positions.Count()==0&&b.m_Mode.owned==0);}
|
||||||
b.BuildPost();assert(!b.m_BuildGroup&&b.m_Positions.Count()==1&&b.m_Groups.Count()==1&&b.m_Mode.owned==16&&b.m_Mode.groups==1&&b.m_Mining.registered==1&&b.m_NextSearch==2100);
|
auto shared=b.m_BuildSpawned;for(int i=4;i<14;i++)assert(shared[i]->replica.streaming);
|
||||||
|
b.BuildPost();for(int i=4;i<14;i++)assert(!shared[i]->replica.streaming);assert(!b.m_BuildGroup&&b.m_Positions.Count()==1&&b.m_Groups.Count()==1&&b.m_Mode.owned==16&&b.m_Mode.groups==1&&b.m_Mining.registered==1&&b.m_NextSearch==2100);
|
||||||
|
}
|
||||||
|
// A server-only or unregistered prop must never count as a complete roadblock.
|
||||||
|
for(int bad=4;bad<14;bad++)for(bool missing:{false,true}){
|
||||||
|
Build b;b.SpawnPost(positions,covers);for(int i=0;i<15;i++)b.BuildPost();
|
||||||
|
auto pieces=b.m_BuildSpawned;pieces[bad]->missing=missing;pieces[bad]->replica.id=-1;
|
||||||
|
b.BuildPost();assert(b.m_Positions.Count()==0&&!b.m_BuildGroup&&b.m_Mode.owned==0&&b.rolledBack==16);
|
||||||
|
for(int i=4;i<14;i++)assert(pieces[i]->replica.streaming); // reject before changing any streaming rule
|
||||||
}
|
}
|
||||||
// Player approaches at any stage: no registered post and every owned piece removed.
|
// Player approaches at any stage: no registered post and every owned piece removed.
|
||||||
for(int stage=0;stage<=15;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<=15;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);}
|
||||||
@@ -151,4 +163,4 @@ int main(){Array<int> positions={1,2,3,4};Array<Prop>covers(10);
|
|||||||
for(auto*n:ERR_Actors::all)delete n;
|
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/ten props/waypoint, and rolls back player approach, obstructions, spawn/ledger failure and cancellation')
|
print('PASS: actual staged post creation spawns at most one entity per callback, commits only all four guards/ten props/waypoint, and rolls back player approach, obstructions, spawn/replication/ledger failure and cancellation; all ten committed props bypass distance streaming')
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ p.add_argument('--external-addons', type=Path, help='Installed RHS/WCS/BMP sourc
|
|||||||
p.add_argument('--compat', type=Path, required=True, help='Disposable cloned Proton compatdata directory outside Steam')
|
p.add_argument('--compat', type=Path, required=True, help='Disposable cloned Proton compatdata directory outside Steam')
|
||||||
p.add_argument('--profile', type=Path, required=True, help='Separate disposable successor Workbench profile')
|
p.add_argument('--profile', type=Path, required=True, help='Separate disposable successor Workbench profile')
|
||||||
p.add_argument('--smoke', choices=['coordinated-ai', 'crash-start', 'crash-geometry', 'normal-escape-route', 'escape-route', 'optional-site-skip', 'infantry-combat', 'medical-combat', 'hunter-faction', 'unconscious-camera', 'self-epinephrine', 'starter-backpack', 'speech-download', 'bundled-speech', 'objective-map', 'town-garrisons', 'site-garrisons', 'radio-interaction', 'enemy-loadout', 'aggression', 'air-logistics', 'air-assault', 'air-recon', 'airfield-markers', 'opening-tools', 'mortar-station', 'generated-sites', 'roadblocks', 'hq-local-handoff', 'infantry-transfer', 'hq-body', 'convoy', 'supply-convoys', 'supply-travel', 'mounted-ammo', 'alarm-hearing', 'hq-memory', 'hq-awareness', 'aircraft-field', 'aircraft-route', 'native-end-active', 'native-end-preparing', 'aircraft-control', 'aircraft-cancel', 'aircraft-translate', 'map-ownership', 'mortar-refresh', 'mortar-salvo', 'mortar-courier', 'station-teardown', 'inventory-hitzone', 'prison-breach', 'powered-flight', 'powered-translate', 'local-admission', 'mortar-expiry', 'mortar-transfer', 'mortar-fire', 'patrol-hq', 'victory', 'lost-helicopter', 'player-death', 'radio-network', 'hq-response', 'mortar-inventory', 'startup-failure', 'site-survey', 'cache-loss', 'extraction-capacity', 'early-helicopter-loss', 'extraction-startup-failure', 'extraction-boundary-failure', 'transport-loss', 'transport-survey', 'lifecycle', 'objective-ui', 'recovery', 'prisoner-kit', 'medical', 'casualty-load', 'casualty-direct', 'casualty-bench', 'casualty-remove', 'environment', 'environment-restart', 'patrols', 'radio-document-loss', 'radio-document-loss-partial', 'guard-cancel', 'casualty-service'], default='victory')
|
p.add_argument('--smoke', choices=['coordinated-ai', 'crash-start', 'crash-geometry', 'normal-escape-route', 'escape-route', 'optional-site-skip', 'infantry-combat', 'medical-combat', 'hunter-faction', 'unconscious-camera', 'self-epinephrine', 'starter-backpack', 'speech-download', 'bundled-speech', 'objective-map', 'town-garrisons', 'site-garrisons', 'radio-interaction', 'enemy-loadout', 'aggression', 'air-logistics', 'air-assault', 'air-recon', 'airfield-markers', 'opening-tools', 'mortar-station', 'generated-sites', 'roadblocks', 'hq-local-handoff', 'infantry-transfer', 'hq-body', 'convoy', 'supply-convoys', 'supply-travel', 'mounted-ammo', 'alarm-hearing', 'hq-memory', 'hq-awareness', 'aircraft-field', 'aircraft-route', 'native-end-active', 'native-end-preparing', 'aircraft-control', 'aircraft-cancel', 'aircraft-translate', 'map-ownership', 'mortar-refresh', 'mortar-salvo', 'mortar-courier', 'station-teardown', 'inventory-hitzone', 'prison-breach', 'powered-flight', 'powered-translate', 'local-admission', 'mortar-expiry', 'mortar-transfer', 'mortar-fire', 'patrol-hq', 'victory', 'lost-helicopter', 'player-death', 'radio-network', 'hq-response', 'mortar-inventory', 'startup-failure', 'site-survey', 'cache-loss', 'extraction-capacity', 'early-helicopter-loss', 'extraction-startup-failure', 'extraction-boundary-failure', 'transport-loss', 'transport-survey', 'lifecycle', 'objective-ui', 'recovery', 'prisoner-kit', 'medical', 'casualty-load', 'casualty-direct', 'casualty-bench', 'casualty-remove', 'environment', 'environment-restart', 'patrols', 'radio-document-loss', 'radio-document-loss-partial', 'guard-cancel', 'casualty-service'], default='victory')
|
||||||
|
p.add_argument('--roadblock-probe-count', type=int, choices=range(1, 31), help='Roadblocks fixture target; default remains 30')
|
||||||
p.add_argument('--speech-probe-url', help='Loopback-only cached audio fixture; requires --smoke speech-download')
|
p.add_argument('--speech-probe-url', help='Loopback-only cached audio fixture; requires --smoke speech-download')
|
||||||
p.add_argument('--trace-random-radius', action='store_true', help='Workbench-only read-only zero-radius caller traces; one normal Chat round, capped at sixteen records')
|
p.add_argument('--trace-random-radius', action='store_true', help='Workbench-only read-only zero-radius caller traces; one normal Chat round, capped at sixteen records')
|
||||||
p.add_argument('--maintenance-refuel', action='store_true', help='Opt-in normal-route check: original held Southern fuel can transfers finite fuel through its native UAZ action; one arranged tank depletion, no stock creation')
|
p.add_argument('--maintenance-refuel', action='store_true', help='Opt-in normal-route check: original held Southern fuel can transfers finite fuel through its native UAZ action; one arranged tank depletion, no stock creation')
|
||||||
@@ -38,6 +39,7 @@ p.add_argument('--rounds', type=int, default=2)
|
|||||||
p.add_argument('--timeout', type=int, default=360)
|
p.add_argument('--timeout', type=int, default=360)
|
||||||
p.add_argument('--project', choices=['core', 'chat'], default='chat', help='Diagnostic fixture family (both load the combined mod)')
|
p.add_argument('--project', choices=['core', 'chat'], default='chat', help='Diagnostic fixture family (both load the combined mod)')
|
||||||
a = p.parse_args()
|
a = p.parse_args()
|
||||||
|
if a.roadblock_probe_count is not None and a.smoke != 'roadblocks': p.error('--roadblock-probe-count requires --smoke roadblocks')
|
||||||
if a.smoke == 'coordinated-ai' and (a.project != 'core' or a.rounds != 1 or a.timeout < 720): p.error('coordinated-ai requires Core, one round and timeout at least720 seconds')
|
if a.smoke == 'coordinated-ai' and (a.project != 'core' or a.rounds != 1 or a.timeout < 720): p.error('coordinated-ai requires Core, one round and timeout at least720 seconds')
|
||||||
if a.coordinated_blocked_only and a.smoke != 'coordinated-ai': p.error('--coordinated-blocked-only requires --smoke coordinated-ai')
|
if a.coordinated_blocked_only and a.smoke != 'coordinated-ai': p.error('--coordinated-blocked-only requires --smoke coordinated-ai')
|
||||||
if a.smoke == 'inventory-hitzone' and (a.project != 'core' or a.rounds != 1 or a.timeout < 240): p.error('inventory-hitzone requires --project core --rounds 1 and timeout at least 240 seconds')
|
if a.smoke == 'inventory-hitzone' and (a.project != 'core' or a.rounds != 1 or a.timeout < 240): p.error('inventory-hitzone requires --project core --rounds 1 and timeout at least 240 seconds')
|
||||||
@@ -140,6 +142,7 @@ for proc in Path('/proc').iterdir():
|
|||||||
p.error('Another Workbench already uses this test profile')
|
p.error('Another Workbench already uses this test profile')
|
||||||
except (FileNotFoundError, PermissionError, ProcessLookupError): pass
|
except (FileNotFoundError, PermissionError, ProcessLookupError): pass
|
||||||
command = ['gamescope', '--backend', 'headless', '-W', '1280', '-H', '720', '-w', '1280', '-h', '720', '-r', '30', '--', str(proton), 'run', str(wb/'ArmaReforgerWorkbenchSteamDiag.exe'), '-gproj', 'Z:'+str(ROOT/'addons/core/addon.gproj'), '-addonsDir', 'Z:'+str(ROOT/'addons'), '-profile', profile_arg, '-logconsole', '-wbModule=WorldEditor', '-load', 'Worlds/ERR_Everon.ent', '-plugin=ERR_ValidateWorldPlugin', '-errPlay', '1', '-errSmoke', '1', '-errSmokeOutcome', a.smoke, '-noThrow', '-disableCrashReporter']
|
command = ['gamescope', '--backend', 'headless', '-W', '1280', '-H', '720', '-w', '1280', '-h', '720', '-r', '30', '--', str(proton), 'run', str(wb/'ArmaReforgerWorkbenchSteamDiag.exe'), '-gproj', 'Z:'+str(ROOT/'addons/core/addon.gproj'), '-addonsDir', 'Z:'+str(ROOT/'addons'), '-profile', profile_arg, '-logconsole', '-wbModule=WorldEditor', '-load', 'Worlds/ERR_Everon.ent', '-plugin=ERR_ValidateWorldPlugin', '-errPlay', '1', '-errSmoke', '1', '-errSmokeOutcome', a.smoke, '-noThrow', '-disableCrashReporter']
|
||||||
|
if a.roadblock_probe_count is not None: command += ['-errRoadblockProbeCount', str(a.roadblock_probe_count)]
|
||||||
if a.smoke == 'speech-download':
|
if a.smoke == 'speech-download':
|
||||||
if a.project != 'core' or not a.speech_probe_url or not a.speech_probe_url.startswith('http://127.0.0.1:'): p.error('speech-download requires Core and an explicit loopback --speech-probe-url')
|
if a.project != 'core' or not a.speech_probe_url or not a.speech_probe_url.startswith('http://127.0.0.1:'): p.error('speech-download requires Core and an explicit loopback --speech-probe-url')
|
||||||
command += ['-errSpeechProbeURL', a.speech_probe_url]
|
command += ['-errSpeechProbeURL', a.speech_probe_url]
|
||||||
|
|||||||
Reference in New Issue
Block a user