Files
jakub 6a667d8deb Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
2026-10-13 22:52:53 +02:00

54 lines
3.3 KiB
Diff

--- a/addons/core/Scripts/Game/ERR/00_Core/ERR_GameMode.c
+++ b/addons/core/Scripts/Game/ERR/00_Core/ERR_GameMode.c
@@ -465,7 +465,7 @@
SCR_PlayerController controller = SCR_PlayerController.Cast(GetGame().GetPlayerController());
if (!controller) return;
controller.ERR_StartObserverMonitor();
- GetGame().GetCallqueue().Remove(BindLocalObserver);
+ // Keep watching for a replacement local controller; Start is idempotent.
}
protected void StartScenario()
--- a/tests/test_observer_ownership.py
+++ b/tests/test_observer_ownership.py
@@ -3,6 +3,10 @@
from pathlib import Path
from policy_harness import ROOT,body,run
p=Path(os.environ.get('ERR_OBSERVER_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c'))
+game_mode=Path(os.environ.get('ERR_GAME_MODE_SOURCE',ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_GameMode.c'))
+binder=body(game_mode,'protected void BindLocalObserver(')
+binder=binder.replace('protected ', '').replace('SCR_PlayerController controller = SCR_PlayerController.Cast(GetGame().GetPlayerController());','Controller* controller = static_cast<Controller*>(GetGame()->GetPlayerController());')
+binder=binder.replace('GetGame().','GetGame()->').replace('GetCallqueue().','GetCallqueue()->').replace('Remove(BindLocalObserver)','Remove(this)').replace('controller.','controller->')
def cpp(s):
s=s.replace('protected ','').replace('null','nullptr')
for n in ('ERR_GameMode','ERR_Actors','ERR_ObserverRules','ERR_ObserverGuidance','SCR_HintManagerComponent','SCR_RespawnSystemComponent','SCR_EditorManagerEntity','Resource','SCR_ManualCamera','RplId','ETransformMode','LogLevel','System'):
@@ -60,7 +64,28 @@
void ERR_OnObserverMonitorStarted(){starts++;}
'''+methods+r'''
};
+struct Binder {bool m_Stopped=false,m_NativeEndHandled=false;
+'''+binder+r'''
+};
int main(){
+ // The real repeating binder must survive the first controller and gaps.
+ Binder binder;Controller first,replacement;queue.owners.insert(&binder);
+ game.local=nullptr;binder.BindLocalObserver();assert(queue.owners.count(&binder)&&queue.added==0);
+ game.local=&first;binder.BindLocalObserver();
+ assert(first.m_ERRObserverMonitor&&first.starts==1&&queue.owners.count(&first));
+ assert(queue.owners.count(&binder));
+ binder.BindLocalObserver();assert(first.starts==1&&queue.added==1);
+ game.local=nullptr;binder.BindLocalObserver();assert(queue.owners.count(&binder));
+ game.local=&replacement;binder.BindLocalObserver();
+ assert(replacement.m_ERRObserverMonitor&&replacement.starts==1&&queue.owners.count(&replacement));
+ first.ERR_UpdateObserverClient();
+ assert(!first.m_ERRObserverMonitor&&!queue.owners.count(&first)&&queue.owners.count(&replacement));
+ binder.BindLocalObserver();assert(replacement.starts==1&&queue.added==2);
+ binder.m_Stopped=true;binder.BindLocalObserver();assert(!queue.owners.count(&binder)&&queue.owners.count(&replacement));
+ binder.m_Stopped=false;binder.m_NativeEndHandled=true;queue.owners.insert(&binder);binder.BindLocalObserver();
+ assert(!queue.owners.count(&binder)&&queue.owners.count(&replacement)&&queue.added==2);
+ queue.owners.clear();queue.added=0;game.local=nullptr;
+
Actor body;CameraBase formerBodyCamera,newBodyCamera;
Controller former,current;former.actor=&body;former.playerCamera=&formerBodyCamera;
game.local=&former;manager.current=&formerBodyCamera;former.ERR_StartObserverMonitor();