Files
EscapistsReintroduced/tests/test_terrain_chat_retention.py
T

90 lines
5.5 KiB
Python

"""Native chat stays world-owned across server/client terrain handover."""
from policy_harness import ROOT, body, run
core = ROOT / 'addons/core/Scripts/Game/ERR/00_Core'
server = core / 'ERR_TerrainTakeover.c'
client = core / 'ERR_TerrainTakeoverClient.c'
managers = core / 'ERR_TerrainTakeoverManagers.c'
run('#include <cassert>\n#include <string>\nusing ResourceName=std::string;\n' +
body(server, 'static ResourceName Bootstrap(') + r'''
int main(){
assert(Bootstrap(true)=="{9D182739ABEF405C}Prefabs/ERR/TerrainTakeover.et");
assert(Bootstrap(false)=="{DA8E364BC095712F}Prefabs/ERR/TerrainTakeoverWithChat.et");
}
''')
retain = body(managers, 'static bool RetainManager(')
retain = (retain.replace('IEntity entity', 'IEntity* entity')
.replace('IEntity ancestor', 'IEntity* ancestor')
.replace('array<IEntity> removed', 'Entities* removed')
.replace('RplComponent rpl', 'RplComponent* rpl')
.replace('SCR_EntityHelper.', 'SCR_EntityHelper::')
.replace('null', 'nullptr'))
for value in ('entity', 'ancestor', 'removed', 'rpl', 'GetParent()', 'GetNode()'):
retain = retain.replace(value + '.', value + '->')
run(r'''
#include <cassert>
#include <vector>
#include <algorithm>
struct Node {Node* parent=nullptr;int changes=0;void SetParent(Node* p){parent=p;changes++;}};
struct RplComponent {Node* node=nullptr;Node* GetNode(){return node;}};
struct IEntity {
IEntity* parent=nullptr;RplComponent* rpl=nullptr;bool refuseDetach=false;int removals=0;
IEntity* GetParent(){return parent;}
void RemoveChild(IEntity* child,bool keepWorld){assert(keepWorld&&child->parent==this);removals++;if(!refuseDetach)child->parent=nullptr;}
};
struct Entities:std::vector<IEntity*> {
using std::vector<IEntity*>::vector;
bool Contains(IEntity* entity){return std::find(begin(),end(),entity)!=end();}
};
namespace SCR_EntityHelper {RplComponent* GetEntityRplComponent(IEntity* e){return e->rpl;}}
''' + retain + r'''
int main(){
IEntity oldMode,chat,intermediate,unrelated;Entities removed{&oldMode,nullptr};
Node oldNode,chatNode;chatNode.parent=&oldNode;RplComponent chatRpl{&chatNode};chat.rpl=&chatRpl;
// Ordinary terrain chat is standalone: no unregister, replacement or reparent.
assert(RetainManager(&chat,&removed)&&chat.parent==nullptr&&chatNode.changes==0);
// Nested chat is detached before old hierarchy deletion; replication cannot
// later remove the retained service with the discarded mode either.
intermediate.parent=&oldMode;chat.parent=&intermediate;
assert(RetainManager(&chat,&removed)&&chat.parent==nullptr&&intermediate.removals==1);
assert(chatNode.parent==nullptr&&chatNode.changes==1);
assert(RetainManager(&chat,&removed)&&chatNode.changes==1);
chat.parent=&unrelated;assert(RetainManager(&chat,&removed)&&chat.parent==&unrelated&&unrelated.removals==0);
assert(!RetainManager(nullptr,&removed)&&!RetainManager(&chat,nullptr));
removed.push_back(&chat);assert(!RetainManager(&chat,&removed)&&chat.parent==&unrelated);removed.pop_back();
oldMode.refuseDetach=true;chat.parent=&oldMode;int changes=chatNode.changes;
assert(!RetainManager(&chat,&removed)&&chat.parent==&oldMode&&chatNode.changes==changes);
oldMode.refuseDetach=false;chat.rpl=nullptr;assert(RetainManager(&chat,&removed)&&chat.parent==nullptr);
}
''')
drain = body(server, 'protected void ERR_DrainTerrainMode(')
capture = body(client, 'void ERR_ClientCaptureOriginal(')
handover = body(client, 'void ERR_BeginClientTerrainTakeover(')
assert 'GetChat()' not in drain.split('array<IEntity> managers =', 1)[1].split(';', 1)[0]
assert 'm_ERRClientOldManagers.Insert(GetChat())' not in capture
assert drain.index('RetainManager(m_ERRTerrainChat, managers)') < drain.index('m_ERRTerrainOriginal.OnGameEnd()')
assert handover.index('RetainManager(GetChat(), m_ERRClientOldManagers)') < handover.index('mode.OnGameEnd()')
assert 'GetChat() == m_ERRTerrainChat' in body(server, 'protected bool ERR_TerrainRegistrationsEmpty(')
assert 'm_ERRTerrainChat = null' in body(server, 'protected void ERR_ResetTerrainTakeover(')
preflight = body(server, 'protected bool ERR_PreflightTerrainTakeover(')
create = body(server, 'protected void ERR_CreateTerrainMode(')
assert 'Bootstrap(GetChat() != null)' in preflight
assert 'if (!GetChat())' not in preflight
assert 'Bootstrap(m_ERRTerrainChat != null)' in create
assert 'Link(mode, !m_ERRTerrainChat)' in create
assert 'if (m_ERRTerrainChat && !ERR_TerrainTakeoverReplication.RetainManager' in drain
assert 'if (GetChat() && !ERR_TerrainTakeoverReplication.RetainManager' in handover
assert '&& m_ERRTerrainChat &&' not in body(server, 'protected bool ERR_TerrainRegistrationsEmpty(')
assert 'if (ownsChat && !Parent(GetGame().GetChat(), mode)) return false' in body(managers, 'static bool Link(')
prefabs = ROOT / 'addons/core/Prefabs/ERR'
assert 'ScriptedChatEntity' not in (prefabs / 'TerrainTakeover.et').read_text()
fallback = (prefabs / 'TerrainTakeoverWithChat.et').read_text()
assert '"{9D182739ABEF405C}Prefabs/ERR/TerrainTakeover.et"' in fallback
assert fallback.count('ScriptedChatEntity Chat') == 1
assert 'Streamable Disabled' in fallback
assert '{DA8E364BC095712F}Prefabs/ERR/TerrainTakeoverWithChat.et' in (prefabs / 'TerrainTakeoverWithChat.et.meta').read_text()
assert 'ScriptedChatEntity' in (prefabs / 'TerrainConnector.et').read_text()
assert 'ScriptedChatEntity' in (ROOT / 'addons/core/Worlds/ERR_Everon_Layers/default.layer').read_text()
print('PASS: native chat retained on both peers, nested ownership detached, absent chat supplied by replicated fallback, authored mission chat preserved')