diff --git a/Maple2.Server.DebugGame/Graphics/Assets/CoreModels.cs b/Maple2.Server.DebugGame/Graphics/Assets/CoreModels.cs index c390f574d..c786d89a3 100644 --- a/Maple2.Server.DebugGame/Graphics/Assets/CoreModels.cs +++ b/Maple2.Server.DebugGame/Graphics/Assets/CoreModels.cs @@ -120,9 +120,9 @@ private Mesh CreateQuad() { List attributeBinding = []; for (int i = 0; i < cubeSolidIndices.Length; i += 3) { - PositionBinding vertA = cubeVertices[i + 0]; - PositionBinding vertB = cubeVertices[i + 1]; - PositionBinding vertC = cubeVertices[i + 2]; + PositionBinding vertA = cubeVertices[cubeSolidIndices[i + 0]]; + PositionBinding vertB = cubeVertices[cubeSolidIndices[i + 1]]; + PositionBinding vertC = cubeVertices[cubeSolidIndices[i + 2]]; positionBinding.Add(vertA); positionBinding.Add(vertB); diff --git a/Maple2.Server.DebugGame/Graphics/DebugFieldRenderer.cs b/Maple2.Server.DebugGame/Graphics/DebugFieldRenderer.cs index 8cbb2850f..7d7d83efb 100644 --- a/Maple2.Server.DebugGame/Graphics/DebugFieldRenderer.cs +++ b/Maple2.Server.DebugGame/Graphics/DebugFieldRenderer.cs @@ -1,65 +1,402 @@ -using Maple2.Server.Game.DebugGraphics; +using System.Numerics; +using Maple2.Server.Game.DebugGraphics; using Maple2.Server.Game.Manager.Field; +using ImGuiNET; +using Maple2.Model.Enum; +using Maple2.Model.Game; +using Maple2.Server.Game.Manager; +using Maple2.Server.Game.Model; -namespace Maple2.Server.DebugGame.Graphics { - public class DebugFieldRenderer : IFieldRenderer { - public DebugGraphicsContext Context { get; init; } - public FieldManager Field { get; init; } - public bool IsActive { - get { - activeMutex.WaitOne(); - bool isActive = activeWindows.Count > 0; - activeMutex.ReleaseMutex(); - return isActive; - } +namespace Maple2.Server.DebugGame.Graphics; + +public class DebugFieldRenderer : IFieldRenderer { + public DebugGraphicsContext Context { get; init; } + public FieldManager Field { get; init; } + public bool IsActive { + get { + activeMutex.WaitOne(); + bool isActive = activeWindows.Count > 0; + activeMutex.ReleaseMutex(); + return isActive; + } + } + + private readonly HashSet activeWindows = []; + private readonly Mutex activeMutex = new(); + private IActor? selectedActor = null; + + public DebugFieldRenderer(DebugGraphicsContext context, FieldManager field) { + Context = context; + Field = field; + } + + public void Update() { + if (!IsActive) { + return; } - private HashSet activeWindows = []; - private Mutex activeMutex = new(); + if (!Context.HasFieldUpdated(Field)) { + Context.FieldUpdated(Field); - public DebugFieldRenderer(DebugGraphicsContext context, FieldManager field) { - Context = context; - Field = field; + Field.Update(); } + } + + public void Render(double delta) { + if (!IsActive) { + return; + } + + // Store field window position and size for positioning player details panel + Vector2 fieldWindowPos = Vector2.Zero; + Vector2 fieldWindowSize = Vector2.Zero; + + // Create a field information window + if (ImGui.Begin("Field Information")) { + // Get window position and size while the window is active + fieldWindowPos = ImGui.GetWindowPos(); + fieldWindowSize = ImGui.GetWindowSize(); + + RenderFieldBasicInfo(); + ImGui.Separator(); + RenderEntityCounts(); + ImGui.Separator(); + RenderActorList(); + ImGui.Separator(); + RenderFieldProperties(); + } + ImGui.End(); + + // Show actor details panel if an actor is selected + if (selectedActor != null) { + RenderActorDetailsPanel(fieldWindowPos, fieldWindowSize); + } + } + + private void RenderFieldBasicInfo() { + ImGui.Text($"Map ID: {Field.MapId}"); + ImGui.Text($"Room ID: {Field.RoomId}"); + ImGui.Text($"Map Name: {Field.Metadata.Name}"); + ImGui.Text($"Field Type: {Field.FieldType}"); + + if (Field.DungeonId > 0) { + ImGui.Text($"Dungeon ID: {Field.DungeonId}"); + } + } + + private void RenderEntityCounts() { + ImGui.Text("Entity Counts:"); + ImGui.Indent(); + ImGui.Text($"Players: {Field.Players.Count}"); + ImGui.Text($"NPCs: {Field.Npcs.Count}"); + ImGui.Text($"Mobs: {Field.Mobs.Count}"); + ImGui.Text($"Pets: {Field.Pets.Count}"); + ImGui.Unindent(); + } + + private void RenderActorList() { + ImGui.Text("Active Actors:"); - public void Update() { - if (!IsActive) { - return; + int totalActors = Field.Players.Count + Field.Npcs.Count + Field.Mobs.Count; + if (totalActors == 0) { + ImGui.Text("No active actors"); + return; + } + + bool showActorDetailsDisabled = selectedActor == null; + + if (showActorDetailsDisabled) { + ImGui.BeginDisabled(); + } + + if (ImGui.Button("Show Actor Details") && !showActorDetailsDisabled) { + // Keep the selected actor to show details panel + } + + if (showActorDetailsDisabled) { + ImGui.EndDisabled(); + } + + ImGui.SameLine(); + + bool clearSelectionDisabled = selectedActor == null; + + if (clearSelectionDisabled) { + ImGui.BeginDisabled(); + } + + if (ImGui.Button("Clear Selection") && !clearSelectionDisabled) { + selectedActor = null; + } + + if (clearSelectionDisabled) { + ImGui.EndDisabled(); + } + + if (ImGui.BeginTable("Active Actors", 5)) { + ImGui.TableNextRow(ImGuiTableRowFlags.Headers); + + ImGui.TableSetColumnIndex(0); + ImGui.Text("Type"); + ImGui.TableSetColumnIndex(1); + ImGui.Text("Name"); + ImGui.TableSetColumnIndex(2); + ImGui.Text("Level"); + ImGui.TableSetColumnIndex(3); + ImGui.Text("Object ID"); + ImGui.TableSetColumnIndex(4); + ImGui.Text("Status"); + + int index = 0; + + // Render Players + foreach ((int objectId, FieldPlayer player) in Field.Players) { + ImGui.TableNextRow(); + + bool selected = player == selectedActor; + bool nextSelected = false; + + ImGui.TableSetColumnIndex(0); + nextSelected |= ImGui.Selectable($"Player##Actor {index} 0", selected); + ImGui.TableSetColumnIndex(1); + nextSelected |= ImGui.Selectable($"{player.Value.Character.Name}##Actor {index} 1", selected); + ImGui.TableSetColumnIndex(2); + nextSelected |= ImGui.Selectable($"{player.Value.Character.Level}##Actor {index} 2", selected); + ImGui.TableSetColumnIndex(3); + nextSelected |= ImGui.Selectable($"{objectId}##Actor {index} 3", selected); + ImGui.TableSetColumnIndex(4); + nextSelected |= ImGui.Selectable($"{(player.IsDead ? "Dead" : "Alive")}##Actor {index} 4", selected); + + if (nextSelected) { + selectedActor = player; + } + + ++index; } - if (!Context.HasFieldUpdated(Field)) { - Context.FieldUpdated(Field); + // Render NPCs + foreach ((int objectId, FieldNpc npc) in Field.Npcs) { + ImGui.TableNextRow(); + + bool selected = npc == selectedActor; + bool nextSelected = false; + + ImGui.TableSetColumnIndex(0); + nextSelected |= ImGui.Selectable($"NPC##Actor {index} 0", selected); + ImGui.TableSetColumnIndex(1); + nextSelected |= ImGui.Selectable($"{npc.Value.Metadata.Name}##Actor {index} 1", selected); + ImGui.TableSetColumnIndex(2); + nextSelected |= ImGui.Selectable($"{npc.Value.Metadata.Basic.Level}##Actor {index} 2", selected); + ImGui.TableSetColumnIndex(3); + nextSelected |= ImGui.Selectable($"{objectId}##Actor {index} 3", selected); + ImGui.TableSetColumnIndex(4); + nextSelected |= ImGui.Selectable($"{(npc.IsDead ? "Dead" : "Alive")}##Actor {index} 4", selected); - Field.Update(); + if (nextSelected) { + selectedActor = npc; + } + + ++index; + } + + // Render Mobs + foreach ((int objectId, FieldNpc mob) in Field.Mobs) { + ImGui.TableNextRow(); + + bool selected = mob == selectedActor; + bool nextSelected = false; + + ImGui.TableSetColumnIndex(0); + nextSelected |= ImGui.Selectable($"Mob##Actor {index} 0", selected); + ImGui.TableSetColumnIndex(1); + nextSelected |= ImGui.Selectable($"{mob.Value.Metadata.Name}##Actor {index} 1", selected); + ImGui.TableSetColumnIndex(2); + nextSelected |= ImGui.Selectable($"{mob.Value.Metadata.Basic.Level}##Actor {index} 2", selected); + ImGui.TableSetColumnIndex(3); + nextSelected |= ImGui.Selectable($"{objectId}##Actor {index} 3", selected); + ImGui.TableSetColumnIndex(4); + nextSelected |= ImGui.Selectable($"{(mob.IsDead ? "Dead" : "Alive")}##Actor {index} 4", selected); + + if (nextSelected) { + selectedActor = mob; + } + + ++index; } + + ImGui.EndTable(); } + } + + private void RenderFieldProperties() { + ImGui.Text("Field Status:"); + ImGui.Indent(); - public void Render(double delta) { + if (Field.RoomTimer != null) { + ImGui.Text($"Room Timer Active: {Field.RoomTimer.Duration}"); + } + if (Field.AccelerationStructure != null) { + ImGui.Text("Acceleration Structure: Active"); } - public void CleanUp() { + ImGui.Text($"Field Instance Type: {Field.FieldInstance.Type}"); + ImGui.Unindent(); + } + + private void RenderActorDetailsPanel(Vector2 fieldWindowPos, Vector2 fieldWindowSize) { + if (selectedActor == null) return; + // Position the actor details panel to the right of the field information panel + ImGui.SetNextWindowPos(new Vector2( + fieldWindowPos.X + fieldWindowSize.X + 10, // 10px gap + fieldWindowPos.Y + )); + + // Set a reasonable size for the actor details panel + ImGui.SetNextWindowSize(new Vector2(350, 500), ImGuiCond.FirstUseEver); + + // Get actor name for window title + string actorName = GetActorName(selectedActor); + string actorType = GetActorType(selectedActor); + + // Create a separate window for actor details + if (ImGui.Begin($"{actorType} Details: {actorName}##ActorDetails")) { + RenderActorBasicInfo(); + ImGui.Separator(); + RenderActorPositionInfo(); + ImGui.Separator(); + RenderActorStatsInfo(); + ImGui.Separator(); + RenderActorAdditionalInfo(); } + ImGui.End(); + } - public void AttachWindow(DebugFieldWindow window) { - activeMutex.WaitOne(); + private string GetActorName(IActor actor) { + return actor switch { + FieldPlayer player => player.Value.Character.Name, + FieldNpc npc => npc.Value.Metadata.Name ?? "Unknown", + _ => "Unknown", + }; + } - if (!activeWindows.Contains(window)) { - activeWindows.Add(window); - } + private string GetActorType(IActor actor) { + return actor switch { + FieldPlayer => "Player", + FieldNpc => "NPC", + _ => "Unknown", + }; + } - activeMutex.ReleaseMutex(); + private void RenderActorBasicInfo() { + if (selectedActor == null) return; + + ImGui.Text($"Type: {GetActorType(selectedActor)}"); + ImGui.Text($"Name: {GetActorName(selectedActor)}"); + ImGui.Text($"Object ID: {selectedActor.ObjectId}"); + ImGui.Text($"Is Dead: {selectedActor.IsDead}"); + + switch (selectedActor) { + case FieldPlayer player: + ImGui.Text($"State: {player.State}"); + ImGui.Text($"Sub State: {player.SubState}"); + Character character = player.Value.Character; + ImGui.Text($"Level: {character.Level}"); + ImGui.Text($"Job: {character.Job}"); + ImGui.Text($"Gender: {character.Gender}"); + ImGui.Text($"Account ID: {player.Value.Account.Id}"); + ImGui.Text($"Character ID: {character.Id}"); + break; + case FieldNpc npc: + ImGui.Text($"State: {npc.State.State}"); + ImGui.Text($"Sub State: {npc.State.SubState}"); + ImGui.Text($"Level: {npc.Value.Metadata.Basic.Level}"); + ImGui.Text($"NPC ID: {npc.Value.Metadata.Id}"); + ImGui.Text($"Model name: {npc.Value.Metadata.Model.Name}"); + ImGui.Text($"Animation Speed: {npc.Value.Metadata.Model.AniSpeed}"); + ImGui.Text($"Is Boss: {npc.Value.IsBoss}"); + break; } + } - public void DetachWindow(DebugFieldWindow window) { - activeMutex.WaitOne(); + private void RenderActorPositionInfo() { + if (selectedActor == null) return; - if (activeWindows.Contains(window)) { - activeWindows.Remove(window); - } + ImGui.Text("Position & Movement:"); + ImGui.Indent(); + ImGui.Text($"Position: {selectedActor.Position}"); + ImGui.Text($"Rotation: {selectedActor.Rotation}"); + if (selectedActor is FieldNpc npc) { + ImGui.Text($"Velocity: {npc.MovementState.Velocity}"); + } + ImGui.Text($"Playing Sequence: {selectedActor.Animation.PlayingSequence?.Name ?? "None"}"); - activeMutex.ReleaseMutex(); + if (selectedActor is FieldPlayer player) { + ImGui.Text($"Last Ground Position: {player.LastGroundPosition}"); + ImGui.Text($"In Battle: {player.InBattle}"); + } + + ImGui.Unindent(); + } + + private void RenderActorStatsInfo() { + if (selectedActor == null) return; + + ImGui.Text("Stats:"); + ImGui.Indent(); + + var stats = selectedActor.Stats; + ImGui.Text($"Health: {stats.Values[BasicAttribute.Health].Current}/{stats.Values[BasicAttribute.Health].Total}"); + + // Show additional stats for players + if (selectedActor is FieldPlayer) { + ImGui.Text($"Spirit: {stats.Values[BasicAttribute.Spirit].Current}/{stats.Values[BasicAttribute.Spirit].Total}"); + ImGui.Text($"Stamina: {stats.Values[BasicAttribute.Stamina].Current}/{stats.Values[BasicAttribute.Stamina].Total}"); + } + + ImGui.Unindent(); + } + + private void RenderActorAdditionalInfo() { + if (selectedActor == null) return; + + ImGui.Text("Additional Information:"); + ImGui.Indent(); + + switch (selectedActor) { + case FieldPlayer player: + ImGui.Text($"Admin Permissions: {player.AdminPermissions}"); + break; + case FieldNpc npc: + ImGui.Text($"NPC Type: {(npc.Value.Metadata.Basic.Kind == 0 ? "Friendly" : "Hostile")}"); + if (npc.Owner != null) { + ImGui.Text($"Spawn Point ID: {npc.SpawnPointId}"); + } + break; + default: + ImGui.Text("No additional information available."); + break; } + + ImGui.Unindent(); + } + + public void CleanUp() { } + + public void AttachWindow(DebugFieldWindow window) { + activeMutex.WaitOne(); + + activeWindows.Add(window); + + activeMutex.ReleaseMutex(); + } + + public void DetachWindow(DebugFieldWindow window) { + activeMutex.WaitOne(); + + activeWindows.Remove(window); + + activeMutex.ReleaseMutex(); } } diff --git a/Maple2.Server.DebugGame/Graphics/DebugFieldWindow.cs b/Maple2.Server.DebugGame/Graphics/DebugFieldWindow.cs index a64df6d59..448ac3af1 100644 --- a/Maple2.Server.DebugGame/Graphics/DebugFieldWindow.cs +++ b/Maple2.Server.DebugGame/Graphics/DebugFieldWindow.cs @@ -43,10 +43,8 @@ public void SetActiveRenderer(DebugFieldRenderer? renderer) { } public void Initialize() { - unsafe { - if (IsInitialized) { - CleanUp(); - } + if (IsInitialized) { + CleanUp(); } IsInitialized = true; @@ -81,7 +79,7 @@ public void CleanUp() { Input?.Dispose(); DxSwapChain = default; - Input = default; + Input = null; Log.Information("Field debugger swap chain cleaning up"); } @@ -90,7 +88,7 @@ public void CleanUp() { if (DebuggerWindow is not null) { DebuggerWindow.Dispose(); - DebuggerWindow = default; + DebuggerWindow = null; Log.Information("Field debugger window cleaning up"); } @@ -117,7 +115,7 @@ private void OnLoad() { }; unsafe { - IDXGISwapChain1* swapChain = default; + IDXGISwapChain1* swapChain = null; SilkMarshal.ThrowHResult(Context.DxFactory.CreateSwapChainForHwnd( pDevice: (IUnknown*) (ID3D11Device*) Context.DxDevice, @@ -163,9 +161,9 @@ public unsafe void OnRender(double delta) { Context.DxDeviceContext.ClearRenderTargetView(renderTargetView, DebugGraphicsContext.WindowClearColor); Viewport viewport = new Viewport(0, 0, DebuggerWindow!.FramebufferSize.X, DebuggerWindow!.FramebufferSize.Y, 0, 1); - Context.DxDeviceContext.RSSetViewports(1, ref viewport); + Context.DxDeviceContext.RSSetViewports(1, in viewport); - Context.DxDeviceContext.OMSetRenderTargets(1, ref renderTargetView.Handle, (ID3D11DepthStencilView*) null); + Context.DxDeviceContext.OMSetRenderTargets(1, in renderTargetView.Handle, (ID3D11DepthStencilView*) null); ImGuiController!.BeginFrame((float) delta); diff --git a/Maple2.Server.DebugGame/Graphics/DebugGraphicsContext.cs b/Maple2.Server.DebugGame/Graphics/DebugGraphicsContext.cs index 5ee443be6..62435ce4b 100644 --- a/Maple2.Server.DebugGame/Graphics/DebugGraphicsContext.cs +++ b/Maple2.Server.DebugGame/Graphics/DebugGraphicsContext.cs @@ -10,464 +10,466 @@ using Silk.NET.Maths; using Silk.NET.Windowing; using Maple2.Server.DebugGame.Graphics.Assets; +using Maple2.Server.Game.Model; using Maple2.Tools.Extensions; -namespace Maple2.Server.DebugGame.Graphics { - public class DebugGraphicsContext : IGraphicsContext { - public static readonly bool ForceDXVK = false; - public static readonly Vector2D DefaultWindowSize = new Vector2D(800, 600); - public static readonly float[] WindowClearColor = [0.0f, 0.0f, 0.0f, 1.0f]; - public static readonly ILogger Logger = Log.Logger.ForContext(); +namespace Maple2.Server.DebugGame.Graphics; - public readonly Dictionary Fields; +public class DebugGraphicsContext : IGraphicsContext { + private const bool ForceDxvk = false; + public static readonly Vector2D DefaultWindowSize = new Vector2D(800, 600); + public static readonly float[] WindowClearColor = [0.0f, 0.0f, 0.0f, 1.0f]; + private static readonly ILogger Logger = Log.Logger.ForContext(); - public IWindow? DebuggerWindow { get; private set; } - public IInputContext? Input { get; private set; } + private readonly Dictionary fields = []; - public D3D11? D3d11 { get; private set; } - public DXGI? Dxgi { get; private set; } - public D3DCompiler? Compiler { get; private set; } + private IWindow? DebuggerWindow { get; set; } + private IInputContext? Input { get; set; } - public ComPtr DxDevice { get; private set; } - public ComPtr DxDeviceContext { get; private set; } - public ComPtr DxFactory { get; private set; } - public ComPtr DxSwapChain { get; private set; } - public VertexShader? VertexShader { get; private set; } - public PixelShader? PixelShader { get; private set; } + public D3D11? D3d11 { get; private set; } + public DXGI? Dxgi { get; private set; } + public D3DCompiler? Compiler { get; private set; } - public CoreModels? CoreModels { get; private set; } - public Texture? SampleTexture; - public ImGuiController? ImGuiController { get; private set; } + public ComPtr DxDevice { get; private set; } + public ComPtr DxDeviceContext { get; private set; } + public ComPtr DxFactory { get; private set; } + public ComPtr DxSwapChain { get; private set; } + public VertexShader? VertexShader { get; private set; } + public PixelShader? PixelShader { get; private set; } - private string resourceRootPath = ""; + public CoreModels? CoreModels { get; private set; } + public Texture? SampleTexture; + public ImGuiController? ImGuiController { get; private set; } - private List fieldRenderers = []; - private Mutex fieldRendererMutex = new(); - public DebugFieldRenderer[] FieldRenderers { - get { - fieldRendererMutex.WaitOne(); - DebugFieldRenderer[] renderers = fieldRenderers.ToArray(); - fieldRendererMutex.ReleaseMutex(); + private string resourceRootPath = ""; - return renderers; - } - } - public IReadOnlyList FieldWindows { get => fieldWindows; } - private List fieldWindows = []; - private HashSet updatedFields = []; - private int deltaIndex = 0; - private List deltaTimes = []; - public int DeltaAverage { get; private set; } - public int DeltaMin { get; private set; } - public int DeltaMax { get; private set; } - private DateTime lastTime = DateTime.Now; - public bool IsClosing { get; private set; } - - public DebugGraphicsContext() { - Fields = new Dictionary(); - } - - public void RunDebugger() { - DebuggerWindow!.Initialize(); - - bool subWindowsUpdating = false; - - while (!(DebuggerWindow?.IsClosing ?? true) || subWindowsUpdating) { - if (DebuggerWindow is not null && !DebuggerWindow.IsClosing) { - UpdateWindow(DebuggerWindow, IsClosing); + private readonly List fieldRenderers = []; + private readonly Mutex fieldRendererMutex = new(); + public DebugFieldRenderer[] FieldRenderers { + get { + fieldRendererMutex.WaitOne(); + DebugFieldRenderer[] renderers = fieldRenderers.ToArray(); + fieldRendererMutex.ReleaseMutex(); - if (DebuggerWindow.IsClosing) { - CleanUp(); - } + return renderers; + } + } + public IReadOnlyList FieldWindows => fieldWindows; + private readonly List fieldWindows = []; + private readonly HashSet updatedFields = []; + private readonly object updatedFieldsLock = new(); + private int deltaIndex = 0; + private readonly List deltaTimes = []; + public int DeltaAverage { get; private set; } + public int DeltaMin { get; private set; } + public int DeltaMax { get; private set; } + private DateTime lastTime = DateTime.Now; + public bool IsClosing { get; private set; } + + public void RunDebugger() { + DebuggerWindow!.Initialize(); + + bool subWindowsUpdating = false; + + while (!(DebuggerWindow?.IsClosing ?? true) || subWindowsUpdating) { + if (DebuggerWindow is not null && !DebuggerWindow.IsClosing) { + UpdateWindow(DebuggerWindow, IsClosing); + + if (DebuggerWindow.IsClosing) { + CleanUp(); } + } - DebugFieldWindow[] windows = fieldWindows.ToArray(); + DebugFieldWindow[] windows = fieldWindows.ToArray(); - subWindowsUpdating = false; + subWindowsUpdating = false; - foreach (DebugFieldWindow window in windows) { - if (!window.IsInitialized) { - window.Initialize(); - } + foreach (DebugFieldWindow window in windows) { + if (!window.IsInitialized) { + window.Initialize(); + } - if (window.DebuggerWindow is not null) { - bool isStillOpen = UpdateWindow(window.DebuggerWindow, window.IsClosing); + if (window.DebuggerWindow is not null) { + bool isStillOpen = UpdateWindow(window.DebuggerWindow, window.IsClosing); - subWindowsUpdating |= isStillOpen; + subWindowsUpdating |= isStillOpen; - if (!isStillOpen) { - window.CleanUp(); - } + if (!isStillOpen) { + window.CleanUp(); } } } - } - public bool UpdateWindow(IView window, bool shouldClose) { - bool startedOpen = window.IsClosing; + } - if (shouldClose && !window.IsClosing) { - window.Close(); - } + public bool UpdateWindow(IView window, bool shouldClose) { + bool startedOpen = !window.IsClosing; - if (!window.IsClosing) { - window.DoEvents(); - } + if (shouldClose && !window.IsClosing) { + window.Close(); + } - if (!window.IsClosing) { - window.DoUpdate(); - } + if (!window.IsClosing) { + window.DoEvents(); + } - if (!window.IsClosing) { - window.DoRender(); - } + if (!window.IsClosing) { + window.DoUpdate(); + } - bool hasUpdated = !window.IsClosing; + if (!window.IsClosing) { + window.DoRender(); + } - if (startedOpen && window.IsClosing) { - window.DoEvents(); - window.Reset(); - } + bool hasUpdated = !window.IsClosing; - return hasUpdated; + if (startedOpen && window.IsClosing) { + window.DoEvents(); + window.Reset(); } - private string GetWindowName() { - return $"Maple2 Visual Debugger"; + return hasUpdated; + } + + private string GetWindowName() { + return $"Maple2 Visual Debugger"; + } + + public void Initialize() { + if (DebuggerWindow is not null) { + CleanUp(); } - public void Initialize() { - unsafe { - if (DebuggerWindow is not null) { - CleanUp(); - } - } + var windowOptions = WindowOptions.Default; + windowOptions.Size = DefaultWindowSize; + windowOptions.Title = GetWindowName(); + windowOptions.API = GraphicsAPI.None; + windowOptions.ShouldSwapAutomatically = false; - var windowOptions = WindowOptions.Default; - windowOptions.Size = DefaultWindowSize; - windowOptions.Title = GetWindowName(); - windowOptions.API = GraphicsAPI.None; - windowOptions.ShouldSwapAutomatically = false; + DebuggerWindow = Window.Create(windowOptions); - DebuggerWindow = Window.Create(windowOptions); + DebuggerWindow.FramebufferResize += OnFramebufferResize; + DebuggerWindow.Render += OnRender; + DebuggerWindow.Update += OnUpdate; + DebuggerWindow.Load += OnLoad; + DebuggerWindow.Closing += OnClose; - DebuggerWindow.FramebufferResize += OnFramebufferResize; - DebuggerWindow.Render += OnRender; - DebuggerWindow.Update += OnUpdate; - DebuggerWindow.Load += OnLoad; - DebuggerWindow.Closing += OnClose; + Logger.Information("Creating window"); - Logger.Information("Creating window"); + new Thread(RunDebugger).Start(); + } - new Thread(RunDebugger).Start(); + public static unsafe void DxLog(Message message) { + if (message.PDescription is null) { + Logger.Error("Null DirectX error"); + + return; } - public static unsafe void DxLog(Message message) { - if (message.PDescription is null) { - Logger.Error("Null DirectX error"); + Logger.Error(SilkMarshal.PtrToString((nint) message.PDescription) ?? "Unknown DirectX error"); + } - return; - } + private void OnClose() { + DebugFieldWindow[] windows = fieldWindows.ToArray(); - Logger.Error(SilkMarshal.PtrToString((nint) message.PDescription) ?? "Unknown DirectX error"); + foreach (DebugFieldWindow window in windows) { + window.Close(); } - private void OnClose() { - DebugFieldWindow[] windows = fieldWindows.ToArray(); + IsClosing = true; + } - foreach (DebugFieldWindow window in windows) { - window.Close(); + private void OnLoad() { + Input = DebuggerWindow!.CreateInput(); + + Dxgi = DXGI.GetApi(DebuggerWindow); + D3d11 = D3D11.GetApi(DebuggerWindow); + Compiler = D3DCompiler.GetApi(); + + Shader.ShaderRootPath = GetResourceRootPath("Shaders"); + Texture.TextureRootPath = GetResourceRootPath("Textures"); + + unsafe { + ComPtr device = default; + ComPtr deviceContext = default; + + SilkMarshal.ThrowHResult(D3d11.CreateDevice( + pAdapter: default(ComPtr), + DriverType: D3DDriverType.Hardware, + Software: 0, + Flags: (uint) CreateDeviceFlag.Debug, + pFeatureLevels: null, + FeatureLevels: 0, + SDKVersion: D3D11.SdkVersion, + ppDevice: ref device, + pFeatureLevel: null, + ppImmediateContext: ref deviceContext)); + + DxDevice = device; + DxDeviceContext = deviceContext; + + if (OperatingSystem.IsWindows()) { + // DirectX debug logging not supported for DXVK + DxDevice.SetInfoQueueCallback(DxLog); } - IsClosing = true; + var swapChainDescription = new SwapChainDesc1 { + BufferCount = 2, // double buffered + Format = Format.FormatB8G8R8A8Unorm, // 32 bit RGBA format + BufferUsage = DXGI.UsageRenderTargetOutput, + SwapEffect = SwapEffect.FlipDiscard, // don't keep old output from previous frames + SampleDesc = new SampleDesc( + count: 1, // 1 buffer sample per pixel (AA needs more) + quality: 0), // no antialiasing + }; + + // Factory1 adds DXGI 1.1 support & Factory2 adds DXGI 1.2 support + DxFactory = Dxgi.CreateDXGIFactory(); + + IDXGISwapChain1* swapChain = null; + + SilkMarshal.ThrowHResult(DxFactory.CreateSwapChainForHwnd( + pDevice: (IUnknown*) (ID3D11Device*) DxDevice, + hWnd: DebuggerWindow!.Native!.DXHandle!.Value, + pDesc: &swapChainDescription, + pFullscreenDesc: (SwapChainFullscreenDesc*) null, + pRestrictToOutput: (IDXGIOutput*) null, + ppSwapChain: &swapChain)); + + DxSwapChain = swapChain; } - private void OnLoad() { - Input = DebuggerWindow!.CreateInput(); - - Dxgi = DXGI.GetApi(DebuggerWindow, ForceDXVK); - D3d11 = D3D11.GetApi(DebuggerWindow, ForceDXVK); - Compiler = D3DCompiler.GetApi(); - - Shader.ShaderRootPath = GetResourceRootPath("Shaders"); - Texture.TextureRootPath = GetResourceRootPath("Textures"); - - unsafe { - ComPtr device = default; - ComPtr deviceContext = default; - - SilkMarshal.ThrowHResult(D3d11.CreateDevice( - pAdapter: default(ComPtr), - DriverType: D3DDriverType.Hardware, - Software: default, - Flags: (uint) CreateDeviceFlag.Debug, - pFeatureLevels: null, - FeatureLevels: 0, - SDKVersion: D3D11.SdkVersion, - ppDevice: ref device, - pFeatureLevel: null, - ppImmediateContext: ref deviceContext)); - - DxDevice = device; - DxDeviceContext = deviceContext; - - if (OperatingSystem.IsWindows()) { - // DirectX debug logging not supported for DXVK - DxDevice.SetInfoQueueCallback(DxLog); - } + if (VertexShader is null) { + VertexShader = new VertexShader(this); + } - var swapChainDescription = new SwapChainDesc1 { - BufferCount = 2, // double buffered - Format = Format.FormatB8G8R8A8Unorm, // 32 bit RGBA format - BufferUsage = DXGI.UsageRenderTargetOutput, - SwapEffect = SwapEffect.FlipDiscard, // don't keep old output from previous frames - SampleDesc = new SampleDesc( - count: 1, // 1 buffer sample per pixel (AA needs more) - quality: 0), // no antialiasing - }; - - // Factory1 adds DXGI 1.1 support & Factory2 adds DXGI 1.2 support - DxFactory = Dxgi.CreateDXGIFactory(); - - IDXGISwapChain1* swapChain = default; - - SilkMarshal.ThrowHResult(DxFactory.CreateSwapChainForHwnd( - pDevice: (IUnknown*) (ID3D11Device*) DxDevice, - hWnd: DebuggerWindow!.Native!.DXHandle!.Value, - pDesc: &swapChainDescription, - pFullscreenDesc: (SwapChainFullscreenDesc*) null, - pRestrictToOutput: (IDXGIOutput*) null, - ppSwapChain: &swapChain)); - - DxSwapChain = swapChain; - } + if (PixelShader is null) { + PixelShader = new PixelShader(this); + } - if (VertexShader is null) { - VertexShader = new VertexShader(this); - } + VertexShader.Load("screenVertex.hlsl", "vs_main"); + PixelShader.Load("screenPixel.hlsl", "ps_main"); - if (PixelShader is null) { - PixelShader = new PixelShader(this); - } + CoreModels = new CoreModels(this); - VertexShader.Load("screenVertex.hlsl", "vs_main"); - PixelShader.Load("screenPixel.hlsl", "ps_main"); + Logger.Information("Graphics context initialized"); - CoreModels = new CoreModels(this); + SampleTexture = new Texture(this); + SampleTexture.Load("sample_derp_wave.png"); - Logger.Information("Graphics context initialized"); + ImGuiController = new ImGuiController(this, Input, ImGuiWindowType.Main); - SampleTexture = new Texture(this); - SampleTexture.Load("sample_derp_wave.png"); + ImGuiController.Initialize(DebuggerWindow); + } - ImGuiController = new ImGuiController(this, Input, ImGuiWindowType.Main); + private string GetResourceRootPath(string rootPath) { + resourceRootPath = Environment.CurrentDirectory; - ImGuiController.Initialize(DebuggerWindow); + if (File.Exists("root_path.txt")) { + resourceRootPath = Path.Combine(resourceRootPath, File.ReadLines("root_path.txt").First()); } - private string GetResourceRootPath(string rootPath) { - resourceRootPath = Environment.CurrentDirectory; + return Path.GetFullPath(Path.Combine(resourceRootPath, rootPath)); + } - if (File.Exists("root_path.txt")) { - resourceRootPath = Path.Combine(resourceRootPath, File.ReadLines("root_path.txt").First()); + public void CleanUp() { + unsafe { + if (DxDevice.Handle is not null) { + VertexShader?.CleanUp(); + PixelShader?.CleanUp(); + DxDevice.Dispose(); + DxDeviceContext.Dispose(); + DxSwapChain.Dispose(); + Input?.Dispose(); + + VertexShader = null; + PixelShader = null; + DxDevice = default; + DxDeviceContext = default; + DxSwapChain = default; + Input = null; + + Logger.Information("Graphics context cleaning up"); } - - return Path.GetFullPath(Path.Combine(resourceRootPath, rootPath)); } - public void CleanUp() { - unsafe { - if (DxDevice.Handle is not null) { - VertexShader?.CleanUp(); - PixelShader?.CleanUp(); - DxDevice.Dispose(); - DxDeviceContext.Dispose(); - DxSwapChain.Dispose(); - Input?.Dispose(); - - VertexShader = default; - PixelShader = default; - DxDevice = default; - DxDeviceContext = default; - DxSwapChain = default; - Input = default; - - Logger.Information("Graphics context cleaning up"); - } - } + if (DebuggerWindow is not null) { + DebuggerWindow.Dispose(); - if (DebuggerWindow is not null) { - DebuggerWindow.Dispose(); + DebuggerWindow = null; - DebuggerWindow = default; + Logger.Information("Window cleaning up"); + } + } - Logger.Information("Window cleaning up"); - } + private static int CompareRenderers(DebugFieldRenderer item1, DebugFieldRenderer item2) { + if (item1.Field.MapId < item2.Field.MapId) { + return -1; } - private static int CompareRenderers(DebugFieldRenderer item1, DebugFieldRenderer item2) { - if (item1.Field.MapId < item2.Field.MapId) { - return -1; - } + if (item1.Field.MapId > item2.Field.MapId) { + return 1; + } - if (item1.Field.MapId > item2.Field.MapId) { - return 1; - } + if (item1.Field.RoomId < item2.Field.RoomId) { + return -1; + } - if (item1.Field.RoomId < item2.Field.RoomId) { - return -1; - } + if (item1.Field.RoomId > item2.Field.RoomId) { + return 1; + } - if (item1.Field.RoomId > item2.Field.RoomId) { - return 1; - } + return 0; + } - return 0; - } + public IFieldRenderer FieldAdded(FieldManager field) { + Logger.Information("Field added {Name} [{Id}]", field.Metadata.Name, field.Metadata.Id); - public IFieldRenderer FieldAdded(FieldManager field) { - Logger.Information("Field added {Name} [{Id}]", field.Metadata.Name, field.Metadata.Id); + DebugFieldRenderer renderer = new DebugFieldRenderer(this, field); - DebugFieldRenderer renderer = new DebugFieldRenderer(this, field); + fieldRendererMutex.WaitOne(); + int index = fieldRenderers.AddSorted(renderer, Comparer.Create(CompareRenderers)); + fieldRendererMutex.ReleaseMutex(); - fieldRendererMutex.WaitOne(); - int index = fieldRenderers.AddSorted(renderer, Comparer.Create(CompareRenderers)); - fieldRendererMutex.ReleaseMutex(); + fields[field] = renderer; + + return renderer; + } - return renderer; + public void FieldRemoved(FieldManager field) { + if (!fields.TryGetValue(field, out DebugFieldRenderer? renderer)) { + return; } - public void FieldRemoved(FieldManager field) { - if (!Fields.TryGetValue(field, out DebugFieldRenderer? renderer)) { - return; - } + Logger.Information("Field removed {Name} [{Id}]", field.Metadata.Name, field.Metadata.Id); - Logger.Information("Field removed {Name} [{Id}]", field.Metadata.Name, field.Metadata.Id); + renderer.CleanUp(); - renderer.CleanUp(); + fieldRendererMutex.WaitOne(); + fieldRenderers.RemoveSorted(renderer, Comparer.Create(CompareRenderers)); + fieldRendererMutex.ReleaseMutex(); - fieldRendererMutex.WaitOne(); - fieldRenderers.RemoveSorted(renderer, Comparer.Create(CompareRenderers)); - fieldRendererMutex.ReleaseMutex(); - - Fields.Remove(field); - } + fields.Remove(field); + } - public DebugFieldWindow FieldWindowOpened() { - DebugFieldWindow window = new DebugFieldWindow(this); + public DebugFieldWindow FieldWindowOpened() { + DebugFieldWindow window = new DebugFieldWindow(this); - fieldWindows.Add(window); + fieldWindows.Add(window); - return window; - } + return window; + } - public void FieldWindowClosed(DebugFieldWindow window) { - fieldWindows.Remove(window); + public void FieldWindowClosed(DebugFieldWindow window) { + fieldWindows.Remove(window); - window.SetActiveRenderer(null); - } + window.SetActiveRenderer(null); + } - private unsafe void OnFramebufferResize(Vector2D newSize) { - // there is currently a bug with resizing where the framebuffer positioning doesn't take into account title bar size - SilkMarshal.ThrowHResult(DxSwapChain.ResizeBuffers(0, (uint) newSize.X, (uint) newSize.Y, Format.FormatB8G8R8A8Unorm, 0)); - } + private unsafe void OnFramebufferResize(Vector2D newSize) { + // there is currently a bug with resizing where the framebuffer positioning doesn't take into account title bar size + SilkMarshal.ThrowHResult(DxSwapChain.ResizeBuffers(0, (uint) newSize.X, (uint) newSize.Y, Format.FormatB8G8R8A8Unorm, 0)); + } - public bool HasFieldUpdated(FieldManager field) { + public bool HasFieldUpdated(FieldManager field) { + lock (updatedFieldsLock) { return updatedFields.Contains(field); } + } - public void FieldUpdated(FieldManager field) { - if (!updatedFields.Contains(field)) { - updatedFields.Add(field); - } + public void FieldUpdated(FieldManager field) { + lock (updatedFieldsLock) { + updatedFields.Add(field); } + } - private void OnUpdate(double delta) { + private void OnUpdate(double delta) { + lock (updatedFieldsLock) { updatedFields.Clear(); } + } - private void UpdateDeltaTracker() { - DateTime currentTime = DateTime.Now; - - int deltaMs = (int) ((currentTime.Ticks - lastTime.Ticks) / TimeSpan.TicksPerMillisecond); + private void UpdateDeltaTracker() { + DateTime currentTime = DateTime.Now; - int timeLeftToWait = int.Max(0, 15 - deltaMs - 1); + int deltaMs = (int) ((currentTime.Ticks - lastTime.Ticks) / TimeSpan.TicksPerMillisecond); - while (timeLeftToWait > 0) { - Thread.Sleep(1); + int timeLeftToWait = int.Max(0, 15 - deltaMs - 1); - currentTime = DateTime.Now; + while (timeLeftToWait > 0) { + Thread.Sleep(1); - deltaMs = (int) ((currentTime.Ticks - lastTime.Ticks) / TimeSpan.TicksPerMillisecond); - timeLeftToWait = int.Max(0, 16 - deltaMs - 1); - } + currentTime = DateTime.Now; - lastTime = currentTime; + deltaMs = (int) ((currentTime.Ticks - lastTime.Ticks) / TimeSpan.TicksPerMillisecond); + timeLeftToWait = int.Max(0, 16 - deltaMs - 1); + } - if (deltaTimes.Count < 50) { - deltaTimes.Add(deltaMs); - } else { - deltaTimes[deltaIndex] = deltaMs; - deltaIndex = (deltaIndex + 1) % 50; - } + lastTime = currentTime; - int total = 0; + if (deltaTimes.Count < 50) { + deltaTimes.Add(deltaMs); + } else { + deltaTimes[deltaIndex] = deltaMs; + deltaIndex = (deltaIndex + 1) % 50; + } - DeltaMin = deltaTimes.FirstOrDefault(); - DeltaMax = 0; + int total = 0; - foreach (int delta in deltaTimes) { - total += delta; + DeltaMin = deltaTimes.FirstOrDefault(); + DeltaMax = 0; - DeltaMin = int.Min(DeltaMin, delta); - DeltaMax = int.Max(DeltaMax, delta); - } + foreach (int delta in deltaTimes) { + total += delta; - DeltaAverage = total / deltaTimes.Count; + DeltaMin = int.Min(DeltaMin, delta); + DeltaMax = int.Max(DeltaMax, delta); } - private unsafe void OnRender(double delta) { - UpdateDeltaTracker(); + DeltaAverage = total / deltaTimes.Count; + } - DebuggerWindow!.MakeCurrent(); + private unsafe void OnRender(double delta) { + UpdateDeltaTracker(); - ComPtr framebuffer = DxSwapChain.GetBuffer(0); + DebuggerWindow!.MakeCurrent(); - ComPtr renderTargetView = default; - SilkMarshal.ThrowHResult(DxDevice.CreateRenderTargetView(framebuffer, null, ref renderTargetView)); + ComPtr framebuffer = DxSwapChain.GetBuffer(0); - DxDeviceContext.ClearRenderTargetView(renderTargetView, WindowClearColor); + ComPtr renderTargetView = default; + SilkMarshal.ThrowHResult(DxDevice.CreateRenderTargetView(framebuffer, null, ref renderTargetView)); - Viewport viewport = new Viewport(0, 0, DebuggerWindow!.FramebufferSize.X, DebuggerWindow!.FramebufferSize.Y, 0, 1); - DxDeviceContext.RSSetViewports(1, ref viewport); + DxDeviceContext.ClearRenderTargetView(renderTargetView, WindowClearColor); - DxDeviceContext.OMSetRenderTargets(1, ref renderTargetView.Handle, (ID3D11DepthStencilView*) null); + Viewport viewport = new Viewport(0, 0, DebuggerWindow!.FramebufferSize.X, DebuggerWindow!.FramebufferSize.Y, 0, 1); + DxDeviceContext.RSSetViewports(1, in viewport); - ImGuiController!.BeginFrame((float) delta); + DxDeviceContext.OMSetRenderTargets(1, in renderTargetView.Handle, (ID3D11DepthStencilView*) null); - #region Render code - // Begin region for render code + ImGuiController!.BeginFrame((float) delta); - // A vertex + pixel shader required to draw meshes - VertexShader!.Bind(); - PixelShader!.Bind(); - SampleTexture!.Bind(); // bind textures to active GPU texture samplers for access in shaders - CoreModels!.Quad.Draw(); // draw a full screen quad/rectangle + #region Render code + // Begin region for render code - // logic - //FieldListWindow(); - //RendererListWindow(); + // A vertex + pixel shader required to draw meshes + VertexShader!.Bind(); + PixelShader!.Bind(); + SampleTexture!.Bind(); // bind textures to active GPU texture samplers for access in shaders + CoreModels!.Quad.Draw(); // draw a full screen quad/rectangle - // End region for render code - #endregion + // logic + //FieldListWindow(); + //RendererListWindow(); - ImGuiController!.EndFrame(); + // End region for render code + #endregion - DxSwapChain.Present(1, 0); + ImGuiController!.EndFrame(); - renderTargetView.Dispose(); - framebuffer.Dispose(); - } + DxSwapChain.Present(1, 0); + + renderTargetView.Dispose(); + framebuffer.Dispose(); } } diff --git a/Maple2.Server.DebugGame/Graphics/ImGuiController.cs b/Maple2.Server.DebugGame/Graphics/ImGuiController.cs index b9615f163..e5c9751ac 100644 --- a/Maple2.Server.DebugGame/Graphics/ImGuiController.cs +++ b/Maple2.Server.DebugGame/Graphics/ImGuiController.cs @@ -434,7 +434,7 @@ public static ImGuiKey SilkKeyToImGui(Key key) { Key.F22 => ImGuiKey.F22, Key.F23 => ImGuiKey.F23, Key.F24 => ImGuiKey.F24, - _ => throw new NotImplementedException(), + _ => ImGuiKey.None, }; } } diff --git a/Maple2.Server.DebugGame/Graphics/Ui/Windows/FieldListWindow.cs b/Maple2.Server.DebugGame/Graphics/Ui/Windows/FieldListWindow.cs index e0806dc03..c81ba9c7a 100644 --- a/Maple2.Server.DebugGame/Graphics/Ui/Windows/FieldListWindow.cs +++ b/Maple2.Server.DebugGame/Graphics/Ui/Windows/FieldListWindow.cs @@ -4,10 +4,10 @@ namespace Maple2.Server.DebugGame.Graphics.Ui.Windows; public class FieldListWindow : IUiWindow { - public bool AllowMainWindow { get => true; } - public bool AllowFieldWindow { get => false; } + public bool AllowMainWindow => true; + public bool AllowFieldWindow => false; public bool Enabled { get; set; } = true; - public string TypeName { get => "Fields"; } + public string TypeName => "Fields"; public DebugGraphicsContext? Context { get; set; } public ImGuiController? ImGuiController { get; set; } public DebugFieldWindow? FieldWindow { get; set; } @@ -94,11 +94,11 @@ public void Render() { } ImGui.TableSetColumnIndex(0); - nextSelected |= ImGui.Selectable(string.Format("{0}##Active fields {1} 0", renderer.Field.MapId, index), selected); + nextSelected |= ImGui.Selectable($"{renderer.Field.MapId}##Active fields {index} 0", selected); ImGui.TableSetColumnIndex(1); - nextSelected |= ImGui.Selectable(string.Format("{0}##Active fields {1} 1", renderer.Field.Metadata.Name, index), selected); + nextSelected |= ImGui.Selectable($"{renderer.Field.Metadata.Name}##Active fields {index} 1", selected); ImGui.TableSetColumnIndex(2); - nextSelected |= ImGui.Selectable(string.Format("{0}##Active fields {1} 2", renderer.Field.RoomId, index), selected); + nextSelected |= ImGui.Selectable($"{renderer.Field.RoomId}##Active fields {index} 2", selected); if (selectFieldDisabled) { ImGui.EndDisabled(); @@ -106,6 +106,9 @@ public void Render() { if (nextSelected) { SelectedRenderer = renderer; + + // Auto-create field window if none exists for this field + AutoCreateFieldWindow(renderer); } ++index; @@ -116,5 +119,22 @@ public void Render() { ImGui.End(); } -} + private void AutoCreateFieldWindow(DebugFieldRenderer renderer) { + if (Context == null) return; + + lock (Context.FieldWindows) { + // Check if a field window already exists for this renderer + foreach (DebugFieldWindow existingWindow in Context.FieldWindows) { + if (existingWindow.ActiveRenderer == renderer) { + // Window already exists for this field, don't create another + return; + } + } + + // No window exists for this field, create one + DebugFieldWindow newWindow = Context.FieldWindowOpened(); + newWindow.SetActiveRenderer(renderer); + } + } +} diff --git a/Maple2.Server.DebugGame/Graphics/Ui/Windows/WindowListWindow.cs b/Maple2.Server.DebugGame/Graphics/Ui/Windows/WindowListWindow.cs index 0de83e998..95ae01d7a 100644 --- a/Maple2.Server.DebugGame/Graphics/Ui/Windows/WindowListWindow.cs +++ b/Maple2.Server.DebugGame/Graphics/Ui/Windows/WindowListWindow.cs @@ -38,9 +38,9 @@ public void Render() { return; } - ImGui.Text(string.Format("Average frame time: {0} ms; {1} FPS", Context!.DeltaAverage, 1000.0f / Context!.DeltaAverage)); - ImGui.Text(string.Format("Min frame time: {0} ms; {1} FPS", Context!.DeltaMin, 1000.0f / Context!.DeltaMin)); - ImGui.Text(string.Format("Max frame time: {0} ms; {1} FPS", Context!.DeltaMax, 1000.0f / Context!.DeltaMax)); + ImGui.Text($"Average frame time: {Context!.DeltaAverage} ms; {1000.0f / Context!.DeltaAverage} FPS"); + ImGui.Text($"Min frame time: {Context!.DeltaMin} ms; {1000.0f / Context!.DeltaMin} FPS"); + ImGui.Text($"Max frame time: {Context!.DeltaMax} ms; {1000.0f / Context!.DeltaMax} FPS"); bool newWindowDisabled = false; @@ -104,13 +104,13 @@ public void Render() { bool nextSelected = false; ImGui.TableSetColumnIndex(0); - nextSelected |= ImGui.Selectable(string.Format("{0}##Active windows {1} 0", window.WindowName, index), selected); + nextSelected |= ImGui.Selectable($"{window.WindowName}##Active windows {index} 0", selected); ImGui.TableSetColumnIndex(1); - nextSelected |= ImGui.Selectable(string.Format("{0}##Active windows {1} 1", window.ActiveRenderer?.Field.MapId.ToString() ?? "", index), selected); + nextSelected |= ImGui.Selectable($"{window.ActiveRenderer?.Field.MapId.ToString() ?? ""}##Active windows {index} 1", selected); ImGui.TableSetColumnIndex(2); - nextSelected |= ImGui.Selectable(string.Format("{0}##Active windows {1} 2", window.ActiveRenderer?.Field.Metadata.Name ?? "", index), selected); + nextSelected |= ImGui.Selectable($"{window.ActiveRenderer?.Field.Metadata.Name ?? ""}##Active windows {index} 2", selected); ImGui.TableSetColumnIndex(3); - nextSelected |= ImGui.Selectable(string.Format("{0}##Active windows {1} 3", window.ActiveRenderer?.Field.RoomId.ToString() ?? "", index), selected); + nextSelected |= ImGui.Selectable($"{window.ActiveRenderer?.Field.RoomId.ToString() ?? ""}##Active windows {index} 3", selected); if (nextSelected) { SelectedWindow = window; diff --git a/Maple2.Server.DebugGame/Program.cs b/Maple2.Server.DebugGame/Program.cs index 3b9bb910c..a4a8148aa 100644 --- a/Maple2.Server.DebugGame/Program.cs +++ b/Maple2.Server.DebugGame/Program.cs @@ -45,7 +45,7 @@ var worldClient = new WorldClient(channel); response = worldClient.AddChannel(new AddChannelRequest { GameIp = Target.GameIp.ToString(), - GrpcGameIp = Target.GrpcGameIp.ToString(), + GrpcGameIp = Target.GrpcGameIp, }); } catch (RpcException e) { @@ -108,6 +108,9 @@ autofac.RegisterType() .PropertiesAutowired() .SingleInstance(); + autofac.RegisterType() + .PropertiesAutowired() + .SingleInstance(); autofac.RegisterType() .SingleInstance(); autofac.RegisterType()