mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 21:01:52 +00:00
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
This commit is contained in:
@@ -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);
|
||||
|
||||
+12
@@ -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)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<T>(MonoMethod* method, MonoObject* instance, params void*[] args)
|
||||
@@ -329,25 +320,17 @@ class ScriptClass : SharpClass
|
||||
public T GetFieldValue<T>(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<T>(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<T>(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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<Entity> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T>(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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user