Retain spawn replication through admission and nearby crate use
This commit is contained in:
@@ -11,7 +11,7 @@ methods = '\n'.join(body(source, key) for key in keys)
|
||||
methods = methods.replace('protected ', '').replace('Replication.IsServer()', 'Replication::IsServer()')
|
||||
methods = methods.replace('ERR_GameMode.Get()', 'Mode::Get()')
|
||||
methods = methods.replace('RplComponent component = RplComponent.Cast(Replication.FindItem(id));', 'RplComponent* component = Replication::FindItem(id);')
|
||||
methods = methods.replace('component.GetEntity()', 'component->GetEntity()')
|
||||
methods = methods.replace('component.GetEntity()', 'component->GetEntity()').replace('RplId.Invalid()', '-1')
|
||||
methods = methods.replace('foreach (RplId id :', 'for (RplId id :')
|
||||
methods = methods.replace('GetGame().GetWorld().GetWorldTime()', 'game.now')
|
||||
methods = methods.replace('GetGame().GetCallqueue().Remove(ERR_CheckClientSpawnScene)', 'game.removes++')
|
||||
@@ -31,8 +31,9 @@ template<typename... T>void PrintFormat(const char*,T...){}
|
||||
struct Probe{
|
||||
int m_ERRSceneToken=4,m_ERRClientSceneToken=4,acks=0,lastAck=0;
|
||||
bool m_ERRScenePending=false,m_ERRSceneAcknowledged=false,m_ERRSceneTimedOut=false,m_ERRClientScenePending=false;
|
||||
bool m_ERRSceneReleased=false,m_ERRSceneVerified=false,m_ERRClientSceneFinal=false,lastFinal=false;float m_ERRClientSceneStableSince=-1;
|
||||
bool m_ERRSceneFinalRequested=false,m_ERRSceneVerified=false,m_ERRClientSceneFinal=false,lastFinal=false;float m_ERRClientSceneStableSince=-1;
|
||||
float m_ERRSceneDeadline=1000,m_ERRSceneStartedAt=0;Nodes m_ERRClientSceneNodes,m_ERRClientStockOwners,m_ERRClientStockItems;
|
||||
void ERR_ReportStockWait(int,int,int){}
|
||||
bool stockReady=true;bool ERR_ClientStockReady(){return stockReady;}void ERR_RefreshSceneInventory(){}
|
||||
int GetPlayerId(){return 1;}void SendAck(int token,bool finalCheck){acks++;lastAck=token;lastFinal=finalCheck;}
|
||||
'''+methods+r'''
|
||||
@@ -59,7 +60,7 @@ int main(){
|
||||
Probe handshake;handshake.m_ERRScenePending=true;
|
||||
handshake.ERR_AcknowledgeSpawnScene(4,true);assert(!handshake.ERR_SpawnSceneVerified());
|
||||
handshake.ERR_AcknowledgeSpawnScene(4,false);handshake.ERR_AcknowledgeSpawnScene(4,true);assert(!handshake.ERR_SpawnSceneVerified());
|
||||
handshake.m_ERRSceneReleased=true;handshake.ERR_AcknowledgeSpawnScene(3,true);assert(!handshake.ERR_SpawnSceneVerified());
|
||||
handshake.m_ERRSceneFinalRequested=true;handshake.ERR_AcknowledgeSpawnScene(3,true);assert(!handshake.ERR_SpawnSceneVerified());
|
||||
handshake.ERR_AcknowledgeSpawnScene(4,true);assert(handshake.ERR_SpawnSceneVerified());
|
||||
Probe finalTimeout;finalTimeout.m_ERRScenePending=true;finalTimeout.m_ERRSceneAcknowledged=true;
|
||||
finalTimeout.ERR_SpawnSceneMayFinish();assert(finalTimeout.m_ERRSceneTimedOut&&!finalTimeout.ERR_SpawnSceneVerified());
|
||||
@@ -89,7 +90,7 @@ for old, new in [('override ', ''), ('SCR_SpawnRequestComponent requestComponent
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
enum class SCR_ESpawnResult{OK,SPAWN_NOT_ALLOWED,CANNOT_ASSIGN};
|
||||
struct Request{bool ready=false,mayFinish=false,verified=false;bool ERR_SpawnSceneVerified(){return ready&&verified;}bool ERR_VerifySceneRelease(){return !ready||verified;}int ends=0;bool ERR_SpawnSceneReady(){return ready;}bool ERR_SpawnSceneMayFinish(){return mayFinish;}bool preloadRequired=true;bool ERR_TakeScenePreload(){if(!ready||!preloadRequired)return false;preloadRequired=false;return true;}void ERR_EndSpawnScene(){ends++;}};
|
||||
struct Request{bool ready=false,mayFinish=false,verified=false;bool ERR_SpawnSceneVerified(){return ready&&verified;}bool ERR_VerifyLoadedScene(){return !ready||verified;}int ends=0,retained=0;void ERR_RetainSpawnScene(void*){retained++;}bool ERR_SpawnSceneReady(){return ready;}bool ERR_SpawnSceneMayFinish(){return mayFinish;}bool preloadRequired=true;bool ERR_TakeScenePreload(){if(!ready||!preloadRequired)return false;preloadRequired=false;return true;}void ERR_EndSpawnScene(){ends++;}};
|
||||
struct Data{};struct Actor{};struct Ticket{};
|
||||
struct Mode{static bool current;static bool Get(){return current;}};bool Mode::current=true;
|
||||
struct Logic{Ticket ticket;bool hasTicket=true,allowed=true;Ticket* TakeSpawnTicket(Request*,Data*,Actor*){return hasTicket?&ticket:nullptr;}bool MayFinalizeSpawn(Ticket*){return allowed;}};
|
||||
@@ -108,7 +109,7 @@ int main(){
|
||||
h.preloadReady=true;assert(!h.CanFinalize_S(&request,&data,&actor));request.verified=true;assert(h.CanFinalize_S(&request,&data,&actor)&&h.preloadStarts==1);
|
||||
h.storage.allowed=false;
|
||||
assert(h.FinalizeRequest_S(&request,&data,&actor)==SCR_ESpawnResult::SPAWN_NOT_ALLOWED&&h.assignments==0&&h.rejects==2);
|
||||
h.storage.allowed=true;assert(h.FinalizeRequest_S(&request,&data,&actor)==SCR_ESpawnResult::OK&&h.assignments==1&&request.ends==3);
|
||||
h.storage.allowed=true;assert(h.FinalizeRequest_S(&request,&data,&actor)==SCR_ESpawnResult::OK&&h.assignments==1&&request.ends==3&&request.retained==1);
|
||||
h.storage.hasTicket=false;assert(h.FinalizeRequest_S(&request,&data,&actor)==SCR_ESpawnResult::SPAWN_NOT_ALLOWED&&h.assignments==1&&h.rejects==2);
|
||||
Mode::current=false;h.logic=nullptr;assert(h.FinalizeRequest_S(&request,&data,&actor)==SCR_ESpawnResult::OK&&h.assignments==2);
|
||||
}
|
||||
@@ -127,7 +128,7 @@ struct Items:std::vector<int>{using std::vector<int>::vector;void Clear(){clear(
|
||||
struct Controller{int GetControlledEntity(){return 42;}};
|
||||
struct Probe{
|
||||
Items m_ERRSceneEntities{1,2},m_ERRSceneRoots,m_ERRSceneStockOwners,m_ERRSceneStockItems;
|
||||
bool m_ERRSceneReleased=true,m_ERRSceneVerified=true;bool m_ERRSceneObserver=true,m_ERRScenePending=true,m_ERRSceneAcknowledged=true,m_ERRSceneTimedOut=true,m_ERRScenePreloadRequired=true;
|
||||
bool m_ERRSceneFinalRequested=true,m_ERRSceneVerified=true;bool m_ERRSceneObserver=true,m_ERRScenePending=true,m_ERRSceneAcknowledged=true,m_ERRSceneTimedOut=true,m_ERRScenePreloadRequired=true;
|
||||
int m_ERRSceneToken=7,restored=0,cancelled=0,observed=0;Controller controller;bool connected=true;
|
||||
Controller* GetPlayerController(){return connected?&controller:nullptr;}
|
||||
void ERR_StreamScene(int,bool normal){assert(normal);restored++;}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
"""Exercise production retention lifetime and the post-preload handoff."""
|
||||
from policy_harness import ROOT, body, run
|
||||
|
||||
source = ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SpawnSceneLease.c'
|
||||
methods = '\n'.join(body(source, key) for key in ('void Start(', 'protected void Check(', 'void Stop('))
|
||||
for old, new in [
|
||||
('protected ', ''), ('SCR_PlayerController controller', 'Controller* controller'),
|
||||
('IEntity actor', 'Actor* actor'), ('vector position', 'float position'),
|
||||
('array<IEntity> entities', 'Items entities'), ('ERR_GameMode mode = ERR_GameMode.Get()', 'Mode* mode = currentMode'),
|
||||
('mode.Active()', 'mode->active'), ('m_Controller.GetControlledEntity()', 'm_Controller->actor'),
|
||||
('vector.DistanceXZ(m_Actor.GetOrigin(), m_Position)', 'std::abs(m_Actor->position - m_Position)'),
|
||||
('GetGame().GetCallqueue().CallLater(Check, 500, true)', 'starts++'),
|
||||
('if (GetGame()) GetGame().GetCallqueue().Remove(Check)', 'removes++'),
|
||||
('foreach (IEntity entity :', 'for (int entity :'), ('null', 'nullptr')]:
|
||||
methods = methods.replace(old, new)
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
struct Items:std::vector<int>{using std::vector<int>::vector;void Clear(){clear();}void Copy(Items other){assign(other.begin(),other.end());}};
|
||||
struct Actor{float position=0;};struct Controller{Actor* actor=nullptr;};
|
||||
struct Mode{bool active=true;}mode;Mode* currentMode=&mode;
|
||||
struct Lease{
|
||||
Controller* m_Controller=nullptr;Actor* m_Actor=nullptr;float m_Position=0;Items m_Entities;
|
||||
int starts=0,removes=0,released=0;void Stream(Controller*,int,bool normal){assert(normal);released++;}
|
||||
'''+methods+r'''
|
||||
};
|
||||
int main(){
|
||||
Actor actor,other;Controller controller{&actor};Lease lease;
|
||||
lease.Start(&controller,&actor,0,{11,12,13});
|
||||
for(int i=0;i<1000;i++){lease.Check();}assert(lease.released==0&&lease.m_Entities.size()==3);
|
||||
actor.position=120;lease.Check();assert(lease.released==0);
|
||||
actor.position=121;lease.Check();assert(lease.released==3&&lease.m_Entities.empty());
|
||||
lease.Stop();assert(lease.released==3);
|
||||
actor.position=0;lease.Start(&controller,&actor,0,{21});controller.actor=&other;lease.Check();assert(lease.released==4);
|
||||
controller.actor=&actor;lease.Start(&controller,&actor,0,{22});currentMode=nullptr;lease.Check();assert(lease.released==5);currentMode=&mode;
|
||||
lease.Start(&controller,&actor,0,{23});mode.active=false;lease.Check();assert(lease.released==6);mode.active=true;
|
||||
lease.Start(&controller,&actor,0,{24});lease.m_Controller=nullptr;lease.Check();assert(lease.m_Entities.empty());
|
||||
lease.Start(&controller,&actor,0,{25});lease.Start(&controller,&actor,0,{26});assert(lease.released==8);lease.Stop();assert(lease.released==9);
|
||||
}
|
||||
''')
|
||||
|
||||
scene = ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SpawnClientScene.c'
|
||||
verify = body(scene, 'bool ERR_VerifyLoadedScene(')
|
||||
for old, new in [('ObserversSystem observers', 'Observers* observers'),
|
||||
('observers.InsertObserverMP', 'observers->InsertObserverMP'),
|
||||
('GetPlayerController().GetRplIdentity()', '1'), ('null', 'nullptr'),
|
||||
('Rpc(ERR_ConfirmClientSpawnScene, m_ERRSceneToken)', 'confirms++')]:
|
||||
verify = verify.replace(old, new)
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
template<typename... T>void PrintFormat(const char*,T...){}
|
||||
struct Observers{int calls=0;void InsertObserverMP(int,float,float,void*){calls++;}};
|
||||
struct Probe{
|
||||
bool m_ERRSceneTimedOut=false,m_ERRScenePreloadRequired=true,m_ERRSceneFinalRequested=false,m_ERRSceneVerified=false,ready=false,preloading=false;
|
||||
int m_ERRSceneToken=1,confirms=0;float m_ERRScenePosition[3]={1,0,2};Observers observer;
|
||||
bool ERR_SpawnSceneReady(){return ready;}bool IsPreloading(){return preloading;}
|
||||
Observers* GetObserversSystem(){return &observer;}bool GetPlayerController(){return true;}int GetPlayerId(){return 1;}
|
||||
// Releasing nodes here would break dedicated final readiness: intentionally
|
||||
// no streaming mutator in this adapter. The production method must retain them.
|
||||
'''+verify+r'''
|
||||
};
|
||||
int main(){
|
||||
Probe p;assert(!p.ERR_VerifyLoadedScene());p.ready=true;assert(!p.ERR_VerifyLoadedScene());
|
||||
p.m_ERRScenePreloadRequired=false;p.preloading=true;assert(!p.ERR_VerifyLoadedScene());p.preloading=false;
|
||||
assert(!p.ERR_VerifyLoadedScene()&&p.confirms==1&&p.observer.calls==1);
|
||||
assert(!p.ERR_VerifyLoadedScene()&&p.confirms==1);p.m_ERRSceneVerified=true;assert(p.ERR_VerifyLoadedScene());
|
||||
Probe timeout;timeout.m_ERRSceneTimedOut=true;assert(timeout.ERR_VerifyLoadedScene()&&timeout.confirms==0);
|
||||
}
|
||||
''')
|
||||
print('PASS: spawn scene remains held through final acknowledgement and nearby use; leaving, reassignment, disconnect and teardown release it')
|
||||
Reference in New Issue
Block a user