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,18 @@
|
||||
{
|
||||
"addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c": {
|
||||
"working_sha256": "4acbd2b23803f1fde3701fbddffd56edd83693196f0be2fe99f3be9b79a12b3d",
|
||||
"index_sha256": "31d08123fa9f49dde380354bd839d1a9deb175cdb940311b4a64dd56eeb04ed7"
|
||||
},
|
||||
"tests/test_hint_delivery.py": {
|
||||
"working_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3",
|
||||
"index_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3"
|
||||
},
|
||||
"tests/test_observer_ownership.py": {
|
||||
"working_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6",
|
||||
"index_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6"
|
||||
},
|
||||
"addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c": {
|
||||
"working_sha256": "7d5ff3389ee806ac213db5be3a68288b3399142ccc29806df0d65b34a0b2fc3c",
|
||||
"index_sha256": "7b9308c9c35413fa76a7bdc8923f99785fb393ad9d06ba4b36df4d8aa29e574b"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
--- a/addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c
|
||||
+++ b/addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c
|
||||
@@ -228,6 +228,7 @@
|
||||
protected bool m_ERRObserverReady;
|
||||
protected ERR_ObserverReason m_ERRShownObserverReason;
|
||||
protected bool m_ERRShownObserverAvailable;
|
||||
+ protected string m_ERRShownRecoveryStatus;
|
||||
protected float m_ERRLastCycle = -1000;
|
||||
protected IEntity m_ERRRecoveryBody;
|
||||
protected float m_ERRRecoveryDeadline = -1;
|
||||
@@ -385,14 +386,18 @@
|
||||
// The server-selected replicated ID distinguishes no target from a target
|
||||
// whose local entity is still streaming. Never imply no survivors from delay.
|
||||
bool survivorAvailable = m_ERRObservedId != RplId.Invalid();
|
||||
- if (ERR_ObserverGuidance.Refresh(m_ERRObserverReady, m_ERRShownObserverReason, m_ERRObserverReason, m_ERRShownObserverAvailable, survivorAvailable))
|
||||
+ string recovery = mode.RecoveryStatus();
|
||||
+ if (ERR_ObserverGuidance.Refresh(m_ERRObserverReady, m_ERRShownObserverReason, m_ERRObserverReason, m_ERRShownObserverAvailable, survivorAvailable) || m_ERRShownRecoveryStatus != recovery)
|
||||
{
|
||||
SCR_HintManagerComponent hints = SCR_HintManagerComponent.GetInstance();
|
||||
if (!hints) return; // retry on the existing monitor; do not lose the first hint
|
||||
- if (!hints.ShowCustom(ERR_ObserverGuidance.Message(m_ERRObserverReason, survivorAvailable), "Spectating", 12)) return;
|
||||
+ string message = ERR_ObserverGuidance.Message(m_ERRObserverReason, survivorAvailable);
|
||||
+ if (!recovery.IsEmpty()) message += "\n" + recovery;
|
||||
+ if (!hints.ShowCustom(message, "Spectating", 12)) return;
|
||||
m_ERRObserverReady = true;
|
||||
m_ERRShownObserverReason = m_ERRObserverReason;
|
||||
m_ERRShownObserverAvailable = survivorAvailable;
|
||||
+ m_ERRShownRecoveryStatus = recovery;
|
||||
}
|
||||
}
|
||||
// Track the actual local body even when an editor/other camera owns the view.
|
||||
@@ -423,6 +428,7 @@
|
||||
protected void ERR_CloseObserverCamera()
|
||||
{
|
||||
m_ERRObserverReady = false;
|
||||
+ m_ERRShownRecoveryStatus = "";
|
||||
if (!m_ERRObserverCamera) return;
|
||||
if (GetGame().GetPlayerController() == this) m_ERRObserverCamera.TrySwitchToControlledEntityCamera();
|
||||
m_ERRObserverCamera.Terminate();
|
||||
--- a/tests/test_hint_delivery.py
|
||||
+++ b/tests/test_hint_delivery.py
|
||||
@@ -4,6 +4,7 @@
|
||||
import os
|
||||
GUIDE=Path(os.environ.get('ERR_GUIDE_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_PlayerGuide.c'))
|
||||
OBSERVER=Path(os.environ.get('ERR_OBSERVER_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c'))
|
||||
+PROBE=Path(os.environ.get('ERR_OBSERVER_PROBE_SOURCE', ROOT/'addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c'))
|
||||
def cpp(text):
|
||||
text=text.replace(' = null;', ' = nullptr;').replace('protected ', '').replace('override ', '').replace('super.OnControlledEntityChanged(from, to);', 'nativeChanges++;')
|
||||
for name in ('ERR_GameMode','ERR_Phase','ERR_ObserverReason','ERR_Actors','ERR_ObjectiveText','SCR_HintManagerComponent','ERR_ObserverGuidance','ERR_ObserverRules','Resource','ETransformMode','SCR_ManualCamera','SCR_RespawnSystemComponent','RplId'):
|
||||
@@ -16,6 +17,7 @@
|
||||
text=text.replace('GetGame().','GetGame()->').replace('EntitySpawnParams params = new EntitySpawnParams();','EntitySpawnParams params;')
|
||||
return text
|
||||
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('))
|
||||
+methods+='\n'+cpp(body(PROBE,'string ERR_ProbeAcceptedRecoveryStatus('))
|
||||
guidance=cpp(body(OBSERVER,'static string Message('))+'\n'+cpp(body(OBSERVER,'static bool Refresh('))
|
||||
run(r'''#include <cassert>
|
||||
#include <string>
|
||||
@@ -25,7 +27,7 @@
|
||||
namespace ERR_ObserverReason {enum {NONE,SPENT_LIFE,LATE_EXTRACTION,SPAWN_UNAVAILABLE,UNCONSCIOUS};}
|
||||
struct Actor {bool alive=true;bool unconscious=false;} actor;using IEntity=Actor*;
|
||||
struct ERR_Actors {static bool Alive(Actor* a){return a&&a->alive;}static bool CanFight(Actor* a){return Alive(a)&&!a->unconscious;}};
|
||||
-struct ERR_GameMode {int phase=ERR_Phase::PRISON;bool active=true;string message="prison";static ERR_GameMode* current;static ERR_GameMode* Get(){return current;}int Phase(){return phase;}bool Active(){return active;}} mode;
|
||||
+struct ERR_GameMode {string recovery;string RecoveryStatus(){return recovery;}int phase=ERR_Phase::PRISON;bool active=true;string message="prison";static ERR_GameMode* current;static ERR_GameMode* Get(){return current;}int Phase(){return phase;}bool Active(){return active;}} mode;
|
||||
ERR_GameMode* ERR_GameMode::current=&mode;
|
||||
struct ERR_ObjectiveText {static string Description(ERR_GameMode* m,bool){return m->message;}static string Title(int){return "objective";}};
|
||||
struct SCR_HintManagerComponent {static SCR_HintManagerComponent* current;bool accepts=true;int attempts=0,shown=0;string last;static SCR_HintManagerComponent* GetInstance(){return current;}bool ShowCustom(string text,string,int){attempts++;if(!accepts)return false;shown++;last=text;return true;}} hints;
|
||||
@@ -57,9 +59,10 @@
|
||||
Actor* controlled=&actor;int m_ERRObserverReason=ERR_ObserverReason::NONE;
|
||||
SCR_ManualCamera* m_ERRObserverCamera=nullptr;Vec m_ERRObserverAnchor;
|
||||
int m_ERRObservedId=1;bool m_ERRObserverReady=false;
|
||||
+ string m_ERRShownRecoveryStatus;
|
||||
int m_ERRShownObserverReason=0;bool m_ERRShownObserverAvailable=false;int closes=0;
|
||||
Actor* GetControlledEntity(){return controlled;}int ERR_ObserverState(){return m_ERRObserverReason;}
|
||||
- void ERR_CloseObserverCamera(){closes++;m_ERRObserverReady=false;m_ERRObserverCamera=nullptr;}
|
||||
+ void ERR_CloseObserverCamera(){closes++;m_ERRObserverReady=false;m_ERRShownRecoveryStatus="";m_ERRObserverCamera=nullptr;}
|
||||
'''+methods+r'''
|
||||
};
|
||||
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;}
|
||||
@@ -135,7 +138,30 @@
|
||||
reset();Controller casualty;actor.unconscious=true;casualty.m_ERRObserverReason=ERR_ObserverReason::UNCONSCIOUS;
|
||||
casualty.ERR_UpdateObserverClient();assert(casualty.m_ERRObserverCamera==&camera&&casualty.controlled==&actor&&hints.last.find("You are unconscious")!=string::npos);
|
||||
casualty.ERR_UpdateObserverClient();assert(hints.shown==1);
|
||||
- actor.unconscious=false;casualty.ERR_UpdateObserverClient();assert(!casualty.m_ERRObserverCamera&&casualty.closes==1&&hints.shown==1);
|
||||
+ // The phase-guide explicitly excludes this server-designated unconscious
|
||||
+ // observer; the existing observer monitor must deliver recovery/defeat facts.
|
||||
+ int attemptsBeforeGuide=hints.attempts;casualty.ERR_UpdateGuide();assert(hints.attempts==attemptsBeforeGuide);
|
||||
+ mode.recovery="Everyone is unconscious with no treatment or recovery progressing. The mission fails after 30 uninterrupted seconds in this state.";
|
||||
+ casualty.ERR_UpdateObserverClient();assert(hints.shown==2&&hints.last.find(mode.recovery)!=string::npos&&casualty.ERR_ProbeAcceptedRecoveryStatus()==mode.recovery);
|
||||
+ casualty.ERR_UpdateObserverClient();assert(hints.shown==2); // stable status never repeats
|
||||
+ mode.recovery="Treatment or natural recovery is possible; the round continues.";hints.accepts=false;
|
||||
+ casualty.ERR_UpdateObserverClient();assert(hints.shown==2&&casualty.m_ERRShownRecoveryStatus.find("30 uninterrupted")!=string::npos);
|
||||
+ hints.accepts=true;casualty.ERR_UpdateObserverClient();assert(hints.shown==3&&hints.last.find(mode.recovery)!=string::npos);
|
||||
+ mode.recovery="A conscious prisoner's reconnection may provide help.";SCR_HintManagerComponent::current=nullptr;
|
||||
+ casualty.ERR_UpdateObserverClient();assert(hints.shown==3&&casualty.m_ERRShownRecoveryStatus.find("natural recovery")!=string::npos);
|
||||
+ SCR_HintManagerComponent::current=&hints;casualty.ERR_UpdateObserverClient();assert(hints.shown==4&&hints.last.find(mode.recovery)!=string::npos);
|
||||
+ mode.recovery="";casualty.ERR_UpdateObserverClient();assert(hints.shown==5&&casualty.m_ERRShownRecoveryStatus.empty()&&hints.last.find("reconnection")==string::npos);
|
||||
+ casualty.ERR_UpdateObserverClient();assert(hints.shown==5);
|
||||
+ // Waking closes the view; a new status cannot keep a recovered observer hint alive.
|
||||
+ mode.recovery="remaining casualty warning";actor.unconscious=false;casualty.ERR_UpdateObserverClient();assert(!casualty.m_ERRObserverCamera&&casualty.closes==1&&hints.shown==5&&casualty.ERR_ProbeAcceptedRecoveryStatus().empty());
|
||||
+ // An old controller cannot consume the replacement's recovery-status update.
|
||||
+ reset();Controller oldCasualty;actor.unconscious=true;oldCasualty.m_ERRObserverReason=ERR_ObserverReason::UNCONSCIOUS;oldCasualty.ERR_UpdateObserverClient();
|
||||
+ Controller newCasualty;newCasualty.m_ERRObserverReason=ERR_ObserverReason::UNCONSCIOUS;mode.recovery="recovery warning after reconnect";int beforeOld=hints.shown;
|
||||
+ oldCasualty.ERR_UpdateObserverClient();assert(hints.shown==beforeOld&&oldCasualty.m_ERRShownRecoveryStatus.empty());
|
||||
+ newCasualty.ERR_UpdateObserverClient();assert(hints.shown==beforeOld+1&&hints.last.find(mode.recovery)!=string::npos);
|
||||
+ mode.active=false;mode.recovery="old round recovery";newCasualty.ERR_UpdateObserverClient();assert(hints.shown==beforeOld+1&&!newCasualty.m_ERRObserverReady&&newCasualty.m_ERRShownRecoveryStatus.empty());
|
||||
+ // A later active round's first observer hint contains only its current fact.
|
||||
+ mode.active=true;mode.recovery="new round recovery";newCasualty.ERR_UpdateObserverClient();assert(hints.shown==beforeOld+2&&hints.last.find("new round recovery")!=string::npos&&hints.last.find("old round recovery")==string::npos);
|
||||
}
|
||||
''')
|
||||
for path,signature in ((GUIDE,'protected void ERR_UpdateGuide('),(OBSERVER,'protected void ERR_UpdateObserverClient(')):
|
||||
--- a/tests/test_observer_ownership.py
|
||||
+++ b/tests/test_observer_ownership.py
|
||||
@@ -23,10 +23,10 @@
|
||||
run(r'''#include <cassert>
|
||||
#include <set>
|
||||
#include <string>
|
||||
-using string=std::string;
|
||||
+struct string:std::string {using std::string::string;using std::string::operator=;string(std::string s):std::string(s){}bool IsEmpty()const{return empty();}};
|
||||
struct Actor {bool alive=true,conscious=true;};using IEntity=Actor*;
|
||||
struct ERR_Actors {static bool Alive(IEntity a){return a&&a->alive;}static bool CanFight(IEntity a){return Alive(a)&&a->conscious;}};
|
||||
-struct ERR_GameMode {bool active=true;static ERR_GameMode*current;static ERR_GameMode*Get(){return current;}bool Active(){return active;}} mode;
|
||||
+struct ERR_GameMode {string RecoveryStatus(){return "";}bool active=true;static ERR_GameMode*current;static ERR_GameMode*Get(){return current;}bool Active(){return active;}} mode;
|
||||
ERR_GameMode*ERR_GameMode::current=&mode;
|
||||
struct ERR_ObserverRules {static bool View(int reason,bool active,bool alive,bool conscious){return active&&((reason==4&&alive&&!conscious)||(reason!=0&&!alive));}};
|
||||
struct ERR_ObserverGuidance {static bool Refresh(bool ready,int oldReason,int reason,bool oldAvailable,bool available){return !ready||oldReason!=reason||oldAvailable!=available;}static string Message(int,bool){return "spectating";}};
|
||||
@@ -58,6 +58,7 @@
|
||||
struct Controller{
|
||||
IEntity actor=nullptr,m_ERRRecoveryBody=nullptr;float m_ERRRecoveryDeadline=-1;
|
||||
bool m_ERRObserverMonitor=false,m_ERRObserverReady=false,m_ERRShownObserverAvailable=false;
|
||||
+ string m_ERRShownRecoveryStatus;
|
||||
int m_ERRObserverReason=0,m_ERRShownObserverReason=0,m_ERRObservedId=1,starts=0;
|
||||
Vec m_ERRObserverAnchor;SCR_ManualCamera*m_ERRObserverCamera=nullptr;CameraBase*playerCamera=nullptr;
|
||||
IEntity GetControlledEntity(){return actor;}CameraBase*GetPlayerCamera(){return playerCamera;}
|
||||
--- a/addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c
|
||||
+++ b/addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c
|
||||
@@ -4,6 +4,7 @@
|
||||
modded class SCR_PlayerController
|
||||
{
|
||||
protected IEntity m_ERRCameraProbeTarget;
|
||||
+ string ERR_ProbeAcceptedRecoveryStatus() { return m_ERRShownRecoveryStatus; }
|
||||
void ERR_ProbeCameraTarget(IEntity target) { m_ERRCameraProbeTarget = target; }
|
||||
override IEntity ERR_ObservedEntity()
|
||||
{
|
||||
@@ -91,6 +92,7 @@
|
||||
if (!ERR_CameraCheck(pc.GetControlledEntity() == m_ERRCameraBody && ERR_Actors.Alive(m_ERRCameraBody) && !ERR_Actors.CanFight(m_ERRCameraBody), "possession and living unconscious body unchanged")) return;
|
||||
if (!ERR_CameraCheck(!pc.ERR_ObservedEntity() && vector.DistanceXZ(pc.ERR_ObserverAnchor(), m_ERRCameraBody.GetOrigin()) < 1, "no teammate falls back to the casualty body")) return;
|
||||
if (!ERR_CameraCheck(playerInventory.Contains(m_ERRCameraWeapon), "enabled retention keeps the exact rifle with the unconscious player")) return;
|
||||
+ if (!ERR_CameraCheck(!RecoveryStatus().IsEmpty() && RecoveryStatus().Contains("Treatment or natural recovery") && pc.ERR_ProbeAcceptedRecoveryStatus() == RecoveryStatus(), "accepted observer hint contains the current ongoing recovery status")) return;
|
||||
vector point = ERR_Actors.Ground(m_ERRCameraBody.GetOrigin() + "25 0 25");
|
||||
m_ERRCameraTarget = ERR_Actors.Spawn(Factions().PrisonerPrefab, point); Own(m_ERRCameraTarget);
|
||||
if (!ERR_CameraCheck(m_ERRCameraTarget != null, "physical follow-target stand-in created")) return;
|
||||
@@ -165,6 +167,7 @@
|
||||
{
|
||||
if (!ERR_CameraCheck(ERR_Actors.CanFight(m_ERRCameraBody) && pc.GetControlledEntity() == m_ERRCameraBody && pc.ERR_ObserverState() == ERR_ObserverReason.NONE && !pc.ERR_HasObserverCamera(), "native healing closes spectator and retains the original conscious player")) return;
|
||||
if (!ERR_CameraCheck(GetGame().GetCameraManager().CurrentCamera() == pc.GetPlayerCamera(), "normal player camera restored")) return;
|
||||
+ if (!ERR_CameraCheck(pc.ERR_ProbeAcceptedRecoveryStatus().IsEmpty(), "awakening clears the accepted observer recovery-status cache")) return;
|
||||
if (m_ERRCameraOther) { m_ERRCameraOther.Terminate(); delete m_ERRCameraOther; m_ERRCameraOther = null; }
|
||||
if (!ERR_CameraCheck(character.GetWeaponManagerComponent().GetCurrentWeapon() && character.GetWeaponManagerComponent().GetCurrentWeapon().GetOwner() == m_ERRCameraWeapon, "revival equips the same rifle without ground pickup")) return;
|
||||
if (!ERR_CameraCheck(Settings().Set("keepWeaponOnUnconscious", 0), "retention disabled through persistent setting")) return;
|
||||
@@ -0,0 +1 @@
|
||||
PASS: actual guide ownership and scoped timer cancellation, current-controller preservation/reacquisition, hint retry/dedup and observer boundaries; native UI not exercised
|
||||
+1
@@ -0,0 +1 @@
|
||||
PASS: actual guide ownership and scoped timer cancellation, current-controller preservation/reacquisition, hint retry/dedup and observer boundaries; native UI not exercised
|
||||
+1
@@ -0,0 +1 @@
|
||||
PASS: actual observer monitor/recovery/cleanup rejects retained old controller, preserves new camera/editor/hints/timer, restarts once and retains normal recovery; native effects modeled from SDK, no runtime claim
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"verdict": "PASS for bounded native accepted-hint lifecycle proof; retained engine errors prevent error-free-engine claim",
|
||||
"evidence": "/home/jakub/devel/escapistsrereintroduced/.build/recovery-warning-native-1130/docs/evidence/native-2026-10-02-recovery-warning",
|
||||
"native_report": {
|
||||
"log": "docs/evidence/native-2026-10-02-recovery-warning/console.log.txt",
|
||||
"smoke_completions": 1,
|
||||
"assertions_passed": 29,
|
||||
"script_or_runtime_failures": [],
|
||||
"engine_errors": [
|
||||
"11:40:50.703 RESOURCES (E): Wrong GUID/name for resource @\"{0AFDD005CD669892}Language/rhs_localization.st\" in property \"StringTableSource\"",
|
||||
"11:40:50.703 RESOURCES (E): Wrong GUID/name for resource @\"{41FA2A08B6971D4C}Language/wcs_armament_localization.st\" in property \"StringTableSource\"",
|
||||
"11:40:50.703 RESOURCES (E): Wrong GUID/name for resource @\"{84270E09F7A4A7A0}Language/wcs_spacecore_localization.st\" in property \"StringTableSource\"",
|
||||
"11:40:50.704 RESOURCES (E): Wrong GUID/name for resource @\"{E50E557B468C7073}Language/wcs_bmp1_localization.st\" in property \"StringTableSource\"",
|
||||
"11:41:29.086 WORLD (E): Unknown keyword/data 'Parent' at offset 35955(0x8c73)",
|
||||
"11:41:29.086 WORLD (E): Unknown keyword/data 'Parent' at offset 36915(0x9033)",
|
||||
"11:41:29.087 WORLD (E): Unknown keyword/data 'Parent' at offset 42646(0xa696)",
|
||||
"11:41:29.087 WORLD (E): Unknown keyword/data 'Parent' at offset 43645(0xaa7d)",
|
||||
"11:41:29.087 WORLD (E): Unknown keyword/data 'Parent' at offset 44617(0xae49)",
|
||||
"11:41:29.087 WORLD (E): Unknown keyword/data 'Parent' at offset 45590(0xb216)",
|
||||
"11:41:29.181 WORLD (E): Unknown keyword/data 'SlidingTrackMaterial' at offset 19441(0x4bf1)",
|
||||
"11:41:35.590 WORLD (E): Unknown keyword/data 'm_HardLandingVelocity' at offset 133(0x85)",
|
||||
"11:41:35.590 WORLD (E): Unknown keyword/data 'm_DeathLandingVelocity' at offset 163(0xa3)"
|
||||
],
|
||||
"missing_end_screen": [],
|
||||
"scope": "scripted local Workbench integration only; no multiplayer, piloting or FPS claim"
|
||||
},
|
||||
"assertion_witnesses": [
|
||||
{
|
||||
"line": 578,
|
||||
"text": "11:41:52.276 SCRIPT : ERR SMOKE PASS: unconscious camera accepted observer hint contains the current ongoing recovery status"
|
||||
},
|
||||
{
|
||||
"line": 599,
|
||||
"text": "11:42:03.883 SCRIPT : ERR SMOKE PASS: unconscious camera awakening clears the accepted observer recovery-status cache"
|
||||
}
|
||||
],
|
||||
"source": {
|
||||
"manifest_entries": 407,
|
||||
"launch_end_manifests_equal": true,
|
||||
"frozen_manifest_mismatches": [],
|
||||
"proposal_observer_and_probe_match_frozen_and_current": true
|
||||
},
|
||||
"cleanup": {
|
||||
"result": "smoke-completed",
|
||||
"result_before_cleanup": "smoke-completed",
|
||||
"cleanup_confirmed": true,
|
||||
"cleanup_report": {
|
||||
"cleanup_confirmed": true,
|
||||
"idle_verified": true,
|
||||
"errors": [],
|
||||
"orphan_cleanup": {
|
||||
"attempted": true,
|
||||
"report": {
|
||||
"scope": "/tmp/erri-headless-compat/pfx",
|
||||
"candidates": [],
|
||||
"term_sent": [],
|
||||
"kill_sent": [],
|
||||
"cleared": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"source_unchanged_after_run": true,
|
||||
"external_addons_unchanged_after_run": true,
|
||||
"runner_error": null
|
||||
},
|
||||
"focused_tests": {
|
||||
"test_observer_ownership.py": {
|
||||
"exit_code": 0,
|
||||
"log": "/tmp/erri-observer-recovery-warning/native-review-test_observer_ownership.py.log"
|
||||
},
|
||||
"test_hint_delivery.py": {
|
||||
"exit_code": 0,
|
||||
"log": "/tmp/erri-observer-recovery-warning/native-review-test_hint_delivery.py.log"
|
||||
}
|
||||
},
|
||||
"ownership_test_review": "Only string IsEmpty adapter, empty RecoveryStatus mode stub and cache field added. Existing ownership cases/assertions unchanged. Actual StartMonitor/UpdateObserverClient/UpdateRecoveryCamera/CloseObserverCamera and binder still exercised.",
|
||||
"limitations": [
|
||||
"Local Workbench scripted fixture only; no multiplayer proof.",
|
||||
"Accepted ShowCustom path and current replicated recovery status verified, not full-resolution readability.",
|
||||
"Thirteen retained external resource/world engine errors remain in report.",
|
||||
"Snapshot predates subsequent launcher changes; no claim those changes were covered."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
PASS: actual observer monitor/recovery/cleanup rejects retained old controller, preserves new camera/editor/hints/timer, restarts once and retains normal recovery; native effects modeled from SDK, no runtime claim
|
||||
@@ -0,0 +1,19 @@
|
||||
check: /tmp/err-policy-59mygi6e/check.cpp:239: int main(): Assertion `hints.shown==2&&hints.last.find(mode.recovery)!=string::npos&&casualty.ERR_ProbeAcceptedRecoveryStatus()==mode.recovery' failed.
|
||||
Traceback (most recent call last):
|
||||
File "/tmp/erri-observer-recovery-warning/tests/test_hint_delivery.py", line 22, in <module>
|
||||
run(r'''#include <cassert>
|
||||
~~~^^^^^^^^^^^^^^^^^^^^^^^
|
||||
#include <string>
|
||||
^^^^^^^^^^^^^^^^^
|
||||
...<141 lines>...
|
||||
}
|
||||
^
|
||||
''')
|
||||
^^^^
|
||||
File "/home/jakub/devel/escapistsrereintroduced/tests/policy_harness.py", line 14, in run
|
||||
subprocess.run([str(p/'check')],check=True)
|
||||
~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/usr/lib/python3.14/subprocess.py", line 578, in run
|
||||
raise CalledProcessError(retcode, process.args,
|
||||
output=stdout, stderr=stderr)
|
||||
subprocess.CalledProcessError: Command '['/tmp/err-policy-59mygi6e/check']' died with <Signals.SIGABRT: 6>.
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"verdict": "Concrete source presentation defect reproduced; minimal proposal fixes it",
|
||||
"before": "All-unconscious status already replicated from RecoveryService contains30s defeat/recovery/reconnect facts. PlayerGuide skips server-designated observers; Observer ignores RecoveryStatus and deduplicates only reason/target availability, so no automatic warning reaches affected unconscious players.",
|
||||
"after": "Existing observer hint appends current replicated recovery status and refreshes only when reason/availability/status changed. Cache advances only on accepted ShowCustom; close clears status; existing ownership and active/conscious gates remain unchanged.",
|
||||
"production_files": [
|
||||
"addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c"
|
||||
],
|
||||
"test_files": [
|
||||
"tests/test_hint_delivery.py",
|
||||
"tests/test_observer_ownership.py"
|
||||
],
|
||||
"baseline_red": "red.log:actual method compiles, fails assertion requiring updated medical warning while observer reason/target unchanged",
|
||||
"proposal_green": [
|
||||
"green.log",
|
||||
"ownership-green.log"
|
||||
],
|
||||
"coverage": [
|
||||
"stable message dedup",
|
||||
"status changes stalled\u2192recovering\u2192reserved help\u2192clear",
|
||||
"rejected hint/missing manager retry without cache advance",
|
||||
"ordinary guide cannot deliver this unconscious state",
|
||||
"wake closes presentation",
|
||||
"old controller cannot display replacement status",
|
||||
"inactive round stops hints and clears cache",
|
||||
"new round uses only current status"
|
||||
],
|
||||
"medical_policy_changed": false,
|
||||
"native_launched": false,
|
||||
"target_changed": false,
|
||||
"native_limits": "Getter proves accepted native hint path/state only; no claim of full-resolution readability, manual UI or multiplayer. Existing fixture itself arranges treatment/knockout, unchanged.",
|
||||
"manifest": {
|
||||
"addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c": {
|
||||
"working_sha256": "4acbd2b23803f1fde3701fbddffd56edd83693196f0be2fe99f3be9b79a12b3d",
|
||||
"index_sha256": "31d08123fa9f49dde380354bd839d1a9deb175cdb940311b4a64dd56eeb04ed7",
|
||||
"proposal_sha256": "682aa5549470c69fc72d261fbfcf9f7a04a41a66e0101b269ccc00bc282ad05d"
|
||||
},
|
||||
"tests/test_hint_delivery.py": {
|
||||
"working_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3",
|
||||
"index_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3",
|
||||
"proposal_sha256": "6066e2b00e54553ac298a3f9b37060a23851f5ca5d4f98eb25e502b2c44240a6"
|
||||
},
|
||||
"tests/test_observer_ownership.py": {
|
||||
"working_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6",
|
||||
"index_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6",
|
||||
"proposal_sha256": "8e485c3d2e6ae90df82cbc7829996138db8e9a5a4a42a5e2bcaa3bffa61368de"
|
||||
},
|
||||
"addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c": {
|
||||
"working_sha256": "7d5ff3389ee806ac213db5be3a68288b3399142ccc29806df0d65b34a0b2fc3c",
|
||||
"index_sha256": "7b9308c9c35413fa76a7bdc8923f99785fb393ad9d06ba4b36df4d8aa29e574b",
|
||||
"proposal_sha256": "66b1c4591b0ecc67cb08418a115bb02ed2b0168b7ae970f1e0a1ea335c59dc5b"
|
||||
}
|
||||
},
|
||||
"apply_check": true,
|
||||
"workbench_only_fixture": "ERR_UnconsciousCameraProbe adds accepted-cache getter and2assertions: stage1 nonempty actual RecoveryStatus containing treatment/natural-recovery branch equals accepted hint cache; stage4 awakening clears cache. No synthetic policy/timing change."
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"addons/core/Scripts/Game/ERR/00_Core/ERR_Observer.c": {
|
||||
"working_sha256": "4acbd2b23803f1fde3701fbddffd56edd83693196f0be2fe99f3be9b79a12b3d",
|
||||
"index_sha256": "31d08123fa9f49dde380354bd839d1a9deb175cdb940311b4a64dd56eeb04ed7",
|
||||
"proposal_sha256": "682aa5549470c69fc72d261fbfcf9f7a04a41a66e0101b269ccc00bc282ad05d"
|
||||
},
|
||||
"tests/test_hint_delivery.py": {
|
||||
"working_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3",
|
||||
"index_sha256": "1ec462cea5890379ad33ea3f16591896e085d1a30fd1af4a3411b14e5dc8acc3",
|
||||
"proposal_sha256": "6066e2b00e54553ac298a3f9b37060a23851f5ca5d4f98eb25e502b2c44240a6"
|
||||
},
|
||||
"tests/test_observer_ownership.py": {
|
||||
"working_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6",
|
||||
"index_sha256": "879c6185d834c03f8d62e7e35359c8bb5f6821f6dde03c73957be2ba614947c6",
|
||||
"proposal_sha256": "8e485c3d2e6ae90df82cbc7829996138db8e9a5a4a42a5e2bcaa3bffa61368de"
|
||||
},
|
||||
"addons/core/Scripts/Game/ERR/00_Core/ERR_UnconsciousCameraProbe.c": {
|
||||
"working_sha256": "7d5ff3389ee806ac213db5be3a68288b3399142ccc29806df0d65b34a0b2fc3c",
|
||||
"index_sha256": "7b9308c9c35413fa76a7bdc8923f99785fb393ad9d06ba4b36df4d8aa29e574b",
|
||||
"proposal_sha256": "66b1c4591b0ecc67cb08418a115bb02ed2b0168b7ae970f1e0a1ea335c59dc5b"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user