Recheck storage slots after spawn preload and streaming handoff
This commit is contained in:
+35
-16
@@ -2,11 +2,11 @@
|
||||
from policy_harness import ROOT, body, run
|
||||
|
||||
source = ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SpawnClientScene.c'
|
||||
keys = ('bool ERR_SpawnSceneReady(', 'bool ERR_SpawnSceneMayFinish(',
|
||||
keys = ('bool ERR_SpawnSceneReady(', 'bool ERR_SpawnSceneMayFinish(', 'bool ERR_SpawnSceneVerified(',
|
||||
'protected void ERR_AcknowledgeSpawnScene(',
|
||||
'protected void ERR_ClearClientSpawnScene(',
|
||||
'protected void ERR_CheckClientSpawnScene(',
|
||||
'protected void ERR_CancelClientSpawnScene(')
|
||||
'protected void ERR_CancelClientSpawnScene(', 'protected void ERR_ConfirmClientSpawnScene(')
|
||||
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()')
|
||||
@@ -15,7 +15,8 @@ methods = methods.replace('component.GetEntity()', 'component->GetEntity()')
|
||||
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++')
|
||||
methods = methods.replace('Rpc(ERR_AcknowledgeSpawnScene, token)', 'SendAck(token)')
|
||||
methods = methods.replace('GetGame().GetCallqueue().CallLater(ERR_CheckClientSpawnScene, 100, true)', 'game.starts++')
|
||||
methods = methods.replace('Rpc(ERR_AcknowledgeSpawnScene, token, m_ERRClientSceneFinal)', 'SendAck(token, m_ERRClientSceneFinal)')
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
@@ -24,39 +25,57 @@ using RplId=int;
|
||||
struct RplComponent{bool entity=true;bool GetEntity(){return entity;}};
|
||||
namespace Replication{bool server=true;std::map<int,RplComponent*> items;bool IsServer(){return server;}RplComponent* FindItem(int id){return items.count(id)?items[id]:nullptr;}}
|
||||
struct Mode{static bool present;static bool Get(){return present;}};bool Mode::present=true;
|
||||
struct Game{float now=0;int removes=0;}game;Game* GetGame(){return &game;}
|
||||
struct Nodes:std::vector<int>{using std::vector<int>::operator=;void Clear(){clear();}int Count(){return size();}};
|
||||
struct Game{float now=0;int removes=0,starts=0;}game;Game* GetGame(){return &game;}
|
||||
struct Nodes:std::vector<int>{using std::vector<int>::operator=;void Clear(){clear();}int Count(){return size();}bool IsEmpty(){return empty();}};
|
||||
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;
|
||||
float m_ERRSceneDeadline=1000,m_ERRSceneStartedAt=0;Nodes m_ERRClientSceneNodes,m_ERRClientStockOwners,m_ERRClientStockItems;
|
||||
bool stockReady=true;bool ERR_ClientStockReady(){return stockReady;}void ERR_RefreshSceneInventory(){}
|
||||
int GetPlayerId(){return 1;}void SendAck(int token){acks++;lastAck=token;}
|
||||
int GetPlayerId(){return 1;}void SendAck(int token,bool finalCheck){acks++;lastAck=token;lastFinal=finalCheck;}
|
||||
'''+methods+r'''
|
||||
};
|
||||
int main(){
|
||||
Probe server;assert(!server.ERR_SpawnSceneReady());
|
||||
server.ERR_AcknowledgeSpawnScene(4);assert(!server.m_ERRSceneAcknowledged);
|
||||
server.ERR_AcknowledgeSpawnScene(4,false);assert(!server.m_ERRSceneAcknowledged);
|
||||
server.m_ERRScenePending=true;assert(!server.ERR_SpawnSceneMayFinish());
|
||||
server.ERR_AcknowledgeSpawnScene(3);assert(!server.ERR_SpawnSceneReady());
|
||||
Replication::server=false;server.ERR_AcknowledgeSpawnScene(4);assert(!server.ERR_SpawnSceneReady());Replication::server=true;
|
||||
server.ERR_AcknowledgeSpawnScene(4);assert(server.ERR_SpawnSceneReady()&&server.ERR_SpawnSceneMayFinish());
|
||||
server.ERR_AcknowledgeSpawnScene(3,false);assert(!server.ERR_SpawnSceneReady());
|
||||
Replication::server=false;server.ERR_AcknowledgeSpawnScene(4,false);assert(!server.ERR_SpawnSceneReady());Replication::server=true;
|
||||
server.ERR_AcknowledgeSpawnScene(4,false);assert(server.ERR_SpawnSceneReady()&&server.ERR_SpawnSceneMayFinish());
|
||||
server.m_ERRSceneAcknowledged=false;game.now=999;assert(!server.ERR_SpawnSceneMayFinish());
|
||||
game.now=1000;assert(server.ERR_SpawnSceneMayFinish()&&server.m_ERRSceneTimedOut&&!server.ERR_SpawnSceneReady());
|
||||
server.ERR_AcknowledgeSpawnScene(4);assert(!server.ERR_SpawnSceneReady());
|
||||
server.ERR_AcknowledgeSpawnScene(4,false);assert(!server.ERR_SpawnSceneReady());
|
||||
Probe client;client.m_ERRClientScenePending=true;client.m_ERRClientSceneNodes={11,22,33};
|
||||
RplComponent root,wall,gate;Replication::items[11]=&root;
|
||||
for(int i=0;i<100;i++){client.ERR_CheckClientSpawnScene();}assert(client.acks==0&&client.m_ERRClientScenePending);
|
||||
Replication::items[22]=&wall;client.ERR_CheckClientSpawnScene();assert(client.acks==0);
|
||||
gate.entity=false;Replication::items[33]=&gate;client.ERR_CheckClientSpawnScene();assert(client.acks==0);
|
||||
gate.entity=true;client.stockReady=false;client.ERR_CheckClientSpawnScene();assert(client.acks==0&&client.m_ERRClientScenePending);
|
||||
client.stockReady=true;client.ERR_CheckClientSpawnScene();assert(client.acks==1&&client.lastAck==4&&!client.m_ERRClientScenePending&&client.m_ERRClientSceneNodes.empty());
|
||||
client.stockReady=true;client.ERR_CheckClientSpawnScene();assert(client.acks==1&&client.lastAck==4&&!client.m_ERRClientScenePending&&!client.m_ERRClientSceneNodes.empty());
|
||||
client.ERR_CheckClientSpawnScene();assert(client.acks==1);
|
||||
// The first reply cannot finalize. Final replies before release are ignored.
|
||||
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.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());
|
||||
// A replica disappearing after preload resets the continuous final check.
|
||||
client.ERR_ConfirmClientSpawnScene(3);assert(!client.m_ERRClientSceneFinal&&game.starts==0);
|
||||
client.ERR_ConfirmClientSpawnScene(4);assert(client.m_ERRClientSceneFinal&&client.m_ERRClientScenePending&&game.starts==1);game.now=2000;
|
||||
client.ERR_CheckClientSpawnScene();game.now=2500;client.ERR_CheckClientSpawnScene();assert(client.acks==1);
|
||||
gate.entity=false;client.ERR_CheckClientSpawnScene();gate.entity=true;game.now=2600;client.ERR_CheckClientSpawnScene();
|
||||
game.now=3500;client.ERR_CheckClientSpawnScene();assert(client.acks==1);
|
||||
client.stockReady=false;client.ERR_CheckClientSpawnScene();client.stockReady=true;game.now=3600;client.ERR_CheckClientSpawnScene();
|
||||
game.now=4599;client.ERR_CheckClientSpawnScene();assert(client.acks==1);
|
||||
game.now=4600;client.ERR_CheckClientSpawnScene();assert(client.acks==2&&client.lastFinal);
|
||||
client.m_ERRClientScenePending=true;client.m_ERRClientSceneToken=5;client.m_ERRClientSceneNodes={99};
|
||||
client.ERR_CancelClientSpawnScene(4);assert(client.m_ERRClientScenePending);
|
||||
client.ERR_CancelClientSpawnScene(5);assert(!client.m_ERRClientScenePending&&client.m_ERRClientSceneNodes.empty());
|
||||
client.m_ERRClientScenePending=true;Mode::present=false;client.ERR_CheckClientSpawnScene();assert(!client.m_ERRClientScenePending&&client.acks==1);
|
||||
client.m_ERRClientScenePending=true;Mode::present=false;client.ERR_CheckClientSpawnScene();assert(!client.m_ERRClientScenePending&&client.acks==2);
|
||||
}
|
||||
''')
|
||||
finalizer = ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SpawnFinalization.c'
|
||||
@@ -70,7 +89,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;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_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 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;}};
|
||||
@@ -86,7 +105,7 @@ int main(){
|
||||
// Timeout can end the pending request, but cannot assign an actor.
|
||||
assert(h.FinalizeRequest_S(&request,&data,&actor)==SCR_ESpawnResult::SPAWN_NOT_ALLOWED&&h.assignments==0&&h.rejects==1&&request.ends==1);
|
||||
request.ready=true;assert(!h.CanFinalize_S(&request,&data,&actor)&&h.preloadStarts==1);
|
||||
h.preloadReady=true;assert(h.CanFinalize_S(&request,&data,&actor)&&h.preloadStarts==1);
|
||||
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);
|
||||
@@ -108,7 +127,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_ERRSceneObserver=true,m_ERRScenePending=true,m_ERRSceneAcknowledged=true,m_ERRSceneTimedOut=true,m_ERRScenePreloadRequired=true;
|
||||
bool m_ERRSceneReleased=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++;}
|
||||
|
||||
@@ -4,6 +4,11 @@ inventory=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SpawnSceneInventory.c'
|
||||
scene=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_SpawnClientScene.c'
|
||||
def cpp(s):
|
||||
s=s.replace('protected ','').replace('static ','')
|
||||
s=s.replace('ERR_SpawnSceneInventory.InStorage(', 'InStorage(')
|
||||
s=s.replace('InventoryItemComponent item = InventoryItemComponent.Cast(entity.FindComponent(InventoryItemComponent));', 'Item* item = entity->item;')
|
||||
s=s.replace('InventoryStorageSlot slot = item.GetParentSlot();', 'Slot* slot = item->slot;')
|
||||
s=s.replace('BaseInventoryStorageComponent storage = slot.GetStorage();', 'Storage* storage = slot->storage;')
|
||||
s=s.replace('storage.FindItemSlot(entity)', 'storage->FindItemSlot(entity)').replace('storage.Get(slot.GetID())', 'storage->Get(slot->id)').replace('storage.GetAll(visible)', 'storage->GetAll(visible)')
|
||||
s=s.replace('out array<IEntity> ', 'Items& ').replace('array<IEntity> ', 'Items ')
|
||||
s=s.replace('foreach (IEntity root : roots)', 'for (IEntity root : roots)').replace('foreach (IEntity item : contents)', 'for (IEntity item : contents)')
|
||||
s=s.replace('InventoryStorageManagerComponent inventory = InventoryStorageManagerComponent.Cast(root.FindComponent(InventoryStorageManagerComponent));', 'Inventory* inventory = root->inventory;')
|
||||
@@ -15,7 +20,7 @@ def cpp(s):
|
||||
s=s.replace('Replication.IsServer()', 'Replication::server').replace('GetGame().GetWorld().GetWorldTime()', 'now')
|
||||
s=s.replace('ERR_SpawnSceneInventory.Gather(', 'Gather(').replace('ERR_SpawnSceneInventory.Same(', 'Same(')
|
||||
return s
|
||||
methods='\n'.join(cpp(body(inventory,key)) for key in ('static bool Gather(', 'static bool Same('))
|
||||
methods='\n'.join(cpp(body(inventory,key)) for key in ('static bool InStorage(', 'static bool Gather(', 'static bool Same('))
|
||||
methods+='\n'+cpp(body(scene,'protected bool ERR_ClientStockReady('))
|
||||
methods+='\n'+cpp(body(scene,'protected void ERR_RefreshSceneInventory('))
|
||||
methods+='\n'+cpp(body(scene,'bool ERR_TakeScenePreload('))
|
||||
@@ -28,7 +33,10 @@ template<class T>struct List:std::vector<T>{using std::vector<T>::vector;using s
|
||||
int Count()const{return this->size();}void Insert(T x){this->push_back(x);}bool Contains(T x)const{return std::find(this->begin(),this->end(),x)!=this->end();}
|
||||
int Find(T x)const{auto i=std::find(this->begin(),this->end(),x);return i==this->end()?-1:i-this->begin();}};
|
||||
struct Entity;using IEntity=Entity*;using Items=List<IEntity>;using RplId=int;
|
||||
struct Inventory{Items items;};struct Entity{Inventory* inventory=nullptr;};
|
||||
struct Storage;struct Slot{Storage* storage=nullptr;int id=0;};struct Item{Slot* slot=nullptr;};
|
||||
struct Inventory{Items items;};struct Entity{Inventory* inventory=nullptr;Item* item=nullptr;};
|
||||
struct Storage{Items visible;IEntity stored=nullptr;Slot* slot=nullptr;
|
||||
Slot* FindItemSlot(IEntity e){return stored==e?slot:nullptr;}IEntity Get(int id){return slot&&slot->id==id?stored:nullptr;}void GetAll(Items& out){out=visible;}};
|
||||
void InventoryItems(Inventory* inv,Items& items){items={};for(auto x:inv->items)if(x&&!items.Contains(x))items.Insert(x);}
|
||||
struct RplComponent{IEntity entity=nullptr;IEntity GetEntity(){return entity;}};
|
||||
namespace Replication{bool server=true;std::map<int,RplComponent*> nodes;RplComponent* FindItem(int id){auto it=nodes.find(id);return it==nodes.end()?nullptr:it->second;}}
|
||||
@@ -61,7 +69,18 @@ int main(){
|
||||
inv.items={};for(int i=0;i<20;i++)assert(!p.ERR_ClientStockReady());
|
||||
inv.items={&gun,&mag};assert(!p.ERR_ClientStockReady());
|
||||
inv.items={&gun,&mag,&other};assert(!p.ERR_ClientStockReady());
|
||||
inv.items={&gun,&mag,&map};assert(p.ERR_ClientStockReady());
|
||||
inv.items={&gun,&mag,&map};assert(!p.ERR_ClientStockReady());
|
||||
Storage gs,ms,ps;Slot gslot{&gs,0},mslot{&ms,1},pslot{&ps,2};Item gi{&gslot},mi{&mslot},pi{&pslot};
|
||||
gun.item=&gi;mag.item=&mi;map.item=π
|
||||
gs.stored=&gun;gs.slot=&gslot;gs.visible={&gun};ms.stored=&mag;ms.slot=&mslot;ms.visible={&mag};ps.stored=↦ps.slot=&pslot;ps.visible={&map};
|
||||
assert(p.ERR_ClientStockReady());
|
||||
// Manager still contains the gun, but the actual crate UI cannot read it.
|
||||
gs.visible={};assert(!p.ERR_ClientStockReady());gs.visible={&gun};
|
||||
gs.stored=&other;assert(!p.ERR_ClientStockReady());gs.stored=&gun;
|
||||
gi.slot=nullptr;assert(!p.ERR_ClientStockReady());gi.slot=&gslot;
|
||||
gslot.storage=nullptr;assert(!p.ERR_ClientStockReady());gslot.storage=&gs;
|
||||
gs.slot=&mslot;assert(!p.ERR_ClientStockReady());gs.slot=&gslot;
|
||||
assert(p.ERR_ClientStockReady());
|
||||
cr.entity=nullptr;assert(!p.ERR_ClientStockReady());cr.entity=&crate;
|
||||
crate.inventory=nullptr;assert(!p.ERR_ClientStockReady());crate.inventory=&inv;
|
||||
mr.entity=nullptr;assert(!p.ERR_ClientStockReady());mr.entity=&mag;
|
||||
|
||||
Reference in New Issue
Block a user