#ifdef WORKBENCH // Opt-in local UI evidence. These adapters execute production methods and always // delegate their overrides; no native map/camera operation is intercepted. modded class ERRM_SharedMapComponent { protected ERRM_NativeMapOwnershipProbe m_ERRMOwnershipProbe; protected int m_ERRMOwnershipSlot; void ERRM_ObserveOwnership(ERRM_NativeMapOwnershipProbe probe, int slot) { m_ERRMOwnershipProbe = probe; m_ERRMOwnershipSlot = slot; } Widget ERRM_OwnershipRoot() { return m_ESMRootWidget; } bool ERRM_OwnershipReady() { return m_ESMRootWidget && m_ESMRenderTarget; } bool ERRM_BeginOwnershipCapture(IEntity holder) { m_bESMInHand = true; m_ESMCharacterOwner = holder; TryCaptureMapSurface(); // Deliberately delay only these fixture-owned callbacks until the replacement // map has completed native initialization. The real method bodies run below. GetGame().GetCallqueue().Remove(CenterMapOnHolder); GetGame().GetCallqueue().Remove(FinishMapCapture); return OwnsMapCapture(); } void ERRM_StaleOwnershipCenter() { CenterMapOnHolder(); } void ERRM_QueueStaleOwnershipCallbacks() { GetGame().GetCallqueue().CallLater(CenterMapOnHolder, 100, false); GetGame().GetCallqueue().CallLater(FinishMapCapture, 200, false); } void ERRM_StaleOwnershipCamera() { RestoreCharacterCamera(); } void ERRM_StopOwnershipCapture() { StopMapCapture(); // Include the real successor + vanilla ModeClear chain, even for a spare map. ModeClear(EGadgetMode.IN_HAND); } protected override void OnDelete(IEntity owner) { ERRM_NativeMapOwnershipProbe probe = m_ERRMOwnershipProbe; int slot = m_ERRMOwnershipSlot; super.OnDelete(owner); // Observe after the complete production/vanilla chain, without clearing any // evidence references or overriding the native deletion operation. if (probe) probe.ObserveDeletion(slot, !m_ESMRootWidget && !m_ESMRenderTarget && !m_ESMMapEntity && !m_ESMMapConfig && !m_ESMCharacterOwner && !m_bESMOwnsMapRenderer && !m_bESMInHand); } protected override void CenterMapOnHolder() { if (m_ERRMOwnershipProbe) m_ERRMOwnershipProbe.ObserveCenter(); super.CenterMapOnHolder(); } protected override void FinishMapCapture() { if (m_ERRMOwnershipProbe) m_ERRMOwnershipProbe.ObserveFinish(); super.FinishMapCapture(); } protected override void RestoreCharacterCamera() { if (m_ERRMOwnershipProbe) m_ERRMOwnershipProbe.ObserveRestore(OwnsMapCapture()); super.RestoreCharacterCamera(); } protected override void CompleteMapModeSwitch() { if (m_ERRMOwnershipProbe) m_ERRMOwnershipProbe.ObserveCompletion(); super.CompleteMapModeSwitch(); } } class ERRM_NativeMapOwnershipProbe { protected static const ResourceName MAP_PREFAB = "{A46500E4A30B653B}Prefabs/Items/Equipment/ERRM_SharedFieldMap.et"; protected static const ResourceName MAP_LAYOUT = "{255E1265F702B4AE}UI/layouts/ERRMap/SharedMapRenderTarget.layout"; protected static const ResourceName MAP_CONFIG = "{A786DD4868598F15}Configs/Map/MapPlain.conf"; protected SCR_MapEntity m_Map; protected IEntity m_CaptureEntity; protected IEntity m_SpareEntity; protected ERRM_SharedMapComponent m_Capture; protected ERRM_SharedMapComponent m_Spare; protected IEntity m_Holder; protected Widget m_ReplacementRoot; protected ref MapConfiguration m_ReplacementConfig; protected MapConfiguration m_ReadyConfig; protected int m_Step; protected int m_CloseEvents; protected int m_RestoreCalls; protected int m_RestoreAuthorized; protected int m_Completions; protected int m_CenterCalls; protected int m_FinishCalls; protected int m_CenterBaseline; protected int m_FinishBaseline; protected int m_CloseBaseline; protected int m_RestoreBaseline; protected int m_AuthorizedBaseline; protected int m_CompletionBaseline; protected vector m_PanBaseline; protected float m_ZoomBaseline; protected bool m_DeleteRequested; protected bool m_DeleteStateFailed; protected int m_CaptureDeleted; protected int m_SpareDeleted; protected float m_DeleteDeadline; protected float m_Deadline; protected float m_WaitUntil; protected bool m_Stopped; protected bool m_Done; static bool Enabled() { string enabled, outcome; return System.GetCLIParam("errSmoke", enabled) && enabled == "1" && System.GetCLIParam("errSmokeOutcome", outcome) && outcome == "map-ownership"; } void ObserveRestore(bool authorized) { m_RestoreCalls++; if (authorized) m_RestoreAuthorized++; } void ObserveDeletion(int slot, bool released) { if (!m_DeleteRequested || !released) m_DeleteStateFailed = true; if (slot == 1) m_CaptureDeleted++; else if (slot == 2) m_SpareDeleted++; else m_DeleteStateFailed = true; Print(string.Format("ERR MAP DELETE: afterSuper slot=%1 released=%2 captureCallbacks=%3 spareCallbacks=%4", slot, released, m_CaptureDeleted, m_SpareDeleted)); } protected bool DeletionComplete() { return m_DeleteRequested && !m_SpareEntity && !m_CaptureEntity && !m_Spare && !m_Capture && m_CaptureDeleted == 1 && m_SpareDeleted == 1 && !m_DeleteStateFailed; } protected void RequestOwnedDeletion() { if (m_DeleteRequested) return; m_DeleteRequested = true; if (m_SpareEntity) SCR_EntityHelper.DeleteEntityAndChildren(m_SpareEntity); if (m_CaptureEntity) SCR_EntityHelper.DeleteEntityAndChildren(m_CaptureEntity); } protected void LogDeletionState(string when) { Print(string.Format("ERR MAP DELETE: %1 captureEntity=%2 spareEntity=%3 captureComponent=%4 spareComponent=%5 captureCallbacks=%6 spareCallbacks=%7 cleanupFailed=%8", when, m_CaptureEntity != null, m_SpareEntity != null, m_Capture != null, m_Spare != null, m_CaptureDeleted, m_SpareDeleted, m_DeleteStateFailed)); } void ObserveCompletion() { m_Completions++; } void ObserveCenter() { m_CenterCalls++; } void ObserveFinish() { m_FinishCalls++; } protected void OnMapClosed(MapConfiguration config) { m_CloseEvents++; } protected void OnMapReady(MapConfiguration config) { m_ReadyConfig = config; } protected bool Check(bool condition, string detail) { if (condition) { Print("ERR SMOKE PASS: shared map ownership " + detail); return true; } Print("ERR SMOKE FAIL: shared map ownership " + detail, LogLevel.ERROR); Stop(); return false; } protected IEntity SpawnMap(vector origin) { EntitySpawnParams params = new EntitySpawnParams(); params.TransformMode = ETransformMode.WORLD; params.Transform[3] = origin; return GetGame().SpawnEntityPrefab(Resource.Load(MAP_PREFAB), GetGame().GetWorld(), params); } // A distinct native configuration with the same vanilla plain-map settings. // It intentionally shares immutable settings/module arrays with the closed // capture configuration; neither configuration is serialized or resource-edited. protected MapConfiguration CopyPlainConfig(MapConfiguration source, Widget root) { MapConfiguration copy = new MapConfiguration(); copy.MapEntityMode = source.MapEntityMode; copy.RootWidgetRef = root; copy.LayerCount = source.LayerCount; copy.MapPropsConfig = source.MapPropsConfig; copy.LayerConfig = source.LayerConfig; copy.DescriptorVisibilityConfig = source.DescriptorVisibilityConfig; copy.DescriptorDefsConfig = source.DescriptorDefsConfig; copy.Modules = source.Modules; copy.Components = source.Components; copy.OtherComponents = source.OtherComponents; return copy; } protected bool ReplacementIsCurrent() { return m_Map && SCR_MapEntity.GetMapInstance() == m_Map && m_Map.IsOpen() && m_ReplacementConfig && m_Map.GetMapConfig() == m_ReplacementConfig && m_ReplacementConfig.RootWidgetRef == m_ReplacementRoot && m_Map.GetMapMenuRoot() == m_ReplacementRoot; } protected void SaveCounters() { m_CloseBaseline = m_CloseEvents; m_RestoreBaseline = m_RestoreCalls; m_AuthorizedBaseline = m_RestoreAuthorized; m_CompletionBaseline = m_Completions; m_CenterBaseline = m_CenterCalls; m_FinishBaseline = m_FinishCalls; } protected bool BeginReplacement(bool reuseConfig) { if (!Check(m_Map && !m_Map.IsOpen() && m_Capture.ERRM_BeginOwnershipCapture(m_Holder), "actual owned prefab captures its native plain-map root")) return false; MapConfiguration captured = m_Map.GetMapConfig(); if (!Check(captured && captured.RootWidgetRef == m_Capture.ERRM_OwnershipRoot(), "capture config belongs to exact fixture component")) return false; // Close only the just-verified fixture view before replacing it; this also // avoids the native double-open warning. Delayed callbacks remain stale. m_Map.CloseMap(); if (reuseConfig) m_ReplacementConfig = m_Map.SetupMapConfig(EMapEntityMode.PLAIN, MAP_CONFIG, m_ReplacementRoot); else m_ReplacementConfig = CopyPlainConfig(captured, m_ReplacementRoot); if (!Check(m_ReplacementConfig && ((reuseConfig && m_ReplacementConfig == captured) || (!reuseConfig && m_ReplacementConfig != captured)), "requested distinct/reused native configuration identity established")) return false; m_ReadyConfig = null; m_Map.OpenMap(m_ReplacementConfig); if (!Check(ReplacementIsCurrent(), "actual replacement root is current native map")) return false; SaveCounters(); return true; } protected bool ExerciseStaleCallbacks() { if (!Check(ReplacementIsCurrent() && m_ReadyConfig == m_ReplacementConfig && !m_Map.IsZooming(), "replacement remains open after native open-complete event")) return false; vector pan = m_Map.GetCurrentPan(); float zoom = m_Map.GetTargetZoomPPU(); m_Capture.ERRM_StaleOwnershipCenter(); if (!Check(m_Map.GetCurrentPan() == pan && m_Map.GetTargetZoomPPU() == zoom && !m_Map.IsZooming(), "actual stale center callback cannot pan or retarget replacement zoom")) return false; m_Capture.ERRM_StaleOwnershipCamera(); if (!Check(m_RestoreCalls == m_RestoreBaseline + 1 && m_RestoreAuthorized == m_AuthorizedBaseline, "stale camera-restore method executes but native-write authorization is denied")) return false; m_PanBaseline = pan; m_ZoomBaseline = zoom; m_Capture.ERRM_QueueStaleOwnershipCallbacks(); return true; } protected bool VerifyStaleAndStop() { if (!Check(m_CenterCalls == m_CenterBaseline + 2 && m_FinishCalls == m_FinishBaseline + 1, "both real delayed production callbacks ran exactly once")) return false; if (!Check(ReplacementIsCurrent() && m_Map.GetCurrentPan() == m_PanBaseline && m_Map.GetTargetZoomPPU() == m_ZoomBaseline, "real delayed center and finish leave replacement view unchanged")) return false; m_Capture.ERRM_StopOwnershipCapture(); if (!Check(ReplacementIsCurrent() && m_CloseEvents == m_CloseBaseline && m_Completions == m_CompletionBaseline && m_RestoreCalls == m_RestoreBaseline + 1, "actual finish, stop and vanilla mode-clear preserve replacement and do not request camera restore")) return false; return true; } // Returns 0 while waiting, 1 only after cleanup, -1 on failure. now is native // world time in milliseconds, matching existing Chat smoke probes. int Tick(float now) { if (m_Done) return 1; if (m_Stopped) return -1; if (!Enabled()) { Stop(); return -1; } if (m_Step == 0) { m_Map = SCR_MapEntity.GetMapInstance(); PlayerController controller = GetGame().GetPlayerController(); if (!Check(m_Map && !m_Map.IsOpen() && controller && controller.GetControlledEntity() && GetGame().GetCameraManager().CurrentCamera() == controller.GetPlayerCamera(), "fixture starts only with closed map and actual local character camera")) return -1; m_Holder = controller.GetControlledEntity(); m_Deadline = now + 15000; m_ReplacementRoot = GetGame().GetWorkspace().CreateWidgets(MAP_LAYOUT); if (!Check(m_ReplacementRoot != null, "owned replacement native map layout created")) return -1; m_ReplacementRoot.SetVisible(false); RTTextureWidget target = RTTextureWidget.Cast(m_ReplacementRoot.FindAnyWidget("RTTexture0")); if (target) target.SetEnabled(false); SCR_MapEntity.GetOnMapClose().Insert(OnMapClosed); SCR_MapEntity.GetOnMapOpenComplete().Insert(OnMapReady); m_CaptureEntity = SpawnMap(m_Holder.GetOrigin() + Vector(0, 0.2, 0)); m_SpareEntity = SpawnMap(m_Holder.GetOrigin() + Vector(0.3, 0.2, 0)); if (!Check(m_CaptureEntity && m_SpareEntity && m_CaptureEntity != m_SpareEntity, "two separate real successor map prefabs spawned without inventory changes")) return -1; m_WaitUntil = now + 1000; m_Step = 1; return 0; } if (now > m_Deadline) { Check(false, "bounded fixture deadline"); return -1; } if (now < m_WaitUntil) return 0; if (m_Step == 1) { if (!Check(m_CaptureEntity && m_SpareEntity, "both fixture entities survive until native component initialization")) return -1; m_Capture = ERRM_SharedMapComponent.Cast(m_CaptureEntity.FindComponent(ERRM_SharedMapComponent)); m_Spare = ERRM_SharedMapComponent.Cast(m_SpareEntity.FindComponent(ERRM_SharedMapComponent)); if (!Check(m_Capture && m_Spare && m_Capture.ERRM_OwnershipReady() && m_Spare.ERRM_OwnershipReady(), "both native map components initialize actual render roots")) return -1; m_Capture.ERRM_ObserveOwnership(this, 1); m_Spare.ERRM_ObserveOwnership(this, 2); if (!BeginReplacement(false)) return -1; m_WaitUntil = now + 1500; m_Step = 2; return 0; } if (m_Step == 2) { if (!ExerciseStaleCallbacks()) return -1; m_WaitUntil = now + 750; m_Step = 3; return 0; } if (m_Step == 3) { if (!VerifyStaleAndStop()) return -1; m_Map.CloseMap(); // exact replacement was checked immediately above if (!BeginReplacement(true)) return -1; m_WaitUntil = now + 1500; m_Step = 4; return 0; } if (m_Step == 4) { if (!ExerciseStaleCallbacks()) return -1; m_WaitUntil = now + 750; m_Step = 5; return 0; } if (m_Step == 5) { if (!VerifyStaleAndStop()) return -1; SaveCounters(); m_Spare.ERRM_StopOwnershipCapture(); RequestOwnedDeletion(); LogDeletionState("request returned"); m_DeleteDeadline = now + 3000; m_WaitUntil = now + 100; m_Step = 6; return 0; } if (m_Step == 6) { if (!ReplacementIsCurrent() || m_CloseEvents != m_CloseBaseline || m_RestoreCalls != m_RestoreBaseline) { Check(false, "replacement remains unchanged while native deletion finishes"); return -1; } if (!DeletionComplete()) { if (now >= m_DeleteDeadline) { LogDeletionState("bounded deletion timeout"); Check(false, "both actual entities/components delete and both superclass cleanup callbacks complete within three seconds"); return -1; } return 0; } LogDeletionState("completed on later tick"); if (!Check(DeletionComplete(), "actual prefab deletion runs successor and vanilla superclass cleanup")) return -1; if (!Check(ReplacementIsCurrent() && m_CloseEvents == m_CloseBaseline && m_RestoreCalls == m_RestoreBaseline, "spare and interrupted map deletion preserve replacement without restore calls")) return -1; m_WaitUntil = now + 1500; m_Step = 7; return 0; } if (!Check(ReplacementIsCurrent() && m_CloseEvents == m_CloseBaseline && m_RestoreCalls == m_RestoreBaseline && m_Completions == m_CompletionBaseline && m_CenterCalls == m_CenterBaseline && m_FinishCalls == m_FinishBaseline, "replacement survives delayed native callback window after both deletions")) return -1; Stop(); m_Done = true; Print("ERR SMOKE PASS: shared map ownership owned UI removed; camera render flag has no native getter and is not claimed as measured"); return 1; } void Stop() { if (m_Stopped) return; m_Stopped = true; if (m_Capture) m_Capture.ERRM_StopOwnershipCapture(); if (m_Spare) m_Spare.ERRM_StopOwnershipCapture(); if (ReplacementIsCurrent()) m_Map.CloseMap(); SCR_MapEntity.GetOnMapClose().Remove(OnMapClosed); SCR_MapEntity.GetOnMapOpenComplete().Remove(OnMapReady); RequestOwnedDeletion(); if (m_ReplacementRoot) m_ReplacementRoot.RemoveFromHierarchy(); m_ReplacementRoot = null; m_ReplacementConfig = null; m_ReadyConfig = null; m_Map = null; m_Capture = null; m_Spare = null; m_Holder = null; } } #endif