From 1254d01545b4645ea36cd1681d1336e060fb71a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Thu, 27 Jul 2023 21:58:42 +0200 Subject: [PATCH] Handle exceptions from C# Scripts + Log window + EditorLogger + Renamed Platform/DX11/ImGui.bf to .../Dx11ImGui.bf for clarity + ImGuiExtension: ImageButtonEx thata takes a TextureViewBinding + EditorLogger that logs to the LogWindow + Current logger can now be changed + Info, Trace, Warning and Error Icons --- .../Assets/Scripts/MyTestEntity.cs | 19 + .../content/Textures/EditorIcons.dds | Bin 147584 -> 213120 bytes .../content/Textures/EditorIcons.dds.ass | 4 +- .../content/Textures/EditorIcons.psd | Bin 35257 -> 53936 bytes GlitchyEditor/src/EditWindows/LogWindow.bf | 367 ++++++++++++++++++ GlitchyEditor/src/Editor.bf | 9 + GlitchyEditor/src/EditorApp.bf | 3 + GlitchyEditor/src/EditorIcons.bf | 8 + GlitchyEditor/src/EditorLayer.bf | 8 + GlitchyEditor/src/EditorLogger.bf | 128 ++++++ GlitchyEngine/src/ImGui/ImGuiExtension.bf | 16 +- .../DX11/ImGui/{ImGui.bf => Dx11ImGui.bf} | 12 + .../src/Scripting/MonoExceptionHelper.bf | 81 ++++ GlitchyEngine/src/Scripting/ScriptClass.bf | 71 ++-- GlitchyEngine/src/Scripting/ScriptEngine.bf | 33 +- GlitchyEngine/src/Scripting/ScriptInstance.bf | 35 +- GlitchyEngineHelper/src/Mono/Mono.bf | 15 +- 17 files changed, 750 insertions(+), 59 deletions(-) create mode 100644 GlitchyEditor/src/EditWindows/LogWindow.bf create mode 100644 GlitchyEditor/src/EditorLogger.bf rename GlitchyEngine/src/Platform/DX11/ImGui/{ImGui.bf => Dx11ImGui.bf} (72%) create mode 100644 GlitchyEngine/src/Scripting/MonoExceptionHelper.bf diff --git a/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs b/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs index 051544c..4432357 100644 --- a/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs +++ b/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs @@ -83,6 +83,22 @@ namespace Sandbox Log.Error("Camera not found."); } } + Log.Warning("Achtung."); + + try + { + SubVoid(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw e; + } + } + + void SubVoid() + { + throw new Exception("Ouha!", new IndexOutOfRangeException("Bist du jecke2?!", new AccessViolationException("Haleluja"))); } /// @@ -113,6 +129,9 @@ namespace Sandbox { force.Y += JumpForce; } + + if (Input.IsKeyPressing(Key.N)) + SubVoid(); _rigidBody.ApplyForceToCenter(force); diff --git a/GlitchyEditor/content/Textures/EditorIcons.dds b/GlitchyEditor/content/Textures/EditorIcons.dds index 036696a31881fc19075283619074fa77d9ec2783..1ebc060faed9f58feffa0c2324af49f84f0d420c 100644 GIT binary patch literal 213120 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 J1B>gx{{wp}!-W6< delta 2629 zcmai0Z)lZO7~eU&+pg)_g)#?jc*%*^yTxAFM8lP@Y$Mku_Zn| z4kI!*b}z>|g##~CO4HHJS31EQN(e1zbqn{w59-LIU`Ub`h9B(rynpU?uUmb2pYuNF z`FEb@_dMrB2MeQv%S!9&cKIVYIXOi>o}+{69G_)5IShn}=3wO)P03{%k ztrq&Y0g=0AY|G|6EH+xQSR|xxlm+5iiABNKFD^Xyn3yz|jmXq5e3cn$Zpe52)?_WB zp*E?Hv*ogKJzFQk$5_DB+B*w`J{Gnmd&in!zD^|ms55WBa;88SeXmKqn?bFcMWud> z)mR2ZW&BgeZ7-cVu00}RGjVAw=Z5g@E;lC*-t<7Z)ixXbatK@BDA@UQex`kFXhFxLQ6j!EF(%$`4w_}{j5TU&+;ucq{2a`yg!~IxUUoXwE*hfX6n~=dBJFa z#~lc6$V|>%Cy55>yT?r|HW2E}>vsVQ5{u2WLM)aI?E~02h~PZRA7OqbjIja{>e_iK z_7DTc*RsQu(_!Q^z@T0XZ#5Q8RZ&VJ&<=yX0XhWQv$!=CTgmRxF}Gu%aL@@l3cAmg zm+*d0CL_GfNt9RVkF!Q<%cmgI3t6hq8PL58b*8AHW$JqlpuflFRJq2&+n_8nghKe@$+|xWj*2;q@bzxBlsCsDB*IZ8n4*L zxAOKM8Z=A&qcu{iXBA4j$@UOh{!hubd!us)l!@8nYtZ-98mQwPyN)h!sE)J3_s`}< zqP(98%tb`EaqB=1CJVWRNf-PCt1Qz^^YS-<$8OWc<7#fi6zYLl(MlLkpLOBrKg8}A}iH#>u;!@PM0EUQ4)HxAdg*lQ7*{70@Whk29EF<_E}bsXJQKdhahN=dHb5Eb_7eDo?108Jj@S6IB&sC^|m8ihQoMO z|3|#Sy2sM@9pcv@f&bwZdYo*vd3x_HPr6jW@ZKZoC9L3-SA?=U?iy BH~s(s diff --git a/GlitchyEditor/content/Textures/EditorIcons.dds.ass b/GlitchyEditor/content/Textures/EditorIcons.dds.ass index 1f70168..94df200 100644 --- a/GlitchyEditor/content/Textures/EditorIcons.dds.ass +++ b/GlitchyEditor/content/Textures/EditorIcons.dds.ass @@ -9,8 +9,8 @@ AddressModeU = .Clamp, AddressModeV = .Clamp, AddressModeW = .Clamp, - MipMinLOD = -340282346638528859811704183484516925440, - MipMaxLOD = 340282346638528859811704183484516925440, + MipMinLOD = -3.40282347e+38, + MipMaxLOD = 3.40282347e+38, MaxAnisotropy = 1, BorderColor = { R = 1, diff --git a/GlitchyEditor/content/Textures/EditorIcons.psd b/GlitchyEditor/content/Textures/EditorIcons.psd index 8b53a3032d1eff1083c0b8759c31690e20443720..01d992861ffd439b59cec58e5f0d805d9a7de5fc 100644 GIT binary patch literal 53936 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 Au>b%7 delta 2391 zcmeH{T}YEr7{{O26@}HpaF&|XO+`Y?%&#@m*SSqCb4K=IP(}pRUAIAP4urapQcu~% z$Ug1E4>F}9GQ^gyEz=F5AR~Rkxs;ZCww2gM*DyJQ^1#|K+Hy`tCRDk(bDnV?-d$K4UkxEIC zue$L0|M>$h{r~*|7q%g?1Mw7(O<9etQln8!#mKCbEAt%Or$dcatyGFx9Mfa0awwBB zWboS}V?}6;Evj=U4PRSAbGi4i;!I0|X^h6{r{<MOO-fie8w$Wz4V%EqlsE=Xia@nVtaDqGU!qR((w%&0eWx9CcUr-z&N8svvmN~2wHFN490pVFvR)i8 zh}Rbip{km8gFW}Qg4Gut;KEbo;F(i9LG?0g^#RwnU8w-yT-gj(UCmeCH9Xv;ycfo6 zytw4rJTR`?3Xaq=hq_fT?U5Y}UM~dI8^t1cx&$iqCNI(0!^?6@rq!*7c1i|styRJe z^@*9jKkWB(FUI6fNAj^~huqXFPPZ(b?b~eMX3yKdJ8y*%pS(b%y}%VYlN0jvHqlq@ z6MdOc8`e5V=}{ww3Biup-n$BpIo``wk^MLyJSq87a*dZOqireTtTz=(ZBG&ElUOn7 zT?zM0g668xnJCUmHT1-cJh_jbQX37Tx{t>n_GN>*elwiZ0Zs}}zgfKP&xNOL@R>oE z?~z)AsF!MBkk_?rh _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 _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 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, .(14, 14), .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, .(16, 16))) + _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, .(16, 16))) + _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, .(16, 16))) + _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 = 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); + } +} \ No newline at end of file diff --git a/GlitchyEditor/src/Editor.bf b/GlitchyEditor/src/Editor.bf index 75224d8..b52aaa0 100644 --- a/GlitchyEditor/src/Editor.bf +++ b/GlitchyEditor/src/Editor.bf @@ -22,6 +22,7 @@ namespace GlitchyEditor private ContentBrowserWindow _contentBrowserWindow ~ delete _; private PropertiesWindow _propertiesWindow ~ delete _; private AssetViewer _assetViewer ~ delete _; + private LogWindow _logWindow ~ delete _; public Scene CurrentScene { @@ -45,6 +46,7 @@ namespace GlitchyEditor public ContentBrowserWindow ContentBrowserWindow => _contentBrowserWindow; public PropertiesWindow PropertiesWindow => _propertiesWindow; public AssetViewer AssetViewer => _assetViewer; + public LogWindow LogWindow => _logWindow; public EditorCamera* CurrentCamera { get; set; } @@ -69,6 +71,11 @@ namespace GlitchyEditor InitWindows(); } + public ~this() + { + s_Instance = null; + } + private void InitWindows() { _sceneViewportWindow = new EditorViewportWindow(this); @@ -78,6 +85,7 @@ namespace GlitchyEditor _contentBrowserWindow = new ContentBrowserWindow((.)Application.Get().ContentManager); _propertiesWindow = new PropertiesWindow(this); _assetViewer = new AssetViewer((.)Application.Get().ContentManager); + _logWindow = new LogWindow(); } public void Update() @@ -89,6 +97,7 @@ namespace GlitchyEditor _contentBrowserWindow.Show(); _propertiesWindow.Show(); _assetViewer.Show(); + _logWindow.Show(); } } } diff --git a/GlitchyEditor/src/EditorApp.bf b/GlitchyEditor/src/EditorApp.bf index a4ccec8..f9436b8 100644 --- a/GlitchyEditor/src/EditorApp.bf +++ b/GlitchyEditor/src/EditorApp.bf @@ -12,6 +12,9 @@ namespace GlitchyEditor public this(String[] args) { PushLayer(new EditorLayer(args, _contentManager)); + + Log.ClientLogger = new EditorLogger(); + Log.EngineLogger = new EditorLogger() { IsEngineLogger = true }; } protected override IContentManager InitContentManager() diff --git a/GlitchyEditor/src/EditorIcons.bf b/GlitchyEditor/src/EditorIcons.bf index c335d5e..796b94d 100644 --- a/GlitchyEditor/src/EditorIcons.bf +++ b/GlitchyEditor/src/EditorIcons.bf @@ -19,6 +19,10 @@ namespace GlitchyEditor public SubTexture2D Simulate ~ _.ReleaseRef(); public SubTexture2D Pause ~ _.ReleaseRef(); public SubTexture2D SingleStep ~ _.ReleaseRef(); + public SubTexture2D Error ~ _.ReleaseRef(); + public SubTexture2D Warning ~ _.ReleaseRef(); + public SubTexture2D Info ~ _.ReleaseRef(); + public SubTexture2D Trace ~ _.ReleaseRef(); public SamplerState SamplerState { @@ -41,6 +45,10 @@ namespace GlitchyEditor Simulate = GetNextGridTexture(ref pen, iconSize); Pause = GetNextGridTexture(ref pen, iconSize); SingleStep = GetNextGridTexture(ref pen, iconSize); + Error = GetNextGridTexture(ref pen, iconSize); + Warning = GetNextGridTexture(ref pen, iconSize); + Info = GetNextGridTexture(ref pen, iconSize); + Trace = GetNextGridTexture(ref pen, iconSize); } private SubTexture2D GetNextGridTexture(ref float2 pen, float2 iconSize) diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index a35da5e..b48497e 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -171,6 +171,11 @@ namespace GlitchyEditor ContentBrowserWindow.s_FolderTexture = _editorIcons.Folder; ContentBrowserWindow.s_FileTexture = _editorIcons.File; + + LogWindow.s_ErrorIcon = _editorIcons.Error; + LogWindow.s_WarningIcon = _editorIcons.Warning; + LogWindow.s_InfoIcon = _editorIcons.Info; + LogWindow.s_TraceIcon = _editorIcons.Trace; } private void InitEditor() @@ -807,6 +812,9 @@ namespace GlitchyEditor if(ImGui.MenuItem(AssetViewer.s_WindowTitle)) _editor.AssetViewer.Open = true; + + if(ImGui.MenuItem(LogWindow.s_WindowTitle)) + _editor.LogWindow.Open = true; ImGui.EndMenu(); } diff --git a/GlitchyEditor/src/EditorLogger.bf b/GlitchyEditor/src/EditorLogger.bf new file mode 100644 index 0000000..cca6085 --- /dev/null +++ b/GlitchyEditor/src/EditorLogger.bf @@ -0,0 +1,128 @@ +using GlitchLog; +using System; +using System.Diagnostics; +using GlitchyEngine.Scripting; + +namespace GlitchyEditor; + +public class EditorLogger : Logger +{ + // {l} = log level (first parameter) + // {t} = current date time (second parameter) + // {n} = logger name (third parameter) + // {m} = message + + private String _name; + + public override String Name + { + get => _name; + set => _name = value; + } + + public bool IsEngineLogger {get; set;} + + public this() + { + //Debug.Assert(Debug.IsDebuggerPresent, "The DebugLogger requires a debugger to be present."); + } + +#if GL_NOLOG || GL_LOG_NOTRACE + [SkipCall] +#endif + [Inline] + public override void Trace(StringView format, params Object[] args) + { + InternalLog(.Trace, format, params args); + } + +#if GL_NOLOG || GL_LOG_NOINFO + [SkipCall] +#endif + [Inline] + public override void Info(StringView format, params Object[] args) + { + InternalLog(.Info, format, params args); + } + +#if GL_NOLOG || GL_LOG_NOWARNING + [SkipCall] +#endif + [Inline] + public override void Warning(StringView format, params Object[] args) + { + InternalLog(.Warning, format, params args); + } + +#if GL_NOLOG || GL_LOG_NOERROR + [SkipCall] +#endif + [Inline] + public override void Error(StringView format, params Object[] args) + { + InternalLog(.Error, format, params args); + } + +#if GL_NOLOG || GL_LOG_NOCRITICAL + [SkipCall] +#endif + [Inline] + public override void Critical(StringView format, params Object[] args) + { + InternalLog(.Critical, format, params args); + } + +#if GL_NOLOG + [SkipCall] +#endif + [Inline] + public override void Log(LogLevel level, StringView format, params Object[] args) + { + InternalLog(level, format, params args); + } + + public override void Assert(bool condition, String error = Compiler.CallerExpression[0], String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum) + { + if (!condition) + { + String failStr = scope .()..AppendF("Assert failed: {} at line {} in {}", error, line, filePath); + InternalLog(.Critical, failStr); + Internal.FatalError(failStr, 1); + } + } + + public override void AssertDebug(bool condition, String error = Compiler.CallerExpression[0], String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum) + { + if (!condition) + { + String failStr = scope .()..AppendF("Assert failed: {} at line {} in {}", error, line, filePath); + InternalLog(.Critical, failStr); + Internal.FatalError(failStr, 1); + } + } + + private void InternalLog(LogLevel level, StringView format, params Object[] args) + { + if(_logLevel > level) + return; + + DateTime timestamp = DateTime.Now; + + String message = scope String(4096); + message.AppendF(format, params args); + + Debug.Write($"[{timestamp:HH:mm:ss.fff}] ({_name})|{level.UpperString}: {message}"); + + if (Editor.Instance == null) + return; + + if (args.Count > 0 && (var ex = args[^1] as MonoExceptionHelper)) + { + Editor.Instance.LogWindow.LogException(timestamp, ex); + } + else + { + Editor.Instance.LogWindow.Log(timestamp, level, message, new .() {IsEngineMessage = IsEngineLogger}); + } + } +} diff --git a/GlitchyEngine/src/ImGui/ImGuiExtension.bf b/GlitchyEngine/src/ImGui/ImGuiExtension.bf index 890f627..758119b 100644 --- a/GlitchyEngine/src/ImGui/ImGuiExtension.bf +++ b/GlitchyEngine/src/ImGui/ImGuiExtension.bf @@ -96,9 +96,21 @@ namespace ImGui return ImageButton(subTexture.Texture.GetViewBinding(), size, (.)subTexture.TexCoords.XY, (.)v, frame_padding, bg_col, tint_col); } - + public static extern bool ImageButton(TextureViewBinding textureViewBinding, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, int32 frame_padding = -1, Vec4 bg_col = Vec4.Zero, Vec4 tint_col = Vec4.Ones); - + + public static bool ImageButtonEx(uint32 id, SubTexture2D subTexture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec2 frame_padding = .Zero, Vec4 bg_col = Vec4.Zero, Vec4 tint_col = Vec4.Ones) + { + if (uv0 != .Zero || uv1 != .Ones) + Runtime.NotImplemented(); + + float2 v = (.)subTexture.TexCoords.XY + subTexture.TexCoords.ZW; + + return ImageButtonEx(id, subTexture.Texture.GetViewBinding(), size, (.)subTexture.TexCoords.XY, (.)v, frame_padding, bg_col, tint_col); + } + + public static extern bool ImageButtonEx(uint32 id, TextureViewBinding textureViewBinding, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec2 frame_padding = .Zero, Vec4 bg_col = Vec4.Zero, Vec4 tint_col = Vec4.Ones); + public static void TextUnformatted(StringView text) => TextUnformattedImpl(text.Ptr, text.Ptr + text.Length); public static void PushID(StringView id) => PushID(id.Ptr, id.Ptr + id.Length); diff --git a/GlitchyEngine/src/Platform/DX11/ImGui/ImGui.bf b/GlitchyEngine/src/Platform/DX11/ImGui/Dx11ImGui.bf similarity index 72% rename from GlitchyEngine/src/Platform/DX11/ImGui/ImGui.bf rename to GlitchyEngine/src/Platform/DX11/ImGui/Dx11ImGui.bf index b886d84..9e561b6 100644 --- a/GlitchyEngine/src/Platform/DX11/ImGui/ImGui.bf +++ b/GlitchyEngine/src/Platform/DX11/ImGui/Dx11ImGui.bf @@ -34,6 +34,18 @@ namespace ImGui return pressed; } + public static override bool ImageButtonEx(uint32 id, TextureViewBinding textureViewBinding, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec2 frame_padding = .Zero, Vec4 bg_col = Vec4.Zero, Vec4 tint_col = Vec4.Ones) + { + var view = textureViewBinding._nativeShaderResourceView..AddRef(); + _resourceViews.Add(view); + + bool pressed = ImGui.ImageButtonEx(id, view, size, uv0, uv1, frame_padding, bg_col, tint_col); + + textureViewBinding.Release(); + + return pressed; + } + protected internal static override void CleanupFrame() { for(var view in _resourceViews) diff --git a/GlitchyEngine/src/Scripting/MonoExceptionHelper.bf b/GlitchyEngine/src/Scripting/MonoExceptionHelper.bf new file mode 100644 index 0000000..1bcb271 --- /dev/null +++ b/GlitchyEngine/src/Scripting/MonoExceptionHelper.bf @@ -0,0 +1,81 @@ +using System; +using GlitchyEngine.Core; +using Mono; + +namespace GlitchyEngine.Scripting; + + +public class MonoExceptionHelper : RefCounter +{ + private String _fullName ~ delete _; + + private String _message ~ delete _; + + private String _stackTrace ~ delete _; + /// The clean stack trace only contains the Managed Stack (the full trace contains one line for the native-to-managed entry) + private StringView _cleanStackTrace; + + private MonoExceptionHelper _innerException ~ _?.ReleaseRef(); + + public StringView FullName => _fullName; + public StringView Message => _message; + + public StringView StackTrace => _stackTrace; + public StringView CleanStackTrace => _cleanStackTrace; + + public MonoExceptionHelper InnerException => _innerException; + + public UUID Instance { get; set; } + + public this(MonoException* exception) + { + MonoObject* exObject = (MonoObject*)exception; + + MonoClass* monoClass = Mono.mono_object_get_class(exObject); + + StringView classNamespace = .(Mono.mono_class_get_namespace(monoClass)); + StringView className = .(Mono.mono_class_get_name(monoClass)); + _fullName = new $"{classNamespace}.{className}"; + + GetMessage(exObject, monoClass); + + GetStackTrace(exception); + + GetInnerException(exObject, monoClass); + } + + private void GetMessage(MonoObject* exceptionObject, MonoClass* monoClass) + { + var messageProperty = Mono.mono_class_get_property_from_name(monoClass, "Message"); + + MonoObject* message = Mono.mono_property_get_value(messageProperty, exceptionObject, null, null); + char8* exMessage = Mono.mono_string_to_utf8((.)message); + + _message = new String(exMessage); + + Mono.mono_free(exMessage); + } + + private void GetStackTrace(MonoException* exception) + { + char8* stacktracePtr = Mono.mono_exception_get_managed_backtrace(exception); + _stackTrace = new String(stacktracePtr); + + int entryIndex = _stackTrace.IndexOf("at (wrapper native-to-managed)"); + + if (entryIndex != -1) + _cleanStackTrace = _stackTrace.Substring(0, entryIndex); + else + _cleanStackTrace = _stackTrace; + } + + private void GetInnerException(MonoObject* exceptionObject, MonoClass* monoClass) + { + MonoProperty* innerExceptionProperty = Mono.mono_class_get_property_from_name(monoClass, "InnerException"); + + MonoObject* innerException = Mono.mono_property_get_value(innerExceptionProperty, exceptionObject, null, null); + + if (innerException != null) + _innerException = new MonoExceptionHelper((MonoException*)innerException); + } +} diff --git a/GlitchyEngine/src/Scripting/ScriptClass.bf b/GlitchyEngine/src/Scripting/ScriptClass.bf index e3998cc..53908de 100644 --- a/GlitchyEngine/src/Scripting/ScriptClass.bf +++ b/GlitchyEngine/src/Scripting/ScriptClass.bf @@ -227,59 +227,40 @@ class ScriptClass : SharpClass _onDestroy = (OnDestroyMethod)GetMethodThunk("OnDestroy"); } - public void OnCreate(MonoObject* instance) + public void OnCreate(MonoObject* instance, out MonoException* exception) { - MonoException* exception = null; + exception = null; + if (_onCreate != null) _onCreate(instance, &exception); } - public void OnUpdate(MonoObject* instance, float deltaTime) + public void OnUpdate(MonoObject* instance, float deltaTime, out MonoException* exception) { - MonoException* exception = null; + exception = null; + if (_onUpdate != null) _onUpdate(instance, deltaTime, &exception); - - if (exception != null) - { - char8* str = Mono.mono_string_to_utf8(exception.Message); - - Log.EngineLogger.Error($"Exception in \"{_fullName}.OnUpdate\". Message:\"{StringView(str)}\""); - - Mono.mono_free(str); - } } - public void OnDestroy(MonoObject* instance) + public void OnDestroy(MonoObject* instance, out MonoException* exception) { - MonoException* exception; + exception = null; + if (_onDestroy != null) _onDestroy(instance, &exception); } - public MonoObject* CreateInstance(UUID uuid) + public MonoObject* CreateInstance(UUID uuid, out MonoException* exception) { MonoObject* instance = Mono.mono_object_new(ScriptEngine.[Friend]s_AppDomain, _monoClass); - // TODO: I think this is a bit dirty // Invoke empty constructor to fill fields Mono.mono_runtime_object_init(instance); // Invoke constructor with UUID #unwarn - ScriptEngine.[Friend]s_EngineObject.Invoke(ScriptEngine.[Friend]s_EngineObject._constructor, instance, &uuid); - - //MonoException* exception = null; -//#unwarn - //ScriptEngine.[Friend]s_EntityRoot._constructor(instance, uuid, &exception); - //ScriptEngine.[Friend]s_EntityRoot.Invoke(_constructor, instance, &uuid); - - /*MonoObject* exception = null; -#unwarn*/ - //Mono.mono_runtime_invoke(_constructor, instance, (.)&uuid, &exception); - //Mono.mono_runtime_object_init(instance); - //MonoException* exception; - //_constructor(instance, uuid, &exception); + ScriptEngine.[Friend]s_EngineObject.Invoke(ScriptEngine.[Friend]s_EngineObject._constructor, instance, out exception, &uuid); return instance; } @@ -312,12 +293,22 @@ class ScriptClass : SharpClass public MonoObject* Invoke(MonoMethod* method, MonoObject* instance, void** args = null) { MonoObject* exception = null; - return Mono.mono_runtime_invoke(method, instance, args, &exception); + + MonoObject* result = Mono.mono_runtime_invoke(method, instance, args, &exception); + + if (exception != null) + ScriptEngine.HandleMonoException((MonoException*)exception); + + return result; } - public MonoObject* Invoke(MonoMethod* method, MonoObject* instance, params void*[] args) + public MonoObject* Invoke(MonoMethod* method, MonoObject* instance, out MonoException* exception, params void*[] args) { - return Mono.mono_runtime_invoke(method, instance, args.Ptr, null); + exception = null; + + MonoObject* result = Mono.mono_runtime_invoke(method, instance, args.Ptr, (.)&exception); + + return result; } public T Invoke(MonoMethod* method, MonoObject* instance, params void*[] args) @@ -329,25 +320,17 @@ class ScriptClass : SharpClass public T GetFieldValue(MonoObject* instance, MonoClassField* field) { T value = default; - Mono.Mono.mono_field_get_value(instance, field, &value); + Mono.mono_field_get_value(instance, field, &value); return value; } public void SetFieldValue(MonoObject* instance, MonoClassField* field, in T value) { - /*if (typeof(T) == typeof(MonoObject*)) - { - // TODO: MonoObject* is a pointer already, so we don't take the pointer - Mono.Mono.mono_field_set_value(instance, field, (void*)value); - } - else - {*/ - Mono.Mono.mono_field_set_value(instance, field, &value); - //} + Mono.mono_field_set_value(instance, field, &value); } public void SetFieldValue(MonoObject* instance, MonoClassField* field, in T value) where T : struct* { - Mono.Mono.mono_field_set_value(instance, field, value); + Mono.mono_field_set_value(instance, field, value); } } diff --git a/GlitchyEngine/src/Scripting/ScriptEngine.bf b/GlitchyEngine/src/Scripting/ScriptEngine.bf index eb8c122..21a58ec 100644 --- a/GlitchyEngine/src/Scripting/ScriptEngine.bf +++ b/GlitchyEngine/src/Scripting/ScriptEngine.bf @@ -209,12 +209,14 @@ static class ScriptEngine if (scriptClass == null) return false; - script.Instance = new ScriptInstance(scriptClass); + UUID entityId = entity.UUID; + + script.Instance = new ScriptInstance(entityId, scriptClass); script.Instance..ReleaseRef(); - _entityScriptInstances[entity.UUID] = script.Instance..AddRef(); + _entityScriptInstances[entityId] = script.Instance..AddRef(); - script.Instance.Instantiate(entity.UUID); + script.Instance.Instantiate(entityId); return true; } @@ -544,4 +546,27 @@ static class ScriptEngine return scriptClass; } -} \ No newline at end of file + + internal static void HandleMonoException(MonoException* exception, ScriptInstance sourceInstance = null) + { + MonoExceptionHelper wrappedException = new MonoExceptionHelper(exception); + + String entityInfo = scope .(); + + if (sourceInstance != null) + { + wrappedException.Instance = sourceInstance.EntityId; + + Result sourceEntity = Context.GetEntityByID(sourceInstance.EntityId); + + if (sourceEntity case .Ok(let e)) + { + entityInfo.AppendF($" ({e.Name} | {sourceInstance.EntityId})"); + } + } + + Log.ClientLogger.Error($"Mono Exception \"{wrappedException.FullName}\": \"{wrappedException.Message}\"{entityInfo}\nStackTrace:\n{wrappedException.StackTrace}", wrappedException); + + wrappedException.ReleaseRef(); + } +} diff --git a/GlitchyEngine/src/Scripting/ScriptInstance.bf b/GlitchyEngine/src/Scripting/ScriptInstance.bf index 1511444..70aa425 100644 --- a/GlitchyEngine/src/Scripting/ScriptInstance.bf +++ b/GlitchyEngine/src/Scripting/ScriptInstance.bf @@ -4,6 +4,8 @@ using System; namespace GlitchyEngine.Scripting; +using internal GlitchyEngine.Scripting; + class ScriptInstance : RefCounter { private ScriptClass _scriptClass; @@ -11,6 +13,8 @@ class ScriptInstance : RefCounter private MonoObject* _instance; private uint32 _gcHandle; + private UUID _entityId; + public ScriptClass ScriptClass => _scriptClass; /// Gets whether or not the instance has ben initialized. @@ -23,9 +27,13 @@ class ScriptInstance : RefCounter internal MonoObject* MonoInstance => _instance; - public this(ScriptClass scriptClass) + public UUID EntityId => _entityId; + + public this(UUID entityId, ScriptClass scriptClass) { Log.EngineLogger.AssertDebug(scriptClass != null); + + _entityId = entityId; _scriptClass = scriptClass..AddRef(); } @@ -33,7 +41,7 @@ class ScriptInstance : RefCounter { if (_instance != null) { - _scriptClass.OnDestroy(_instance); + InvokeOnDestroy(); Mono.mono_gchandle_free(_gcHandle); } _scriptClass?.ReleaseRef(); @@ -41,24 +49,36 @@ class ScriptInstance : RefCounter public void Instantiate(UUID uuid) { - _instance = _scriptClass.CreateInstance(uuid); + _instance = _scriptClass.CreateInstance(uuid, let exception); _gcHandle = Mono.mono_gchandle_new(_instance, true); + + if (exception != null) + ScriptEngine.HandleMonoException(exception, this); } public void InvokeOnCreate() { - _scriptClass.OnCreate(_instance); + _scriptClass.OnCreate(_instance, let exception); _isCreated = true; + + if (exception != null) + ScriptEngine.HandleMonoException(exception, this); } public void InvokeOnUpdate(float deltaTime) { - _scriptClass.OnUpdate(_instance, deltaTime); + _scriptClass.OnUpdate(_instance, deltaTime, let exception); + + if (exception != null) + ScriptEngine.HandleMonoException(exception, this); } public void InvokeOnDestroy() { - _scriptClass.OnDestroy(_instance); + _scriptClass.OnDestroy(_instance, let exception); + + if (exception != null) + ScriptEngine.HandleMonoException(exception, this); } public T GetFieldValue(ScriptField field) @@ -83,6 +103,9 @@ class ScriptInstance : RefCounter Mono.mono_property_set_value(entityProperty, componentInstance, (void**)&_instance, &exception); + if (exception != null) + ScriptEngine.HandleMonoException((MonoException*)exception, this); + return componentInstance; } } \ No newline at end of file diff --git a/GlitchyEngineHelper/src/Mono/Mono.bf b/GlitchyEngineHelper/src/Mono/Mono.bf index 4e7d896..72780f7 100644 --- a/GlitchyEngineHelper/src/Mono/Mono.bf +++ b/GlitchyEngineHelper/src/Mono/Mono.bf @@ -232,12 +232,25 @@ static class Mono [LinkName(.C)] public static extern MonoThread* mono_thread_current(); - + +#region Property + [LinkName(.C)] public static extern MonoProperty* mono_class_get_property_from_name(MonoClass *klass, char8* name); [LinkName(.C)] public static extern void mono_property_set_value(MonoProperty *prop, void *obj, void **@params, MonoObject **exc); + + [LinkName(.C)] + public static extern MonoObject* mono_property_get_value(MonoProperty *prop, void *obj, void** @params, MonoObject** exc); + +#endregion + + [LinkName(.C)] + public static extern char8* mono_exception_get_managed_backtrace(MonoException* exc); + + [LinkName(.C)] + public static extern MonoClass* mono_object_get_class(MonoObject* obj); } struct MonoDomain;