From 5f27b8001a487b9bc170001eba4794fa6fdf160c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Thu, 20 Jul 2023 22:11:11 +0200 Subject: [PATCH] Automatic script assembly reloading --- .../Assets/Scripts/MyTestEntity.cs | 21 ---- GlitchyEngine/src/Application.bf | 35 +++++- GlitchyEngine/src/Scripting/ScriptEngine.bf | 116 ++++++------------ ScriptCoreGenerator/FunctionGenerator.cs | 98 --------------- 4 files changed, 73 insertions(+), 197 deletions(-) delete mode 100644 ScriptCoreGenerator/FunctionGenerator.cs diff --git a/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs b/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs index f69f1f5..c9a7284 100644 --- a/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs +++ b/GlitchyEditor/SandboxProject/Assets/Scripts/MyTestEntity.cs @@ -112,27 +112,6 @@ namespace Sandbox Log.Info($"Ouha! {MyNumber}"); Physics2D.Gravity *= new float2(1, -1); } - - Test.Test.P(); - - float2 f2 = new(); - - f2.X = 5; - - Log.Info($"Haleluja! {f2.X}"); - - float4 floaty = new float4(f2, f2); - - float4 megaFloat = floaty.WXYZ; - - if (all(abs(normalize(megaFloat).YZWX - normalize(floaty)) < 0.01f)) - { - Log.Info($"Haleluja3!"); - } - - float frac = modf(5.6f, out var intPart); - - Log.Info($"Haleluja2! {frac} {intPart}"); } /// diff --git a/GlitchyEngine/src/Application.bf b/GlitchyEngine/src/Application.bf index 9507705..f7cb7e1 100644 --- a/GlitchyEngine/src/Application.bf +++ b/GlitchyEngine/src/Application.bf @@ -5,6 +5,8 @@ using GlitchyEngine.Renderer; using GlitchyEngine.Debug; using GlitchyEngine.Content; using GlitchyEngine.Scripting; +using System.Collections; +using System.Threading; namespace GlitchyEngine { @@ -28,6 +30,9 @@ namespace GlitchyEngine private IContentManager _contentManager; + private append List _jobQueue = .() ~ ClearAndDeleteItems!(_); + private append Monitor _jobQueueMutex = .(); + public bool IsRunning => _running; public Window Window => _window; @@ -37,9 +42,11 @@ namespace GlitchyEngine public GameTime GameTime => _gameTime; - [Inline] + [Inline, Obsolete("Use Application.Instance instead.", false)] public static Application Get() => s_Instance; + public static Application Instance => s_Instance; + public Settings Settings {get; private set;} = new .() ~ delete _; public this() @@ -157,6 +164,8 @@ namespace GlitchyEngine } #endif + RunJobs(); + if(allowFrame && !_isMinimized) { Debug.Profiler.ProfileScope!("Update Layers"); @@ -218,5 +227,29 @@ namespace GlitchyEngine return false; } + + /// Executes the given Job on the main thread. Takes ownership of the delegate. + public void InvokeOnMainThread(delegate void() ownJob) + { + using (_jobQueueMutex.Enter()) + { + _jobQueue.Add(ownJob); + } + } + + private void RunJobs() + { + Debug.Profiler.ProfileFunction!(); + + using (_jobQueueMutex.Enter()) + { + for (let job in _jobQueue) + { + job(); + } + + ClearAndDeleteItems!(_jobQueue); + } + } } } diff --git a/GlitchyEngine/src/Scripting/ScriptEngine.bf b/GlitchyEngine/src/Scripting/ScriptEngine.bf index 4e12d89..b18117a 100644 --- a/GlitchyEngine/src/Scripting/ScriptEngine.bf +++ b/GlitchyEngine/src/Scripting/ScriptEngine.bf @@ -72,7 +72,7 @@ static class ScriptEngine } delete _; } - + public static Dictionary EntityClasses => _entityScripts; public static Scene Context => s_Context; @@ -86,6 +86,8 @@ static class ScriptEngine delete _; }; + + private static FileSystemWatcher _userAssemblyWatcher ~ delete _; internal static class Attributes { @@ -102,6 +104,40 @@ static class ScriptEngine ScriptGlue.Init(); LoadScriptAssemblies(); + + InitAssemblyWatcher(); + } + + static void InitAssemblyWatcher() + { + if (_userAssemblyWatcher == null) + { + // TODO: Obviously don't hardcode path + _userAssemblyWatcher = new FileSystemWatcher("SandboxProject/Assets/Scripts/bin/", "*/Sandbox.dll"); + _userAssemblyWatcher.OnChanged.Add(new (fileName) => + { + // TODO: Temporary, we want to be able to reload while in play-mode. (+ Editor Scripts will be a thing some day) + if (_entityScriptInstances.Count > 0) + { + Log.EngineLogger.Warning("There are script instances. Skipping assembly reload."); + return; + } + + Log.EngineLogger.Info("Script reload requested."); + _userAssemblyWatcher.StopRaisingEvents(); + + Application.Instance.InvokeOnMainThread(new () => + { + Log.EngineLogger.Info("Reloading scripts..."); + ReloadAssemblies(); + Log.EngineLogger.Info("Scripts reloaded!"); + + _userAssemblyWatcher.StartRaisingEvents(); + }); + + }); + } + _userAssemblyWatcher.StartRaisingEvents(); } static void LoadScriptAssemblies() @@ -259,6 +295,8 @@ static class ScriptEngine public static void ReloadAssemblies() { + Debug.Profiler.ProfileFunction!(); + Mono.mono_domain_set(s_RootDomain, false); Mono.mono_domain_unload(s_AppDomain); @@ -382,80 +420,4 @@ static class ScriptEngine return scriptClass; } - - - - - - - - - - - - - /*static void Samples() - { - PrintAssemblyTypes(s_CoreAssembly); - - function MonoString*() v = => Sample; - - Mono.mono_add_internal_call("GlitchyEngine.CSharpTesting::Sample", v); - - function void(in float3, in float3, out float3) v2 = => Add; - - Mono.mono_add_internal_call("GlitchyEngine.float3::Add_Internal", v2); - - // Create object - ScriptClass myClass = scope .("GlitchyEngine", "CSharpTesting"); - MonoObject* instance = myClass.CreateInstance(); - - MonoMethod* simpleMethod = myClass.GetMethod("PrintFloatVar"); - myClass.Invoke(simpleMethod, instance); - - MonoMethod* methodWithArg = myClass.GetMethod("IncrementFloatVar", 1); - - float increment = 2.0f; - float returnedValue = myClass.Invoke(methodWithArg, instance, &increment); - - Log.EngineLogger.Info($"C# returned: {returnedValue}"); - - Mono.mono_runtime_invoke(simpleMethod, instance, null, null); - } - - [LinkName(.C), AlwaysInclude, Export] - public static void DoSomething() - { - Console.WriteLine("P/Invoke: Hallo von der Engine!"); - } - - [LinkName(.C), AlwaysInclude, Export] - public static void Add(in float3 a, in float3 b, out float3 c) - { - c = a + b; - } - - [LinkName(.C), AlwaysInclude] - public static MonoString* Sample() - { - return Mono.mono_string_new(Mono.mono_domain_get(), "Hello!"); - } - - private static void PrintAssemblyTypes(MonoAssembly* assembly) - { - MonoImage* image = Mono.mono_assembly_get_image(assembly); - MonoTableInfo* typeDefinitionsTable = Mono.mono_image_get_table_info(image, .MONO_TABLE_TYPEDEF); - int32 numTypes = Mono.mono_table_info_get_rows(typeDefinitionsTable); - - 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); - - char8* nameSpace = Mono.mono_metadata_string_heap(image, (.)cols[(.)SOME_RANDOM_ENUM.MONO_TYPEDEF_NAMESPACE]); - char8* name = Mono.mono_metadata_string_heap(image, (.)cols[(.)SOME_RANDOM_ENUM.MONO_TYPEDEF_NAME]); - - Log.EngineLogger.Info($"{StringView(nameSpace)}.{StringView(name)}"); - } - }*/ } \ No newline at end of file diff --git a/ScriptCoreGenerator/FunctionGenerator.cs b/ScriptCoreGenerator/FunctionGenerator.cs deleted file mode 100644 index 5de9c04..0000000 --- a/ScriptCoreGenerator/FunctionGenerator.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace ScriptCoreGenerator -{ - [Generator] - public class HelloSourceGenerator : ISourceGenerator - { - public void Execute(GeneratorExecutionContext context) - { - var receiver = (MainSyntaxReceiver)context.SyntaxReceiver; - - - - string output = @" -namespace Test{ -public class Test -{ - public static void P() => GlitchyEngine.Log.Error(""Hello World""); -}} -"; - - // Code generation goes here - context.AddSource("Test/Hello.g.cs", output); - } - - public void Initialize(GeneratorInitializationContext context) - { - context.RegisterForSyntaxNotifications(() => new MainSyntaxReceiver()); - } - } - - public class MainSyntaxReceiver : ISyntaxReceiver - { - public DefinitionAggregate Definitions { get; } = new(); - public GivethsAggregate Giveths { get; } = new(); - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - Definitions.OnVisitSyntaxNode(syntaxNode); - Giveths.OnVisitSyntaxNode(syntaxNode); - } - } - - public class DefinitionAggregate : ISyntaxReceiver - { - public List Captures { get; } = new(); - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is not AttributeSyntax { Name: IdentifierNameSyntax { Identifier.Text: "Define" } } attr) - { - return; - } - - var method = attr.GetParent(); - var key = method.Identifier.Text; - - Captures.Add(new Capture(key, method)); - } - - public record Capture(string Key, MethodDeclarationSyntax Method) - { - public string Key { get; } = Key; - public MethodDeclarationSyntax Method { get; } = Method; - } - } - - public class GivethsAggregate : ISyntaxReceiver - { - public List Captures { get; } = new(); - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is not AttributeSyntax { Name: IdentifierNameSyntax { Identifier.Text: "Give" } } attr) - { - return; - } - - var target = (attr.ArgumentList.Arguments.Single().Expression as LiteralExpressionSyntax).Token.ValueText; - - var method = attr.GetParent(); - var @class = attr.GetParent(); - - Captures.Add(new Capture(target, method, @class)); - } - - public record Capture(string TargetImplementation, MethodDeclarationSyntax Method, ClassDeclarationSyntax Class) - { - public string TargetImplementation { get; } = TargetImplementation; - public MethodDeclarationSyntax Method { get; } = Method; - public ClassDeclarationSyntax Class { get; } = Class; - } - } -}