Capture current mission, AI, map and administration development
Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
--- a/addons/core/Scripts/Game/ERR/00_Core/ERR_PlayerGuide.c
|
||||
+++ b/addons/core/Scripts/Game/ERR/00_Core/ERR_PlayerGuide.c
|
||||
@@ -30,6 +30,14 @@
|
||||
}
|
||||
protected void ERR_UpdateGuide()
|
||||
{
|
||||
+ // A retained controller may outlive its local ownership during replacement.
|
||||
+ if (!GetGame()) return;
|
||||
+ if (this != GetGame().GetPlayerController())
|
||||
+ {
|
||||
+ GetGame().GetCallqueue().Remove(ERR_UpdateGuide);
|
||||
+ m_ERRGuideStarted = false;
|
||||
+ return;
|
||||
+ }
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
if (!mode) return;
|
||||
if (mode.Phase() != ERR_Phase.PREPARING && (ERR_ObserverState() != ERR_ObserverReason.NONE || !ERR_Actors.Alive(GetControlledEntity()))) return;
|
||||
--- a/tests/test_hint_delivery.py
|
||||
+++ b/tests/test_hint_delivery.py
|
||||
@@ -12,12 +12,14 @@
|
||||
text=text.replace(typename+' ',typename+'* ')
|
||||
for variable in ('mode','hints','resource','respawn','manager'):
|
||||
text=text.replace(variable+'.',variable+'->')
|
||||
+ text=text.replace('GetCallqueue().','GetCallqueue()->').replace('Remove(ERR_UpdateGuide)','Remove(this)').replace('CallLater(ERR_UpdateGuide, 1000, true)','CallLater(this, 1000, true)')
|
||||
text=text.replace('GetGame().','GetGame()->').replace('EntitySpawnParams params = new EntitySpawnParams();','EntitySpawnParams params;')
|
||||
return text
|
||||
-methods=cpp(body(GUIDE,'override void OnControlledEntityChanged('))+'\n'+cpp(body(GUIDE,'protected void ERR_UpdateGuide('))+'\n'+cpp(body(OBSERVER,'protected void ERR_UpdateObserverClient('))
|
||||
+methods=cpp(body(GUIDE,'protected void ERR_StartGuide(')).replace('ERR_StartGuide()', 'StartActual()')+'\n'+cpp(body(GUIDE,'override void OnControlledEntityChanged('))+'\n'+cpp(body(GUIDE,'protected void ERR_UpdateGuide('))+'\n'+cpp(body(OBSERVER,'protected void ERR_UpdateObserverClient('))
|
||||
guidance=cpp(body(OBSERVER,'static string Message('))+'\n'+cpp(body(OBSERVER,'static bool Refresh('))
|
||||
run(r'''#include <cassert>
|
||||
#include <string>
|
||||
+#include <set>
|
||||
struct string:std::string {using std::string::string;using std::string::operator=;string(std::string s):std::string(s){}bool IsEmpty()const{return empty();}};
|
||||
namespace ERR_Phase {enum {PREPARING,PRISON,RADIO_SEARCH,RADIO_DEFENCE,EXTRACTION};}
|
||||
namespace ERR_ObserverReason {enum {NONE,SPENT_LIFE,LATE_EXTRACTION,SPAWN_UNAVAILABLE,UNCONSCIOUS};}
|
||||
@@ -44,11 +46,13 @@
|
||||
struct SCR_RespawnSystemComponent {int destroys=0;static SCR_RespawnSystemComponent* current;static SCR_RespawnSystemComponent* GetInstance(){return current;}void DestroyLoadingPlaceholder(){destroys++;}} respawn;
|
||||
SCR_RespawnSystemComponent* SCR_RespawnSystemComponent::current=&respawn;
|
||||
struct RplId {static int Invalid(){return -1;}};
|
||||
-struct Game {int spawns=0;SCR_ManualCamera* SpawnEntityPrefab(Resource*,int,EntitySpawnParams){spawns++;return &camera;}int GetWorld(){return 1;}CameraManager* GetCameraManager(){return &manager;}} game;
|
||||
-Game* GetGame(){return &game;}
|
||||
+struct Queue {std::set<void*> scheduled;int added=0;void CallLater(void* owner,int,bool){added++;scheduled.insert(owner);}void Remove(void* owner){scheduled.erase(owner);}} queue;
|
||||
+struct Game {Queue* GetCallqueue(){return &queue;}void* local=nullptr;void* GetPlayerController(){return local;}int spawns=0;SCR_ManualCamera* SpawnEntityPrefab(Resource*,int,EntitySpawnParams){spawns++;return &camera;}int GetWorld(){return 1;}CameraManager* GetCameraManager(){return &manager;}} game;
|
||||
+bool gameAvailable=true;Game* GetGame(){return gameAvailable?&game:nullptr;}
|
||||
struct Controller {
|
||||
+ Controller(){game.local=this;}
|
||||
void ERR_UpdateRecoveryCamera(ERR_GameMode*){}
|
||||
- int nativeChanges=0,starts=0;void ERR_StartGuide(){starts++;}
|
||||
+ int nativeChanges=0,starts=0;bool m_ERRGuideStarted=false;void ERR_StartGuide(){starts++;StartActual();}
|
||||
int m_ERRGuidePhase=-1;string m_ERRGuideMessage;
|
||||
Actor* controlled=&actor;int m_ERRObserverReason=ERR_ObserverReason::NONE;
|
||||
SCR_ManualCamera* m_ERRObserverCamera=nullptr;Vec m_ERRObserverAnchor;
|
||||
@@ -58,7 +62,7 @@
|
||||
void ERR_CloseObserverCamera(){closes++;m_ERRObserverReady=false;m_ERRObserverCamera=nullptr;}
|
||||
'''+methods+r'''
|
||||
};
|
||||
-void reset(){mode=ERR_GameMode();ERR_GameMode::current=&mode;hints=SCR_HintManagerComponent();SCR_HintManagerComponent::current=&hints;actor=Actor();Resource::current=&resource;manager.current=&camera;}
|
||||
+void reset(){queue=Queue();gameAvailable=true;mode=ERR_GameMode();ERR_GameMode::current=&mode;hints=SCR_HintManagerComponent();SCR_HintManagerComponent::current=&hints;actor=Actor();Resource::current=&resource;manager.current=&camera;}
|
||||
int main(){
|
||||
reset();Controller g;
|
||||
SCR_HintManagerComponent::current=nullptr;g.ERR_UpdateGuide();assert(g.m_ERRGuidePhase==-1&&hints.attempts==0);
|
||||
@@ -95,6 +99,22 @@
|
||||
Controller unconscious;actor.unconscious=true;unconscious.ERR_UpdateGuide();assert(unconscious.m_ERRGuidePhase==mode.phase);actor.unconscious=false;
|
||||
Controller preparing;preparing.controlled=nullptr;mode.phase=ERR_Phase::PREPARING;mode.message="preparing";preparing.ERR_UpdateGuide();assert(preparing.m_ERRGuideMessage=="preparing");
|
||||
ERR_GameMode::current=nullptr;int attempts=hints.attempts;preparing.ERR_UpdateGuide();assert(hints.attempts==attempts);
|
||||
+ // A prior local controller can remain alive until native teardown. Its
|
||||
+ // recurring callback must not write global hints after ownership changed.
|
||||
+ reset();Controller former;former.StartActual();former.ERR_UpdateGuide();Controller current;current.StartActual();
|
||||
+ assert(queue.scheduled.count(&former)==1&&queue.scheduled.count(¤t)==1);
|
||||
+ mode.phase=ERR_Phase::PREPARING;mode.message="new round preparing";
|
||||
+ former.controlled=nullptr;int beforeStale=hints.attempts;
|
||||
+ former.ERR_UpdateGuide();assert(hints.attempts==beforeStale);
|
||||
+ assert(!former.m_ERRGuideStarted&&queue.scheduled.count(&former)==0&&queue.scheduled.count(¤t)==1);
|
||||
+ assert(former.m_ERRGuidePhase==ERR_Phase::PRISON&&former.m_ERRGuideMessage=="prison");
|
||||
+ current.ERR_UpdateGuide();assert(hints.attempts==beforeStale+1&¤t.m_ERRGuideMessage==mode.message);
|
||||
+ current.ERR_UpdateGuide();assert(hints.attempts==beforeStale+1);
|
||||
+ mode.phase=ERR_Phase::RADIO_SEARCH;mode.message="new radio clue";former.controlled=&actor;
|
||||
+ former.ERR_UpdateGuide();assert(hints.attempts==beforeStale+1);
|
||||
+ game.local=&former;former.StartActual();assert(former.m_ERRGuideStarted&&queue.scheduled.count(&former)==1);int added=queue.added;former.StartActual();assert(queue.added==added);hints.accepts=false;former.ERR_UpdateGuide();assert(former.m_ERRGuideMessage=="prison");
|
||||
+ hints.accepts=true;former.ERR_UpdateGuide();assert(former.m_ERRGuideMessage==mode.message);
|
||||
+ int beforeNoGame=hints.attempts;gameAvailable=false;former.ERR_UpdateGuide();assert(hints.attempts==beforeNoGame);gameAvailable=true;
|
||||
reset();Controller s;s.controlled=nullptr;s.m_ERRObserverReason=ERR_ObserverReason::SPENT_LIFE;
|
||||
// Actual camera setup executes; missing manager cannot consume delivery state.
|
||||
SCR_HintManagerComponent::current=nullptr;s.ERR_UpdateObserverClient();assert(s.m_ERRObserverCamera==&camera&&!s.m_ERRObserverReady);
|
||||
@@ -120,4 +140,4 @@
|
||||
''')
|
||||
for path,signature in ((GUIDE,'protected void ERR_UpdateGuide('),(OBSERVER,'protected void ERR_UpdateObserverClient(')):
|
||||
assert 'CallLater' not in body(path,signature), 'Delivery retry uses the existing monitor only'
|
||||
-print('PASS: actual guide/observer monitors retry native refusal, cache successful delivery, deduplicate and preserve living/preparing/spectator boundaries; native UI not exercised')
|
||||
+print('PASS: actual guide ownership and scoped timer cancellation, current-controller preservation/reacquisition, hint retry/dedup and observer boundaries; native UI not exercised')
|
||||
Reference in New Issue
Block a user