Start of using CoreCLR and automatic scriptglue generation

This commit is contained in:
Simon Lübeß
2025-07-14 22:14:41 +02:00
parent 063cbfa415
commit edfcfd3b20
24 changed files with 1548 additions and 540 deletions
+12 -6
View File
@@ -414,12 +414,18 @@ public class Entity : EngineObject
return new Entity(newEntityId);
}
// Will be executed once after the entity has be created.
// void OnCreate();
/// <summary>
/// Will be executed once after the entity has be created.
/// </summary>
protected internal virtual void OnCreate() { }
// Will be executed every frame.
// void OnUpdate(GameTime);
/// <summary>
/// Is called once every frame.
/// </summary>
protected internal virtual void OnUpdate(float deltaTime) { }
// Will be executed once when the entity is being destroyed.
// void OnDestroy();
/// <summary>
/// Will be executed once when the entity is being destroyed.
/// </summary>
protected internal virtual void OnDestroy() { }
}
+1 -1
View File
@@ -148,7 +148,7 @@ public static class TypeExtension
string name = fullName.TrimEnd(']').ToString();
// Non generic type, easy!
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies().Reverse())
foreach (Assembly assembly in ((IEnumerable<Assembly>)AppDomain.CurrentDomain.GetAssemblies()).Reverse())
{
Type? type = assembly.GetType(name);
+284 -284
View File
@@ -1,284 +1,284 @@
using System;
using System.Runtime.CompilerServices;
namespace GlitchyEngine.Math;
/// <summary>
/// Represents a 16-bit (half-precision) floating point number. (aka. IEEE 754 half-precision binary floating-point (binary16))
/// </summary>
/// <remarks>
/// Even though it is possible, it's not recommended to perform large calculations using this type directly because most operators will simply cast the operands to <see cref="float"/> and cast the result back to <see cref="Half"/>.
/// If you need to do larger calculations consider casting to <see cref="float"/> once and cast the result back to <see cref="Half"/> afterwards. This will not only have better performance, but will also increase the accuracy of the result.
/// </remarks>
public struct Half : IComparable , IComparable<Half>, IConvertible, IEquatable<Half>, IFormattable
{
public static readonly Half MinValue = new(-65504); // Should be 0xFBFF
public static readonly Half MaxValue = new(65504); // Should be 0x7BFF
// The numbers for Infinity, NaN and Zero need to be hardcoded as binaries,
// because the conversion itself relies on them.
public static readonly Half PositiveInfinity = new(0x7C00);
public static readonly Half NegativeInfinity = new(0xFC00);
public static readonly Half NaN = new(0x7CFF);
public static readonly Half Zero = new(0x0000);
public static readonly Half NegativeZero = new(0x8000);
private ushort _data;
public bool IsNegative => IsNegative_Impl(this);
public bool IsFinite => IsFinite_Impl(this);
public bool IsInfinity => IsInfinity_Impl(this);
public bool IsPositiveInfinity => _data == PositiveInfinity._data;
public bool IsNegativeInfinity => _data == NegativeInfinity._data;
public bool IsNaN => IsNan_Impl(this);
public bool IsSubnormal => IsSubnormal_Impl(this);
public Half(float value)
{
FromFloat32(value, out Half halfValue);
this = halfValue;
}
private Half(ushort data)
{
_data = data;
}
public static explicit operator float(Half value)
{
ToFloat32(value, out float floatValue);
return floatValue;
}
public static explicit operator Half(float value)
{
FromFloat32(value, out Half halfValue);
return halfValue;
}
/// <summary>Converts the numeric value of this instance to its equivalent string representation.</summary>
/// <returns>The string representation of the value of this instance.</returns>
public override string ToString() => ((float)this).ToString();
public int CompareTo(object obj)
{
if (obj == null)
return 1;
if (obj is not Half value)
throw new ArgumentException($"Object must be of type {typeof(Half)}");
return CompareTo(value);
}
public int CompareTo(Half other)
{
return ((float)this).CompareTo((float)other);
}
public TypeCode GetTypeCode() => TypeCode.Object;
public bool ToBoolean(IFormatProvider provider) => Convert.ToBoolean((float)this, provider);
public byte ToByte(IFormatProvider provider) => Convert.ToByte((float)this, provider);
public char ToChar(IFormatProvider provider) => Convert.ToChar((float)this, provider);
public DateTime ToDateTime(IFormatProvider provider) => Convert.ToDateTime((float)this, provider);
public decimal ToDecimal(IFormatProvider provider) => Convert.ToDecimal((float)this, provider);
public double ToDouble(IFormatProvider provider) => Convert.ToDouble((float)this, provider);
public short ToInt16(IFormatProvider provider) => Convert.ToInt16((float)this, provider);
public int ToInt32(IFormatProvider provider) => Convert.ToInt32((float)this, provider);
public long ToInt64(IFormatProvider provider) => Convert.ToInt64((float)this, provider);
public sbyte ToSByte(IFormatProvider provider) => Convert.ToSByte((float)this, provider);
public float ToSingle(IFormatProvider provider) => Convert.ToSingle((float)this, provider);
/// <summary>Converts the numeric value of this instance to its equivalent string representation using the specified culture-specific format information.</summary>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <returns>The string representation of the value of this instance as specified by <paramref name="provider">provider</paramref>.</returns>
public string ToString(IFormatProvider provider) => ((float)this).ToString(provider);
/// <summary>Converts the numeric value of this instance to its equivalent string representation, using the specified format.</summary>
/// <param name="format">A numeric format string.</param>
/// <returns>The string representation of the value of this instance as specified by <paramref name="format">format</paramref>.</returns>
/// <exception cref="T:System.FormatException"><paramref name="format">format</paramref> is invalid.</exception>
public string ToString(string format) => ((float)this).ToString(format);
/// <summary>Converts the numeric value of this instance to its equivalent string representation using the specified format and culture-specific format information.</summary>
/// <param name="format">A numeric format string.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <returns>The string representation of the value of this instance as specified by <paramref name="format">format</paramref> and <paramref name="provider">provider</paramref>.</returns>
public string ToString(string format, IFormatProvider provider) => ((float)this).ToString(format, provider);
public object ToType(Type conversionType, IFormatProvider provider) => ((IConvertible)(float)this).ToType(conversionType, provider);
public ushort ToUInt16(IFormatProvider provider) => Convert.ToUInt16((float)this, provider);
public uint ToUInt32(IFormatProvider provider) => Convert.ToUInt32((float)this, provider);
public ulong ToUInt64(IFormatProvider provider) => Convert.ToUInt64((float)this, provider);
public bool Equals(Half other)
{
return _data == other._data;
}
public override bool Equals(object obj)
{
return obj is Half other && Equals(other);
}
public override int GetHashCode()
{
return _data.GetHashCode();
}
#region Operators
public static bool operator ==(Half left, Half right) => left._data == right._data;
public static bool operator !=(Half left, Half right) => left._data != right._data;
public static bool operator <(Half left, Half right)
{
LessThan_Impl(left, right, out bool result);
return result;
}
public static bool operator <=(Half left, Half right)
{
LessThanOrEqual_Impl(left, right, out bool result);
return result;
}
public static bool operator >(Half left, Half right)
{
GreaterThan_Impl(left, right, out bool result);
return result;
}
public static bool operator >=(Half left, Half right)
{
GreaterThanOrEqual_Impl(left, right, out bool result);
return result;
}
public static Half operator +(Half value) => value;
public static Half operator -(Half value)
{
Negate_Impl(value, out Half result);
return result;
}
public static Half operator +(Half left, Half right)
{
Add_Impl(left, right, out Half result);
return result;
}
public static Half operator -(Half left, Half right)
{
Subtract_Impl(left, right, out Half result);
return result;
}
public static Half operator *(Half left, Half right)
{
Multiply_Impl(left, right, out Half result);
return result;
}
public static Half operator /(Half left, Half right)
{
Divide_Impl(left, right, out Half result);
return result;
}
public static Half operator %(Half left, Half right)
{
Modulo_Impl(left, right, out Half result);
return result;
}
public static Half operator ++(Half value)
{
Increment_Impl(value, out Half result);
return result;
}
public static Half operator --(Half value)
{
Decrement_Impl(value, out Half result);
return result;
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void LessThan_Impl(Half left, Half right, out bool result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void LessThanOrEqual_Impl(Half left, Half right, out bool result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void GreaterThan_Impl(Half left, Half right, out bool result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void GreaterThanOrEqual_Impl(Half left, Half right, out bool result);
// Externe Methoden hier als Platzhalter
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Add_Impl(Half left, Half right, out Half result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Subtract_Impl(Half left, Half right, out Half result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Multiply_Impl(Half left, Half right, out Half result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Divide_Impl(Half left, Half right, out Half result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Modulo_Impl(Half left, Half right, out Half result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Negate_Impl(Half value, out Half result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Increment_Impl(Half value, out Half result);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Decrement_Impl(Half value, out Half result);
#endregion
#region Bindings
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void FromFloat32(float value, out Half halfValue);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void ToFloat32(Half value, out float floatValue);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool IsNegative_Impl(Half self);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool IsFinite_Impl(Half self);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool IsInfinity_Impl(Half self);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool IsNan_Impl(Half self);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool IsSubnormal_Impl(Half self);
#endregion
}
// using System;
// using System.Runtime.CompilerServices;
//
// namespace GlitchyEngine.Math;
//
// /// <summary>
// /// Represents a 16-bit (half-precision) floating point number. (aka. IEEE 754 half-precision binary floating-point (binary16))
// /// </summary>
// /// <remarks>
// /// Even though it is possible, it's not recommended to perform large calculations using this type directly because most operators will simply cast the operands to <see cref="float"/> and cast the result back to <see cref="Half"/>.
// /// If you need to do larger calculations consider casting to <see cref="float"/> once and cast the result back to <see cref="Half"/> afterwards. This will not only have better performance, but will also increase the accuracy of the result.
// /// </remarks>
// public struct Half : IComparable , IComparable<Half>, IConvertible, IEquatable<Half>, IFormattable
// {
// public static readonly Half MinValue = new(-65504); // Should be 0xFBFF
// public static readonly Half MaxValue = new(65504); // Should be 0x7BFF
//
// // The numbers for Infinity, NaN and Zero need to be hardcoded as binaries,
// // because the conversion itself relies on them.
//
// public static readonly Half PositiveInfinity = new(0x7C00);
// public static readonly Half NegativeInfinity = new(0xFC00);
// public static readonly Half NaN = new(0x7CFF);
//
// public static readonly Half Zero = new(0x0000);
// public static readonly Half NegativeZero = new(0x8000);
//
// private ushort _data;
//
// public bool IsNegative => IsNegative_Impl(this);
//
// public bool IsFinite => IsFinite_Impl(this);
// public bool IsInfinity => IsInfinity_Impl(this);
// public bool IsPositiveInfinity => _data == PositiveInfinity._data;
// public bool IsNegativeInfinity => _data == NegativeInfinity._data;
// public bool IsNaN => IsNan_Impl(this);
//
// public bool IsSubnormal => IsSubnormal_Impl(this);
//
// public Half(float value)
// {
// FromFloat32(value, out Half halfValue);
// this = halfValue;
// }
//
// private Half(ushort data)
// {
// _data = data;
// }
//
// public static explicit operator float(Half value)
// {
// ToFloat32(value, out float floatValue);
// return floatValue;
// }
//
// public static explicit operator Half(float value)
// {
// FromFloat32(value, out Half halfValue);
// return halfValue;
// }
//
// /// <summary>Converts the numeric value of this instance to its equivalent string representation.</summary>
// /// <returns>The string representation of the value of this instance.</returns>
// public override string ToString() => ((float)this).ToString();
//
// public int CompareTo(object obj)
// {
// if (obj == null)
// return 1;
//
// if (obj is not Half value)
// throw new ArgumentException($"Object must be of type {typeof(Half)}");
//
// return CompareTo(value);
// }
//
// public int CompareTo(Half other)
// {
// return ((float)this).CompareTo((float)other);
// }
//
// public TypeCode GetTypeCode() => TypeCode.Object;
//
// public bool ToBoolean(IFormatProvider provider) => Convert.ToBoolean((float)this, provider);
//
// public byte ToByte(IFormatProvider provider) => Convert.ToByte((float)this, provider);
//
// public char ToChar(IFormatProvider provider) => Convert.ToChar((float)this, provider);
//
// public DateTime ToDateTime(IFormatProvider provider) => Convert.ToDateTime((float)this, provider);
//
// public decimal ToDecimal(IFormatProvider provider) => Convert.ToDecimal((float)this, provider);
//
// public double ToDouble(IFormatProvider provider) => Convert.ToDouble((float)this, provider);
//
// public short ToInt16(IFormatProvider provider) => Convert.ToInt16((float)this, provider);
//
// public int ToInt32(IFormatProvider provider) => Convert.ToInt32((float)this, provider);
//
// public long ToInt64(IFormatProvider provider) => Convert.ToInt64((float)this, provider);
//
// public sbyte ToSByte(IFormatProvider provider) => Convert.ToSByte((float)this, provider);
//
// public float ToSingle(IFormatProvider provider) => Convert.ToSingle((float)this, provider);
//
// /// <summary>Converts the numeric value of this instance to its equivalent string representation using the specified culture-specific format information.</summary>
// /// <param name="provider">An object that supplies culture-specific formatting information.</param>
// /// <returns>The string representation of the value of this instance as specified by <paramref name="provider">provider</paramref>.</returns>
// public string ToString(IFormatProvider provider) => ((float)this).ToString(provider);
//
// /// <summary>Converts the numeric value of this instance to its equivalent string representation, using the specified format.</summary>
// /// <param name="format">A numeric format string.</param>
// /// <returns>The string representation of the value of this instance as specified by <paramref name="format">format</paramref>.</returns>
// /// <exception cref="T:System.FormatException"><paramref name="format">format</paramref> is invalid.</exception>
// public string ToString(string format) => ((float)this).ToString(format);
//
// /// <summary>Converts the numeric value of this instance to its equivalent string representation using the specified format and culture-specific format information.</summary>
// /// <param name="format">A numeric format string.</param>
// /// <param name="provider">An object that supplies culture-specific formatting information.</param>
// /// <returns>The string representation of the value of this instance as specified by <paramref name="format">format</paramref> and <paramref name="provider">provider</paramref>.</returns>
// public string ToString(string format, IFormatProvider provider) => ((float)this).ToString(format, provider);
//
// public object ToType(Type conversionType, IFormatProvider provider) => ((IConvertible)(float)this).ToType(conversionType, provider);
//
// public ushort ToUInt16(IFormatProvider provider) => Convert.ToUInt16((float)this, provider);
//
// public uint ToUInt32(IFormatProvider provider) => Convert.ToUInt32((float)this, provider);
//
// public ulong ToUInt64(IFormatProvider provider) => Convert.ToUInt64((float)this, provider);
//
// public bool Equals(Half other)
// {
// return _data == other._data;
// }
//
// public override bool Equals(object obj)
// {
// return obj is Half other && Equals(other);
// }
//
// public override int GetHashCode()
// {
// return _data.GetHashCode();
// }
//
// #region Operators
//
// public static bool operator ==(Half left, Half right) => left._data == right._data;
//
// public static bool operator !=(Half left, Half right) => left._data != right._data;
//
// public static bool operator <(Half left, Half right)
// {
// LessThan_Impl(left, right, out bool result);
// return result;
// }
//
// public static bool operator <=(Half left, Half right)
// {
// LessThanOrEqual_Impl(left, right, out bool result);
// return result;
// }
// public static bool operator >(Half left, Half right)
// {
// GreaterThan_Impl(left, right, out bool result);
// return result;
// }
//
// public static bool operator >=(Half left, Half right)
// {
// GreaterThanOrEqual_Impl(left, right, out bool result);
// return result;
// }
//
// public static Half operator +(Half value) => value;
//
// public static Half operator -(Half value)
// {
// Negate_Impl(value, out Half result);
// return result;
// }
//
// public static Half operator +(Half left, Half right)
// {
// Add_Impl(left, right, out Half result);
// return result;
// }
//
// public static Half operator -(Half left, Half right)
// {
// Subtract_Impl(left, right, out Half result);
// return result;
// }
//
// public static Half operator *(Half left, Half right)
// {
// Multiply_Impl(left, right, out Half result);
// return result;
// }
//
// public static Half operator /(Half left, Half right)
// {
// Divide_Impl(left, right, out Half result);
// return result;
// }
//
// public static Half operator %(Half left, Half right)
// {
// Modulo_Impl(left, right, out Half result);
// return result;
// }
//
// public static Half operator ++(Half value)
// {
// Increment_Impl(value, out Half result);
// return result;
// }
//
// public static Half operator --(Half value)
// {
// Decrement_Impl(value, out Half result);
// return result;
// }
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void LessThan_Impl(Half left, Half right, out bool result);
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void LessThanOrEqual_Impl(Half left, Half right, out bool result);
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void GreaterThan_Impl(Half left, Half right, out bool result);
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void GreaterThanOrEqual_Impl(Half left, Half right, out bool result);
//
// // Externe Methoden hier als Platzhalter
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void Add_Impl(Half left, Half right, out Half result);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void Subtract_Impl(Half left, Half right, out Half result);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void Multiply_Impl(Half left, Half right, out Half result);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void Divide_Impl(Half left, Half right, out Half result);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void Modulo_Impl(Half left, Half right, out Half result);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void Negate_Impl(Half value, out Half result);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void Increment_Impl(Half value, out Half result);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void Decrement_Impl(Half value, out Half result);
//
// #endregion
//
// #region Bindings
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void FromFloat32(float value, out Half halfValue);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern void ToFloat32(Half value, out float floatValue);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern bool IsNegative_Impl(Half self);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern bool IsFinite_Impl(Half self);
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern bool IsInfinity_Impl(Half self);
//
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern bool IsNan_Impl(Half self);
// [MethodImpl(MethodImplOptions.InternalCall)]
// private static extern bool IsSubnormal_Impl(Half self);
//
// #endregion
// }
+2 -1
View File
@@ -1,4 +1,5 @@
using GlitchyEngine.Math.Attributes;
using System;
using GlitchyEngine.Math.Attributes;
namespace GlitchyEngine.Math;
+2 -1
View File
@@ -1,4 +1,5 @@
using GlitchyEngine.Math.Attributes;
using System;
using GlitchyEngine.Math.Attributes;
namespace GlitchyEngine.Math;
+18 -15
View File
@@ -1,27 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<BaseOutputPath></BaseOutputPath>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<RootNamespace>GlitchyEngine</RootNamespace>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
</PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<BaseOutputPath>
</BaseOutputPath>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<RootNamespace>GlitchyEngine</RootNamespace>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<EnableDynamicLoading>true</EnableDynamicLoading>
<!--<DebugType>embedded</DebugType>-->
<!--<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>-->
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="PowerShell ./postbuild.ps1 -sourceDir $(OutDir) -destinationDir &quot;..\GlitchyEditor\resources\scripts&quot;" />
</Target>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Memory" Version="4.6.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ScriptCoreGenerator\ScriptCoreGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\vendor\ImGui.NET\src\ImGui.NET\ImGui.NET.csproj" />
</ItemGroup>
</Project>
</Project>
+469 -4
View File
@@ -1,8 +1,19 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Numerics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using GlitchyEngine.Core;
using GlitchyEngine.Editor;
using GlitchyEngine.Extensions;
using GlitchyEngine.Graphics;
using GlitchyEngine.Graphics.Text;
using GlitchyEngine.Math;
@@ -11,16 +22,470 @@ using GlitchyEngine.Serialization;
namespace GlitchyEngine;
[StructLayout(LayoutKind.Sequential)]
internal unsafe partial struct EngineFunctions
{
public delegate* unmanaged[Cdecl]<Log.LogLevel, char*, char*, int, void> Log_LogMessage;
}
/// <summary>
/// All methods in here are glued to the ScriptGlue.bf in the engine.
/// TODO: This could be auto-generated fairly easily
/// </summary>
internal static class ScriptGlue
internal static partial class ScriptGlue
{
#region Script Glueing infrastructure
static ScriptGlue()
{
NativeLibrary.SetDllImportResolver(typeof(ScriptGlue).Assembly, ImportResolver);
}
private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
if (libraryName == "__Internal")
{
// Lade die aktuelle exe selbst
return NativeLibrary.Load(Process.GetCurrentProcess().MainModule.FileName);
}
return IntPtr.Zero;
}
private static EngineFunctions _engineFunctions;
[UnmanagedCallersOnly]
public static unsafe void SetEngineFunctions(EngineFunctions* engineFunctions)
{
_engineFunctions = *engineFunctions;
Log.Info("Yeah");
}
private static AssemblyLoadContext? _scriptAssemblyContext;
private static Assembly? _appAssembly;
[UnmanagedCallersOnly]
public static unsafe void LoadScriptAssembly(byte* assemblyData, long assemblyLength, byte* pdbData, long pdbLength)
{
using UnmanagedMemoryStream assemblyStream = new(assemblyData, assemblyLength);
using UnmanagedMemoryStream? pdbStream = (pdbData != null) ? new UnmanagedMemoryStream(pdbData, pdbLength) : null;
LoadAssembly(assemblyStream, pdbStream);
}
private static void LoadAssembly(Stream assemblyStream, Stream? pdbStream)
{
try
{
_scriptAssemblyContext ??= new AssemblyLoadContext("ScriptContext", true);
_appAssembly = _scriptAssemblyContext.LoadFromStream(assemblyStream, pdbStream);
Debug.Assert(_appAssembly != null);
}
catch (Exception e)
{
Console.WriteLine($"Fehler: {e}");
}
}
[UnmanagedCallersOnly]
public static void UnloadAssemblies()
{
_scriptAssemblyContext?.Unload();
_scriptAssemblyContext = null;
}
struct ScriptClassInfo
{
public byte[] Name;
public Guid Guid;
}
private static NativeScriptClassInfo[]? _unsafeClasses;
struct NativeScriptClassInfo
{
public IntPtr Name;
public Guid Guid;
public ScriptMethods Methods;
public bool RunInEditMode;
}
[Flags]
public enum ScriptMethods
{
None = 0,
OnCreate = 0x1,
OnUpdate = 0x2,
OnDestroy = 0x4
}
[UnmanagedCallersOnly]
public static unsafe void GetScriptClasses(void** outBuffer, long* length)
{
using var contextualReflection = AssemblyLoadContext.EnterContextualReflection(_appAssembly);
Debug.Assert(_appAssembly != null);
Internal_FreeScriptClassNames();
var types = _appAssembly.GetTypes();
List<(string Name, Guid Guid, ScriptMethods AvailableMethods, bool runInEditMode)> scriptClasses = new();
foreach (var type in types)
{
if (type.IsSubclassOf(typeof(Entity)))
{
string? name = type.FullName;
if (name == null)
continue;
Guid guid = type.GUID;
ScriptMethods methods = ScriptMethods.None;
static ScriptMethods HasMethod(Type type, string methodName, ScriptMethods methodFlag)
{
MethodInfo? methodInfo = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
return methodInfo != null ? methodFlag : ScriptMethods.None;
}
methods |= HasMethod(type, nameof(Entity.OnCreate), ScriptMethods.OnCreate);
methods |= HasMethod(type, nameof(Entity.OnUpdate), ScriptMethods.OnUpdate);
methods |= HasMethod(type, nameof(Entity.OnDestroy), ScriptMethods.OnDestroy);
bool runInEditMode = type.HasCustomAttribute<RunInEditModeAttribute>();
scriptClasses.Add((name, guid, methods, runInEditMode));
}
}
_unsafeClasses = new NativeScriptClassInfo[scriptClasses.Count];
for (int i = 0; i < _unsafeClasses.Length; i++)
{
_unsafeClasses[i] = new NativeScriptClassInfo()
{
Guid = scriptClasses[i].Guid,
Name = Marshal.StringToCoTaskMemUTF8(scriptClasses[i].Name),
Methods = scriptClasses[i].AvailableMethods,
RunInEditMode = scriptClasses[i].runInEditMode
};
}
*outBuffer = (void*)Marshal.UnsafeAddrOfPinnedArrayElement(_unsafeClasses, 0);
*length = _unsafeClasses.Length;
}
[UnmanagedCallersOnly]
public static void FreeScriptClassNames()
{
Internal_FreeScriptClassNames();
}
internal static void Internal_FreeScriptClassNames()
{
if (_unsafeClasses == null)
return;
foreach (NativeScriptClassInfo info in _unsafeClasses)
{
Marshal.FreeCoTaskMem(info.Name);
}
_unsafeClasses = null;
}
private static Dictionary<UUID, (Entity Entity, Type Type)> _entityScripts = new();
[UnmanagedCallersOnly]
public static void ShowEntityEditor(UUID entityId)
{
(Entity entity, Type type) = _entityScripts[entityId];
EntityEditor.ShowEntityEditor(entity);
}
[UnmanagedCallersOnly]
public static void InvokeEntityOnCreate(UUID entityId)
{
try
{
(Entity entity, Type type) = _entityScripts[entityId];
entity.OnCreate();
}
catch (Exception e)
{
Console.WriteLine(e);
// TODO: Log exceptions to console
}
}
[UnmanagedCallersOnly]
public static void InvokeEntityOnUpdate(UUID entityId, float deltaTime)
{
// using var _ = AssemblyLoadContext.EnterContextualReflection(_appAssembly); // TODO: Check if reflection works correctly in entities
(Entity entity, Type type) = _entityScripts[entityId];
entity.OnUpdate(deltaTime);
}
[UnmanagedCallersOnly]
public static void InvokeEntityOnDestroy(UUID entityId, float deltaTime)
{
(Entity entity, Type type) = _entityScripts[entityId];
entity.OnDestroy();
}
[UnmanagedCallersOnly]
public static unsafe void CreateScriptInstance(UUID entityId, byte* scriptClassName)
{
using var _ = AssemblyLoadContext.EnterContextualReflection(_appAssembly);
Debug.Assert(_appAssembly != null);
string? typeName = Marshal.PtrToStringUTF8((IntPtr)scriptClassName);
Debug.Assert(typeName != null);
Type? scriptType = _appAssembly.GetType(typeName);
Debug.Assert(scriptType != null, "Script class Type not found.");
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.");
//
// if (scriptInstance != null)
// scriptInstance._uuid = entityId;
//ScriptFunctions functions = new();
_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)
{
if (method == null)
{
throw new ArgumentNullException(nameof(method));
}
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>
/// Create delegate by methodinfo in target
/// </summary>
/// <param name="method">method info</param>
/// <param name="target">A instance of the object which contains the method where will be execute</param>
/// <returns>delegate or null</returns>
public static Delegate? CreateDelegateWithTarget(MethodInfo? method, object? target)
{
if (method is null ||
target is null)
return null;
//if (method.IsStatic)
// return null;
if (method.IsGenericMethod)
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);
_modBuilder = asmBuilder.DefineDynamicModule(DelegateTypesAssemblyName);
_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();
Type delegateType = GetDelegateType(parameters, meth.ReturnType);
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
#region Log
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Log_LogMessage(Log.LogLevel logLevel, string message, string filePath, int line);
internal static unsafe void Log_LogMessage(Log.LogLevel logLevel, string message, string filePath, int line)
{
// unsafe
// {
// byte* mess = (byte*)Marshal.StringToCoTaskMemUTF8(message);
//
// fixed (char* messagePtr = message)
// fixed (char* filePathPtr = filePath)
// {
// _engineFunctions.Log_LogMessage(logLevel, messagePtr, filePathPtr, line);
// }
//
// Marshal.FreeCoTaskMem((IntPtr)mess);
// }
}
[MethodImpl(MethodImplOptions.InternalCall)]
@@ -148,7 +148,7 @@ public class DeserializationObject
}
[StructLayout(LayoutKind.Explicit)]
private struct DataHelper
public struct DataHelper
{
[StructLayout(LayoutKind.Sequential)]
public unsafe struct StringView