Remove mono from Glue functions

This commit is contained in:
Simon Lübeß
2025-07-17 20:07:19 +02:00
parent f77b153512
commit 271286fb7e
19 changed files with 546 additions and 742 deletions
@@ -21,9 +21,11 @@ namespace GlitchyEditor.EditWindows
public static this() public static this()
{ {
ScriptGlue.OnRegisterNativeCalls.Add(new () => { /*ScriptGlue.OnRegisterNativeCalls.Add(new () => {
ScriptGlue.RegisterCall<function bool(ref AssetHandle)>("ScriptGlue::ImGuiExtension_ShowAssetDropTarget", => ShowAssetDropTarget); // TODO: Implement
}); Runtime.NotImplemented();
//ScriptGlue.RegisterCall<function bool(ref AssetHandle)>("ScriptGlue::ImGuiExtension_ShowAssetDropTarget", => ShowAssetDropTarget);
});*/
} }
public static void ShowComponents(Entity entity, Type componentType = null) public static void ShowComponents(Entity entity, Type componentType = null)
+3 -3
View File
@@ -45,7 +45,7 @@ class MessageSource
/// If true, the message is only meant for engine developers... so only me :( /// If true, the message is only meant for engine developers... so only me :(
public bool IsEngineMessage = false; public bool IsEngineMessage = false;
public MonoExceptionHelper Exception = null ~ _?.ReleaseRef(); public ScriptException Exception = null ~ _?.ReleaseRef();
public String AdditionalData = null ~ delete _; public String AdditionalData = null ~ delete _;
} }
@@ -370,7 +370,7 @@ class LogWindow : EditorWindow
_messages.Add(logMessage); _messages.Add(logMessage);
} }
public void LogException(DateTime timestamp, MonoExceptionHelper exception) public void LogException(DateTime timestamp, ScriptException exception)
{ {
StringView firstLine = exception.StackTrace; StringView firstLine = exception.StackTrace;
@@ -383,7 +383,7 @@ class LogWindow : EditorWindow
message.AppendF($"Exception: \"{exception.FullName}\" | Message: \"{exception.Message}\" {firstLine}\0"); message.AppendF($"Exception: \"{exception.FullName}\" | Message: \"{exception.Message}\" {firstLine}\0");
// TODO: are mono exceptions never engine only? // TODO: are mono exceptions never engine only?
LogMessage logMessage = new LogMessage(timestamp, message, .Error, new MessageSource(){Entity = exception.Instance, Exception = exception..AddRef(), IsEngineMessage = false}); LogMessage logMessage = new LogMessage(timestamp, message, .Error, new MessageSource(){Entity = exception.EntityId, Exception = exception..AddRef(), IsEngineMessage = false});
_messages.Add(logMessage); _messages.Add(logMessage);
} }
} }
+2 -2
View File
@@ -110,12 +110,12 @@ public class EditorLogger : Logger
String message = scope String(4096); String message = scope String(4096);
MonoExceptionHelper exceptionHelper = null; ScriptException exceptionHelper = null;
MessageOrigin messageOrigin = null; MessageOrigin messageOrigin = null;
if (args.Count > 0) if (args.Count > 0)
{ {
exceptionHelper = args[^1] as MonoExceptionHelper; exceptionHelper = args[^1] as ScriptException;
messageOrigin = args[^1] as MessageOrigin; messageOrigin = args[^1] as MessageOrigin;
} }
+1 -1
View File
@@ -21,7 +21,7 @@ abstract class Asset : RefCounter
/// This identifier can be used to request the Asset from the content manager. /// This identifier can be used to request the Asset from the content manager.
public StringView Identifier public StringView Identifier
{ {
get => _identifier; get => _identifier..EnsureNullTerminator();
set => _identifier.Set(value); set => _identifier.Set(value);
} }
@@ -36,6 +36,12 @@ static class CoreClrHelper
private function void CreateScriptInstanceFunc(UUID entityId, char8* scriptClassName); private function void CreateScriptInstanceFunc(UUID entityId, char8* scriptClassName);
static CreateScriptInstanceFunc _createScriptInstance; static CreateScriptInstanceFunc _createScriptInstance;
private function void ThrowExceptionFunc(char8* message);
static ThrowExceptionFunc _throwException;
private function void RegisterComponentTypeFunc(StringView fullComponentTypeName, function void(UUID entityId) addComponent, function bool(UUID entityId) hasComponent, function void(UUID entityId) removeComponent);
static RegisterComponentTypeFunc _registerComponentType;
public static ScriptFunctionPointers _entityScriptFunctions; public static ScriptFunctionPointers _entityScriptFunctions;
public static void Init(StringView coreAssemblyPath) public static void Init(StringView coreAssemblyPath)
@@ -106,6 +112,9 @@ static class CoreClrHelper
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "CreateScriptInstance", out _createScriptInstance); GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "CreateScriptInstance", out _createScriptInstance);
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "ThrowException", out _throwException);
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "RegisterComponentType", out _registerComponentType);
InitEntityFunctions(); InitEntityFunctions();
} }
@@ -182,4 +191,14 @@ static class CoreClrHelper
return rc; return rc;
} }
public static void ThrowException(StringView message)
{
_throwException(message.Ptr);
}
public static void RegisterComponent(StringView fullComponentTypeName, function void(UUID entityId) addComponent, function bool(UUID entityId) hasComponent, function void(UUID entityId) removeComponent)
{
_registerComponentType(fullComponentTypeName, addComponent, hasComponent, removeComponent);
}
} }
@@ -1,21 +1,21 @@
using System; using System;
using GlitchyEngine.Core; using GlitchyEngine.Core;
using Mono;
namespace GlitchyEngine.Scripting; namespace GlitchyEngine.Scripting;
public class MonoExceptionHelper : RefCounter public class ScriptException : RefCounter
{ {
private String _fullName ~ delete _; private String _fullName ~ delete:append _;
private String _message ~ delete _; private String _message ~ delete:append _;
private String _stackTrace ~ delete _; private String _stackTrace ~ delete:append _;
/// The clean stack trace only contains the Managed Stack (the full trace contains one line for the native-to-managed entry) /// 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 StringView _cleanStackTrace;
private MonoExceptionHelper _innerException ~ _?.ReleaseRef(); //TODO
//private ScriptException _innerException ~ _?.ReleaseRef();
public StringView FullName => _fullName; public StringView FullName => _fullName;
public StringView Message => _message; public StringView Message => _message;
@@ -23,59 +23,22 @@ public class MonoExceptionHelper : RefCounter
public StringView StackTrace => _stackTrace; public StringView StackTrace => _stackTrace;
public StringView CleanStackTrace => _cleanStackTrace; public StringView CleanStackTrace => _cleanStackTrace;
public MonoExceptionHelper InnerException => _innerException; //public ScriptException InnerException => _innerException;
public UUID Instance { get; set; } public UUID EntityId { get; set; }
public this(MonoException* exception) [AllowAppend]
public this(UUID entityId, StringView fullExceptionClassName, StringView message, StringView stackTrace)
{ {
MonoObject* exObject = (MonoObject*)exception; String allocFullExceptionClassName = append String(fullExceptionClassName);
String allocMessage = append String(fullExceptionClassName);
String allocStackTrace = append String(fullExceptionClassName);
MonoClass* monoClass = Mono.mono_object_get_class(exObject); _fullName = allocFullExceptionClassName;
_message = allocMessage;
_stackTrace = allocStackTrace;
StringView classNamespace = .(Mono.mono_class_get_namespace(monoClass)); // TODO
StringView className = .(Mono.mono_class_get_name(monoClass)); _cleanStackTrace = _stackTrace;
_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);
} }
} }
+10 -8
View File
@@ -507,15 +507,18 @@ static class ScriptEngine
return scriptClass; return scriptClass;
} }
internal static void HandleMonoException(MonoException* exception, UUID entityId) internal static void HandleException()
{ {
MonoExceptionHelper wrappedException = new MonoExceptionHelper(exception);
}
internal static void LogScriptException(ScriptException exception, UUID entityId)
{
String entityInfo = scope .(); String entityInfo = scope .();
if (entityId != .Zero) if (entityId != .Zero)
{ {
wrappedException.Instance = entityId; exception.EntityId = entityId;
Result<Entity> sourceEntity = Context.GetEntityByID(entityId); Result<Entity> sourceEntity = Context.GetEntityByID(entityId);
@@ -525,14 +528,13 @@ static class ScriptEngine
} }
} }
Log.ClientLogger.Error($"Mono Exception \"{wrappedException.FullName}\": \"{wrappedException.Message}\"{entityInfo}\nStackTrace:\n{wrappedException.StackTrace}", wrappedException); Log.ClientLogger.Error($"Mono Exception \"{exception.FullName}\": \"{exception.Message}\"{entityInfo}\nStackTrace:\n{exception.StackTrace}", exception);
wrappedException.ReleaseRef();
} }
// TODO: This can go soon?
internal static void HandleMonoException(MonoException* exception, ScriptInstance sourceInstance = null) internal static void HandleMonoException(MonoException* exception, ScriptInstance sourceInstance = null)
{ {
HandleMonoException(exception, sourceInstance?.EntityId ?? .Zero); //LogScriptException(exception, sourceInstance?.EntityId ?? .Zero);
} }
public static void ShowScriptEditor(Entity entity, ScriptComponent* scriptComponent) public static void ShowScriptEditor(Entity entity, ScriptComponent* scriptComponent)
File diff suppressed because it is too large Load Diff
@@ -90,7 +90,7 @@ class SerializedObject
Fields.Add(nameCopy, (fieldType, data)); Fields.Add(nameCopy, (fieldType, data));
} }
public void AddField(StringView name, SerializationType primitiveType, MonoObject* value, MonoString* fullTypeName) public void AddField(StringView name, SerializationType primitiveType, void* value, StringView fullTypeName)
{ {
FieldData data = .(); FieldData data = .();
@@ -102,15 +102,10 @@ class SerializedObject
if (value != null) if (value != null)
{ {
MonoString* string = (.)value; String stringValue = new String((char8*)value);
char8* rawStringValue = Mono.mono_string_to_utf8(string);
String stringValue = new String(rawStringValue);
_ownedString.Add(stringValue); _ownedString.Add(stringValue);
Mono.mono_free(rawStringValue);
valueView = stringValue; valueView = stringValue;
} }
@@ -118,21 +113,14 @@ class SerializedObject
case .EngineObjectReference: case .EngineObjectReference:
String typeName = null; String typeName = null;
if (fullTypeName != null) if (!fullTypeName.IsEmpty)
{ {
char8* rawTypeName = Mono.mono_string_to_utf8(fullTypeName); _ownedString.Add(new String(fullTypeName));
typeName = new String(rawTypeName);
_ownedString.Add(typeName);
Mono.mono_free(rawTypeName);
} }
data.EngineObject = (FullTypeName: typeName, ID: *(UUID*)Mono.mono_object_unbox(value)); data.EngineObject = (FullTypeName: typeName, ID: *(UUID*)value);
default: default:
void* rawValue = Mono.mono_object_unbox(value); SetDataSimple(primitiveType, value, ref data);
SetDataSimple(primitiveType, rawValue, ref data);
} }
AddField(name, primitiveType, data); AddField(name, primitiveType, data);
+2 -2
View File
@@ -686,7 +686,7 @@ namespace GlitchyEngine.World
private append List<Entity> _destroyQueue = .(); private append List<Entity> _destroyQueue = .();
private append List<ScriptInstance> _destroyScriptQueue = .(); private append List<NewScriptInstance> _destroyScriptQueue = .();
float physicsDelta = 0; float physicsDelta = 0;
@@ -926,7 +926,7 @@ namespace GlitchyEngine.World
* @param scriptInstance The script instance to destroy. * @param scriptInstance The script instance to destroy.
* @param removeComponent If set to true the ScriptComponent will be removed from the entity. * @param removeComponent If set to true the ScriptComponent will be removed from the entity.
*/ */
public void DestroyScriptDeferred(ScriptInstance scriptInstance, bool removeComponent) public void DestroyScriptDeferred(NewScriptInstance scriptInstance, bool removeComponent)
{ {
_destroyScriptQueue.Add(scriptInstance..AddRef()); _destroyScriptQueue.Add(scriptInstance..AddRef());
+30 -12
View File
@@ -70,12 +70,17 @@ public class Entity : EngineObject
private void Create(string? name, Type[]? components) private void Create(string? name, Type[]? components)
{ {
ScriptGlue.Entity_Create(this, name, components, out _uuid); ScriptGlue.Entity_Create(name, out _uuid);
if (_uuid == UUID.Zero) if (_uuid == UUID.Zero)
{ {
throw new InvalidOperationException("Failed to create the Entity. Received UUID.Zero from the engine."); throw new InvalidOperationException("Failed to create the Entity. Received UUID.Zero from the engine.");
} }
if (components != null)
{
AddComponents(components);
}
} }
/// <summary> /// <summary>
@@ -98,7 +103,10 @@ public class Entity : EngineObject
/// </summary> /// </summary>
/// <param name="type">The type of the component.</param> /// <param name="type">The type of the component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasComponent(Type type) => ScriptGlue.Entity_HasComponent(_uuid, type); public bool HasComponent(Type type)
{
return ScriptGlue.Entity_HasComponent(_uuid, type);
}
/// <summary> /// <summary>
/// Gets the component with the specified type. /// Gets the component with the specified type.
@@ -208,8 +216,6 @@ public class Entity : EngineObject
} }
} }
ScriptGlue.Entity_AddComponents(_uuid, componentTypes);
Component?[] components = new Component[componentTypes.Length]; Component?[] components = new Component[componentTypes.Length];
foreach ((Type componentType, int index) in componentTypes.WithIndex()) foreach ((Type componentType, int index) in componentTypes.WithIndex())
@@ -218,6 +224,8 @@ public class Entity : EngineObject
if (componentType == null!) if (componentType == null!)
continue; continue;
ScriptGlue.Entity_AddComponent(_uuid, componentType);
components[index] = ActivatorExtension.CreateComponent(componentType, _uuid); components[index] = ActivatorExtension.CreateComponent(componentType, _uuid);
} }
@@ -232,7 +240,8 @@ public class Entity : EngineObject
where T1 : Component, new() where T1 : Component, new()
where T2 : Component, new() where T2 : Component, new()
{ {
ScriptGlue.Entity_AddComponents(_uuid, new []{typeof(T1), typeof(T2)}); ScriptGlue.Entity_AddComponent(_uuid, typeof(T1));
ScriptGlue.Entity_AddComponent(_uuid, typeof(T2));
return (new T1 { _uuid = _uuid }, new T2 { _uuid = _uuid }); return (new T1 { _uuid = _uuid }, new T2 { _uuid = _uuid });
} }
@@ -246,7 +255,9 @@ public class Entity : EngineObject
where T2 : Component, new() where T2 : Component, new()
where T3 : Component, new() where T3 : Component, new()
{ {
ScriptGlue.Entity_AddComponents(_uuid, new []{typeof(T1), typeof(T2), typeof(T3)}); ScriptGlue.Entity_AddComponent(_uuid, typeof(T1));
ScriptGlue.Entity_AddComponent(_uuid, typeof(T2));
ScriptGlue.Entity_AddComponent(_uuid, typeof(T3));
return (new T1 { _uuid = _uuid }, new T2 { _uuid = _uuid }, new T3 { _uuid = _uuid }); return (new T1 { _uuid = _uuid }, new T2 { _uuid = _uuid }, new T3 { _uuid = _uuid });
} }
@@ -285,7 +296,14 @@ public class Entity : EngineObject
/// <returns>A reference to the new script or <see langword="null"/>, if the operation failed.</returns> /// <returns>A reference to the new script or <see langword="null"/>, if the operation failed.</returns>
public T? SetScript<T>() where T : Entity public T? SetScript<T>() where T : Entity
{ {
return ScriptGlue.Entity_SetScript(_uuid, typeof(T)) as T; Entity? scriptInstance = null;
if (ScriptGlue.Entity_SetScript(_uuid, typeof(T).FullName))
{
ScriptGlue.Entity_GetScriptInstance(_uuid, out scriptInstance);
}
return scriptInstance as T;
} }
/// <summary> /// <summary>
@@ -323,7 +341,7 @@ public class Entity : EngineObject
/// <returns><see langword="true"/> if the <see cref="Entity"/> has a script component of the given type; or <see langword="false"/> if the <see cref="Entity"/> either has no script or the script is not of the specified type.</returns> /// <returns><see langword="true"/> if the <see cref="Entity"/> has a script component of the given type; or <see langword="false"/> if the <see cref="Entity"/> either has no script or the script is not of the specified type.</returns>
public bool Is<T>() where T : Entity public bool Is<T>() where T : Entity
{ {
ScriptGlue.Entity_GetScriptInstance(_uuid, out object? scriptInstance); ScriptGlue.Entity_GetScriptInstance(_uuid, out Entity? scriptInstance);
return scriptInstance is T; return scriptInstance is T;
} }
@@ -341,7 +359,7 @@ public class Entity : EngineObject
return false; return false;
} }
ScriptGlue.Entity_GetScriptInstance(_uuid, out object? scriptInstance); ScriptGlue.Entity_GetScriptInstance(_uuid, out Entity? scriptInstance);
return type.IsInstanceOfType(scriptInstance); return type.IsInstanceOfType(scriptInstance);
} }
@@ -353,7 +371,7 @@ public class Entity : EngineObject
/// <returns>The script instance of the given type; or <see langword="null"/> if the <see cref="Entity"/> has no script of the given type.</returns> /// <returns>The script instance of the given type; or <see langword="null"/> if the <see cref="Entity"/> has no script of the given type.</returns>
public T? As<T>() where T : Entity public T? As<T>() where T : Entity
{ {
ScriptGlue.Entity_GetScriptInstance(_uuid, out object? scriptInstance); ScriptGlue.Entity_GetScriptInstance(_uuid, out Entity? scriptInstance);
return scriptInstance as T; return scriptInstance as T;
} }
@@ -371,7 +389,7 @@ public class Entity : EngineObject
return null; return null;
} }
ScriptGlue.Entity_GetScriptInstance(_uuid, out object? scriptInstance); ScriptGlue.Entity_GetScriptInstance(_uuid, out Entity? scriptInstance);
return scriptInstance; return scriptInstance;
} }
@@ -386,7 +404,7 @@ public class Entity : EngineObject
{ {
Debug.Assert(typeof(Entity).IsAssignableFrom(type)); Debug.Assert(typeof(Entity).IsAssignableFrom(type));
ScriptGlue.Entity_GetScriptInstance(id, out object? scriptInstance); ScriptGlue.Entity_GetScriptInstance(id, out Entity? scriptInstance);
return scriptInstance as Entity; return scriptInstance as Entity;
} }
+3 -1
View File
@@ -77,7 +77,9 @@ public static class ImGuiExtension
public static bool ShowAssetDropTarget(ref UUID uuid) public static bool ShowAssetDropTarget(ref UUID uuid)
{ {
return ScriptGlue.ImGuiExtension_ShowAssetDropTarget(ref uuid); // TODO: !!!
// return ScriptGlue.ImGuiExtension_ShowAssetDropTarget(ref uuid);
return false;
} }
public static bool Checkbox2(string label, ref bool2 value) => CheckboxN(2, label, ref value.X); public static bool Checkbox2(string label, ref bool2 value) => CheckboxN(2, label, ref value.X);
+3 -2
View File
@@ -1,4 +1,5 @@
using System.Diagnostics.SymbolStore; using System;
using System.Diagnostics.SymbolStore;
using GlitchyEngine.Core; using GlitchyEngine.Core;
using GlitchyEngine.Math; using GlitchyEngine.Math;
@@ -22,7 +23,7 @@ public class Material : Asset
{ {
unsafe unsafe
{ {
ScriptGlue.Material_SetVariable(_uuid, name, ShaderVariableType.Float, 1, 4, 1, &value, sizeof(float4)); ScriptGlue.Material_SetVariable(_uuid, name, ShaderVariableType.Float, 1, 4, 1, (IntPtr)(void*)&value, sizeof(float4));
} }
} }
+13 -3
View File
@@ -30,10 +30,20 @@ public class SpriteRenderer : Component
{ {
get get
{ {
ScriptGlue.SpriteRenderer_GetUvTransform(_uuid, out UVTransform uvTransform); unsafe
return uvTransform; {
// TODO: Is this a type we want to have in the engine?
ScriptGlue.SpriteRenderer_GetUvTransform(_uuid, out float4 uvTransform);
return *(UVTransform*)&uvTransform;
}
}
set
{
unsafe
{
ScriptGlue.SpriteRenderer_SetUvTransform(_uuid, *(float4*)&value);
}
} }
set => ScriptGlue.SpriteRenderer_SetUvTransform(_uuid, value);
} }
/// <summary> /// <summary>
+1 -1
View File
@@ -141,6 +141,6 @@ public class Log
/// <param name="exception">The exception to log.</param> /// <param name="exception">The exception to log.</param>
public static void Exception(Exception exception) public static void Exception(Exception exception)
{ {
ScriptGlue.Log_LogException(exception); ScriptGlue.Log_LogException(UUID.Zero, exception.GetType().FullName, exception.Message, exception.StackTrace);
} }
} }
+4 -4
View File
@@ -152,16 +152,16 @@ public static partial class Math
/// <summary> /// <summary>
/// Splits the value x into fractional and integer parts, each of which has the same sign as x. /// Splits the value x into fractional and integer parts, each of which has the same sign as x.
/// </summary> /// </summary>
public static float modf(float x, out float integerPart) => ScriptGlue.modf_float(x, out integerPart); public static float modf(float x, out float integerPart) => ScriptGlue.Math_ModfFloat(x, out integerPart);
/// <inheritdoc cref="modf(float,out float)"/> /// <inheritdoc cref="modf(float,out float)"/>
public static float2 modf(float2 x, out float2 integerPart) => ScriptGlue.modf_float2(x, out integerPart); public static float2 modf(float2 x, out float2 integerPart) => ScriptGlue.Math_ModfFloat2(x, out integerPart);
/// <inheritdoc cref="modf(float2,out float2)"/> /// <inheritdoc cref="modf(float2,out float2)"/>
public static float3 modf(float3 x, out float3 integerPart) => ScriptGlue.modf_float3(x, out integerPart); public static float3 modf(float3 x, out float3 integerPart) => ScriptGlue.Math_ModfFloat3(x, out integerPart);
/// <inheritdoc cref="modf(float2,out float2)"/> /// <inheritdoc cref="modf(float2,out float2)"/>
public static float4 modf(float4 x, out float4 integerPart) => ScriptGlue.modf_float4(x, out integerPart); public static float4 modf(float4 x, out float4 integerPart) => ScriptGlue.Math_ModfFloat4(x, out integerPart);
/// <summary> /// <summary>
/// Returns the fractional (or decimal) part of x; which is greater than or equal to 0 and less than 1. /// Returns the fractional (or decimal) part of x; which is greater than or equal to 0 and less than 1.
+11
View File
@@ -0,0 +1,11 @@
{
"profiles": {
"Start Editor": {
"commandName": "Executable",
"executablePath": "..\\build\\Debug_Win64\\GlitchyEditor\\GlitchyEditor.exe",
"commandLineArgs": "\"D:\\Development\\Git\\SingleStateToOrbit\"",
"workingDirectory": "..\\GlitchyEditor",
"nativeDebugging": true
}
}
}
+140 -217
View File
@@ -26,24 +26,31 @@ namespace GlitchyEngine;
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct EngineFunctions internal unsafe partial struct EngineFunctions
{ {
//public delegate* unmanaged[Cdecl]<GlitchyEngine.Log.LogLevel, char*, char*, int, void> Log_LogMessage;
} }
/// <summary> /// <summary>
/// All methods in here are glued to the ScriptGlue.bf in the engine. /// All methods in here are glued to the ScriptGlue.bf in the engine.
/// TODO: This could be auto-generated fairly easily
/// </summary> /// </summary>
internal static unsafe partial class ScriptGlue internal static unsafe partial class ScriptGlue
{ {
#region Script Glueing infrastructure #region Script Glueing infrastructure
static ScriptGlue() static ScriptGlue()
{
ConfigureDllImportResolver();
}
private static void ConfigureDllImportResolver()
{ {
NativeLibrary.SetDllImportResolver(typeof(ScriptGlue).Assembly, ImportResolver); NativeLibrary.SetDllImportResolver(typeof(ScriptGlue).Assembly, ImportResolver);
// TODO: With this we can actually use the official ImGui.NET-Branch in the future! // TODO: With this we can actually use the official ImGui.NET-Branch in the future!
NativeLibrary.SetDllImportResolver(typeof(ImGui).Assembly, ImportResolver); NativeLibrary.SetDllImportResolver(typeof(ImGui).Assembly, ImportResolver);
} }
/// <summary>
/// Resolves an import request for the DLL <b>__Internal</b> (<c>[DllImport("__Internal")]</c>) to the current executable file.
/// This allows calling functions that are part of the native executable (e.g. native libraries we want to use from C# scripts).
/// </summary>
private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{ {
if (libraryName == "__Internal") if (libraryName == "__Internal")
@@ -59,24 +66,34 @@ internal static unsafe partial class ScriptGlue
return IntPtr.Zero; return IntPtr.Zero;
} }
private static EngineFunctions _engineFunctions; private static EngineFunctions _engineFunctions;
private static AssemblyLoadContext? _scriptAssemblyContext;
private static Assembly? _appAssembly;
/// <summary>
/// Entity script instances
/// </summary>
private static readonly Dictionary<UUID, (Entity Entity, Type Type)> EntityScriptInstances = new();
/// <summary>
/// Called by the engine to provide a struct containing all functions pointers that can be called from C##
/// </summary>
/// <param name="engineFunctions">The struct containing the function pointers to the engine functions.</param>
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static unsafe void SetEngineFunctions(EngineFunctions* engineFunctions) public static void SetEngineFunctions(EngineFunctions* engineFunctions)
{ {
_engineFunctions = *engineFunctions; _engineFunctions = *engineFunctions;
Log.Info("Yeah"); Log.Info("Yeah");
} }
private static AssemblyLoadContext? _scriptAssemblyContext; /// <summary>
/// Called by the engine to load the provided assembly (containing user scripts) and optionally debug symbols.
private static Assembly? _appAssembly; /// </summary>
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static unsafe void LoadScriptAssembly(byte* assemblyData, long assemblyLength, byte* pdbData, long pdbLength) public static void LoadScriptAssembly(byte* assemblyData, long assemblyLength, byte* pdbData, long pdbLength)
{ {
using UnmanagedMemoryStream assemblyStream = new(assemblyData, assemblyLength); using UnmanagedMemoryStream assemblyStream = new(assemblyData, assemblyLength);
@@ -100,7 +117,10 @@ internal static unsafe partial class ScriptGlue
Console.WriteLine($"Fehler: {e}"); Console.WriteLine($"Fehler: {e}");
} }
} }
/// <summary>
/// Called by the engine to unload the assembly (containing user scripts).
/// </summary>
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static void UnloadAssemblies() public static void UnloadAssemblies()
{ {
@@ -108,15 +128,9 @@ internal static unsafe partial class ScriptGlue
_scriptAssemblyContext = null; _scriptAssemblyContext = null;
} }
struct ScriptClassInfo
{
public byte[] Name;
public Guid Guid;
}
private static NativeScriptClassInfo[]? _unsafeClasses; private static NativeScriptClassInfo[]? _unsafeClasses;
struct NativeScriptClassInfo public struct NativeScriptClassInfo
{ {
public IntPtr Name; public IntPtr Name;
public Guid Guid; public Guid Guid;
@@ -133,8 +147,16 @@ internal static unsafe partial class ScriptGlue
OnDestroy = 0x4 OnDestroy = 0x4
} }
/// <summary>
/// Called by the engine to receive a list of all script classes.
/// </summary>
/// <param name="outBuffer">Pointer to the array of <see cref="NativeScriptClassInfo"/>s</param>
/// <param name="length">The number of elements in <see cref="outBuffer"/></param>
/// <remarks>
/// The array returned by <see cref="outBuffer"/> must be freed using <see cref="FreeScriptClassNames"/>.
/// </remarks>
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static unsafe void GetScriptClasses(void** outBuffer, long* length) public static void GetScriptClasses(NativeScriptClassInfo** outBuffer, long* length)
{ {
using var contextualReflection = AssemblyLoadContext.EnterContextualReflection(_appAssembly); using var contextualReflection = AssemblyLoadContext.EnterContextualReflection(_appAssembly);
@@ -189,17 +211,20 @@ internal static unsafe partial class ScriptGlue
}; };
} }
*outBuffer = (void*)Marshal.UnsafeAddrOfPinnedArrayElement(_unsafeClasses, 0); *outBuffer = (NativeScriptClassInfo*)Marshal.UnsafeAddrOfPinnedArrayElement(_unsafeClasses, 0);
*length = _unsafeClasses.Length; *length = _unsafeClasses.Length;
} }
/// <summary>
/// Called by the engine to free the data allocated by <see cref="GetScriptClasses"/>
/// </summary>
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static void FreeScriptClassNames() public static void FreeScriptClassNames()
{ {
Internal_FreeScriptClassNames(); Internal_FreeScriptClassNames();
} }
internal static void Internal_FreeScriptClassNames() private static void Internal_FreeScriptClassNames()
{ {
if (_unsafeClasses == null) if (_unsafeClasses == null)
return; return;
@@ -212,13 +237,19 @@ internal static unsafe partial class ScriptGlue
_unsafeClasses = null; _unsafeClasses = null;
} }
private static Dictionary<UUID, (Entity Entity, Type Type)> _entityScripts = new();
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static void ShowEntityEditor(UUID entityId) public static void ShowEntityEditor(UUID entityId)
{ {
(Entity entity, Type type) = _entityScripts[entityId]; try
EntityEditor.ShowEntityEditor(entity); {
(Entity entity, Type type) = EntityScriptInstances[entityId];
EntityEditor.ShowEntityEditor(entity);
}
catch (Exception e)
{
Console.WriteLine(e);
// TODO: Log exceptions to console
}
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
@@ -226,7 +257,7 @@ internal static unsafe partial class ScriptGlue
{ {
try try
{ {
(Entity entity, Type type) = _entityScripts[entityId]; (Entity entity, Type type) = EntityScriptInstances[entityId];
entity.OnCreate(); entity.OnCreate();
} }
catch (Exception e) catch (Exception e)
@@ -241,19 +272,19 @@ internal static unsafe partial class ScriptGlue
{ {
// using var _ = AssemblyLoadContext.EnterContextualReflection(_appAssembly); // TODO: Check if reflection works correctly in entities // using var _ = AssemblyLoadContext.EnterContextualReflection(_appAssembly); // TODO: Check if reflection works correctly in entities
(Entity entity, Type type) = _entityScripts[entityId]; (Entity entity, Type type) = EntityScriptInstances[entityId];
entity.OnUpdate(deltaTime); entity.OnUpdate(deltaTime);
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static void InvokeEntityOnDestroy(UUID entityId, float deltaTime) public static void InvokeEntityOnDestroy(UUID entityId, float deltaTime)
{ {
(Entity entity, Type type) = _entityScripts[entityId]; (Entity entity, Type type) = EntityScriptInstances[entityId];
entity.OnDestroy(); entity.OnDestroy();
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static unsafe void CreateScriptInstance(UUID entityId, byte* scriptClassName) public static void CreateScriptInstance(UUID entityId, byte* scriptClassName)
{ {
using var _ = AssemblyLoadContext.EnterContextualReflection(_appAssembly); using var _ = AssemblyLoadContext.EnterContextualReflection(_appAssembly);
@@ -269,212 +300,104 @@ internal static unsafe partial class ScriptGlue
Entity? scriptInstance = ActivatorExtension.CreateEngineObject(scriptType, entityId) as Entity; Entity? scriptInstance = ActivatorExtension.CreateEngineObject(scriptType, entityId) as Entity;
//
// // Get the constructor
// ConstructorInfo? constructor = scriptType.GetConstructor(
// BindingFlags.Instance | BindingFlags.Public,
// null,
// [],
// null);
//
// Debug.Assert(constructor != null, "Script class constructor not found.");
//
// // Call the constructor to create an instance
// Entity? scriptInstance = constructor?.Invoke(null) as Entity;
Debug.Assert(scriptInstance != null, "Failed to create script instance."); Debug.Assert(scriptInstance != null, "Failed to create script instance.");
//
// if (scriptInstance != null)
// scriptInstance._uuid = entityId;
//ScriptFunctions functions = new(); EntityScriptInstances.Add(entityId, (scriptInstance!, scriptType));
_entityScripts.Add(entityId, (scriptInstance!, scriptType));
//MethodInfo? onCreateMethod = scriptType.GetMethod("OnCreate", BindingFlags.Instance | BindingFlags.NonPublic);
//if (onCreateMethod != null)
//{
// var v = MethodHelpers.GetFunctionPointerForNativeCode(onCreateMethod, null);
//}
//if (onCreateMethod != null)
//{
// //Delegate del = CreateDelegateWithTarget(onCreateMethod, scriptInstance);
// //var createDelegate = onCreateMethod.CreateDelegate<Action>(scriptInstance);
// //var createDelegate = onCreateMethod.CreateDelegate(typeof(OnCreateMethodDelegate), scriptInstance);
// //onCreateMethod.CreateDelegate(scriptInstance);
// //functions.OnCreateMethod = Marshal.GetFunctionPointerForDelegate(createDelegate);
//}
//
// MethodInfo? onUpdateMethod = scriptType.GetMethod("OnUpdate", BindingFlags.Instance | BindingFlags.NonPublic);
// if (onUpdateMethod != null)
// {
// // Create the delegate from your method and instance
// OnUpdateMethodDelegate onUpdateDelegate = (OnUpdateMethodDelegate)Delegate.CreateDelegate(typeof(OnUpdateMethodDelegate), scriptInstance, onUpdateMethod);
//
// // Get the function pointer from your delegate
// functions.OnUpdateMethod = Marshal.GetFunctionPointerForDelegate(onUpdateDelegate);
// }
//functions.OnUpdateMethod = Marshal.GetFunctionPointerForDelegate((float deltaTime) => onUpdateMethod.Invoke(scriptInstance, new object?[]{ deltaTime }));
//MethodInfo? onDestroyMethod = scriptType.GetMethod("OnUpdate", BindingFlags.Instance | BindingFlags.NonPublic);
//if (onDestroyMethod != null)
// functions.OnDestroyMethod = Marshal.GetFunctionPointerForDelegate(() => onDestroyMethod.Invoke(scriptInstance, null));
//return functions;
} }
static Delegate CreateDelegate(MethodInfo method) struct ComponentFunctionPointers
{ {
if (method == null) public delegate* unmanaged[Cdecl]<UUID, void> AddComponent;
{ public delegate* unmanaged[Cdecl]<UUID, bool> HasComponent;
throw new ArgumentNullException(nameof(method)); public delegate* unmanaged[Cdecl]<UUID, void> RemoveComponent;
}
if (!method.IsStatic)
{
throw new ArgumentException("The provided method must be static.", nameof(method));
}
if (method.IsGenericMethod)
{
throw new ArgumentException("The provided method must not be generic.", nameof(method));
}
return method.CreateDelegate(Expression.GetDelegateType(
(from parameter in method.GetParameters() select parameter.ParameterType)
.Concat(new[] { method.ReturnType })
.ToArray()));
} }
/// <summary> private static readonly Dictionary<Type, ComponentFunctionPointers> ComponentTypeFunctions = new();
/// Create delegate by methodinfo in target
/// </summary> [UnmanagedCallersOnly]
/// <param name="method">method info</param> public static void RegisterComponentType(byte* fullComponentTypeName,
/// <param name="target">A instance of the object which contains the method where will be execute</param> delegate* unmanaged[Cdecl]<UUID, void> addComponent,
/// <returns>delegate or null</returns> delegate* unmanaged[Cdecl]<UUID, bool> hasComponent,
public static Delegate? CreateDelegateWithTarget(MethodInfo? method, object? target) delegate* unmanaged[Cdecl]<UUID, void> removeComponent)
{ {
if (method is null || string? beefComponentTypeName = Marshal.PtrToStringUTF8((IntPtr)fullComponentTypeName);
target is null)
return null;
//if (method.IsStatic) if (beefComponentTypeName == null)
// return null; return;
if (method.IsGenericMethod) foreach(Type componentType in TypeExtension.FindDerivedTypes(typeof(Component)))
return null;
return method.CreateDelegate(Expression.GetDelegateType(
(from parameter in method.GetParameters() select parameter.ParameterType)
.Concat(new[] { method.ReturnType })
.ToArray()), target);
}
internal static class MethodHelpers
{
private const string DelegateTypesAssemblyName = "JitDelegateTypes";
private static ModuleBuilder _modBuilder;
private static ConcurrentDictionary<(string, object), Delegate> _delegatesCache;
private static ConcurrentDictionary<string, Type> _delegateTypesCache;
static MethodHelpers()
{ {
AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(DelegateTypesAssemblyName), AssemblyBuilderAccess.Run); if (!componentType.TryGetCustomAttribute(out EngineClassAttribute mapping) ||
mapping.EngineClassName != beefComponentTypeName) continue;
_modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName);
ComponentTypeFunctions[componentType] = new ComponentFunctionPointers
_delegatesCache = new ConcurrentDictionary<(string, object), Delegate>();
_delegateTypesCache = new ConcurrentDictionary<string, Type>();
}
public static IntPtr GetFunctionPointerForNativeCode(MethodInfo meth, object instance = null)
{
string funcName = GetFullName(meth);
Delegate dlg = _delegatesCache.GetOrAdd((funcName, instance), (_) =>
{ {
Type[] parameters = meth.GetParameters().Select(x => x.ParameterType).ToArray(); AddComponent = addComponent,
HasComponent = hasComponent,
RemoveComponent = removeComponent
};
Type delegateType = GetDelegateType(parameters, meth.ReturnType); break;
return Delegate.CreateDelegate(delegateType, instance, meth);
});
return Marshal.GetFunctionPointerForDelegate<Delegate>(dlg);
}
private static string GetFullName(MethodInfo meth)
{
return $"{meth.DeclaringType.FullName}.{meth.Name}";
}
private static Type GetDelegateType(Type[] parameters, Type returnType)
{
string key = GetFunctionSignatureKey(parameters, returnType);
return _delegateTypesCache.GetOrAdd(key, (_) => MakeDelegateType(parameters, returnType, key));
}
private const MethodAttributes CtorAttributes =
MethodAttributes.RTSpecialName |
MethodAttributes.HideBySig |
MethodAttributes.Public;
private const MethodImplAttributes ImplAttributes =
MethodImplAttributes.Runtime |
MethodImplAttributes.Managed;
private const MethodAttributes InvokeAttributes =
MethodAttributes.Public |
MethodAttributes.HideBySig |
MethodAttributes.NewSlot |
MethodAttributes.Virtual;
private const TypeAttributes DelegateTypeAttributes =
TypeAttributes.Class |
TypeAttributes.Public |
TypeAttributes.Sealed |
TypeAttributes.AnsiClass |
TypeAttributes.AutoClass;
private static readonly Type[] _delegateCtorSignature = { typeof(object), typeof(IntPtr) };
private static Type MakeDelegateType(Type[] parameters, Type returnType, string name)
{
TypeBuilder builder = _modBuilder.DefineType(name, DelegateTypeAttributes, typeof(MulticastDelegate));
builder.DefineConstructor(CtorAttributes, CallingConventions.Standard, _delegateCtorSignature).SetImplementationFlags(ImplAttributes);
builder.DefineMethod("Invoke", InvokeAttributes, returnType, parameters).SetImplementationFlags(ImplAttributes);
return builder.CreateTypeInfo();
}
private static string GetFunctionSignatureKey(Type[] parameters, Type returnType)
{
string sig = GetTypeName(returnType);
foreach (Type type in parameters)
{
sig += '_' + GetTypeName(type);
}
return sig;
}
private static string GetTypeName(Type type)
{
return type.FullName.Replace(".", string.Empty);
} }
} }
#endregion #endregion
public static void Entity_AddComponent(UUID entityId, Type componentType)
{
ComponentTypeFunctions[componentType].AddComponent(entityId);
}
public static bool Entity_HasComponent(UUID entityId, Type componentType)
{
return ComponentTypeFunctions[componentType].HasComponent(entityId);
}
public static void Entity_RemoveComponent(UUID entityId, Type componentType)
{
ComponentTypeFunctions[componentType].RemoveComponent(entityId);
}
public static void Entity_GetScriptInstance(UUID entityId, out Entity? instance)
{
// We currently can implement this method here, because we only have C# scripts.
// If we ever need to do something to interop with other script languages, then this would change.
if (EntityScriptInstances.TryGetValue(entityId, out var match))
{
instance = match.Entity;
}
instance = null;
}
public static void Serialization_SerializeField(IntPtr serializationContext, SerializationType type, string fieldName, object? valueObject, string fullTypeName)
{
byte* fieldNameConverted = (byte*)Marshal.StringToCoTaskMemUTF8(fieldName);
byte* fullTypeNameConverted = (byte*)Marshal.StringToCoTaskMemUTF8(fullTypeName);
void* valueObjectConverted = null;
bool deleteValueObject = false;
switch (type)
{
case SerializationType.String:
valueObjectConverted = (void*)Marshal.StringToCoTaskMemUTF8(fullTypeName);
deleteValueObject = true;
break;
default:
if (valueObject is not null)
valueObjectConverted = Unsafe.AsPointer(ref Unsafe.Unbox<byte>(valueObject));
break;
}
_engineFunctions.Serialization_SerializeField((void*)serializationContext, type, fieldNameConverted, valueObjectConverted, fullTypeNameConverted);
Marshal.FreeCoTaskMem((IntPtr)fieldNameConverted);
Marshal.FreeCoTaskMem((IntPtr)fullTypeNameConverted);
if (deleteValueObject)
Marshal.FreeCoTaskMem((IntPtr)valueObjectConverted);
}
//#region Log //#region Log
+36 -1
View File
@@ -100,6 +100,8 @@ public class ScriptGlueGenerator : IIncrementalGenerator
public string? WrapperConvertInput; public string? WrapperConvertInput;
public string? WrapperCleanupInput; public string? WrapperCleanupInput;
public string? WrapperOutConversion;
public string? ReturnValueConversion = "return returnValue;"; public string? ReturnValueConversion = "return returnValue;";
public string CSharpWrapperType public string CSharpWrapperType
@@ -237,6 +239,17 @@ public class ScriptGlueGenerator : IIncrementalGenerator
WrapperConvertInput = "fixed (char* {0} = {1}) {{", WrapperConvertInput = "fixed (char* {0} = {1}) {{",
WrapperCleanupInput = "}}" WrapperCleanupInput = "}}"
}); });
beefTypeToMappedType.Add("char8*", new MappedType
{
BeefTypeName = "char8*",
CSharpTypeName = "byte*",
CSharpWrapperType = "string",
ReturnValueConversion = "return Marshal.PtrToStringUTF8((IntPtr)returnValue);",
WrapperConvertInput = "byte* {0} = (byte*)Marshal.StringToCoTaskMemUTF8({1});",
WrapperCleanupInput = "Marshal.FreeCoTaskMem((IntPtr){0});",
WrapperOutConversion = "{1} = Marshal.PtrToStringUTF8((IntPtr){0}) ?? \"\";"
});
beefTypeToMappedType.Add("int32", new MappedType beefTypeToMappedType.Add("int32", new MappedType
{ {
@@ -249,6 +262,16 @@ public class ScriptGlueGenerator : IIncrementalGenerator
BeefTypeName = "System.Numerics.Quaternion", BeefTypeName = "System.Numerics.Quaternion",
ReturnValueConversion = null ReturnValueConversion = null
}); });
beefTypeToMappedType.Add("void*", new MappedType
{
BeefTypeName = "void*",
CSharpTypeName = "void*",
CSharpWrapperType = "IntPtr",
ReturnValueConversion = "return (IntPtr)returnValue;",
WrapperConvertInput = "void* {0} = (void*){1};",
WrapperOutConversion = "{1} = (IntPtr){0};"
});
} }
private static void GenerateFunctionPointer(GlueMethod method, Dictionary<string, MappedType> beefTypeToMappedType, StringBuilder output) private static void GenerateFunctionPointer(GlueMethod method, Dictionary<string, MappedType> beefTypeToMappedType, StringBuilder output)
@@ -341,7 +364,17 @@ public class ScriptGlueGenerator : IIncrementalGenerator
break; break;
} }
if (parameterType.WrapperConvertInput is not null) if (parameterModifier == TypeModifier.Out && parameterType.WrapperOutConversion is not null)
{
string tmpArgName = $"{param.Name}Tmp";
call.Append($"var {tmpArgName}");
cleanup.Append("\t\t");
cleanup.AppendFormat(parameterType.WrapperOutConversion, tmpArgName, param.Name);
cleanup.AppendLine();
}
else if (parameterType.WrapperConvertInput is not null)
{ {
string convertedParamName = $"{param.Name}Converted"; string convertedParamName = $"{param.Name}Converted";
@@ -422,7 +455,9 @@ public class ScriptGlueGenerator : IIncrementalGenerator
wrappers.Append(""" wrappers.Append("""
// <auto-generated /> // <auto-generated />
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace GlitchyEngine; namespace GlitchyEngine;