Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
63 lines
5.3 KiB
Python
63 lines
5.3 KiB
Python
"""Production server ledger and permit handshake with native owner RPC substituted."""
|
|
from policy_harness import ROOT,body,run
|
|
p=ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_WelcomeIdentity.c'
|
|
claim=body(p,'bool Claim(').replace('m_Claimed.','m_Claimed.').replace('UUID identity','UUID identity')
|
|
methods='\n'.join(body(p,k) for k in ['bool ERR_WelcomePermit(', 'protected void ERR_RequestWelcomePermit(', 'protected void ERR_ReceiveWelcomePermit('])
|
|
methods=methods.replace('protected ', '').replace('ERR_GameMode mode','ERR_GameMode* mode').replace('ERR_PlayerLife life','ERR_PlayerLife* life').replace('null','nullptr')
|
|
for obj in ['mode','life']:methods=methods.replace(obj+'.',obj+'->')
|
|
for kind in ['ERR_GameMode','Replication','ERR_WelcomeVoiceRules']:methods=methods.replace(kind+'.',kind+'::')
|
|
methods=methods.replace('GetGame().','GetGame()->').replace('GetWorld().','GetWorld()->').replace('GetPlayerManager().','GetPlayerManager()->').replace('Players().','Players()->')
|
|
methods=methods.replace('Rpc(ERR_RequestWelcomePermit, serial)', 'SendRequest(serial)').replace('Rpc(ERR_ReceiveWelcomePermit, serial, m_ERRWelcomeServerGranted)', 'SendReply(serial, m_ERRWelcomeServerGranted)')
|
|
run(r'''
|
|
#include <cassert>
|
|
#include <set>
|
|
#include <string>
|
|
struct UUID {int value=0; bool IsNull(){return value==0;}bool operator<(UUID b)const{return value<b.value;}};
|
|
struct Claims {std::set<UUID> values;int Find(UUID id){return values.count(id)?0:-1;}int Count(){return values.size();}void Insert(UUID id){values.insert(id);}};
|
|
struct Ledger {Claims m_Claimed;
|
|
'''+claim+r'''
|
|
};
|
|
struct Controller;
|
|
struct ERR_PlayerLife {int ConnectionId=1;UUID AccountId{7};};
|
|
struct Roster {ERR_PlayerLife life;bool valid=true;ERR_PlayerLife* CurrentActor(bool actor){return actor&&valid?&life:nullptr;}};
|
|
struct ERR_GameMode {bool active=true,pending=false;std::string url="clip";Roster roster;static ERR_GameMode* current;static ERR_GameMode* Get(){return current;}
|
|
bool Active(){return active;}bool ERR_WelcomePending(){return pending;}std::string ERR_WelcomeVoiceURL(){return url;}Roster* Players(){return &roster;}};
|
|
ERR_GameMode* ERR_GameMode::current;
|
|
namespace Replication {bool server=true;bool IsServer(){return server;}}
|
|
struct ERR_WelcomeVoiceRules {static bool URL(std::string s){return !s.empty();}};
|
|
struct Game {Controller* owner=nullptr;Controller* canonical=nullptr;float now=1000;Ledger ledger;Controller* GetPlayerController(){return owner;}
|
|
Game* GetWorld(){return this;}Game* GetPlayerManager(){return this;}float GetWorldTime(){return now;}Controller* GetPlayerController(int){return canonical;}
|
|
bool ERR_ClaimWelcomeIdentity(UUID id){return ledger.Claim(id);}} game;
|
|
Game* GetGame(){return &game;}
|
|
struct Controller {
|
|
int m_ERRWelcomeSerial=0,m_ERRWelcomeDecision=0,m_ERRWelcomeServerSerial=0;bool m_ERRWelcomeServerGranted=false;
|
|
float m_ERRWelcomeRetry=0,m_ERRWelcomeServerReply=-1000;bool body=true,dropReply=false;int requests=0,replies=0;
|
|
bool GetControlledEntity(){return body;}
|
|
void SendRequest(int serial){requests++;ERR_RequestWelcomePermit(serial);}
|
|
void SendReply(int serial,bool granted){replies++;if(!dropReply)ERR_ReceiveWelcomePermit(serial,granted);}
|
|
'''+methods+r'''
|
|
};
|
|
int main(){
|
|
ERR_GameMode mode;ERR_GameMode::current=&mode;Controller first;game.owner=game.canonical=&first;
|
|
mode.url="";assert(!first.ERR_WelcomePermit(1));assert(game.ledger.m_Claimed.Count()==0);
|
|
game.now+=2001;mode.url="clip";mode.roster.valid=false;assert(!first.ERR_WelcomePermit(1));assert(game.ledger.m_Claimed.Count()==0);
|
|
game.now+=2001;mode.roster.valid=true;mode.pending=true;assert(!first.ERR_WelcomePermit(1));assert(game.ledger.m_Claimed.Count()==0);
|
|
game.now+=2001;mode.pending=false;first.dropReply=true;assert(!first.ERR_WelcomePermit(1));assert(game.ledger.m_Claimed.Count()==1);
|
|
assert(!first.ERR_WelcomePermit(1));int before=first.requests;
|
|
game.now+=2001;first.dropReply=false;assert(first.ERR_WelcomePermit(1));assert(first.requests==before+1); // idempotent retry
|
|
first.ERR_ReceiveWelcomePermit(0,false);assert(first.ERR_WelcomePermit(1)); // stale reply
|
|
Controller crashedClient;game.owner=game.canonical=&crashedClient;game.now+=2001;
|
|
assert(!crashedClient.ERR_WelcomePermit(1));assert(crashedClient.m_ERRWelcomeDecision==-1); // new process/controller, same account
|
|
before=crashedClient.requests;game.now+=9999;assert(!crashedClient.ERR_WelcomePermit(1));assert(crashedClient.requests==before);
|
|
Controller otherAccount;game.owner=game.canonical=&otherAccount;mode.roster.life.AccountId={8};assert(otherAccount.ERR_WelcomePermit(1));
|
|
// Reused IDs are scoped to authenticated account. A previous controller cannot claim a new account.
|
|
Controller stale;game.owner=&stale;game.now+=2001;assert(!stale.ERR_WelcomePermit(1));assert(game.ledger.m_Claimed.Count()==2);
|
|
game.owner=game.canonical=&crashedClient;mode.roster.life.AccountId={7};game.now+=2001;
|
|
assert(!crashedClient.ERR_WelcomePermit(2)); // round/client serial changes do not reset ledger
|
|
assert(!game.ledger.Claim(UUID{0}));
|
|
Ledger nextServer;assert(nextServer.Claim(UUID{7})); // actual server process restart starts a new session
|
|
Ledger capped;for(int i=1;i<=10000;i++)assert(capped.Claim(UUID{i}));assert(!capped.Claim(UUID{10001}));
|
|
}
|
|
''')
|
|
print('PASS: account greeting survives client/controller/round changes; owner validation, delayed readiness, idempotent retry, stale replies and bounded ledger')
|