mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Start of using CoreCLR and automatic scriptglue generation
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
using NetHostBeef;
|
||||
using System;
|
||||
using System.Interop;
|
||||
using GlitchyEngine.Core;
|
||||
|
||||
namespace GlitchyEngine.Scripting;
|
||||
|
||||
public struct ScriptFunctionPointers
|
||||
{
|
||||
public function void() OnCreate;
|
||||
public function void(float) OnUpdate;
|
||||
public function void() OnDestroy;
|
||||
}
|
||||
|
||||
static class CoreClrHelper
|
||||
{
|
||||
static HostFxr.InitializeForDotnetCommandLineFn HostFxr_Init;
|
||||
static HostFxr.GetRuntimeDelegateFn HostFxr_GetDelegate;
|
||||
static HostFxr.CloseFn HostFxr_Close;
|
||||
|
||||
static CoreClr.LoadAssemblyAndGetFunctionPointerFn LoadAssemblyAndGetFunctionPointerFn;
|
||||
static CoreClr.GetFunctionPointerFn GetFunctionPointerFn;
|
||||
|
||||
private function void LoadScriptAssemblyFunc(uint8* appData, int64 appLength, uint8* pdbData, int64 pdbLength);
|
||||
|
||||
//static function void(char8* path) LoadScriptAssembly;
|
||||
static LoadScriptAssemblyFunc _loadScriptAssembly;
|
||||
static function void() _unloadAssemblies;
|
||||
|
||||
// public static unsafe void GetScriptClasses(void** outBuffer, long* length)
|
||||
private function void GetScriptClassesFunc(void** outBuffer, int64* length);
|
||||
static GetScriptClassesFunc _getScriptClasses;
|
||||
static function void() _freeScriptClassNames;
|
||||
|
||||
//public static unsafe ScriptFunctions CreateScriptInstance(UUID entityId, byte* scriptClassName)
|
||||
private function void CreateScriptInstanceFunc(UUID entityId, char8* scriptClassName);
|
||||
static CreateScriptInstanceFunc _createScriptInstance;
|
||||
|
||||
public static ScriptFunctionPointers _entityScriptFunctions;
|
||||
|
||||
public static void Init(StringView coreAssemblyPath)
|
||||
{
|
||||
LoadHostFxr();
|
||||
InitAndStartRuntime(coreAssemblyPath);
|
||||
|
||||
PrepareFunction();
|
||||
}
|
||||
|
||||
static void LoadHostFxr()
|
||||
{
|
||||
char_t[256] buffer = ?;
|
||||
c_size bufferSize = buffer.Count;
|
||||
int rc = NetHost.get_hostfxr_path(&buffer, &bufferSize, null);
|
||||
|
||||
Log.EngineLogger.AssertDebug(rc == 0, "Failed to get HostFxr path.");
|
||||
|
||||
// Load hostfxr and get desired exports
|
||||
void* lib = NetHostHelper.LoadLibrary(&buffer);
|
||||
HostFxr_Init = NetHostHelper.GetExport<HostFxr.InitializeForDotnetCommandLineFn>(lib, "hostfxr_initialize_for_dotnet_command_line");
|
||||
HostFxr_GetDelegate = NetHostHelper.GetExport<HostFxr.GetRuntimeDelegateFn>(lib, "hostfxr_get_runtime_delegate");
|
||||
HostFxr_Close = NetHostHelper.GetExport<HostFxr.CloseFn>(lib, "hostfxr_close");
|
||||
|
||||
Log.EngineLogger.AssertDebug(HostFxr_Init != null && HostFxr_GetDelegate != null && HostFxr_Close != null, "Retrieving at least one HostFxr function failed.");
|
||||
}
|
||||
|
||||
static void InitAndStartRuntime(StringView coreAssembly)
|
||||
{
|
||||
char_t* ptr = NetHostHelper.GetScopedRawPtr!(coreAssembly);
|
||||
|
||||
char_t*[2] args = char_t*[](
|
||||
ptr,
|
||||
null
|
||||
);
|
||||
|
||||
HostFxr.Handle cxt = null;
|
||||
int rc = HostFxr_Init(1, &args, null, &cxt);
|
||||
|
||||
if (rc != 0 || cxt == null)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to initialize HostFxr: {rc}");
|
||||
HostFxr_Close(cxt);
|
||||
}
|
||||
|
||||
Log.EngineLogger.Assert(rc == 0, "Failed to initialize HostFxr.");
|
||||
|
||||
rc = HostFxr_GetDelegate(cxt, .LoadAssemblyAndGetFunctionPointer, (void**)&LoadAssemblyAndGetFunctionPointerFn);
|
||||
|
||||
Log.EngineLogger.Assert(rc == 0, scope $"Get delegate failed: {rc}. (LoadAssemblyAndGetFunctionPointer)");
|
||||
|
||||
rc = HostFxr_GetDelegate(cxt, .GetFunctionPointer, (void**)&GetFunctionPointerFn);
|
||||
|
||||
Log.EngineLogger.Assert(rc == 0, scope $"Get delegate failed: {rc}. (GetFunctionPointer)");
|
||||
|
||||
HostFxr_Close(cxt);
|
||||
}
|
||||
|
||||
static void PrepareFunction()
|
||||
{
|
||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "LoadScriptAssembly", out _loadScriptAssembly);
|
||||
|
||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "UnloadAssemblies", out _unloadAssemblies);
|
||||
|
||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "GetScriptClasses", out _getScriptClasses);
|
||||
|
||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "FreeScriptClassNames", out _freeScriptClassNames);
|
||||
|
||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "CreateScriptInstance", out _createScriptInstance);
|
||||
|
||||
InitEntityFunctions();
|
||||
}
|
||||
|
||||
|
||||
static void InitEntityFunctions()
|
||||
{
|
||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "InvokeEntityOnCreate", out _entityScriptFunctions.OnCreate);
|
||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "InvokeEntityOnUpdate", out _entityScriptFunctions.OnUpdate);
|
||||
GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "InvokeEntityOnDestroy", out _entityScriptFunctions.OnDestroy);
|
||||
}
|
||||
|
||||
public static void LoadAppAssembly(Span<uint8> appAssemblyData, Span<uint8> pdbData)
|
||||
{
|
||||
_loadScriptAssembly(appAssemblyData.Ptr, appAssemblyData.Length, pdbData.Ptr, pdbData.Length);
|
||||
}
|
||||
|
||||
public static void UnloadAssemblies()
|
||||
{
|
||||
_unloadAssemblies();
|
||||
}
|
||||
|
||||
/// Gets an array of script class infos. Use FreeScriptClassNames to release the buffer.
|
||||
public static void GetScriptClasses(out void* outBuffer, out int64 entryCount)
|
||||
{
|
||||
outBuffer = null;
|
||||
entryCount = 0;
|
||||
_getScriptClasses(&outBuffer, &entryCount);
|
||||
}
|
||||
|
||||
/// Releases the buffer created by GetScriptClasses
|
||||
public static void FreeScriptClassNames()
|
||||
{
|
||||
_freeScriptClassNames();
|
||||
}
|
||||
|
||||
public static void CreateScriptInstance(UUID entityId, StringView scriptName)
|
||||
{
|
||||
_createScriptInstance(entityId, scriptName.Ptr);
|
||||
}
|
||||
|
||||
public static int GetFunctionPointer<T>(StringView typeName, StringView methodName, StringView delegateName, out T outDelegate) where T: operator explicit void*
|
||||
{
|
||||
void* funPtr = null;
|
||||
|
||||
int rc = GetFunctionPointerFn(NetHostHelper.GetScopedRawPtr!(typeName),
|
||||
NetHostHelper.GetScopedRawPtr!(methodName), NetHostHelper.GetScopedRawPtr!(delegateName),
|
||||
null, null, out funPtr);
|
||||
|
||||
outDelegate = (T)funPtr;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static int GetFunctionPointerDefaultDelegate(StringView typeName, StringView methodName, out CoreClr.DefaultEntryPoint outDelegate)
|
||||
{
|
||||
void* funPtr = null;
|
||||
|
||||
int rc = GetFunctionPointerFn(NetHostHelper.GetScopedRawPtr!(typeName),
|
||||
NetHostHelper.GetScopedRawPtr!(methodName), null, null, null, out funPtr);
|
||||
|
||||
outDelegate = (CoreClr.DefaultEntryPoint)funPtr;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static int GetFunctionPointerUnmanagedCallersOnly<T>(StringView typeName, StringView methodName, out T outDelegate) where T: operator explicit void*
|
||||
{
|
||||
void* funPtr = null;
|
||||
|
||||
int rc = GetFunctionPointerFn(NetHostHelper.GetScopedRawPtr!(typeName),
|
||||
NetHostHelper.GetScopedRawPtr!(methodName), CoreClr.UNMANAGEDCALLERSONLY_METHOD, null, null, out funPtr);
|
||||
|
||||
outDelegate = (T)funPtr;
|
||||
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,28 @@
|
||||
using System;
|
||||
using Mono;
|
||||
using GlitchyEngine.Core;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GlitchyEngine.Scripting;
|
||||
|
||||
using internal GlitchyEngine.Scripting;
|
||||
|
||||
class EntityEditorWrapper : ScriptClass
|
||||
class EntityEditorWrapper : NewScriptClass
|
||||
{
|
||||
function void ShowEntityEditorFunc(MonoObject* scriptInstance, MonoException** exception);
|
||||
function void ShowEntityEditorFunc(UUID entityId);
|
||||
|
||||
private ShowEntityEditorFunc _showEntityEditorFunc;
|
||||
|
||||
[AllowAppend]
|
||||
public this(StringView classNamespace, StringView className, MonoImage* image) : base(classNamespace, className, image)
|
||||
public this() : base(FullName, .Empty, .None, false)
|
||||
{
|
||||
_showEntityEditorFunc = (ShowEntityEditorFunc)GetMethodThunk("ShowEntityEditor", 1);
|
||||
|
||||
if (_showEntityEditorFunc == null)
|
||||
{
|
||||
Log.EngineLogger.Error("Entity editor has no show entity editor func.");
|
||||
}
|
||||
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "ShowEntityEditor", out _showEntityEditorFunc);
|
||||
|
||||
Debug.Assert(_showEntityEditorFunc != null);
|
||||
}
|
||||
|
||||
public void ShowEntityEditor(ScriptInstance instance, UUID entityId)
|
||||
public void ShowEntityEditor(NewScriptInstance instance, UUID entityId)
|
||||
{
|
||||
MonoException* exception = null;
|
||||
|
||||
_showEntityEditorFunc(instance.MonoInstance, &exception);
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
ScriptEngine.HandleMonoException(exception, entityId);
|
||||
}
|
||||
_showEntityEditorFunc(entityId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using GlitchyEngine.Core;
|
||||
using static GlitchyEngine.Scripting.ScriptEngine;
|
||||
namespace GlitchyEngine.Scripting;
|
||||
|
||||
class NewScriptClass
|
||||
{
|
||||
public String FullName ~ delete _;
|
||||
public StringView Name;
|
||||
public Guid Guid;
|
||||
public ScriptMethods Methods;
|
||||
|
||||
public bool RunInEditMode;
|
||||
|
||||
public this(StringView fullName, Guid guid, ScriptMethods methods, bool runInEditMode = false)
|
||||
{
|
||||
FullName = new String(fullName);
|
||||
Guid = guid;
|
||||
|
||||
int lastDotIndex = FullName.LastIndexOf('.');
|
||||
|
||||
if (lastDotIndex == -1)
|
||||
Name = FullName;
|
||||
else
|
||||
Name = FullName.Substring(lastDotIndex + 1);
|
||||
|
||||
Methods = methods;
|
||||
|
||||
RunInEditMode = runInEditMode;
|
||||
}
|
||||
}
|
||||
|
||||
using internal GlitchyEngine.Scripting;
|
||||
|
||||
class NewScriptInstance : RefCounter
|
||||
{
|
||||
private NewScriptClass _scriptClass;
|
||||
|
||||
private UUID _entityId;
|
||||
|
||||
private bool _isCreated = false;
|
||||
|
||||
public NewScriptClass ScriptClass => _scriptClass;
|
||||
|
||||
/// Gets whether or not the instance has ben initialized.
|
||||
public bool IsInitialized {get; private set;};
|
||||
|
||||
/// Gets whether or not the Create-Method of this instance has been called before.
|
||||
public bool IsCreated => _isCreated;
|
||||
|
||||
public UUID EntityId => _entityId;
|
||||
|
||||
public this(UUID entityId, NewScriptClass scriptClass)
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(scriptClass != null);
|
||||
|
||||
_entityId = entityId;
|
||||
_scriptClass = scriptClass;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
public void Instantiate()
|
||||
{
|
||||
CoreClrHelper.CreateScriptInstance(_entityId, _scriptClass.FullName);
|
||||
IsInitialized = true;
|
||||
}
|
||||
|
||||
public void InvokeOnCreate()
|
||||
{
|
||||
if (_scriptClass.Methods.HasFlag(.OnCreate))
|
||||
CoreClrHelper._entityScriptFunctions.OnCreate();
|
||||
}
|
||||
|
||||
public void InvokeOnUpdate(float deltaTime)
|
||||
{
|
||||
if (_scriptClass.Methods.HasFlag(.OnUpdate))
|
||||
CoreClrHelper._entityScriptFunctions.OnUpdate(deltaTime);
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
if (!IsInitialized)
|
||||
return;
|
||||
|
||||
if (_scriptClass.Methods.HasFlag(.OnDestroy) && (ScriptEngine.ApplicationInfo.IsInPlayMode || ScriptClass.RunInEditMode))
|
||||
{
|
||||
CoreClrHelper._entityScriptFunctions.OnDestroy();
|
||||
}
|
||||
|
||||
IsInitialized = false;
|
||||
|
||||
ScriptEngine.UnregisterScriptInstance(_entityId);
|
||||
}
|
||||
}
|
||||
@@ -81,8 +81,8 @@ class ScriptClass : SharpClass
|
||||
// TODO: This is only relevant for the editor!
|
||||
MonoCustomAttrInfo* attributes = Mono.mono_custom_attrs_from_class(_monoClass);
|
||||
|
||||
if (attributes != null && ScriptEngine.Classes.RunInEditModeAttribute != null)
|
||||
_runInEditMode = Mono.mono_custom_attrs_has_attr(attributes, ScriptEngine.Classes.RunInEditModeAttribute._monoClass);
|
||||
//if (attributes != null && ScriptEngine.Classes.RunInEditModeAttribute != null)
|
||||
// _runInEditMode = Mono.mono_custom_attrs_has_attr(attributes, ScriptEngine.Classes.RunInEditModeAttribute._monoClass);
|
||||
}
|
||||
|
||||
private MonoMethod* FindConstructor()
|
||||
@@ -148,8 +148,8 @@ class ScriptClass : SharpClass
|
||||
|
||||
if (_onCollisionEnter2D != null)
|
||||
{
|
||||
MonoObject* monoObject = ScriptEngine.Classes.Collision2D.BoxValue(collision);
|
||||
_onCollisionEnter2D(instance, monoObject, &exception);
|
||||
//MonoObject* monoObject = ScriptEngine.Classes.Collision2D.BoxValue(collision);
|
||||
//_onCollisionEnter2D(instance, monoObject, &exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +159,8 @@ class ScriptClass : SharpClass
|
||||
|
||||
if (_onCollisionEnter2D != null)
|
||||
{
|
||||
MonoObject* monoObject = ScriptEngine.Classes.Collision2D.BoxValue(collision);
|
||||
_onCollisionLeave2D(instance, monoObject, &exception);
|
||||
//MonoObject* monoObject = ScriptEngine.Classes.Collision2D.BoxValue(collision);
|
||||
//_onCollisionLeave2D(instance, monoObject, &exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,31 +15,31 @@ using internal GlitchyEngine.Scripting;
|
||||
class EngineClasses
|
||||
{
|
||||
// TODO: Not all of these are actually ScriptClasses (actually none of them are...)
|
||||
|
||||
private ScriptClass s_ComponentRoot;
|
||||
private ScriptClass s_EntityRoot;
|
||||
private ScriptClass s_EngineObject;
|
||||
|
||||
private NewScriptClass s_EngineObject;
|
||||
private NewScriptClass s_ComponentRoot;
|
||||
private NewScriptClass s_EntityRoot;
|
||||
|
||||
private EntityEditorWrapper s_EntityEditor;
|
||||
|
||||
private EntitySerializerWrapper s_EntitySerializer;
|
||||
private ScriptClass s_SerializationContext;
|
||||
//private NewScriptClass s_SerializationContext;
|
||||
|
||||
private ScriptClass s_Collision2D;
|
||||
private NewScriptClass s_Collision2D;
|
||||
|
||||
private ScriptClass s_RunInEditModeAttribute;
|
||||
|
||||
public ScriptClass ComponentRoot => s_ComponentRoot;
|
||||
public ScriptClass EntityRoot => s_EntityRoot;
|
||||
public ScriptClass EngineObject => s_EngineObject;
|
||||
private NewScriptClass s_RunInEditModeAttribute;
|
||||
|
||||
public NewScriptClass EngineObject => s_EngineObject;
|
||||
public NewScriptClass ComponentRoot => s_ComponentRoot;
|
||||
public NewScriptClass EntityRoot => s_EntityRoot;
|
||||
|
||||
public EntityEditorWrapper EntityEditor => s_EntityEditor;
|
||||
|
||||
public EntitySerializerWrapper EntitySerializer => s_EntitySerializer;
|
||||
|
||||
public ScriptClass Collision2D => s_Collision2D;
|
||||
public NewScriptClass Collision2D => s_Collision2D;
|
||||
|
||||
public ScriptClass RunInEditModeAttribute => s_RunInEditModeAttribute;
|
||||
public NewScriptClass RunInEditModeAttribute => s_RunInEditModeAttribute;
|
||||
|
||||
public ~this()
|
||||
{
|
||||
@@ -48,37 +48,37 @@ class EngineClasses
|
||||
|
||||
internal void ReleaseAndNullify()
|
||||
{
|
||||
ReleaseRefAndNullify!(s_EngineObject);
|
||||
ReleaseRefAndNullify!(s_EntityRoot);
|
||||
ReleaseRefAndNullify!(s_ComponentRoot);
|
||||
DeleteAndNullify!(s_EngineObject);
|
||||
DeleteAndNullify!(s_EntityRoot);
|
||||
DeleteAndNullify!(s_ComponentRoot);
|
||||
|
||||
ReleaseRefAndNullify!(s_EntityEditor);
|
||||
DeleteAndNullify!(s_EntityEditor);
|
||||
|
||||
ReleaseRefAndNullify!(s_EntitySerializer);
|
||||
ReleaseRefAndNullify!(s_SerializationContext);
|
||||
//ReleaseRefAndNullify!(s_SerializationContext);
|
||||
|
||||
ReleaseRefAndNullify!(s_Collision2D);
|
||||
DeleteAndNullify!(s_Collision2D);
|
||||
|
||||
ReleaseRefAndNullify!(s_RunInEditModeAttribute);
|
||||
DeleteAndNullify!(s_RunInEditModeAttribute);
|
||||
}
|
||||
|
||||
internal void LoadClasses(MonoImage* image)
|
||||
internal void LoadClasses()
|
||||
{
|
||||
ReleaseAndNullify();
|
||||
|
||||
s_EngineObject = new ScriptClass("GlitchyEngine.Core", "EngineObject", image);
|
||||
s_EntityRoot = new ScriptClass("GlitchyEngine", "Entity", image);
|
||||
s_ComponentRoot = new ScriptClass("GlitchyEngine.Core", "Component", image);
|
||||
s_EngineObject = new NewScriptClass("GlitchyEngine.Core.EngineObject", .Empty, .None);
|
||||
s_EntityRoot = new NewScriptClass("GlitchyEngine.Core.Entity", .Empty, .None);
|
||||
s_ComponentRoot = new NewScriptClass("GlitchyEngine.Core.Component", .Empty, .None);
|
||||
|
||||
// Editor classes
|
||||
s_EntityEditor = new EntityEditorWrapper("GlitchyEngine.Editor", "EntityEditor", image);
|
||||
s_EntityEditor = new EntityEditorWrapper();
|
||||
|
||||
s_EntitySerializer = new EntitySerializerWrapper("GlitchyEngine.Serialization", "EntitySerializer", image);
|
||||
//s_EntitySerializer = new EntitySerializerWrapper("GlitchyEngine.Serialization", "EntitySerializer");
|
||||
|
||||
s_Collision2D = new ScriptClass("GlitchyEngine.Physics", "Collision2D", image);
|
||||
s_Collision2D = new NewScriptClass("GlitchyEngine.Physics.Collision2D", .Empty, .None);
|
||||
|
||||
// Attributes
|
||||
s_RunInEditModeAttribute = new ScriptClass("GlitchyEngine.Editor", "RunInEditModeAttribute", image);
|
||||
s_RunInEditModeAttribute = new NewScriptClass("GlitchyEngine.Editor.RunInEditModeAttribute", .Empty, .None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,9 +99,9 @@ static class ScriptEngine
|
||||
|
||||
public static EngineClasses Classes => _classes;
|
||||
|
||||
private static Dictionary<StringView, ScriptClass> _entityScripts = new .() ~ DeleteDictionaryAndReleaseValues!(_);
|
||||
private static Dictionary<StringView, NewScriptClass> _entityScripts = new .() ~ DeleteDictionaryAndValues!(_);
|
||||
|
||||
internal static Dictionary<UUID, ScriptInstance> _entityScriptInstances = new .() ~ {
|
||||
internal static Dictionary<UUID, NewScriptInstance> _entityScriptInstances = new .() ~ {
|
||||
for (var entry in _)
|
||||
{
|
||||
entry.value?.ReleaseRef();
|
||||
@@ -109,7 +109,7 @@ static class ScriptEngine
|
||||
delete _;
|
||||
}
|
||||
|
||||
public static Dictionary<StringView, ScriptClass> EntityClasses => _entityScripts;
|
||||
public static Dictionary<StringView, NewScriptClass> EntityClasses => _entityScripts;
|
||||
|
||||
/// Gets or sets the current scene context for the runtime.
|
||||
public static Scene Context
|
||||
@@ -189,7 +189,8 @@ static class ScriptEngine
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
InitMono();
|
||||
InitRuntime();
|
||||
//InitMono();
|
||||
|
||||
LoadScriptAssemblies();
|
||||
}
|
||||
@@ -202,7 +203,17 @@ static class ScriptEngine
|
||||
ReloadAssemblies();
|
||||
}
|
||||
|
||||
static void InitMono()
|
||||
static void InitRuntime()
|
||||
{
|
||||
// TODO: We currently have the utility methods (e.g. assembly loading) in ScriptCore,
|
||||
// the problem is, that CoreCLR currently can't unload the initial assembly.
|
||||
// this means that we cannot easily reload changes to ScriptCore.
|
||||
// Since this basic infrastructure shouldn't really ever change we could make a tiny
|
||||
// library with only that enabling us to reload script core.
|
||||
CoreClrHelper.Init("resources/scripts/ScriptCore.dll");
|
||||
}
|
||||
|
||||
/*static void InitMono()
|
||||
{
|
||||
Mono.mono_set_assemblies_path("mono/lib/4.5");
|
||||
|
||||
@@ -226,7 +237,7 @@ static class ScriptEngine
|
||||
}
|
||||
|
||||
Mono.mono_thread_set_main(Mono.mono_thread_current());
|
||||
}
|
||||
}*/
|
||||
|
||||
static bool _requestingReload = false;
|
||||
|
||||
@@ -278,26 +289,27 @@ static class ScriptEngine
|
||||
|
||||
ScriptGlue.Init();
|
||||
|
||||
CreateAppDomain("GlitchyEngineScriptRuntime");
|
||||
(s_CoreAssembly, s_CoreAssemblyImage) = LoadAssembly("resources/scripts/ScriptCore.dll", _debuggingEnabled);
|
||||
|
||||
// TODO: Check if files exist
|
||||
if (File.Exists(_appAssemblyPath))
|
||||
(s_AppAssembly, s_AppAssemblyImage) = LoadAssembly(_appAssemblyPath, _debuggingEnabled);
|
||||
else
|
||||
{
|
||||
s_AppAssembly = null;
|
||||
s_AppAssemblyImage = null;
|
||||
List<uint8> data = new:ScopedAlloc! List<uint8>(1024);
|
||||
File.ReadAll(_appAssemblyPath, data);
|
||||
|
||||
List<uint8> pdbData = new:ScopedAlloc! List<uint8>(1024);
|
||||
|
||||
String pdbPath = scope .();
|
||||
Path.ChangeExtension(_appAssemblyPath, ".pdb", pdbPath);
|
||||
|
||||
File.ReadAll(pdbPath, pdbData);
|
||||
|
||||
CoreClrHelper.LoadAppAssembly(data, pdbData);
|
||||
|
||||
GetEntitiesFromAssemblies();
|
||||
|
||||
//ScriptGlue.RegisterManagedComponents();
|
||||
}
|
||||
|
||||
Classes.LoadClasses(s_CoreAssemblyImage);
|
||||
|
||||
ClearDictionaryAndReleaseValues!(_entityScripts);
|
||||
|
||||
GetEntitiesFromAssemblies();
|
||||
|
||||
ScriptGlue.RegisterManagedComponents();
|
||||
|
||||
InitAssemblyWatcher();
|
||||
//InitAssemblyWatcher();
|
||||
}
|
||||
|
||||
/// Starts the script runtime and sets the context scene.
|
||||
@@ -338,14 +350,16 @@ static class ScriptEngine
|
||||
/// Disposes of and replaces the old instance, if one exists.
|
||||
public static bool InitializeInstance(Entity entity, ScriptComponent* script)
|
||||
{
|
||||
ScriptClass scriptClass = GetScriptClass(script.ScriptClassName);
|
||||
Log.EngineLogger.Error($"{Compiler.CallerMemberName} not updated yet.");
|
||||
|
||||
NewScriptClass scriptClass = GetScriptClass(script.ScriptClassName);
|
||||
|
||||
if (scriptClass == null)
|
||||
return false;
|
||||
|
||||
UUID entityId = entity.UUID;
|
||||
|
||||
script.Instance = new ScriptInstance(entityId, scriptClass);
|
||||
script.Instance = new NewScriptInstance(entityId, scriptClass);
|
||||
script.Instance..ReleaseRef();
|
||||
|
||||
if (_entityScriptInstances.TryGetValue(entityId, let currentInstance))
|
||||
@@ -353,7 +367,7 @@ static class ScriptEngine
|
||||
|
||||
_entityScriptInstances[entityId] = script.Instance..AddRef();
|
||||
|
||||
script.Instance.Instantiate(entityId);
|
||||
script.Instance.Instantiate();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -381,109 +395,50 @@ static class ScriptEngine
|
||||
_entityScriptInstances.Remove(entityId);
|
||||
}
|
||||
|
||||
private static MonoAssembly* LoadCSharpAssembly(StringView assemblyPath, bool loadPDB = false)
|
||||
public enum ScriptMethods : uint32
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
List<uint8> data = new List<uint8>(1024);
|
||||
|
||||
File.ReadAll(assemblyPath, data);
|
||||
|
||||
// NOTE: We can't use this image for anything other than loading the assembly because this image doesn't have a reference to the assembly
|
||||
MonoImageOpenStatus status = .ImageInvalid;
|
||||
MonoImage* image = Mono.mono_image_open_from_data_full(data.Ptr, (.)data.Count, true, &status, false);
|
||||
|
||||
delete data;
|
||||
|
||||
if (status != .Ok)
|
||||
{
|
||||
char8* errorMessage = Mono.mono_image_strerror(status);
|
||||
|
||||
Log.EngineLogger.Error($"Failed to load C# Assembly: \"{StringView(errorMessage)}\"");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (loadPDB)
|
||||
{
|
||||
String pdbPath = scope .();
|
||||
Path.ChangeExtension(assemblyPath, ".pdb", pdbPath);
|
||||
|
||||
if (File.Exists(pdbPath))
|
||||
{
|
||||
Log.EngineLogger.Trace($"Loading PDB \"{pdbPath}\"...");
|
||||
|
||||
List<uint8> pdbData = new List<uint8>(1024);
|
||||
|
||||
let result = File.ReadAll(pdbPath, pdbData);
|
||||
|
||||
if (result case .Err(let error))
|
||||
{
|
||||
Log.EngineLogger.Error("Failed to load PDB file ({error}).");
|
||||
}
|
||||
|
||||
Mono.mono_debug_open_image_from_memory(image, pdbData.Ptr, (int32)pdbData.Count);
|
||||
|
||||
delete pdbData;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Warning($"Debugging enabled but no PDB-File found \"{assemblyPath}\".");
|
||||
}
|
||||
}
|
||||
|
||||
MonoAssembly* assembly = Mono.mono_assembly_load_from_full(image, assemblyPath.ToScopeCStr!(), &status, 0);
|
||||
|
||||
Mono.mono_image_close(image);
|
||||
|
||||
return assembly;
|
||||
None = 0,
|
||||
OnCreate = 0x1,
|
||||
OnUpdate = 0x2,
|
||||
OnDestroy = 0x4,
|
||||
}
|
||||
|
||||
static void CreateAppDomain(StringView name)
|
||||
struct ScriptClassInfo
|
||||
{
|
||||
s_AppDomain = Mono.mono_domain_create_appdomain(name.ToScopeCStr!(), null);
|
||||
Mono.mono_domain_set(s_AppDomain, true);
|
||||
}
|
||||
|
||||
static (MonoAssembly* assembly, MonoImage* image) LoadAssembly(StringView filepath, bool loadPDB = false)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
MonoAssembly* assembly = LoadCSharpAssembly(filepath, loadPDB);
|
||||
MonoImage* image = Mono.mono_assembly_get_image(assembly);
|
||||
|
||||
return (assembly, image);
|
||||
public char8* Name;
|
||||
public Guid Guid;
|
||||
public ScriptMethods Methods;
|
||||
public bool RunInEditMode;
|
||||
}
|
||||
|
||||
private static void GetEntitiesFromAssemblies()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
if (s_AppAssemblyImage == null)
|
||||
return;
|
||||
// TODO: We should probably verify, that all required types actually exist.
|
||||
Classes.LoadClasses();
|
||||
|
||||
MonoTableInfo* typeDefinitionsTable = Mono.mono_image_get_table_info(s_AppAssemblyImage, .MONO_TABLE_TYPEDEF);
|
||||
int32 numTypes = Mono.mono_table_info_get_rows(typeDefinitionsTable);
|
||||
CoreClrHelper.GetScriptClasses(let data, let entryCount);
|
||||
|
||||
Span<ScriptClassInfo> scriptClasses = .((.)data, entryCount);
|
||||
|
||||
List<NewScriptClass> newClasses = scope .();
|
||||
|
||||
for (int32 i = 0; i < numTypes; i++)
|
||||
{
|
||||
int32[(.)SOME_RANDOM_ENUM.MONO_TYPEDEF_SIZE] cols = .();
|
||||
Mono.mono_metadata_decode_row(typeDefinitionsTable, i, (.)&cols, (.)SOME_RANDOM_ENUM.MONO_TYPEDEF_SIZE);
|
||||
ClearDictionaryAndDeleteValues!(_entityScripts);
|
||||
|
||||
char8* nameSpace = Mono.mono_metadata_string_heap(s_AppAssemblyImage, (.)cols[(.)SOME_RANDOM_ENUM.MONO_TYPEDEF_NAMESPACE]);
|
||||
char8* name = Mono.mono_metadata_string_heap(s_AppAssemblyImage, (.)cols[(.)SOME_RANDOM_ENUM.MONO_TYPEDEF_NAME]);
|
||||
|
||||
MonoClass* monoClass = Mono.mono_class_from_name(s_AppAssemblyImage, nameSpace, name);
|
||||
|
||||
// Check if it is an entity
|
||||
if (monoClass != null && Mono.mono_class_is_subclass_of(monoClass, Classes.EntityRoot.[Friend]_monoClass, false))
|
||||
{
|
||||
ScriptClass entityScript = new ScriptClass(StringView(nameSpace), StringView(name), s_AppAssemblyImage);
|
||||
_entityScripts.Add(entityScript.FullName, entityScript);
|
||||
for (var entry in scriptClasses)
|
||||
{
|
||||
NewScriptClass scriptClass = new .(StringView(entry.Name), entry.Guid, entry.Methods, entry.RunInEditMode);
|
||||
|
||||
Log.EngineLogger.Info($"Added entity \"{entityScript.FullName}\"");
|
||||
}
|
||||
}
|
||||
newClasses.Add(scriptClass);
|
||||
}
|
||||
|
||||
for (var scriptClass in newClasses)
|
||||
{
|
||||
_entityScripts.Add(scriptClass.FullName, scriptClass);
|
||||
}
|
||||
|
||||
CoreClrHelper.FreeScriptClassNames();
|
||||
}
|
||||
|
||||
public static void ReloadAssemblies()
|
||||
@@ -492,20 +447,20 @@ static class ScriptEngine
|
||||
|
||||
Log.EngineLogger.Info("Reloading script assemblies.");
|
||||
|
||||
|
||||
ScriptInstanceSerializer contextSerializer = scope .();
|
||||
|
||||
contextSerializer.SerializeScriptInstances();
|
||||
|
||||
Mono.mono_domain_set(s_RootDomain, true);
|
||||
|
||||
Mono.mono_domain_unload(s_AppDomain);
|
||||
// TODO: Unload script assemblies, remove class handles, fire unload events?
|
||||
|
||||
LoadScriptAssemblies();
|
||||
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("Initialize Instances");
|
||||
|
||||
// We need to create a new instance for every entity
|
||||
Log.EngineLogger.Error("Recreating and copying instances (ReloadAssemblies) not updated yet.");
|
||||
/*// We need to create a new instance for every entity
|
||||
for (let (id, scriptInstance) in _entityScriptInstances)
|
||||
{
|
||||
if (Context.GetEntityByID(id) case .Ok(let entity))
|
||||
@@ -517,7 +472,7 @@ static class ScriptEngine
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(false, "Entities script was just serialized but the entity doesn't exist anymore.");
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
contextSerializer.DeserializeScriptInstances();
|
||||
@@ -527,25 +482,25 @@ static class ScriptEngine
|
||||
|
||||
public static void Shutdown()
|
||||
{
|
||||
Mono.mono_domain_set(s_RootDomain, false);
|
||||
/*Mono.mono_domain_set(s_RootDomain, false);
|
||||
|
||||
Mono.mono_domain_unload(s_AppDomain);
|
||||
s_AppDomain = null;
|
||||
|
||||
Mono.mono_jit_cleanup(s_RootDomain);
|
||||
s_RootDomain = null;
|
||||
s_RootDomain = null;*/
|
||||
}
|
||||
|
||||
/// Returns the script instance or null.
|
||||
public static MonoObject* GetManagedInstance(UUID entityId)
|
||||
{
|
||||
if (_entityScriptInstances.TryGetValue(entityId, let scriptInstance))
|
||||
return scriptInstance.MonoInstance;
|
||||
//if (_entityScriptInstances.TryGetValue(entityId, let scriptInstance))
|
||||
// return scriptInstance.MonoInstance;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ScriptClass GetScriptClass(StringView name)
|
||||
public static NewScriptClass GetScriptClass(StringView name)
|
||||
{
|
||||
EntityClasses.TryGetValue(name, let scriptClass);
|
||||
|
||||
|
||||
@@ -14,7 +14,10 @@ using GlitchyEngine.Editor;
|
||||
using GlitchyEngine.World.Components;
|
||||
using GlitchyEngine.Content;
|
||||
using GlitchyEngine.Renderer;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using static GlitchyEngine.Renderer.Text.FontRenderer;
|
||||
using System.Linq;
|
||||
|
||||
namespace GlitchyEngine.Scripting;
|
||||
|
||||
@@ -39,22 +42,250 @@ class MessageOrigin
|
||||
}
|
||||
}
|
||||
|
||||
struct RegisterCallAttribute : Attribute
|
||||
{
|
||||
public String MethodName;
|
||||
|
||||
public this(String methodName)
|
||||
{
|
||||
MethodName = methodName;
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeTranslationTemplate
|
||||
{
|
||||
public String StartCode;
|
||||
public String EndCode;
|
||||
public String PrettyCSharpParamType;
|
||||
|
||||
public this(String startCode, String endCode, String prettyCSharpParamType)
|
||||
{
|
||||
StartCode = startCode;
|
||||
EndCode = endCode;
|
||||
PrettyCSharpParamType = prettyCSharpParamType;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(.Struct)]
|
||||
struct EngineFunctionsGeneratorAttribute : Attribute, IComptimeTypeApply
|
||||
{
|
||||
private static readonly Dictionary<String, String> BeefToCsharpTypeMap = new .()
|
||||
{
|
||||
("int32", "int"),
|
||||
("char16*", "char*"),
|
||||
("char8*", "byte*"),
|
||||
("GlitchyEngine.Events.MouseButton", "GlitchyEngine.MouseButton")
|
||||
};
|
||||
|
||||
// {0}... Out name,
|
||||
// {1}... In name
|
||||
private static readonly Dictionary<String, TypeTranslationTemplate> BeefTypeToCSharpWrapperTemplate = new .()
|
||||
{
|
||||
("char8*", .("byte* {0} = (byte*)Marshal.StringToCoTaskMemUTF8({1});", "Marshal.FreeCoTaskMem((IntPtr){0});", "string")),
|
||||
("char16*", .("fixed (char* {0} = {1})\n{{", "}}", "string")),
|
||||
};
|
||||
|
||||
private static readonly Dictionary<String, TypeTranslationTemplate> CSharpTypeToReturnValueTemplate = new .()
|
||||
{
|
||||
("void", .("", "", "void")),
|
||||
};
|
||||
|
||||
private static String GetCSharpInterfaceType(String beefType)
|
||||
{
|
||||
if (BeefToCsharpTypeMap.TryGetValueAlt(beefType, let csharpType))
|
||||
{
|
||||
return csharpType;
|
||||
}
|
||||
|
||||
return beefType;
|
||||
}
|
||||
|
||||
private static TypeTranslationTemplate GetCSharpWrapperTemplate(String beefType)
|
||||
{
|
||||
if (BeefTypeToCSharpWrapperTemplate.TryGetValueAlt(beefType, let template))
|
||||
{
|
||||
return template;
|
||||
}
|
||||
|
||||
return .("", "", GetCSharpInterfaceType(beefType));
|
||||
}
|
||||
|
||||
private static TypeTranslationTemplate GetCSharpReturnValueTemplate(String beefType)
|
||||
{
|
||||
if (CSharpTypeToReturnValueTemplate.TryGetValueAlt(beefType, let template))
|
||||
{
|
||||
return template;
|
||||
}
|
||||
|
||||
return .("var returnValue = ", "return returnValue;", GetCSharpInterfaceType(beefType));
|
||||
}
|
||||
|
||||
private static void GenerateCSharpFunctionPointer(MethodInfo method, String outString)
|
||||
{
|
||||
String parameters = new String();
|
||||
|
||||
for (int i < method.ParamCount)
|
||||
{
|
||||
if (i != 0)
|
||||
parameters.Append(", ");
|
||||
|
||||
String typeHolder = scope String();
|
||||
|
||||
method.GetParamType(i).ToString(typeHolder);
|
||||
|
||||
StringView csharpType = GetCSharpInterfaceType(typeHolder);
|
||||
|
||||
parameters.AppendF($"{csharpType}");
|
||||
}
|
||||
|
||||
String csharpFunctionPointer = scope $" public delegate* unmanaged[Cdecl]<{parameters}{(parameters.IsEmpty ? "" : ", ")}{method.ReturnType}> {method.Name};\n";
|
||||
outString.Append(csharpFunctionPointer);
|
||||
}
|
||||
|
||||
private static void GenerateCSharpWrapperMethod(MethodInfo method, String outString)
|
||||
{
|
||||
String wrapperParameters = new .();
|
||||
|
||||
String callArguments = new .();
|
||||
|
||||
String translation = new .();
|
||||
String cleanup = new .();
|
||||
|
||||
for (int i < method.ParamCount)
|
||||
{
|
||||
String beefParameterType = scope String();
|
||||
method.GetParamType(i).ToString(beefParameterType);
|
||||
|
||||
TypeTranslationTemplate template = GetCSharpWrapperTemplate(beefParameterType);
|
||||
|
||||
StringView paramName = method.GetParamName(i);
|
||||
|
||||
// Generate the parameter for the wrapper head.
|
||||
{
|
||||
if (i != 0)
|
||||
wrapperParameters.Append(", ");
|
||||
|
||||
wrapperParameters.AppendF($"{template.PrettyCSharpParamType} {paramName}");
|
||||
}
|
||||
|
||||
{
|
||||
if (i != 0)
|
||||
callArguments.Append(", ");
|
||||
|
||||
String translatedParamName = new String(paramName);
|
||||
|
||||
if (!template.StartCode.IsEmpty)
|
||||
{
|
||||
translatedParamName.Append("Converted");
|
||||
|
||||
callArguments.AppendF($"{translatedParamName}");
|
||||
|
||||
translation.AppendF(template.StartCode, translatedParamName, paramName);
|
||||
translation.Append('\n');
|
||||
}
|
||||
else
|
||||
{
|
||||
callArguments.AppendF($"{paramName}");
|
||||
}
|
||||
|
||||
if (!template.EndCode.IsEmpty)
|
||||
{
|
||||
cleanup.AppendF(template.EndCode, translatedParamName, paramName);
|
||||
cleanup.Append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String returnTypeName = new .();
|
||||
method.ReturnType.ToString(returnTypeName);
|
||||
String csharpReturnType = GetCSharpInterfaceType(returnTypeName);
|
||||
TypeTranslationTemplate template = GetCSharpReturnValueTemplate(csharpReturnType);
|
||||
|
||||
outString.AppendF($"""
|
||||
internal static unsafe {template.PrettyCSharpParamType} NEW_{method.Name}({wrapperParameters})
|
||||
{{
|
||||
{translation}
|
||||
{template.StartCode}_engineFunctions.{method.Name}({callArguments});
|
||||
|
||||
{cleanup}
|
||||
{template.EndCode}
|
||||
}}
|
||||
|
||||
|
||||
""");
|
||||
}
|
||||
|
||||
[Comptime]
|
||||
public void ApplyToType(Type self)
|
||||
{
|
||||
String csharpEngineFunctionsStruct = new String("""
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEngine.Core;
|
||||
|
||||
namespace GlitchyEngine;
|
||||
|
||||
internal unsafe partial struct EngineFunctions
|
||||
{
|
||||
|
||||
""");
|
||||
|
||||
String csharpScriptGlue = new String("""
|
||||
|
||||
|
||||
internal static partial class ScriptGlue
|
||||
{
|
||||
|
||||
""");
|
||||
|
||||
for (MethodInfo methodInfo in typeof(ScriptGlue).GetMethods(.Static | .NonPublic))
|
||||
{
|
||||
if (methodInfo.GetCustomAttribute<RegisterCallAttribute>() case .Ok(let attribute))
|
||||
{
|
||||
String parameters = new String();
|
||||
|
||||
for (int i < methodInfo.ParamCount)
|
||||
{
|
||||
if (i != 0)
|
||||
parameters.Append(", ");
|
||||
|
||||
parameters.AppendF($"{methodInfo.GetParamType(i)}");
|
||||
}
|
||||
|
||||
String line = scope $"public function {methodInfo.ReturnType}({parameters}) {methodInfo.Name};\n";
|
||||
|
||||
Compiler.EmitTypeBody(self, line);
|
||||
|
||||
GenerateCSharpFunctionPointer(methodInfo, csharpEngineFunctionsStruct);
|
||||
|
||||
GenerateCSharpWrapperMethod(methodInfo, csharpScriptGlue);
|
||||
}
|
||||
}
|
||||
|
||||
csharpEngineFunctionsStruct.Append('}');
|
||||
csharpScriptGlue.Append('}');
|
||||
|
||||
csharpEngineFunctionsStruct.Append(csharpScriptGlue);
|
||||
|
||||
if (File.WriteAllText("../ScriptCore/ScriptGlue.gen.cs", csharpEngineFunctionsStruct) case .Err)
|
||||
{
|
||||
Runtime.FatalError("Failed to write file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[EngineFunctionsGenerator]
|
||||
struct EngineFunctions
|
||||
{
|
||||
}
|
||||
|
||||
static class ScriptGlue
|
||||
{
|
||||
private static Dictionary<MonoType*, function void(Entity entityId)> s_AddComponentMethods = new .() ~ delete _;
|
||||
private static Dictionary<MonoType*, function bool(Entity entityId)> s_HasComponentMethods = new .() ~ delete _;
|
||||
private static Dictionary<MonoType*, function void(Entity entityId)> s_RemoveComponentMethods = new .() ~ delete _;
|
||||
|
||||
struct RegisterCallAttribute : Attribute
|
||||
{
|
||||
public String MethodName;
|
||||
|
||||
public this(String methodName)
|
||||
{
|
||||
MethodName = methodName;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adding this attribute to a method will log method entry and returned Result<T> errors */
|
||||
[AttributeUsage(.Method)]
|
||||
struct RegisterMethodAttribute : Attribute, IOnMethodInit
|
||||
@@ -62,27 +293,47 @@ static class ScriptGlue
|
||||
[Comptime]
|
||||
public void OnMethodInit(MethodInfo method, Self* prev)
|
||||
{
|
||||
String functionContent = new .();
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (var methodInfo in typeof(ScriptGlue).GetMethods(.Static))
|
||||
{
|
||||
if (methodInfo.GetCustomAttribute<RegisterCallAttribute>() case .Ok(let attribute))
|
||||
{
|
||||
String functionType = scope $"{methodInfo.ReturnType}({methodInfo.GetParamsDecl(.. scope .())})";
|
||||
|
||||
String line = scope $"RegisterCall<function {functionType}>(\"{attribute.MethodName}\", => {methodInfo.Name});\n";
|
||||
|
||||
Compiler.EmitMethodEntry(method, line);
|
||||
functionContent.AppendF($"functions.{methodInfo.Name} = => {methodInfo.Name};\n");
|
||||
//functionContent.AppendF($"functions[{i}] = (void*)( => {methodInfo.Name});\n");
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
functionContent.Insert(0, scope $"EngineFunctions functions = .();\n");
|
||||
|
||||
functionContent.Append("_setEngineFunctions(&functions);");
|
||||
|
||||
Compiler.EmitMethodEntry(method, functionContent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Event<delegate void()> OnRegisterNativeCalls ~ _.Dispose();
|
||||
|
||||
/*private function void SetEngineFunctions(EngineFunctions* engineFunctions);
|
||||
private static SetEngineFunctions _setEngineFunctions;*/
|
||||
|
||||
private function void SetEngineFunctions(EngineFunctions* engineFunctions);
|
||||
private static SetEngineFunctions _setEngineFunctions;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (_setEngineFunctions == null)
|
||||
{
|
||||
CoreClrHelper.GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "SetEngineFunctions", out _setEngineFunctions);
|
||||
}
|
||||
|
||||
RegisterCalls();
|
||||
|
||||
RegisterMathFunctions();
|
||||
//RegisterMathFunctions();
|
||||
}
|
||||
|
||||
public static void RegisterManagedComponents()
|
||||
@@ -103,11 +354,18 @@ static class ScriptGlue
|
||||
RegisterComponent<MeshRendererComponent>("GlitchyEngine.Graphics.MeshRenderer");
|
||||
}
|
||||
|
||||
[RegisterMethod]
|
||||
private static void RegisterCalls()
|
||||
{
|
||||
FillEngineFunctions();
|
||||
|
||||
OnRegisterNativeCalls.Invoke();
|
||||
}
|
||||
|
||||
[RegisterMethod]
|
||||
private static void FillEngineFunctions()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private static void RegisterComponent<T>(StringView cSharpClassName = "") where T : struct, new
|
||||
{
|
||||
@@ -248,7 +506,7 @@ static class ScriptGlue
|
||||
|
||||
#region Log
|
||||
|
||||
[RegisterCall("ScriptGlue::Log_LogMessage")]
|
||||
/*[RegisterCall("ScriptGlue::Log_LogMessage")]
|
||||
static void Log_LogMessage(int32 logLevel, MonoString* message, MonoString* fileName, int lineNumber)
|
||||
{
|
||||
char8* utfMessage = Mono.mono_string_to_utf8(message);
|
||||
@@ -275,6 +533,30 @@ static class ScriptGlue
|
||||
}
|
||||
|
||||
Mono.mono_free(utfMessage);
|
||||
}*/
|
||||
|
||||
[RegisterCall("ScriptGlue::Log_LogMessage")]
|
||||
[CallingConvention(.Cdecl)]
|
||||
static void Log_LogMessage(int32 logLevel, char16* messagePtr, char16* fileNamePtr, int lineNumber)
|
||||
{
|
||||
String escapedMessage = new:ScopedAlloc! String(messagePtr);
|
||||
|
||||
// Why exactly do we have to replace these? Can we get away without copying the string above?
|
||||
escapedMessage.Replace("{", "{{");
|
||||
escapedMessage.Replace("}", "}}");
|
||||
|
||||
if (fileNamePtr != null)
|
||||
{
|
||||
String fileName = new:ScopedAlloc! String(fileNamePtr);
|
||||
|
||||
MessageOrigin messageOrigin = scope MessageOrigin(fileName, lineNumber);
|
||||
|
||||
Log.ClientLogger.Log((LogLevel)logLevel, escapedMessage, messageOrigin);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.ClientLogger.Log((LogLevel)logLevel, escapedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
[RegisterCall("ScriptGlue::Log_LogException")]
|
||||
@@ -457,8 +739,8 @@ static class ScriptGlue
|
||||
scriptComponent = entity.GetComponent<ScriptComponent>();
|
||||
}
|
||||
|
||||
if (scriptComponent.Instance != null)
|
||||
ScriptEngine.Context.DestroyScriptDeferred(scriptComponent.Instance, false);
|
||||
//if (scriptComponent.Instance != null)
|
||||
// ScriptEngine.Context.DestroyScriptDeferred(scriptComponent.Instance, false);
|
||||
|
||||
scriptComponent.Instance = null;
|
||||
|
||||
@@ -470,7 +752,8 @@ static class ScriptGlue
|
||||
// TODO: this returns false, if no script with ScriptClassName exists, we have to handle this case correctly I think.
|
||||
ScriptEngine.InitializeInstance(entity, scriptComponent);
|
||||
|
||||
return scriptComponent.Instance.MonoInstance;
|
||||
//return scriptComponent.Instance.MonoInstance;
|
||||
return null;
|
||||
}
|
||||
|
||||
[RegisterCall("ScriptGlue::Entity_RemoveScript")]
|
||||
@@ -478,7 +761,7 @@ static class ScriptGlue
|
||||
{
|
||||
ScriptComponent* scriptComponent = GetComponentSafe<ScriptComponent>(entityId);
|
||||
|
||||
ScriptEngine.Context.DestroyScriptDeferred(scriptComponent.Instance, true);
|
||||
//ScriptEngine.Context.DestroyScriptDeferred(scriptComponent.Instance, true);
|
||||
}
|
||||
|
||||
[RegisterCall("ScriptGlue::Entity_GetName")]
|
||||
@@ -653,7 +936,7 @@ static class ScriptGlue
|
||||
}
|
||||
|
||||
[Packed]
|
||||
struct AxisAngle
|
||||
public struct AxisAngle
|
||||
{
|
||||
public float3 Axis;
|
||||
public float Angle;
|
||||
@@ -1014,8 +1297,8 @@ static class ScriptGlue
|
||||
innerRadius = circleRenderer.InnerRadius;
|
||||
}
|
||||
|
||||
[RegisterCall("ScriptGlue::CircleRenderer_GetInnerRadius")]
|
||||
static void CircleRenderer_GetInnerRadius(UUID entityId, float innerRadius)
|
||||
[RegisterCall("ScriptGlue::CircleRenderer_SetInnerRadius")]
|
||||
static void CircleRenderer_SetInnerRadius(UUID entityId, float innerRadius)
|
||||
{
|
||||
CircleRendererComponent* circleRenderer = GetComponentSafe<CircleRendererComponent>(entityId);
|
||||
circleRenderer.InnerRadius = innerRadius;
|
||||
@@ -1085,8 +1368,8 @@ static class ScriptGlue
|
||||
|
||||
#region TextRenderer
|
||||
|
||||
[RegisterCall("ScriptGlue::TextRenderer_SetIsRichText")]
|
||||
static bool TextRenderer_SetIsRichText(UUID entityId)
|
||||
[RegisterCall("ScriptGlue::TextRenderer_GetIsRichText")]
|
||||
static bool TextRenderer_GetIsRichText(UUID entityId)
|
||||
{
|
||||
return GetComponentSafe<TextRendererComponent>(entityId).IsRichText;
|
||||
}
|
||||
@@ -1419,7 +1702,7 @@ static class ScriptGlue
|
||||
}
|
||||
|
||||
[RegisterCall("ScriptGlue::Asset_SetIdentifier")]
|
||||
static void Asset_GetIdentifier(UUID assetId, MonoString* text)
|
||||
static void Asset_SetIdentifier(UUID assetId, MonoString* text)
|
||||
{
|
||||
ThrowNotImplementedException("Asset.GetIdentifier is not implemented.");
|
||||
|
||||
|
||||
@@ -23,12 +23,12 @@ public class ScriptInstanceSerializer
|
||||
public void Clear()
|
||||
{
|
||||
ClearDictionaryAndDeleteValues!(_serializedData);
|
||||
ScriptEngine.Classes.EntitySerializer.DestroySerializationContext(this);
|
||||
//ScriptEngine.Classes.EntitySerializer.DestroySerializationContext(this);
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
ScriptEngine.Classes.EntitySerializer.CreateSerializationContext(this);
|
||||
//ScriptEngine.Classes.EntitySerializer.CreateSerializationContext(this);
|
||||
}
|
||||
|
||||
/// Serializes all script instances that are currently managed by the ScriptEngine.
|
||||
@@ -36,7 +36,11 @@ public class ScriptInstanceSerializer
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
Init();
|
||||
Log.EngineLogger.Error("SerializeScriptInstances not implemented.");
|
||||
|
||||
return;
|
||||
|
||||
/*Init();
|
||||
|
||||
for (let (id, scriptInstance) in ScriptEngine._entityScriptInstances)
|
||||
{
|
||||
@@ -46,7 +50,7 @@ public class ScriptInstanceSerializer
|
||||
for (let (name, scriptClass) in ScriptEngine.EntityClasses)
|
||||
{
|
||||
SerializeStaticScriptClassFields(scriptClass);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/// Serializes the given script instance.
|
||||
@@ -67,8 +71,12 @@ public class ScriptInstanceSerializer
|
||||
public void DeserializeScriptInstances()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
Log.EngineLogger.Error("SerializeScriptInstances not implemented.");
|
||||
|
||||
Init();
|
||||
return;
|
||||
|
||||
/*Init();
|
||||
|
||||
for (let (id, script) in ScriptEngine._entityScriptInstances)
|
||||
{
|
||||
@@ -78,7 +86,7 @@ public class ScriptInstanceSerializer
|
||||
for (let (name, scriptClass) in ScriptEngine.EntityClasses)
|
||||
{
|
||||
DeserializeStaticScriptClassFields(scriptClass);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/// Deserializes the data into the given script instance, if there is data available.
|
||||
|
||||
@@ -674,7 +674,7 @@ namespace GlitchyEngine.World
|
||||
{
|
||||
private String _scriptClassName = null;
|
||||
|
||||
private ScriptInstance _instance = null;
|
||||
private NewScriptInstance _instance = null;
|
||||
|
||||
public StringView ScriptClassName
|
||||
{
|
||||
@@ -688,7 +688,7 @@ namespace GlitchyEngine.World
|
||||
}
|
||||
}
|
||||
|
||||
public ScriptInstance Instance
|
||||
public NewScriptInstance Instance
|
||||
{
|
||||
[Inline]
|
||||
get => _instance;
|
||||
|
||||
@@ -394,9 +394,10 @@ namespace GlitchyEngine.World
|
||||
collision.OtherEntity = colliderEntityB.UUID;
|
||||
collision.Rigidbody = rigidbodyEntityA.UUID;
|
||||
collision.OtherRigidbody = rigidbodyEntityB.UUID;
|
||||
|
||||
scriptOfColliderA?.Instance?.InvokeOnCollisionEnter2D(collision);
|
||||
scriptOfRigidbodyA?.Instance?.InvokeOnCollisionEnter2D(collision);
|
||||
|
||||
// TODO:
|
||||
//scriptOfColliderA?.Instance?.InvokeOnCollisionEnter2D(collision);
|
||||
//scriptOfRigidbodyA?.Instance?.InvokeOnCollisionEnter2D(collision);
|
||||
}
|
||||
|
||||
bool fireEventB = colliderEntityB.TryGetComponent<ScriptComponent>(let scriptOfColliderB);
|
||||
@@ -410,8 +411,8 @@ namespace GlitchyEngine.World
|
||||
collision.Rigidbody = rigidbodyEntityB.UUID;
|
||||
collision.OtherRigidbody = rigidbodyEntityA.UUID;
|
||||
|
||||
scriptOfColliderB?.Instance?.InvokeOnCollisionEnter2D(collision);
|
||||
scriptOfRigidbodyB?.Instance?.InvokeOnCollisionEnter2D(collision);
|
||||
//scriptOfColliderB?.Instance?.InvokeOnCollisionEnter2D(collision);
|
||||
//scriptOfRigidbodyB?.Instance?.InvokeOnCollisionEnter2D(collision);
|
||||
}
|
||||
};
|
||||
_contactListener.endContactCallback = (contact, userData) => {
|
||||
@@ -446,8 +447,8 @@ namespace GlitchyEngine.World
|
||||
collision.Rigidbody = rigidbodyEntityA.UUID;
|
||||
collision.OtherRigidbody = rigidbodyEntityB.UUID;
|
||||
|
||||
scriptOfColliderA?.Instance?.InvokeOnCollisionLeave2D(collision);
|
||||
scriptOfRigidbodyA?.Instance?.InvokeOnCollisionLeave2D(collision);
|
||||
//scriptOfColliderA?.Instance?.InvokeOnCollisionLeave2D(collision);
|
||||
//scriptOfRigidbodyA?.Instance?.InvokeOnCollisionLeave2D(collision);
|
||||
}
|
||||
|
||||
bool fireEventB = colliderEntityB.TryGetComponent<ScriptComponent>(let scriptOfColliderB);
|
||||
@@ -461,8 +462,8 @@ namespace GlitchyEngine.World
|
||||
collision.Rigidbody = rigidbodyEntityB.UUID;
|
||||
collision.OtherRigidbody = rigidbodyEntityA.UUID;
|
||||
|
||||
scriptOfColliderB?.Instance?.InvokeOnCollisionLeave2D(collision);
|
||||
scriptOfRigidbodyB?.Instance?.InvokeOnCollisionLeave2D(collision);
|
||||
//scriptOfColliderB?.Instance?.InvokeOnCollisionLeave2D(collision);
|
||||
//scriptOfRigidbodyB?.Instance?.InvokeOnCollisionLeave2D(collision);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -992,13 +993,13 @@ namespace GlitchyEngine.World
|
||||
|
||||
if (sourceScript.Instance != null)
|
||||
{
|
||||
scriptSerializer.SerializeScriptInstance(sourceScript.Instance);
|
||||
//scriptSerializer.SerializeScriptInstance(sourceScript.Instance);
|
||||
|
||||
// Initializes the created instance
|
||||
// TODO: this returns false, if no script with ScriptClassName exists, we have to handle this case correctly I think.
|
||||
ScriptEngine.InitializeInstance(copy, targetScript);
|
||||
|
||||
newScripts.Add((original.UUID, targetScript.Instance));
|
||||
//newScripts.Add((original.UUID, targetScript.Instance));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user