Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
54 lines
3.3 KiB
Diff
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();
|