refactor(app): compose settings and developer tools
This commit is contained in:
parent
60a1698ce7
commit
cd7b519f78
24 changed files with 2073 additions and 333 deletions
|
|
@ -16,7 +16,8 @@ public sealed class GameWindow :
|
|||
IDisposable,
|
||||
IGameWindowPlatformPublication<GL, IInputContext>,
|
||||
IGameWindowHostInputCameraPublication,
|
||||
IGameWindowContentEffectsAudioPublication
|
||||
IGameWindowContentEffectsAudioPublication,
|
||||
IGameWindowSettingsDevToolsPublication
|
||||
{
|
||||
private static double ClientTimerNow() =>
|
||||
System.Diagnostics.Stopwatch.GetTimestamp()
|
||||
|
|
@ -33,7 +34,6 @@ public sealed class GameWindow :
|
|||
private SilkWindowCallbackBinding? _windowCallbacks;
|
||||
private GL? _gl;
|
||||
private IInputContext? _input;
|
||||
private AcDream.App.Input.QuiescentInputContext? _devToolsInputContext;
|
||||
private TerrainModernRenderer? _terrain;
|
||||
/// <summary>Phase N.5b: terrain_modern.vert/.frag program. Owned by
|
||||
/// <see cref="_terrain"/> at draw time but allocated + disposed here.</summary>
|
||||
|
|
@ -345,10 +345,9 @@ public sealed class GameWindow :
|
|||
|
||||
// Phase D.2a — ImGui devtools UI overlay. Null unless ACDREAM_DEVTOOLS=1.
|
||||
// See docs/plans/2026-04-24-ui-framework.md for the staged UI strategy.
|
||||
private AcDream.App.Rendering.ImGuiDevToolsFrameBackend? _devToolsBackend;
|
||||
private AcDream.App.Rendering.DevToolsFramePresenter? _devToolsFramePresenter;
|
||||
private AcDream.App.Rendering.DevToolsCameraMenuOperations? _devToolsCameraMenu;
|
||||
private AcDream.App.Rendering.DevToolsCommandBusSource? _devToolsCommandBus;
|
||||
private DevToolsCompositionOwner? _devToolsComposition;
|
||||
private AcDream.UI.Abstractions.Panels.Vitals.VitalsVM? _vitalsVm;
|
||||
private AcDream.UI.Abstractions.Panels.Chat.ChatVM? _retailChatVm;
|
||||
// Phase D.2b — retained host + composition runtime. Null unless ACDREAM_RETAIL_UI=1.
|
||||
|
|
@ -434,7 +433,6 @@ public sealed class GameWindow :
|
|||
private readonly AcDream.App.Input.LocalPlayerSkillState _localPlayerSkills = new();
|
||||
private readonly AcDream.App.Input.ViewportAspectState _viewportAspect = new();
|
||||
private readonly FramebufferResizeController _framebufferResize;
|
||||
private IFramebufferDevToolsTarget? _framebufferDevToolsTarget;
|
||||
private AcDream.App.Input.PlayerModeController? _playerModeController;
|
||||
private readonly AcDream.App.Interaction.PlayerApproachCompletionState
|
||||
_playerApproachCompletions = new();
|
||||
|
|
@ -784,6 +782,26 @@ public sealed class GameWindow :
|
|||
_audioSink = value.HookSink;
|
||||
}
|
||||
|
||||
void IGameWindowSettingsDevToolsPublication.PublishDevTools(
|
||||
DevToolsCompositionOwner value)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
if (_devToolsComposition is not null
|
||||
|| _devToolsFramePresenter is not null
|
||||
|| _devToolsCommandBus is not null
|
||||
|| _debugVm is not null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The GameWindow composition shell already owns developer tools.");
|
||||
}
|
||||
|
||||
_devToolsComposition = value;
|
||||
_devToolsFramePresenter = value.Presenter;
|
||||
_devToolsCommandBus = value.CommandBus;
|
||||
_vitalsVm = value.Vitals;
|
||||
_debugVm = value.Debug;
|
||||
}
|
||||
|
||||
private static void PublishCompositionOwner<T>(
|
||||
ref T? destination,
|
||||
T value,
|
||||
|
|
@ -856,6 +874,64 @@ public sealed class GameWindow :
|
|||
Console.Error.WriteLine),
|
||||
this).Compose(platform, hostInputCamera);
|
||||
|
||||
SettingsDevToolsOptionalDependencies? optionalDevTools = null;
|
||||
if (DevToolsEnabled)
|
||||
{
|
||||
var devToolsWorldEntities =
|
||||
new DeferredCanonicalWorldEntityCountSource();
|
||||
var devToolsFrameDiagnostics =
|
||||
new DeferredRenderFrameDiagnosticsSource();
|
||||
var devToolsPlayerModeCommands =
|
||||
new DeferredDevToolsPlayerModeCommands();
|
||||
var devToolsFacts = new DevToolsRuntimeFacts(
|
||||
_localPlayerMode,
|
||||
_playerControllerSlot,
|
||||
hostInputCamera.CameraController,
|
||||
devToolsWorldEntities,
|
||||
_animatedEntities,
|
||||
_debugVmRenderFacts,
|
||||
_physicsEngine,
|
||||
_worldSceneDebugState,
|
||||
_renderRange,
|
||||
hostInputCamera.CameraPointerInput,
|
||||
_worldEnvironment,
|
||||
Lighting,
|
||||
contentEffectsAudio.ParticleSystem,
|
||||
devToolsFrameDiagnostics);
|
||||
IRuntimeKeyBindingTarget? keyBindingTarget =
|
||||
hostInputCamera.InputDispatcher is { } settingsDispatcher
|
||||
? new RuntimeKeyBindingTarget(settingsDispatcher)
|
||||
: null;
|
||||
optionalDevTools = new SettingsDevToolsOptionalDependencies(
|
||||
devToolsFacts,
|
||||
keyBindingTarget,
|
||||
devToolsWorldEntities,
|
||||
devToolsFrameDiagnostics,
|
||||
devToolsPlayerModeCommands);
|
||||
}
|
||||
SettingsDevToolsResult settingsDevTools =
|
||||
new SettingsDevToolsCompositionPhase(
|
||||
new SettingsDevToolsDependencies(
|
||||
_window!,
|
||||
_runtimeSettings,
|
||||
new RuntimeSettingsStartupTargets(
|
||||
new SilkRuntimeDisplayWindowTarget(_window!),
|
||||
_displayFramePacing,
|
||||
hostInputCamera.CameraController,
|
||||
contentEffectsAudio.Audio?.Engine),
|
||||
_hostQuiescence,
|
||||
Chat,
|
||||
Combat,
|
||||
LocalPlayer,
|
||||
optionalDevTools,
|
||||
_runtimeDiagnosticCommands,
|
||||
_combatFeedback,
|
||||
_keyBindings,
|
||||
_frameProfiler,
|
||||
_framebufferResize,
|
||||
Console.WriteLine),
|
||||
this).Compose(platform, hostInputCamera, contentEffectsAudio);
|
||||
|
||||
_gl.ClearColor(0.05f, 0.10f, 0.18f, 1.0f);
|
||||
_gl.Enable(EnableCap.DepthTest);
|
||||
|
||||
|
|
@ -906,222 +982,6 @@ public sealed class GameWindow :
|
|||
Console.WriteLine("world-hud font: no system monospace font found");
|
||||
}
|
||||
|
||||
// Settings are runtime state, not devtools state. Apply the immutable
|
||||
// pre-window snapshot once after camera/audio exist and before any
|
||||
// optional frontend or world/render factory consumes it.
|
||||
_runtimeSettings.ApplyStartup(
|
||||
new RuntimeSettingsStartupTargets(
|
||||
new SilkRuntimeDisplayWindowTarget(_window!),
|
||||
_displayFramePacing,
|
||||
_cameraController!,
|
||||
_audioEngine));
|
||||
|
||||
// Phase D.2a — ImGui devtools overlay. Zero cost when the env var
|
||||
// isn't set: no context creation, no per-frame branches hit.
|
||||
// See docs/plans/2026-04-24-ui-framework.md + memory/project_ui_architecture.md.
|
||||
if (DevToolsEnabled)
|
||||
{
|
||||
AcDream.UI.ImGui.ImGuiBootstrapper? imguiBootstrap = null;
|
||||
AcDream.UI.Abstractions.Panels.Settings.SettingsVM? settingsVm = null;
|
||||
try
|
||||
{
|
||||
_devToolsInputContext = new AcDream.App.Input.QuiescentInputContext(
|
||||
_input!,
|
||||
_hostQuiescence);
|
||||
imguiBootstrap =
|
||||
new AcDream.UI.ImGui.ImGuiBootstrapper(
|
||||
_gl!,
|
||||
_window!,
|
||||
_devToolsInputContext);
|
||||
_devToolsInputContext.Activate();
|
||||
var panelHost = new AcDream.UI.ImGui.ImGuiPanelHost();
|
||||
|
||||
// VitalsVM: GUID=0 at construction; set later at EnterWorld
|
||||
// (see the _playerServerGuid assignment path). Pre-login the
|
||||
// HP bar just reads 1.0 (safe default) — harmless. Stam/Mana
|
||||
// bars surface only after the first PlayerDescription has
|
||||
// populated LocalPlayer (Issue #5).
|
||||
_vitalsVm = new AcDream.UI.Abstractions.Panels.Vitals.VitalsVM(Combat, LocalPlayer);
|
||||
var vitalsPanel =
|
||||
new AcDream.UI.Abstractions.Panels.Vitals.VitalsPanel(_vitalsVm);
|
||||
panelHost.Register(vitalsPanel);
|
||||
|
||||
// ChatPanel: reads the tail of the shared ChatLog. No GUID
|
||||
// dependency — works pre-login (empty) and post-login (live
|
||||
// tail of received speech/tells/channels/system msgs).
|
||||
// FpsProvider + PositionProvider plumb the runtime state
|
||||
// the client-side /framerate and /loc commands need; the
|
||||
// panel asks the VM, the VM asks GameWindow via these
|
||||
// delegates, no panel-vs-renderer-vs-state coupling.
|
||||
var chatVm = new AcDream.UI.Abstractions.Panels.Chat.ChatVM(Chat)
|
||||
{
|
||||
FpsProvider = () =>
|
||||
(float)(_renderFrameDiagnostics?.Snapshot.Fps ?? 60.0),
|
||||
PositionProvider = () => GetDebugPlayerPosition(),
|
||||
};
|
||||
var chatPanel = new AcDream.UI.Abstractions.Panels.Chat.ChatPanel(chatVm);
|
||||
panelHost.Register(chatPanel);
|
||||
|
||||
// Phase I.2: DebugPanel — replaces the deleted custom
|
||||
// DebugOverlay (six floating panels + hint bar + toast).
|
||||
// The VM closes over every data source the old snapshot
|
||||
// record exposed; reads are live (no per-frame snapshot
|
||||
// build). Action hooks tie the panel's cycle/toggle
|
||||
// buttons back to the same routines the F2/F7/F10
|
||||
// keybinds use.
|
||||
_debugVm = new AcDream.UI.Abstractions.Panels.Debug.DebugVM(
|
||||
getPlayerPosition: () => GetDebugPlayerPosition(),
|
||||
getPlayerHeadingDeg: () => GetDebugPlayerHeadingDeg(),
|
||||
getPlayerCellId: () => GetDebugPlayerCellId(),
|
||||
getPlayerOnGround: () => GetDebugPlayerOnGround(),
|
||||
getInPlayerMode: () => _playerMode,
|
||||
getInFlyMode: () => _cameraController?.IsFlyMode ?? false,
|
||||
getVerticalVelocity: () => _playerController?.VerticalVelocity ?? 0f,
|
||||
getEntityCount: () => _worldState.Entities.Count,
|
||||
getAnimatedCount: () => _animatedEntities.Count,
|
||||
getLandblocksVisible: () =>
|
||||
_debugVmRenderFacts.DebugVmFacts.VisibleLandblocks,
|
||||
getLandblocksTotal: () =>
|
||||
_debugVmRenderFacts.DebugVmFacts.TotalLandblocks,
|
||||
getShadowObjectCount: () => _physicsEngine.ShadowObjects.TotalRegistered,
|
||||
getNearestObjDist: () =>
|
||||
_debugVmRenderFacts.DebugVmFacts.NearestObjectDistance,
|
||||
getNearestObjLabel: () =>
|
||||
_debugVmRenderFacts.DebugVmFacts.NearestObjectLabel,
|
||||
getColliding: () =>
|
||||
_debugVmRenderFacts.DebugVmFacts.Colliding,
|
||||
getDebugWireframes: () =>
|
||||
_worldSceneDebugState.CollisionWireframesVisible,
|
||||
getStreamingRadius: () => _nearRadius, // A.5 T16 follow-up: was _streamingRadius (legacy single-tier); show near tier
|
||||
|
||||
getMouseSensitivity: () =>
|
||||
_cameraPointerInput?.ActiveSensitivity ?? 1f,
|
||||
getChaseDistance: () => _chaseCamera?.Distance ?? 0f,
|
||||
getRmbOrbit: () =>
|
||||
_cameraPointerInput?.RmbOrbitHeld ?? false,
|
||||
getHourName: () => WorldTime.CurrentCalendar.Hour.ToString(),
|
||||
getDayFraction: () => (float)WorldTime.DayFraction,
|
||||
getWeather: () => Weather.Kind.ToString(),
|
||||
getActiveLights: () => Lighting.ActiveCount,
|
||||
getRegisteredLights: () => Lighting.RegisteredCount,
|
||||
getParticleCount: () => _particleSystem?.ActiveParticleCount ?? 0,
|
||||
getFps: () =>
|
||||
(float)(_renderFrameDiagnostics?.Snapshot.Fps ?? 60.0),
|
||||
getFrameMs: () =>
|
||||
(float)(_renderFrameDiagnostics?.Snapshot.FrameMilliseconds ?? 16.7),
|
||||
combat: Combat);
|
||||
_debugVm.CycleTimeOfDay =
|
||||
_runtimeDiagnosticCommands.CycleTimeOfDay;
|
||||
_debugVm.CycleWeather =
|
||||
_runtimeDiagnosticCommands.CycleWeather;
|
||||
_debugVm.ToggleCollisionWires =
|
||||
_runtimeDiagnosticCommands.ToggleCollisionWireframes;
|
||||
// Phase K.2: free-fly toggle button — same routine the
|
||||
// legacy F-key alias hits. Cancels the one-shot
|
||||
// auto-entry if the user opts out of player mode before
|
||||
// it fires, so the chase camera doesn't snap on top of
|
||||
// the fly camera mid-inspection.
|
||||
_debugVm.ToggleFlyMode = () =>
|
||||
_playerModeController?.ToggleFlyOrChase();
|
||||
_combatFeedback.ViewModel = _debugVm;
|
||||
var debugPanel =
|
||||
new AcDream.UI.Abstractions.Panels.Debug.DebugPanel(_debugVm);
|
||||
panelHost.Register(debugPanel);
|
||||
|
||||
// Phase K.3 — Settings panel. SettingsVM owns a draft
|
||||
// copy of the active KeyBindings. Save replaces the
|
||||
// dispatcher's live table + writes JSON; Cancel reverts
|
||||
// the draft. Construction is null-safe vs. the
|
||||
// dispatcher because the dispatcher is built earlier in
|
||||
// the same OnLoad path (see _inputDispatcher field).
|
||||
AcDream.UI.Abstractions.Panels.Settings.SettingsPanel? settingsPanel = null;
|
||||
if (_inputDispatcher is not null)
|
||||
{
|
||||
settingsVm = _runtimeSettings.CreateViewModel(
|
||||
_keyBindings,
|
||||
_inputDispatcher,
|
||||
bindings =>
|
||||
{
|
||||
_inputDispatcher.SetBindings(bindings);
|
||||
try
|
||||
{
|
||||
bindings.SaveToFile(
|
||||
AcDream.UI.Abstractions.Input.KeyBindings.DefaultPath());
|
||||
Console.WriteLine(
|
||||
"keybinds: saved to "
|
||||
+ AcDream.UI.Abstractions.Input.KeyBindings.DefaultPath());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"keybinds: save failed: {ex.Message}");
|
||||
}
|
||||
});
|
||||
settingsPanel =
|
||||
new AcDream.UI.Abstractions.Panels.Settings.SettingsPanel(settingsVm);
|
||||
panelHost.Register(settingsPanel);
|
||||
}
|
||||
|
||||
Console.WriteLine("devtools: ImGui panel host ready (VitalsPanel + ChatPanel + DebugPanel + SettingsPanel registered)");
|
||||
|
||||
// L.0 Display tab: seed sensible default positions for
|
||||
// every registered panel. cond=FirstUseEver means imgui.ini
|
||||
// takes precedence on subsequent launches — the user's
|
||||
// dragged positions persist. Without this, the first-run
|
||||
// experience stacks every panel at (0,0) which looks
|
||||
// broken.
|
||||
_devToolsBackend = new AcDream.App.Rendering.ImGuiDevToolsFrameBackend(
|
||||
imguiBootstrap,
|
||||
panelHost);
|
||||
_devToolsCameraMenu =
|
||||
new AcDream.App.Rendering.DevToolsCameraMenuOperations(
|
||||
_cameraController!);
|
||||
_devToolsCommandBus =
|
||||
new AcDream.App.Rendering.DevToolsCommandBusSource();
|
||||
_devToolsFramePresenter =
|
||||
new AcDream.App.Rendering.DevToolsFramePresenter(
|
||||
_devToolsBackend,
|
||||
_devToolsCameraMenu,
|
||||
_devToolsCommandBus,
|
||||
_frameProfiler,
|
||||
new AcDream.App.Rendering.DevToolsPanelSet(
|
||||
vitalsPanel,
|
||||
chatPanel,
|
||||
debugPanel,
|
||||
_debugVm,
|
||||
settingsPanel));
|
||||
_framebufferDevToolsTarget = new DevToolsFramebufferTarget(
|
||||
_devToolsFramePresenter);
|
||||
_framebufferResize.BindDevTools(_framebufferDevToolsTarget);
|
||||
_devToolsFramePresenter.ResetLayout(
|
||||
_window!.Size.X,
|
||||
_window.Size.Y,
|
||||
AcDream.App.Rendering.DevToolsPanelLayoutCondition.FirstUseEver);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"devtools: ImGui init failed: {ex.Message} — devtools disabled");
|
||||
// The focused backend borrows the bootstrap during normal
|
||||
// operation. Construction failure still owns this local and
|
||||
// must release it before abandoning the optional frontend.
|
||||
imguiBootstrap?.Dispose();
|
||||
if (_framebufferDevToolsTarget is { } framebufferTarget)
|
||||
{
|
||||
_framebufferResize.UnbindDevTools(framebufferTarget);
|
||||
_framebufferDevToolsTarget = null;
|
||||
}
|
||||
_devToolsInputContext?.Dispose();
|
||||
_devToolsInputContext = null;
|
||||
_devToolsBackend = null;
|
||||
_devToolsFramePresenter = null;
|
||||
_devToolsCameraMenu = null;
|
||||
_devToolsCommandBus = null;
|
||||
_vitalsVm = null;
|
||||
_combatFeedback.ViewModel = null;
|
||||
_debugVm = null;
|
||||
_runtimeSettings.UnbindViewModel(settingsVm);
|
||||
}
|
||||
}
|
||||
|
||||
uint centerLandblockId = 0xA9B4FFFFu;
|
||||
Console.WriteLine($"loading world view centered on 0x{centerLandblockId:X8}");
|
||||
|
||||
|
|
@ -1755,6 +1615,7 @@ public sealed class GameWindow :
|
|||
wbSpawnAdapter,
|
||||
onLandblockUnloaded: _classificationCache.InvalidateLandblock,
|
||||
entityScriptActivator: entityScriptActivator);
|
||||
_devToolsComposition?.LateBindings.WorldEntities.Bind(_worldState);
|
||||
_liveEntities = new AcDream.App.World.LiveEntityRuntime(
|
||||
_worldState,
|
||||
new AcDream.App.World.CompositeLiveEntityResourceLifecycle(
|
||||
|
|
@ -2117,6 +1978,8 @@ public sealed class GameWindow :
|
|||
_renderDiagnosticLog,
|
||||
_options.UiProbeDump,
|
||||
resourceDiagnostics);
|
||||
_devToolsComposition?.LateBindings.FrameDiagnostics.Bind(
|
||||
_renderFrameDiagnostics);
|
||||
|
||||
// Apply radii from the same immutable quality snapshot used for the
|
||||
// window's MSAA, terrain anisotropy, and dispatcher A2C setup.
|
||||
|
|
@ -2498,7 +2361,8 @@ public sealed class GameWindow :
|
|||
_movementTruthDiagnostics,
|
||||
_localPlayerSkills,
|
||||
_viewportAspect);
|
||||
_devToolsCameraMenu?.Bind(_playerModeController);
|
||||
_devToolsComposition?.LateBindings.PlayerModeCommands.Bind(
|
||||
_playerModeController);
|
||||
_devToolsCommandBus?.Bind(_liveSessionController);
|
||||
_playerModeAutoEntry = new AcDream.App.Input.PlayerModeAutoEntry(
|
||||
new AcDream.App.Input.LivePlayerModeAutoEntryContext(
|
||||
|
|
@ -3216,59 +3080,6 @@ public sealed class GameWindow :
|
|||
// EXPECTED-DIFF: local sidestep pacing now matches how remotes have
|
||||
// always played (w6-cutover-map.md R3).
|
||||
|
||||
// ── Phase I.2 — DebugPanel helpers ────────────────────────────────
|
||||
//
|
||||
// The ImGui DebugPanel reads through DebugVM closures that ask
|
||||
// GameWindow for live state on every frame. The helper methods below
|
||||
// are the *named* targets of those closures (and of the F-key
|
||||
// shortcuts that share the same actions). Keeping them as methods
|
||||
// (vs ad-hoc lambdas where the VM is constructed) means both the
|
||||
// panel button and the keybind run the *same* code, so behavior
|
||||
// can't drift between the two surfaces.
|
||||
|
||||
/// <summary>Player-mode-aware position source for the DebugPanel.</summary>
|
||||
private System.Numerics.Vector3 GetDebugPlayerPosition()
|
||||
{
|
||||
if (_playerMode && _playerController is not null)
|
||||
return _playerController.Position;
|
||||
if (_cameraController?.Active is { } cam)
|
||||
{
|
||||
// Camera world position from inverse of view matrix — same
|
||||
// computation used by the scene-lighting UBO each frame.
|
||||
System.Numerics.Matrix4x4.Invert(cam.View, out var inv);
|
||||
return new System.Numerics.Vector3(inv.M41, inv.M42, inv.M43);
|
||||
}
|
||||
return System.Numerics.Vector3.Zero;
|
||||
}
|
||||
|
||||
/// <summary>Heading in degrees, [0..360). Player yaw in player mode, camera-forward heading otherwise.</summary>
|
||||
private float GetDebugPlayerHeadingDeg()
|
||||
{
|
||||
float deg;
|
||||
if (_playerMode && _playerController is not null)
|
||||
{
|
||||
deg = _playerController.Yaw * (180f / MathF.PI);
|
||||
}
|
||||
else if (_cameraController?.Active is { } cam)
|
||||
{
|
||||
// Camera-relative heading from view matrix forward vector. Use
|
||||
// the same -invView.Mxx convention the snapshot block used.
|
||||
System.Numerics.Matrix4x4.Invert(cam.View, out var inv);
|
||||
var fwd = new System.Numerics.Vector3(-inv.M31, -inv.M32, -inv.M33);
|
||||
deg = MathF.Atan2(fwd.Y, fwd.X) * (180f / MathF.PI);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
deg %= 360f;
|
||||
if (deg < 0f) deg += 360f;
|
||||
return deg;
|
||||
}
|
||||
|
||||
private uint GetDebugPlayerCellId() =>
|
||||
_playerMode && _playerController is not null ? _playerController.CellId : 0u;
|
||||
|
||||
private bool GetDebugPlayerOnGround() =>
|
||||
_playerMode && _playerController is not null && !_playerController.IsAirborne;
|
||||
|
||||
|
|
@ -3419,7 +3230,7 @@ public sealed class GameWindow :
|
|||
new("mouse source", () => _mouseSource?.Deactivate()),
|
||||
new("keyboard source", () => _kbSource?.Deactivate()),
|
||||
new("retained UI input", _retailUiLease.QuiesceInput),
|
||||
new("developer tools input", () => _devToolsInputContext?.Deactivate()),
|
||||
new("developer tools input", () => _devToolsComposition?.DeactivateInput()),
|
||||
]),
|
||||
// Live-session reset retires the complete streaming window. Every
|
||||
// reset callback owner, especially LandblockStreamer, must remain live
|
||||
|
|
@ -3473,7 +3284,7 @@ public sealed class GameWindow :
|
|||
_gameplayInputActions = null;
|
||||
}),
|
||||
new("retained UI input", _retailUiLease.DeactivateInput),
|
||||
new("developer tools input", () => _devToolsInputContext?.Dispose()),
|
||||
new("developer tools input", () => _devToolsComposition?.DetachInput()),
|
||||
new("camera pointer", () =>
|
||||
{
|
||||
AcDream.App.Input.CameraPointerInputController? pointer =
|
||||
|
|
@ -3650,17 +3461,17 @@ public sealed class GameWindow :
|
|||
[
|
||||
new("developer tools", () =>
|
||||
{
|
||||
if (_framebufferDevToolsTarget is { } framebufferTarget)
|
||||
{
|
||||
_framebufferResize.UnbindDevTools(framebufferTarget);
|
||||
_framebufferDevToolsTarget = null;
|
||||
}
|
||||
DevToolsCompositionOwner? owner = _devToolsComposition;
|
||||
if (owner is null)
|
||||
return;
|
||||
owner.DisposeFrontend();
|
||||
if (!owner.IsDisposalComplete)
|
||||
throw new InvalidOperationException(
|
||||
"Developer-tools cleanup remains incomplete.");
|
||||
_devToolsComposition = null;
|
||||
_devToolsFramePresenter = null;
|
||||
_devToolsCameraMenu = null;
|
||||
_devToolsCommandBus = null;
|
||||
_devToolsBackend?.Dispose();
|
||||
_devToolsBackend = null;
|
||||
_devToolsInputContext = null;
|
||||
_debugVm = null;
|
||||
}),
|
||||
new("portal tunnel", () =>
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue