Add ground starting supplies and mission telemetry; record Workshop 0.0.123
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-10-28 14:43:43 +01:00
parent 9233320fd1
commit 95d21b06ff
78 changed files with 15375 additions and 81 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ navigation = source.with_name('ERR_AdminMenuPages.c')
layout = BASE / 'addons/core/UI/layouts/ERR/AdminMenu.layout'
keys = re.findall(r'"([A-Za-z]+)"', body(fields, 'static array<string> Keys('))
settings = re.findall(r'"([A-Za-z]+)"', body(BASE / 'addons/core/Scripts/Game/ERR/00_Core/ERR_Settings.c', 'static array<string> Keys('))
assert len(keys) == len(set(keys)) and set(settings) - set(keys) == {"automaticConvoyEnabled", "keepWeaponOnUnconscious", "selfEpinephrine", "planeReconEnabled", "restartOnEmpty", "showAllSites"}
assert len(keys) == len(set(keys)) and set(settings) - set(keys) == {"telemetryEnabled", "automaticConvoyEnabled", "keepWeaponOnUnconscious", "selfEpinephrine", "planeReconEnabled", "restartOnEmpty", "showAllSites"}
assert set(keys) <= set(settings) # Every menu field must have a server setting.
# Native layouts place child widget declarations inside an anonymous Children
# block. A missing brace can preserve most widget names yet prevent menu load.
+3
View File
@@ -11,6 +11,9 @@ rules=(rules.replace('string ', 'std::string ').replace('out int ', 'int& ')
.replace(' < 0) return false;', ' == std::string::npos) return false;').replace('text.ToInt()', 'std::stoi(text)'))
run('#include <cassert>\n#include <string>\n'+rules+r'''
int main(){
assert(CoreSetting("mission","telemetry")=="telemetryEnabled");
assert(Valid("telemetryEnabled",0)&&Valid("telemetryEnabled",1));
assert(!Valid("telemetryEnabled",-1)&&!Valid("telemetryEnabled",2));
assert(CoreSetting("intel","radiosites")=="radioIntelSiteCount");
assert(Valid("radioIntelSiteCount",0)&&Valid("radioIntelSiteCount",5)&&Valid("radioIntelSiteCount",20));
assert(!Valid("radioIntelSiteCount",-1)&&!Valid("radioIntelSiteCount",21));
+5 -3
View File
@@ -27,13 +27,14 @@ def cpp(s):
s=s.replace('GetGame().GetWorld().GetWorldTime()', 'clockNow')
s=s.replace('string.Format(', 'Format(')
s=s.replace('int visited;', 'int visited=0;').replace('int pending, accepted, pendingGuards;', 'int pending=0, accepted=0, pendingGuards=0;')
s=s.replace('int pendingGuards;', 'int pendingGuards=0;').replace('int pending, accepted;', 'int pending=0, accepted=0;')
s=s.replace('int pendingTransports;', 'int pendingTransports=0;').replace('bool driver, passenger;', 'bool driver=false, passenger=false;')
s=s.replace('if (!GetGame()) return;', '')
return s
methods='\n'.join(cpp(body(source,sig)) for sig in (
'protected bool TransportEmpty(', 'protected bool TransportFailure(',
'protected bool CreateTransport(', 'protected bool TransportReady(',
'protected bool Fail(', 'ERR_PrepareResult Prepare(', 'void Rollback(', 'void Stop('))
'protected bool Fail(', 'protected ERR_PrepareResult PrepareStock(', 'ERR_PrepareResult Prepare(', 'void Rollback(', 'void Stop('))
run(r'''
#include <cassert>
#include <algorithm>
@@ -72,14 +73,15 @@ struct ERR_SiteTransport_ {bool Optional=false;int Prefab=123;vector Offset{};fl
struct Definition {ERR_SiteTransport Transport=nullptr;string Id="clue-site";vector Position{};};
struct LocationType{bool prepared=false;void SetPrepared(bool value){prepared=value;}};
struct Site {LocationType*Location=nullptr;struct Definition* Definition=nullptr;IEntity Transport=nullptr,TransportEntity=nullptr;bool TransportAttempted=false,TransportOmitted=false;float TransportDeadline=-1;Site* Responders=nullptr;int guard=PENDING;};using ERR_SiteRuntime=Site*;
struct Inventory{Array<IEntity>items;bool accept=true;int calls=0;void GetItems(Array<IEntity>&out){out=items;}bool TryInsertItem(IEntity){calls++;return accept;}};
struct Placement{IEntity Item=nullptr;Inventory* Inventory_=nullptr;bool Accepted=false,Optional=false,Skipped=false;};
struct Inventory{Array<IEntity>items;bool accept=true;int calls=0;void GetItems(Array<IEntity>&out){out=items;}bool CanInsertItem(IEntity){return accept;}bool TryInsertItem(IEntity){calls++;return accept;}};
struct Placement{IEntity Item=nullptr;Inventory* Inventory_=nullptr;bool Accepted=false,Optional=false,Skipped=false,OnGround=false;};
struct Extraction{ERR_PrepareResult result=READY;int stops=0;ERR_PrepareResult Poll(float){return result;}string FailureReason(){return "aircraft failed";}void Stop(){stops++;}};
struct ExtractionStatus{bool pickup=true;bool PickupConfigured(){return pickup;}};
struct Mode{ExtractionStatus status;ExtractionStatus&Extraction(){return status;}Array<IEntity>owned;void Own(IEntity e){owned.Insert(e);}};
using ERR_StockPlacement=Placement*;
struct Service{Mode mode;Mode* m_Mode=&mode;Extraction extraction;Extraction* m_ExtractionPlacement=&extraction;Array<ERR_SiteRuntime>m_Sites;Array<IEntity>m_Entities;Array<ERR_StockPlacement>m_Placements;
bool m_Begun=true,m_Stopped=false,m_SingleSite=false,clear=true;int guardChecks=0,cancels=0,patrolStops=0;ERR_PrepareResult m_State=PENDING;float m_Deadline=15000;string m_FailureReason;
void LeaveStartingItem(ERR_StockPlacement p){p->Skipped=true;}
bool TransportClearance(vector,float,float){return clear;}
ERR_PrepareResult InitialGuards(ERR_SiteRuntime site){guardChecks++;if(site->guard==FAILED){Fail("guards failed");return FAILED;}return ERR_PrepareResult(site->guard);}
void CancelInitialGuardRequests(){cancels++;}void StopPatrols(){patrolStops++;}
+1 -1
View File
@@ -5,7 +5,7 @@ from policy_harness import ROOT,body,run
spec=importlib.util.spec_from_file_location('import_settings',BASE/'tools/import_settings.py')
m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
result,deferred=m.merge({'configVersion':1,'reportIntervalSeconds':20.0,'supportSquadLimit':2,'responseRadiusMetres':1500,'convoyCount':3},{'aiIntelChancePercent':0,'intelRevealChancePercent':100})
assert result==dict(droneReconCount=0,droneCooldownSeconds=300,aiDoctrine=0,aiShadow=0,planeAltitudeMetres=250,planeReplacementSeconds=60,planeSectorSeconds=120,planeReconEnabled=1,roadblockCount=4,civilianTrafficCount=3,startMode=0,keepWeaponOnUnconscious=1,selfEpinephrine=0,restartOnEmpty=1,emptyRestartHours=5,reportIntervalSeconds=20,supportSquadLimit=2,responseRadiusMetres=1500,aiIntelChancePercent=0,intelRevealChancePercent=100,radioIntelSiteCount=5,showAllSites=0,radioDefenceSeconds=180,startTimeMinutes=840,timeMultiplier=1,startDateCode=0,weatherProfile=0,viewDistanceMetres=0,automaticEquipmentChancePercent=25,antiAirChancePercent=7,antiPersonnelMineChancePercent=60,automaticConvoyEnabled=1,convoyCount=3,heliTimerMinutes=15)
assert result==dict(telemetryEnabled=1,droneReconCount=0,droneCooldownSeconds=300,aiDoctrine=0,aiShadow=0,planeAltitudeMetres=250,planeReplacementSeconds=60,planeSectorSeconds=120,planeReconEnabled=1,roadblockCount=4,civilianTrafficCount=3,startMode=0,keepWeaponOnUnconscious=1,selfEpinephrine=0,restartOnEmpty=1,emptyRestartHours=5,reportIntervalSeconds=20,supportSquadLimit=2,responseRadiusMetres=1500,aiIntelChancePercent=0,intelRevealChancePercent=100,radioIntelSiteCount=5,showAllSites=0,radioDefenceSeconds=180,startTimeMinutes=840,timeMultiplier=1,startDateCode=0,weatherProfile=0,viewDistanceMetres=0,automaticEquipmentChancePercent=25,antiAirChancePercent=7,antiPersonnelMineChancePercent=60,automaticConvoyEnabled=1,convoyCount=3,heliTimerMinutes=15)
assert deferred==[]
for count in (0,1,3,20):
values,notes=m.merge(ai={'convoyCount':count})
+18 -4
View File
@@ -1,7 +1,7 @@
"""Execute production starting-supply validation, edits, mode switches and save guards."""
from policy_harness import ROOT, body, run
p=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_StartBackpack.c'
methods='\n'.join(body(p,n) for n in ['static bool Valid(', 'protected bool Save(', 'bool Custom(', 'bool SetMode(', 'bool Set(', 'bool Remove('])
methods='\n'.join(body(p,n) for n in ['static string Destination(', 'static bool Valid(', 'protected bool Save(', 'bool Custom(', 'bool SetMode(', 'bool Set(', 'bool Remove('])
methods=methods.replace('protected ','').replace('array<string>', 'Array<string>').replace('set<string> seen = new set<string>();','Array<string> seen;').replace('!records || ', '').replace('ResourceName','string').replace('quantity.ToString()', 'string(std::to_string(quantity))')
methods=methods.replace('int total;', 'int total = 0;')
methods=methods.replace('ERR_TerrainCatalog.Key','Key').replace('ERR_ItemPreferences.Guid','Guid').replace('ERR_CommandArguments.Integer','Integer').replace('Replication.IsServer()', 'authority').replace('ERR_ProfileConfig.Save(PATH, json)', 'Persist(json)').replace('JsonSaveContext json = new JsonSaveContext();','JsonSaveContext json;')
@@ -26,6 +26,14 @@ int main(){
string a="{AAAAAAAAAAAAAAAA}Prefabs/rifle.et",b="{BBBBBBBBBBBBBBBB}Prefabs/magazine.et";
Supplies s;assert(!s.Custom());assert(s.Set(a,2)&&s.Set(b,4)&&s.m_Records.size()==3);
assert(s.SetMode(true)&&s.Custom());assert(s.Set(a,3)&&s.m_Records.size()==3);
assert(s.Set(a,2,"ground")&&s.m_Records.size()==4);
assert(s.Remove("aaaaaaaaaaaaaaaa","ground")&&s.m_Records.size()==3);
assert(!s.Remove("aaaaaaaaaaaaaaaa","ground"));
assert(!s.Set(a,1,"invalid"));
assert(Supplies::Valid({"custom",a+"|2",a+"|3|ground"})); // legacy bag plus ground
assert(!Supplies::Valid({"custom",a+"|2",a+"|3|backpack"})); // duplicate old/new bag entry
assert(!Supplies::Valid({"custom",a+"|48|ground",a+"|17|backpack"}));
assert(!Supplies::Valid({"custom",a+"|2|ground",a+"|3|ground"}));
auto saved=s.m_Records;s.fail=true;assert(!s.SetMode(false)&&!s.Remove("aaaaaaaaaaaaaaaa")&&s.m_Records==saved);s.fail=false;
s.authority=false;assert(!s.Set(a,1)&&!s.SetMode(false)&&s.m_Records==saved);s.authority=true;
s.m_ReadOnly=true;assert(!s.Set(b,1)&&s.m_Records==saved);s.m_ReadOnly=false;
@@ -44,9 +52,15 @@ int main(){
session=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_AdminSession.c'
assert body(session,'void Command(').index('if (!Authorize()) return;') < body(session,'void Command(').index('StartBackpackCommand(')
command=body(session,'protected bool StartBackpackCommand(')
assert command.index('catalog.Contains(parts[2], parts[3])') < command.index('supplies.Set(item.Prefab, count)')
assert command.index('catalog.Contains(parts[2], parts[3])') < command.index('supplies.Set(item.Prefab, count, destination)')
stock=(ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SiteService.c').read_text()
assert 'spec.Kind == ERR_LocationKind.PRISON && !customStart' in stock
assert 'placement.Optional && GetGame().GetWorld().GetWorldTime() > m_Deadline' in stock
assert 'ERR_Actors.DeleteSpawned(placement.Item); placement.Skipped = true;' in stock
assert 'placement.Optional && (expired || (!placement.Accepted && !placement.Inventory.CanInsertItem(placement.Item)))' in stock
assert 'LeaveStartingItem(placement); continue;' in stock
assert 'ERR_Actors.DeleteSpawned(placement.Item)' not in stock
print('PASS: exact starting-stock edits, quantities/type/total limits, duplicate rejection, saved custom/default modes, authority/read-only/save-failure retention and authenticated catalog routing')
assert '(version == 1 || version == 2)' in p.read_text()
assert 'item.OnGround = Destination(parts) == "ground"' in p.read_text()
assert 'placement.OnGround = customStart && stock.OnGround;' in stock
assert 'if (placement.OnGround) { LeaveStartingItem(placement); continue; }' in stock
+56
View File
@@ -0,0 +1,56 @@
"""Execute the real stock-preparation loop with native inventory boundaries stubbed."""
from policy_harness import ROOT, body, run
p=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SiteService.c'
s=body(p,'protected ERR_PrepareResult PrepareStock(')
for a,b in [('protected ',''),('int pending, accepted;', 'int pending=0, accepted=0;'),('ERR_PrepareResult.', 'Result::'),('foreach (ERR_StockPlacement placement :', 'for (Placement* placement :'),('array<IEntity> items = {};','Array<IEntity> items;'),('placement.', 'placement->'),('.GetPrefabData()', '->GetPrefabData()'),('.GetPrefabName()', '->GetPrefabName()'),('.GetItems(', '->GetItems('),('.CanInsertItem(', '->CanInsertItem('),('.TryInsertItem(', '->TryInsertItem('),('GetGame().GetWorld().GetWorldTime()', 'now'),('LogLevel.WARNING','0'),('string.Format(', 'string::Format(')]:s=s.replace(a,b)
run(r'''
#include <cassert>
#include <string>
#include <vector>
#include <algorithm>
struct string:std::string{using std::string::string;bool IsEmpty(){return empty();}static string Format(string,int,int,string){return "timeout";}};
template<class T>struct Array:std::vector<T>{using std::vector<T>::vector;bool Contains(T x){return std::find(this->begin(),this->end(),x)!=this->end();}};
struct Item{string name="item";Item* GetPrefabData(){return this;}string GetPrefabName(){return name;}};using IEntity=Item*;
struct Inventory{Array<IEntity> contents;bool fits=true,accept=true;int requests=0;
void GetItems(Array<IEntity>& out){out=contents;}bool CanInsertItem(IEntity){return fits;}bool TryInsertItem(IEntity){requests++;return accept;}};
struct Placement{IEntity Item=nullptr;struct Inventory* Inventory=nullptr;bool Skipped=false,Accepted=false,Optional=false,OnGround=false;};
namespace Result{enum{FAILED=-1,PENDING,READY};}using ERR_PrepareResult=int;
int now=0;void Print(string,int){}
struct Service{Array<Placement*> m_Placements;int m_Deadline=100,failed=0,dropped=0;Array<IEntity> loose;
void Fail(string){failed++;}void LeaveStartingItem(Placement* p){dropped++;loose.push_back(p->Item);p->Skipped=true;}
'''+s+r'''
};
int main(){
Item floor;Inventory room;Placement explicitGround{&floor,&room,false,false,true,true};
Service direct;direct.m_Placements={&explicitGround};
assert(direct.PrepareStock()==Result::READY&&direct.dropped==1&&room.requests==0);
assert(direct.loose[0]==&floor);assert(direct.PrepareStock()==Result::READY&&direct.dropped==1);
Item map,rifle,pistol;Inventory bag;bag.fits=false;
Placement big{&rifle,&bag,false,false,true};Service s;s.m_Placements={&big};
assert(s.PrepareStock()==Result::READY&&s.dropped==1&&s.loose[0]==&rifle&&bag.requests==0);
assert(s.PrepareStock()==Result::READY&&s.dropped==1); // same item is not repeatedly dropped/spawned
Inventory good;Placement required{&map,&good},small{&pistol,&good,false,false,true};
Service mixed;mixed.m_Placements={&required,&small,&big};
assert(mixed.PrepareStock()==Result::PENDING&&good.requests==2);
assert(mixed.PrepareStock()==Result::PENDING&&good.requests==2); // async accepted requests are not repeated
good.contents={&map,&pistol};assert(mixed.PrepareStock()==Result::READY&&mixed.dropped==0);
Inventory delayed;delayed.fits=false;Placement moving{&rifle,&delayed,false,true,true};
Service wait;wait.m_Placements={&moving};assert(wait.PrepareStock()==Result::PENDING&&wait.dropped==0);
delayed.contents={&rifle};assert(wait.PrepareStock()==Result::READY&&wait.dropped==0);
Placement gone{nullptr,&bag,false,false,true};Service missing;missing.m_Placements={&gone};
assert(missing.PrepareStock()==Result::READY&&missing.failed==0&&gone.Skipped);
Placement lostMap{nullptr,&bag};Service broken;broken.m_Placements={&lostMap};
assert(broken.PrepareStock()==Result::FAILED&&broken.failed==1);
Inventory refusing;refusing.fits=false;refusing.accept=false;Placement noMap{&map,&refusing};
Service requiredFailure;requiredFailure.m_Placements={&noMap};
assert(requiredFailure.PrepareStock()==Result::PENDING);now=101;
assert(requiredFailure.PrepareStock()==Result::FAILED&&requiredFailure.failed==1);
Inventory stalled;Placement timeout{&rifle,&stalled,false,true,true};Service optionalTimeout;optionalTimeout.m_Placements={&timeout};
assert(optionalTimeout.PrepareStock()==Result::READY&&optionalTimeout.dropped==1&&optionalTimeout.failed==0);
}
''')
leave=body(p,'protected void LeaveStartingItem(')
assert '!item.GetParentSlot()' in leave and 'DryGround(point)' in leave
assert 'BackpackClearance(point)' in leave and 'TraceMove(access, null)' in leave
assert 'DeleteSpawned' not in leave and 'Spawn(' not in leave
print('PASS: native-rejected custom items remain available without startup delay; required map and async insertion safeguards retained')
+95
View File
@@ -0,0 +1,95 @@
"""Execute production outcome, completion and HTTP callback logic with fake engine boundaries."""
from policy_harness import ROOT, body, run
p=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_Telemetry.c'
s=p.read_text()
def cpp(v):
for a,b in [('protected ',''),('m_Config.', 'config.'),('Replication.IsServer()', 'authority'),('m_Mode.Settings()', 'settings'),('FileIO.DeleteFile(', 'DeleteFile('),('ERR_Phase.','Phase::'),('ERR_TelemetryRules.','Rules::'),('ERR_MissionRules.','MissionRules::'),('Math.Min(', 'std::min('),('Math.Pow(', 'power('),('System.GetTickCount()', '::now'),('LogLevel.WARNING','0'),('m_State.', 'm_State->'),('m_Callback = null', 'm_Callback = nullptr'),('m_Context = null', 'm_Context = nullptr'),('m_Mode = null', 'm_Mode = nullptr'),('RestCallback callback', 'RestCallback* callback'),('callback.GetHttpCode()', 'callback->GetHttpCode()'),('GetGame().GetCallqueue().Remove(Pulse)', 'removed++')]: v=v.replace(a,b)
return v
rules='\n'.join(cpp(body(p,x)) for x in ['static bool Acknowledged(', 'static int RetrySeconds(', 'static string Outcome('])
methods='\n'.join(cpp(body(p,x)) for x in ['bool Enabled(', 'void Refresh(', 'protected void Purge(', 'void Finish(', 'void PhaseChanged(', 'protected void Delivered(', 'protected void Failed(', 'void Close('])
run(r'''
#include <cassert>
#include <cmath>
#include <string>
#include <vector>
#include <algorithm>
struct string:std::string {using std::string::string;using std::string::operator=;bool IsEmpty()const{return empty();}}; using ERR_Phase=int;
namespace Phase {enum {PREPARING, PRISON, RADIO_SEARCH, RADIO_DEFENCE, EXTRACTION, VICTORY, DEFEAT};}
int now=123456; bool GetGame(){return true;} void Print(string,int){} int power(int a,int b){return std::pow(a,b);}
namespace MissionRules {bool Terminal(int p){return p==Phase::VICTORY||p==Phase::DEFEAT;}}
struct Rules {
'''+rules+r'''
};
struct Pending:std::vector<string>{using std::vector<string>::operator=;void Clear(){clear();}bool IsEmpty(){return empty();}void RemoveItem(string x){erase(std::remove(begin(),end(),x),end());}};
struct State{string previousOutcome,previousMissionId;Pending pending;};
struct RestCallback{int code=200;int GetHttpCode(){return code;}};
struct Service {
struct Config{string Endpoint="https://fixture.invalid/";}config;
struct Settings{int enabled=1;int Get(string){return enabled;}explicit operator bool()const{return true;}}settings;
Pending m_Players;bool authority=true,m_Collected=false,m_Partial=false,m_TruncatedPlayers=false,current=true;
string m_MissionId="same-round";string STATE="queue";
int m_Started=now,m_LastSample=0,m_NextHeartbeat=0,m_Peak=0,deletes=0;
void DeleteFile(std::string){deletes++;}
bool m_Enabled=true,m_Closed=false,m_Ended=false,m_Dirty=false,desired=true,saveOK=true;
State state;State* m_State=&state;RestCallback* m_Callback=nullptr;void* m_Context=nullptr;void* m_Mode=nullptr;
string m_InFlight;int m_Failures=0,m_Delivered=0,m_NextAttempt=0,samples=0,events=0,removed=0,saves=0;
string type,result;
bool Current(){return current;}
void Sample(){samples++;}
void Event(string t,string o="",string p="",string prior=""){type=t;result=o;events++;}
bool Save(){saves++;if(saveOK)m_Dirty=false;return saveOK;}
'''+methods+r'''
};
int main(){
for(int p=Phase::PREPARING;p<=Phase::EXTRACTION;p++) assert(Rules::Outcome(p,false,false)=="interrupted");
assert(Rules::Outcome(Phase::VICTORY,false,false)=="victory");
assert(Rules::Outcome(Phase::DEFEAT,false,false)=="defeat");
assert(Rules::Outcome(Phase::DEFEAT,true,false)=="restarted_empty");
assert(Rules::Outcome(Phase::PREPARING,false,true)=="preparation_failed");
for(int status:{200,201,202,204,299}) assert(Rules::Acknowledged(status));
for(int status:{0,199,300,400,401,429,500,503}) assert(!Rules::Acknowledged(status));
assert(Rules::RetrySeconds(1)==10&&Rules::RetrySeconds(2)==20);
assert(Rules::RetrySeconds(6)==300&&Rules::RetrySeconds(100000)==300);
Service win;win.PhaseChanged(Phase::EXTRACTION,Phase::VICTORY);
assert(win.events==1&&win.result=="victory"&&win.samples==1&&win.m_Ended);
win.Finish("interrupted");win.PhaseChanged(Phase::VICTORY,Phase::DEFEAT);
assert(win.events==1&&win.state.previousOutcome=="victory");
Service loss;loss.Finish("preparation_failed");loss.Close();loss.Close();
assert(loss.events==1&&loss.result=="preparation_failed"&&loss.removed==1);
Service abandoned;abandoned.Close();assert(abandoned.result=="interrupted");
Service disabled;disabled.settings.enabled=0;disabled.Finish("victory");assert(disabled.events==0);
RestCallback old,current;Service delivery;
delivery.state.pending={"first","second"};delivery.m_InFlight="first";delivery.m_Callback=&current;
delivery.Delivered(&old);assert(delivery.state.pending.size()==2);
current.code=500;delivery.Delivered(&current);
assert(delivery.state.pending.size()==2&&delivery.m_Failures==1&&delivery.m_NextAttempt==now+10000);
delivery.m_Callback=&current;delivery.m_InFlight="first";current.code=204;delivery.Delivered(&current);
assert(delivery.state.pending.size()==1&&delivery.state.pending[0]=="second"&&delivery.m_Failures==0&&delivery.saves==1);
delivery.Delivered(&current);assert(delivery.saves==1);
delivery.m_Callback=&current;delivery.m_InFlight="second";delivery.m_Enabled=false;
delivery.Delivered(&current);assert(delivery.state.pending.size()==1);
delivery.m_Enabled=true;delivery.m_Closed=true;delivery.Failed(&current);assert(delivery.m_Failures==0);
Service resume;resume.m_Enabled=false;resume.Refresh();
assert(resume.events==1&&resume.type=="mission_started"&&!resume.m_Partial);
resume.settings.enabled=0;resume.Refresh();assert(!resume.Enabled()&&resume.deletes==3);
resume.settings.enabled=1;resume.Refresh();
assert(resume.events==2&&resume.type=="telemetry_resumed"&&resume.m_Partial&&resume.m_MissionId=="same-round");
Service unconfigured;unconfigured.m_Enabled=false;unconfigured.config.Endpoint="";unconfigured.Refresh();assert(!unconfigured.Enabled()&&unconfigured.events==0);
Service client;client.m_Enabled=false;client.authority=false;client.Refresh();assert(client.events==0);
Service stale;stale.current=false;stale.Close();assert(stale.events==0&&stale.removed==1);
Service restart;restart.m_Enabled=false;restart.state.previousMissionId="earlier";restart.state.previousOutcome="victory";restart.Refresh();assert(restart.events==2&&restart.type=="mission_started");
Service progress;progress.PhaseChanged(Phase::PRISON,Phase::RADIO_SEARCH);assert(progress.type=="phase_changed"&&!progress.m_Ended);
}
''')
lifecycle=(p.parent/'ERR_TelemetryLifecycle.c').read_text()
assert lifecycle.index('m_ERRTelemetry.Finish(ERR_TelemetryRules.Outcome') < lifecycle.index('super.OnGameModeEnd(endData)')
assert '#ifdef WORKBENCH' in lifecycle and 'errTelemetryTest' in lifecycle
assert 'GetRestApi' not in lifecycle
assert 'm_State.pending.Count() >= 64' in s and 'payload.Length() > 65536' in s
assert s.index('if (m_Dirty && !Save()) return;') < s.index('m_Context.POST(')
assert 'ReadValue("token"' in s and 'WriteValue("token", m_Config.Token)' in s
# Persisted event data must not contain authentication material.
event=body(p,'protected void Event(')
assert 'Token' not in event and 'Endpoint' not in event
assert 'DidEscape' in body(p,'protected void Sample(')
print('PASS: telemetry outcomes, exactly-once finish, opt-out gate, stale callbacks, HTTP status handling and bounded retry')