mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
372 lines
8.9 KiB
Beef
372 lines
8.9 KiB
Beef
using System;
|
|
using ImGui;
|
|
using System.Collections;
|
|
using GlitchyEngine.Core;
|
|
using GlitchyEngine.World;
|
|
using GlitchLog;
|
|
using GlitchyEngine.Scripting;
|
|
using GlitchyEngine.Renderer;
|
|
|
|
namespace GlitchyEditor.EditWindows;
|
|
|
|
enum MessageType
|
|
{
|
|
case None = 0;
|
|
case Trace = 1;
|
|
case Info = 2;
|
|
case Warning = 4;
|
|
case Error = 8;
|
|
|
|
public this(LogLevel level)
|
|
{
|
|
switch (level)
|
|
{
|
|
case .Error:
|
|
this = Error;
|
|
case .Warning:
|
|
this = Warning;
|
|
case .Info:
|
|
this = Info;
|
|
case .Trace:
|
|
this = Trace;
|
|
default:
|
|
this = None;
|
|
}
|
|
}
|
|
}
|
|
|
|
class MessageSource
|
|
{
|
|
public UUID? Entity = null;
|
|
public StringView? ScriptName = null;
|
|
public int? Line = null;
|
|
|
|
/// If true, the message is only meant for engine developers... so only me :(
|
|
public bool IsEngineMessage = false;
|
|
|
|
public MonoExceptionHelper Exception = null ~ _?.ReleaseRef();
|
|
|
|
public String AdditionalData = null ~ delete _;
|
|
}
|
|
|
|
class LogMessage
|
|
{
|
|
private String _message ~ delete _;
|
|
|
|
public MessageType MessageType { get; private set; }
|
|
|
|
public DateTime Timestamp { get; private set; }
|
|
|
|
public MessageSource Source { get; private set; } ~ delete _;
|
|
|
|
public StringView Message => _message;
|
|
|
|
public this(DateTime timestamp, StringView message, MessageType logLevel, MessageSource ownSource)
|
|
{
|
|
Timestamp = timestamp;
|
|
_message = new String(message);
|
|
MessageType = logLevel;
|
|
Source = ownSource;
|
|
}
|
|
}
|
|
|
|
class LogWindow : EditorWindow
|
|
{
|
|
public const String s_WindowTitle = "Log";
|
|
|
|
private append List<LogMessage> _messages = .() ~ ClearAndDeleteItems!(_);
|
|
|
|
public static SubTexture2D s_ErrorIcon;
|
|
public static SubTexture2D s_WarningIcon;
|
|
public static SubTexture2D s_InfoIcon;
|
|
public static SubTexture2D s_TraceIcon;
|
|
|
|
private bool _showGameMessages = true;
|
|
private bool _showEngineMessages = false;
|
|
private bool _autoScroll = true;
|
|
private bool _collapseMessages = true;
|
|
|
|
private MessageType _visibleMessageTypes = .Error | .Warning | .Info | .Trace;
|
|
|
|
protected override void InternalShow()
|
|
{
|
|
defer { ImGui.End(); }
|
|
if(!ImGui.Begin(s_WindowTitle, &_open, .MenuBar))
|
|
return;
|
|
|
|
ShowMenuBar();
|
|
|
|
ShowMessages();
|
|
}
|
|
|
|
private void ShowMenuBar()
|
|
{
|
|
if(ImGui.BeginMenuBar())
|
|
{
|
|
if (ImGui.MenuItem("Clear"))
|
|
{
|
|
ClearLog();
|
|
}
|
|
|
|
if (ImGui.BeginMenu("Filter"))
|
|
{
|
|
ImGui.Checkbox("Show game messages", &_showGameMessages);
|
|
ImGui.AttachTooltip("If checked, the log will show messages generated by the game (e.g. scripts).");
|
|
|
|
ImGui.Checkbox("Show engine messages", &_showEngineMessages);
|
|
ImGui.AttachTooltip("""
|
|
If checked, the log will show messages generated by the engine.
|
|
These messages are usually only necessary for engine debugging/development and don't provide practical information for game developers.
|
|
""");
|
|
|
|
ImGui.EndMenu();
|
|
}
|
|
|
|
ImGui.Checkbox("Collapse", &_collapseMessages);
|
|
ImGui.AttachTooltip("If checked, identical messages will be collapsed into one.");
|
|
|
|
var maxSpace = ImGui.GetFontSize();
|
|
|
|
ImGui.Vec2 buttonSize = .(maxSpace, maxSpace);
|
|
|
|
var col = ImGui.GetStyleColorVec4(.Button);
|
|
|
|
if (_visibleMessageTypes.HasFlag(.Trace))
|
|
ImGui.PushStyleColor(.Button, *col);
|
|
else
|
|
ImGui.PushStyleColor(.Button, .(0, 0, 0, 0));
|
|
|
|
if (ImGui.ImageButtonEx(1, s_TraceIcon, buttonSize, .Zero, .Ones, .(2, 2)))
|
|
_visibleMessageTypes ^= .Trace;
|
|
|
|
ImGui.PopStyleColor();
|
|
|
|
if (_visibleMessageTypes.HasFlag(.Info))
|
|
ImGui.PushStyleColor(.Button, *col);
|
|
else
|
|
ImGui.PushStyleColor(.Button, .(0, 0, 0, 0));
|
|
|
|
if (ImGui.ImageButtonEx(2, s_InfoIcon, buttonSize, .Zero, .Ones, .(2, 2)))
|
|
_visibleMessageTypes ^= .Info;
|
|
|
|
ImGui.PopStyleColor();
|
|
|
|
if (_visibleMessageTypes.HasFlag(.Warning))
|
|
ImGui.PushStyleColor(.Button, *col);
|
|
else
|
|
ImGui.PushStyleColor(.Button, .(0, 0, 0, 0));
|
|
|
|
if (ImGui.ImageButtonEx(3, s_WarningIcon, buttonSize, .Zero, .Ones, .(2, 2)))
|
|
_visibleMessageTypes ^= .Warning;
|
|
|
|
ImGui.PopStyleColor();
|
|
|
|
if (_visibleMessageTypes.HasFlag(.Error))
|
|
ImGui.PushStyleColor(.Button, *col);
|
|
else
|
|
ImGui.PushStyleColor(.Button, .(0, 0, 0, 0));
|
|
|
|
|
|
if (ImGui.ImageButtonEx(4, s_ErrorIcon, buttonSize, .Zero, .Ones, .(2, 2)))
|
|
_visibleMessageTypes ^= .Error;
|
|
|
|
ImGui.PopStyleColor();
|
|
|
|
ImGui.EndMenuBar();
|
|
}
|
|
}
|
|
|
|
private void ShowMessages()
|
|
{
|
|
if (ImGui.BeginTable("Messages", 3, .BordersInnerH | .SizingFixedFit))
|
|
{
|
|
ImGui.TableSetupColumn("", .WidthFixed);
|
|
ImGui.TableSetupColumn("", .WidthStretch);
|
|
ImGui.TableSetupColumn("", .WidthFixed);
|
|
|
|
//LogMessage lastMessage = null;
|
|
int count = 1;
|
|
|
|
// Message ID for ImGui
|
|
int imGuiMessageId = 0;
|
|
|
|
for (let message in _messages)
|
|
{
|
|
if (!_visibleMessageTypes.HasFlag(message.MessageType))
|
|
continue;
|
|
|
|
if ((message.Source.IsEngineMessage && !_showEngineMessages) || (!message.Source.IsEngineMessage && !_showGameMessages))
|
|
continue;
|
|
|
|
/*defer
|
|
{
|
|
lastMessage = message;
|
|
}*/
|
|
|
|
do
|
|
{
|
|
LogMessage lastMessage = (message != _messages.Back) ? _messages[@message.Index + 1] : null;
|
|
|
|
if (_collapseMessages && lastMessage != null)
|
|
{
|
|
if (message.MessageType != lastMessage.MessageType)
|
|
break;
|
|
|
|
if (message.Message != lastMessage.Message)
|
|
break;
|
|
|
|
if (message.Source.Entity != lastMessage.Source.Entity)
|
|
break;
|
|
|
|
// For exceptions the stack trace is basically the only relevant thing
|
|
if (message.Source.Exception?.StackTrace != lastMessage.Source.Exception?.StackTrace)
|
|
break;
|
|
|
|
// We collapse this message with the previous one:
|
|
// Increment counter and go to next message.
|
|
count++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Push current index as ID
|
|
ImGui.PushID((void*)++imGuiMessageId);
|
|
|
|
ImGui.TableNextRow();
|
|
ImGui.TableSetColumnIndex(0);
|
|
|
|
switch (message.MessageType)
|
|
{
|
|
case .Error:
|
|
ImGui.Image(s_ErrorIcon, ImGui.Vec2(32, 32));
|
|
ImGui.AttachTooltip("Error");
|
|
case .Warning:
|
|
ImGui.Image(s_WarningIcon, ImGui.Vec2(32, 32));
|
|
ImGui.AttachTooltip("Warning");
|
|
case .Info:
|
|
ImGui.Image(s_InfoIcon, ImGui.Vec2(32, 32));
|
|
ImGui.AttachTooltip("Info");
|
|
case .Trace:
|
|
ImGui.Image(s_TraceIcon, ImGui.Vec2(32, 32));
|
|
ImGui.AttachTooltip("Trace");
|
|
default:
|
|
ImGui.TextUnformatted("Unknown");
|
|
}
|
|
|
|
ImGui.TableNextColumn();
|
|
|
|
// Timestamp
|
|
ImGui.TextWrapped($"[{message.Timestamp:HH:mm:ss.fff}]");
|
|
|
|
if (message.Source.IsEngineMessage)
|
|
{
|
|
ImGui.SameLine();
|
|
ImGui.TextUnformatted("Engine");
|
|
}
|
|
|
|
// Show entity
|
|
if (message.Source?.Entity != null)
|
|
{
|
|
ImGui.SameLine();
|
|
|
|
Result<Entity> entity = Editor.Instance.CurrentScene.GetEntityByID(message.Source.Entity.Value);
|
|
|
|
if (entity case .Ok(let e))
|
|
{
|
|
ImGui.Text($"Entity: \"{e.Name}\" (ID: {message.Source.Entity})");
|
|
|
|
if (ImGui.IsItemClicked())
|
|
Editor.Instance.EntityHierarchyWindow.HighlightEntity(entity);
|
|
}
|
|
else
|
|
{
|
|
ImGui.Text($"Entity: (ID: {message.Source.Entity})");
|
|
}
|
|
}
|
|
|
|
if (message.Source.Exception != null)
|
|
{
|
|
if (ImGui.CollapsingHeader(message.Message.Ptr))
|
|
{
|
|
// Show the native to managed entry point only if we show engine messages
|
|
|
|
if (_showEngineMessages)
|
|
ImGui.TextUnformatted(message.Source.Exception.StackTrace);
|
|
else
|
|
ImGui.TextUnformatted(message.Source.Exception.CleanStackTrace);
|
|
|
|
ImGui.NewLine();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui.TextUnformatted(message.Message);
|
|
}
|
|
|
|
// Dont show the counter if we only have one message.
|
|
if (count > 1)
|
|
{
|
|
// the message is not collapsible with the previous one.
|
|
// Print message count for last message and reset counter.
|
|
// This message will be printed normally.
|
|
|
|
ImGui.TableNextColumn();
|
|
ImGui.Text($"{count}");
|
|
}
|
|
|
|
count = 1;
|
|
|
|
ImGui.PopID();
|
|
}
|
|
|
|
if (_autoScroll)
|
|
{
|
|
if (ImGui.GetIO().MouseWheel > 0)
|
|
{
|
|
_autoScroll = false;
|
|
}
|
|
else
|
|
{
|
|
ImGui.SetScrollY(ImGui.GetScrollMaxY());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ImGui.GetScrollMaxY() == ImGui.GetScrollY())
|
|
{
|
|
_autoScroll = true;
|
|
}
|
|
}
|
|
|
|
ImGui.EndTable();
|
|
}
|
|
}
|
|
|
|
public void ClearLog()
|
|
{
|
|
ClearAndDeleteItems!(_messages);
|
|
}
|
|
|
|
public void Log(DateTime timestamp, LogLevel severity, StringView message, MessageSource source)
|
|
{
|
|
LogMessage logMessage = new LogMessage(timestamp, message, MessageType(severity), source);
|
|
_messages.Add(logMessage);
|
|
}
|
|
|
|
public void LogException(DateTime timestamp, MonoExceptionHelper exception)
|
|
{
|
|
StringView firstLine = exception.StackTrace;
|
|
|
|
int firstInIndex = exception.StackTrace.IndexOf("\n");
|
|
|
|
if (firstInIndex != -1)
|
|
firstLine = firstLine.Substring(0, firstInIndex);
|
|
|
|
String message = scope .(128);
|
|
message.AppendF($"Exception: \"{exception.FullName}\" | Message: \"{exception.Message}\" {firstLine}\0");
|
|
|
|
// TODO: are mono exceptions never engine only?
|
|
LogMessage logMessage = new LogMessage(timestamp, message, .Error, new MessageSource(){Entity = exception.Instance, Exception = exception..AddRef(), IsEngineMessage = false});
|
|
_messages.Add(logMessage);
|
|
}
|
|
} |