Preserve native chat during terrain takeover with fallback for missing chat
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""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')
|
||||
Reference in New Issue
Block a user