Better engine exception for scripting

This commit is contained in:
Simon Lübeß
2025-07-30 18:58:26 +02:00
parent 72e87ee9ad
commit ed644b5b41
6 changed files with 74 additions and 36 deletions
+3 -2
View File
@@ -259,7 +259,8 @@ class LogWindow : EditorWindow
ImGui.TableNextColumn(); ImGui.TableNextColumn();
if (ImGui.BeginChild("Message", .Zero, .None, .None)) ImGui.BeginGroup();
{ {
// Timestamp // Timestamp
ImGui.TextWrapped($"[{message.Timestamp:HH:mm:ss.fff}]"); ImGui.TextWrapped($"[{message.Timestamp:HH:mm:ss.fff}]");
@@ -310,7 +311,7 @@ class LogWindow : EditorWindow
} }
} }
ImGui.EndChild(); ImGui.EndGroup();
if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(.Left)) if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(.Left))
{ {
+1 -1
View File
@@ -513,7 +513,7 @@ static class ScriptEngine
} }
} }
Log.ClientLogger.Error($"Mono Exception \"{exception.FullName}\": \"{exception.Message}\"{entityInfo}\nStackTrace:\n{exception.StackTrace}", exception); Log.ClientLogger.Error($"Exception \"{exception.FullName}\": \"{exception.Message}\"{entityInfo}\nStackTrace:\n{exception.StackTrace}", exception);
} }
// TODO: Wrap like the other classes // TODO: Wrap like the other classes
@@ -31,8 +31,8 @@ public class ScriptException
public this(UUID entityId, StringView fullExceptionClassName, StringView message, StringView stackTrace) public this(UUID entityId, StringView fullExceptionClassName, StringView message, StringView stackTrace)
{ {
String allocFullExceptionClassName = append String(fullExceptionClassName); String allocFullExceptionClassName = append String(fullExceptionClassName);
String allocMessage = append String(fullExceptionClassName); String allocMessage = append String(message);
String allocStackTrace = append String(fullExceptionClassName); String allocStackTrace = append String(stackTrace);
_fullName = allocFullExceptionClassName; _fullName = allocFullExceptionClassName;
_message = allocMessage; _message = allocMessage;
+50 -9
View File
@@ -224,6 +224,8 @@ static class ScriptGlue
private function void SetEngineFunctions(EngineFunctions* engineFunctions); private function void SetEngineFunctions(EngineFunctions* engineFunctions);
private static SetEngineFunctions _setEngineFunctions; private static SetEngineFunctions _setEngineFunctions;
private static String _lastExceptionMessage = new .() ~ delete _;
public static void Init() public static void Init()
{ {
if (_setEngineFunctions == null) if (_setEngineFunctions == null)
@@ -389,16 +391,29 @@ static class ScriptGlue
public enum EngineResult : int32 public enum EngineResult : int32
{ {
Ok = 0, // Success, for boolean return value means True case Ok = 0; // Success, for boolean return value means True
False = 1, // Success, for boolean return value means False case False = 1; // Success, for boolean return value means False
Error = -1, case Error = -1;
NotImplemented = -2, case NotImplemented = -2;
ArgumentError = -3, case ArgumentError = -3;
// Entity Errors: // Entity Errors:
EntityNotFound = -4, // The entity doesn't exist or was deleted. case EntityNotFound = -4; // The entity doesn't exist or was deleted.
EntityDoesntHaveComponent = -5, // The entity has no component of type {typeof(T)} or it was deleted. case EntityDoesntHaveComponent = -5; // The entity has no component of type {typeof(T)} or it was deleted.
AssetNotFound = -6, // No asset exists for AssetHandle \"{assetId}\". case AssetNotFound = -6; // No asset exists for AssetHandle \"{assetId}\".
case CustomError = 1 << 31;
public static EngineResult CustomError(int32 payload)
{
return CustomError | payload;
}
}
[RegisterCall]
static char8* GetLastExceptionMessage()
{
return _lastExceptionMessage.Length == 0 ? null : _lastExceptionMessage.CStr();
} }
#region Log #region Log
@@ -1492,6 +1507,11 @@ static class ScriptGlue
asset asset
}*/ }*/
static void SetExceptionMessage(StringView message)
{
_lastExceptionMessage.Set(message);
}
static mixin GetAssetOrReturn<T>(UUID assetId) where T : Asset static mixin GetAssetOrReturn<T>(UUID assetId) where T : Asset
{ {
AssetHandle handle = .(assetId); AssetHandle handle = .(assetId);
@@ -1499,6 +1519,7 @@ static class ScriptGlue
if (asset == null) if (asset == null)
{ {
SetExceptionMessage(scope $"Asset (Id: {assetId}) not found.");
return EngineResult.AssetNotFound; return EngineResult.AssetNotFound;
} }
@@ -1511,6 +1532,7 @@ static class ScriptGlue
if (asset == null) if (asset == null)
{ {
SetExceptionMessage(scope $"Asset (Handle: {assetHandle}) not found.");
return EngineResult.AssetNotFound; return EngineResult.AssetNotFound;
} }
@@ -1552,7 +1574,26 @@ static class ScriptGlue
{ {
Material material = GetAssetOrReturn!<Material>(assetHandle); Material material = GetAssetOrReturn!<Material>(assetHandle);
material.[Friend]SetVariableRaw(StringView(variableName), elementType, rows, columns , arrayLength, Span<uint8>((uint8*)rawData, dataLength)); StringView variableNameView = StringView(variableName);
if (material.[Friend]SetVariableRaw(variableNameView, elementType, rows, columns , arrayLength, Span<uint8>((uint8*)rawData, dataLength)) case .Err(let error))
{
switch (error)
{
case .VariableNotFound:
SetExceptionMessage(scope $"Material \"{material.Identifier}\" has no variable \"{variableNameView}\".");
return .ArgumentError;
case .ElementTypeMismatch:
SetExceptionMessage(scope $"Variable \"{variableNameView}\" has incompatible element type.");
return .ArgumentError;
case .MatrixDimensionMismatch:
SetExceptionMessage(scope $"Variable \"{variableNameView}\" has incompatible matrix dimension type.");
return .ArgumentError;
case .ProvidedBufferTooShort:
SetExceptionMessage(scope $"The provided data buffer for variable \"{variableNameView}\" is not large enough.");
}
return .Error;
}
return .Ok; return .Ok;
} }
+13 -19
View File
@@ -17,24 +17,16 @@ internal enum EngineResult
EntityNotFound = -4, // The entity doesn't exist or was deleted. EntityNotFound = -4, // The entity doesn't exist or was deleted.
EntityDoesntHaveComponent = -5, // The entity has no component of type {typeof(T)} or it was deleted. EntityDoesntHaveComponent = -5, // The entity has no component of type {typeof(T)} or it was deleted.
AssetNotFound = -6, // No asset exists for AssetHandle \"{assetId}\". AssetNotFound = -6, // No asset exists for AssetHandle \"{assetId}\".
CustomError = 1 << 31
} }
public class EngineException(string message) : Exception(message); public class EngineException(string message) : Exception(message);
public class EntityNotFoundException : Exception public class EntityNotFoundException(string? message = null) : Exception(message);
{
} public class ComponentNotFoundException(string? message = null) : Exception(message);
public class ComponentNotFoundException : Exception public class AssetNotFoundException(string? message = null) : Exception(message);
{
}
public class AssetNotFoundException : Exception
{
}
internal static class EngineErrors internal static class EngineErrors
{ {
@@ -43,22 +35,24 @@ internal static class EngineErrors
if (result >= 0) if (result >= 0)
return; return;
string engineMessage = ScriptGlue.GetLastExceptionMessage();
switch (result) switch (result)
{ {
case EngineResult.Error: case EngineResult.Error:
throw new EngineException($"Unspecified engine excepiton in {callerName}"); throw new EngineException(engineMessage ?? $"Unspecified engine excepiton in {callerName}");
case EngineResult.NotImplemented: case EngineResult.NotImplemented:
throw new NotImplementedException($"Function {callerName} is not implemented in the engine."); throw new NotImplementedException(engineMessage ?? $"Function {callerName} is not implemented in the engine.");
case EngineResult.ArgumentError: case EngineResult.ArgumentError:
throw new ArgumentException($"An argument passed to {callerName} is invalid."); throw new ArgumentException(engineMessage ?? $"An argument passed to {callerName} is invalid.");
case EngineResult.EntityNotFound: case EngineResult.EntityNotFound:
throw new EntityNotFoundException(); throw new EntityNotFoundException(engineMessage);
case EngineResult.EntityDoesntHaveComponent: case EngineResult.EntityDoesntHaveComponent:
throw new ComponentNotFoundException(); throw new ComponentNotFoundException(engineMessage);
case EngineResult.AssetNotFound: case EngineResult.AssetNotFound:
throw new AssetNotFoundException(); throw new AssetNotFoundException(engineMessage);
default: default:
throw new EngineException($"Unknown engine excepiton in {callerName}"); throw new EngineException(engineMessage ?? $"Unknown engine excepiton in {callerName}");
} }
} }
} }
+5 -3
View File
@@ -319,7 +319,7 @@ internal static unsafe partial class ScriptGlue
struct ComponentFunctionPointers struct ComponentFunctionPointers
{ {
public delegate* unmanaged[Cdecl]<UUID, void> AddComponent; public delegate* unmanaged[Cdecl]<UUID, void> AddComponent;
public delegate* unmanaged[Cdecl]<UUID, bool> HasComponent; public delegate* unmanaged[Cdecl]<UUID, EngineResult> HasComponent;
public delegate* unmanaged[Cdecl]<UUID, void> RemoveComponent; public delegate* unmanaged[Cdecl]<UUID, void> RemoveComponent;
} }
@@ -328,7 +328,7 @@ internal static unsafe partial class ScriptGlue
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static void RegisterComponentType(byte* fullComponentTypeName, public static void RegisterComponentType(byte* fullComponentTypeName,
delegate* unmanaged[Cdecl]<UUID, void> addComponent, delegate* unmanaged[Cdecl]<UUID, void> addComponent,
delegate* unmanaged[Cdecl]<UUID, bool> hasComponent, delegate* unmanaged[Cdecl]<UUID, EngineResult> hasComponent,
delegate* unmanaged[Cdecl]<UUID, void> removeComponent) delegate* unmanaged[Cdecl]<UUID, void> removeComponent)
{ {
string? beefComponentTypeName = Marshal.PtrToStringUTF8((IntPtr)fullComponentTypeName); string? beefComponentTypeName = Marshal.PtrToStringUTF8((IntPtr)fullComponentTypeName);
@@ -421,7 +421,9 @@ internal static unsafe partial class ScriptGlue
public static bool Entity_HasComponent(UUID entityId, Type componentType) public static bool Entity_HasComponent(UUID entityId, Type componentType)
{ {
return ComponentTypeFunctions[componentType].HasComponent(entityId); EngineResult returnValue = ComponentTypeFunctions[componentType].HasComponent(entityId);
EngineErrors.ThrowIfError(returnValue);
return returnValue == EngineResult.Ok;
} }
public static void Entity_RemoveComponent(UUID entityId, Type componentType) public static void Entity_RemoveComponent(UUID entityId, Type componentType)