From b686b3bcd64494a869aba5cbe84bacd086a8cc56 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Thu, 23 Nov 2023 23:54:38 +0100 Subject: [PATCH 1/5] Update NWN.Core to 8193.35.21. --- NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj | 2 +- NWN.Anvil.Tests/NWN.Anvil.Tests.csproj | 2 +- NWN.Anvil/NWN.Anvil.csproj | 2 +- docs/NWN.Anvil.Samples.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj b/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj index 757ac1581..91d75bf9b 100644 --- a/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj +++ b/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj @@ -69,7 +69,7 @@ - + diff --git a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj index cab1c50be..8605ef181 100644 --- a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj +++ b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj @@ -42,7 +42,7 @@ - + diff --git a/NWN.Anvil/NWN.Anvil.csproj b/NWN.Anvil/NWN.Anvil.csproj index 45c10d4ef..7d3873ae7 100644 --- a/NWN.Anvil/NWN.Anvil.csproj +++ b/NWN.Anvil/NWN.Anvil.csproj @@ -65,7 +65,7 @@ - + diff --git a/docs/NWN.Anvil.Samples.csproj b/docs/NWN.Anvil.Samples.csproj index 50f1d0292..f7c68d25a 100644 --- a/docs/NWN.Anvil.Samples.csproj +++ b/docs/NWN.Anvil.Samples.csproj @@ -30,7 +30,7 @@ - + From 054348c5e4560e87db7d9875e0855b3748e1aca5 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Thu, 23 Nov 2023 23:55:18 +0100 Subject: [PATCH 2/5] Consolidate VirtualMachineFunctionHandler into AnvilCore. Implement unmanaged handlers. --- .../src/main/AnvilCore.FunctionHandlers.cs | 164 ++++++++++++++++++ NWN.Anvil/src/main/AnvilCore.cs | 75 ++------ .../Core/VirtualMachineFunctionHandler.cs | 107 ------------ .../Services/Services/AnvilServiceManager.cs | 1 - 4 files changed, 174 insertions(+), 173 deletions(-) create mode 100644 NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs delete mode 100644 NWN.Anvil/src/main/Services/Core/VirtualMachineFunctionHandler.cs diff --git a/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs b/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs new file mode 100644 index 000000000..a1b6f62b4 --- /dev/null +++ b/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Anvil.API; +using Anvil.Internal; +using Anvil.Services; +using NWN.Core; +using NWN.Native.API; +using Action = System.Action; + +namespace Anvil +{ + public sealed partial class AnvilCore : ICoreFunctionHandler + { + [Inject] + private static ScriptDispatchService? ScriptDispatchService { get; set; } + + [Inject] + private static ServerUpdateLoopService? ServerUpdateLoopService { get; set; } + + private static readonly Dictionary Closures = new Dictionary(); + private static readonly Stack ScriptContexts = new Stack(); + + private static ulong nextEventId; + private static uint objectSelf; + + uint ICoreFunctionHandler.ObjectSelf => objectSelf; + + void ICoreFunctionHandler.ClosureActionDoCommand(uint obj, Action func) + { + if (VM.ClosureActionDoCommand(obj, nextEventId) != 0) + { + Closures.Add(nextEventId++, func); + } + } + + void ICoreFunctionHandler.ClosureAssignCommand(uint obj, Action func) + { + if (VM.ClosureAssignCommand(obj, nextEventId) != 0) + { + Closures.Add(nextEventId++, func); + } + } + + void ICoreFunctionHandler.ClosureDelayCommand(uint obj, float duration, Action func) + { + if (VM.ClosureDelayCommand(obj, duration, nextEventId) != 0) + { + Closures.Add(nextEventId++, func); + } + } + + [UnmanagedCallersOnly] + private static void OnNWNXSignal(IntPtr signalPtr) + { + string signal = signalPtr.ReadNullTerminatedString(); + + switch (signal) + { + case "ON_NWNX_LOADED": + instance.Init(); + break; + case "ON_MODULE_LOAD_FINISH": + instance.LoadAndStart(); + break; + case "ON_DESTROY_SERVER": + Log.Info("Server is shutting down..."); + instance.Unload(); + break; + case "ON_DESTROY_SERVER_AFTER": + instance.Shutdown(); + break; + } + } + + [UnmanagedCallersOnly] + private static int OnRunScript(IntPtr scriptPtr, uint oidSelf) + { + string script = scriptPtr.ReadNullTerminatedString(); + int retVal = 0; + objectSelf = oidSelf; + ScriptContexts.Push(oidSelf); + + try + { + if (ScriptDispatchService != null) + { + retVal = (int)ScriptDispatchService.TryExecuteScript(script, oidSelf); + } + } + catch (Exception e) + { + Log.Error(e, "An exception occured while executing script {Script}", script); + } + + ScriptContexts.Pop(); + objectSelf = ScriptContexts.Count == 0 ? NWScript.OBJECT_INVALID : ScriptContexts.Peek(); + return retVal; + } + + [UnmanagedCallersOnly] + private static void OnClosure(ulong eid, uint oidSelf) + { + uint old = objectSelf; + objectSelf = oidSelf; + + try + { + Closures[eid].Invoke(); + } + catch (Exception e) + { + Log.Error(e); + } + + Closures.Remove(eid); + objectSelf = old; + } + + [UnmanagedCallersOnly] + private static void OnLoop(ulong _) + { + ServerUpdateLoopService?.Update(); + } + + [UnmanagedCallersOnly] + private static void OnAssertFail(IntPtr messagePtr, IntPtr nativeStackTracePtr) + { + string message = messagePtr.ReadNullTerminatedString(); + string nativeStackTrace = nativeStackTracePtr.ReadNullTerminatedString(); + + StackTrace stackTrace = new StackTrace(true); + Log.Error("An assertion failure occurred in native code.\n" + + $"{message}{nativeStackTrace}\n" + + $"{stackTrace}"); + } + + [UnmanagedCallersOnly] + private static void OnServerCrash(int signal, IntPtr nativeStackTracePtr) + { + string stackTrace = nativeStackTracePtr.ReadNullTerminatedString(); + Version serverVersion = NwServer.Instance.ServerVersion; + + string error = signal switch + { + 4 => "Illegal instruction", + 6 => "Program aborted", + 8 => "Floating point exception", + 11 => "Segmentation fault", + _ => "Unknown error", + }; + + Log.Fatal("\n==============================================================\n" + + " Please file a bug at https://github.com/nwn-dotnet/Anvil/issues\n" + + $" {Assemblies.Anvil.GetName().Name} {AssemblyInfo.VersionInfo.InformationalVersion} has crashed. Fatal error: {error} ({signal})\n" + + $" Using: NWN {serverVersion}, NWN.Core {Assemblies.Core.GetName().Version}, NWN.Native {Assemblies.Native.GetName().Version}\n" + + "==============================================================\n" + + " Managed Backtrace:\n" + + $"{new StackTrace(true)}" + + $"{stackTrace}"); + } + } +} diff --git a/NWN.Anvil/src/main/AnvilCore.cs b/NWN.Anvil/src/main/AnvilCore.cs index 61ed33fd0..d41968308 100644 --- a/NWN.Anvil/src/main/AnvilCore.cs +++ b/NWN.Anvil/src/main/AnvilCore.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -17,15 +16,12 @@ namespace Anvil /// Handles bootstrap and interop between %NWN, %NWN.Core and the %Anvil %API. The entry point of the implementing module should point to this class.
/// Until is called, all APIs are unavailable for usage. /// - public sealed class AnvilCore + public sealed partial class AnvilCore { private static readonly Logger Log = LogManager.GetCurrentClassLogger(); private static AnvilCore instance = null!; - [Inject] - private VirtualMachineFunctionHandler VirtualMachineFunctionHandler { get; init; } = null!; - private readonly IServiceManager serviceManager; private AnvilCore(IServiceManager serviceManager) @@ -53,22 +49,22 @@ private AnvilCore(IServiceManager serviceManager) /// A custom service manager to use instead of the default . For advanced users only. /// The init result code to return back to NWNX. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Init(IntPtr arg, int argLength, IServiceManager? serviceManager = default) + public static unsafe int Init(IntPtr arg, int argLength, IServiceManager? serviceManager = default) { serviceManager ??= new AnvilServiceManager(); instance = new AnvilCore(serviceManager); - NWNCore.NativeEventHandles eventHandles = new NWNCore.NativeEventHandles + NWNCore.NativeEventHandlesUnmanaged eventHandles = new NWNCore.NativeEventHandlesUnmanaged { - Signal = instance.OnNWNXSignal, - RunScript = instance.VirtualMachineFunctionHandler.OnRunScript, - Closure = instance.VirtualMachineFunctionHandler.OnClosure, - MainLoop = instance.VirtualMachineFunctionHandler.OnLoop, - AssertFail = instance.OnAssertFail, - CrashHandler = instance.OnServerCrash, + Signal = &OnNWNXSignal, + RunScript = &OnRunScript, + Closure = &OnClosure, + MainLoop = &OnLoop, + AssertFail = &OnAssertFail, + CrashHandler = &OnServerCrash, }; - return NWNCore.Init(arg, argLength, instance.VirtualMachineFunctionHandler, eventHandles); + return NWNCore.Init(arg, argLength, instance, eventHandles); } /// @@ -137,57 +133,6 @@ private void LoadAndStart() serviceManager.Start(); } - private void OnNWNXSignal(string signal) - { - switch (signal) - { - case "ON_NWNX_LOADED": - Init(); - break; - case "ON_MODULE_LOAD_FINISH": - LoadAndStart(); - break; - case "ON_DESTROY_SERVER": - Log.Info("Server is shutting down..."); - Unload(); - break; - case "ON_DESTROY_SERVER_AFTER": - Shutdown(); - break; - } - } - - private void OnAssertFail(string message, string nativeStackTrace) - { - StackTrace stackTrace = new StackTrace(true); - Log.Error("An assertion failure occurred in native code.\n" + - $"{message}{nativeStackTrace}\n" + - $"{stackTrace}"); - } - - private void OnServerCrash(int signal, string stackTrace) - { - Version serverVersion = NwServer.Instance.ServerVersion; - - string error = signal switch - { - 4 => "Illegal instruction", - 6 => "Program aborted", - 8 => "Floating point exception", - 11 => "Segmentation fault", - _ => "Unknown error", - }; - - Log.Fatal("\n==============================================================\n" + - " Please file a bug at https://github.com/nwn-dotnet/Anvil/issues\n" + - $" {Assemblies.Anvil.GetName().Name} {AssemblyInfo.VersionInfo.InformationalVersion} has crashed. Fatal error: {error} ({signal})\n" + - $" Using: NWN {serverVersion}, NWN.Core {Assemblies.Core.GetName().Version}, NWN.Native {Assemblies.Native.GetName().Version}\n" + - "==============================================================\n" + - " Managed Backtrace:\n" + - $"{new StackTrace(true)}" + - $"{stackTrace}"); - } - private void PrelinkNative() { if (!EnvironmentConfig.NativePrelinkEnabled) diff --git a/NWN.Anvil/src/main/Services/Core/VirtualMachineFunctionHandler.cs b/NWN.Anvil/src/main/Services/Core/VirtualMachineFunctionHandler.cs deleted file mode 100644 index 19e823aed..000000000 --- a/NWN.Anvil/src/main/Services/Core/VirtualMachineFunctionHandler.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using NLog; -using NWN.Core; -using Action = System.Action; - -namespace Anvil.Services -{ - internal sealed class VirtualMachineFunctionHandler : ICoreService, ICoreFunctionHandler - { - private static readonly Logger Log = LogManager.GetCurrentClassLogger(); - - [Inject] - private static ScriptDispatchService? ScriptDispatchService { get; set; } - - [Inject] - private static ServerUpdateLoopService? ServerUpdateLoopService { get; set; } - - private readonly Dictionary closures = new Dictionary(); - private readonly Stack scriptContexts = new Stack(); - - private ulong nextEventId; - private uint objectSelf; - - uint ICoreFunctionHandler.ObjectSelf => objectSelf; - - public void OnLoop(ulong _) - { - ServerUpdateLoopService?.Update(); - } - - void ICoreFunctionHandler.ClosureActionDoCommand(uint obj, Action func) - { - if (VM.ClosureActionDoCommand(obj, nextEventId) != 0) - { - closures.Add(nextEventId++, func); - } - } - - void ICoreFunctionHandler.ClosureAssignCommand(uint obj, Action func) - { - if (VM.ClosureAssignCommand(obj, nextEventId) != 0) - { - closures.Add(nextEventId++, func); - } - } - - void ICoreFunctionHandler.ClosureDelayCommand(uint obj, float duration, Action func) - { - if (VM.ClosureDelayCommand(obj, duration, nextEventId) != 0) - { - closures.Add(nextEventId++, func); - } - } - - void ICoreService.Init() {} - - void ICoreService.Load() {} - - void ICoreService.Shutdown() {} - - void ICoreService.Start() {} - - void ICoreService.Unload() {} - - internal void OnClosure(ulong eid, uint oidSelf) - { - uint old = objectSelf; - objectSelf = oidSelf; - - try - { - closures[eid].Invoke(); - } - catch (Exception e) - { - Log.Error(e); - } - - closures.Remove(eid); - objectSelf = old; - } - - internal int OnRunScript(string script, uint oidSelf) - { - int retVal = 0; - objectSelf = oidSelf; - scriptContexts.Push(oidSelf); - - try - { - if (ScriptDispatchService != null) - { - retVal = (int)ScriptDispatchService.TryExecuteScript(script, oidSelf); - } - } - catch (Exception e) - { - Log.Error(e, "An exception occured while executing script {Script}", script); - } - - scriptContexts.Pop(); - objectSelf = scriptContexts.Count == 0 ? NWScript.OBJECT_INVALID : scriptContexts.Peek(); - return retVal; - } - } -} diff --git a/NWN.Anvil/src/main/Services/Services/AnvilServiceManager.cs b/NWN.Anvil/src/main/Services/Services/AnvilServiceManager.cs index 346d06a18..e5634ddc5 100644 --- a/NWN.Anvil/src/main/Services/Services/AnvilServiceManager.cs +++ b/NWN.Anvil/src/main/Services/Services/AnvilServiceManager.cs @@ -227,7 +227,6 @@ private void InstallCoreContainer() RegisterCoreService(); RegisterCoreService(); RegisterCoreService(); - RegisterCoreService(); RegisterCoreService(); RegisterCoreService(); RegisterCoreService(); From e54bed2d56258c5714f4b53ff0e73ac59b080f0d Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Thu, 23 Nov 2023 23:55:43 +0100 Subject: [PATCH 3/5] Remove redundant try/catch. --- .../ScriptDispatch/ScriptDispatchService.cs | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/NWN.Anvil/src/main/Services/ScriptDispatch/ScriptDispatchService.cs b/NWN.Anvil/src/main/Services/ScriptDispatch/ScriptDispatchService.cs index c0ec31a1a..6ef4c1b45 100644 --- a/NWN.Anvil/src/main/Services/ScriptDispatch/ScriptDispatchService.cs +++ b/NWN.Anvil/src/main/Services/ScriptDispatch/ScriptDispatchService.cs @@ -1,15 +1,11 @@ -using System; using System.Collections.Generic; using System.Linq; -using NLog; namespace Anvil.Services { [ServiceBinding(typeof(ScriptDispatchService))] internal sealed class ScriptDispatchService { - private static readonly Logger Log = LogManager.GetCurrentClassLogger(); - private readonly List dispatchers; public ScriptDispatchService(IReadOnlyList dispatchers) @@ -20,25 +16,17 @@ public ScriptDispatchService(IReadOnlyList dispatchers) public ScriptHandleResult TryExecuteScript(string script, uint objectSelf) { - try + ScriptHandleResult result = ScriptHandleResult.NotHandled; + foreach (IScriptDispatcher dispatcher in dispatchers) { - ScriptHandleResult result = ScriptHandleResult.NotHandled; - foreach (IScriptDispatcher dispatcher in dispatchers) + result = dispatcher.ExecuteScript(script, objectSelf); + if (result != ScriptHandleResult.NotHandled) { - result = dispatcher.ExecuteScript(script, objectSelf); - if (result != ScriptHandleResult.NotHandled) - { - break; - } + break; } - - return result; - } - catch (Exception e) - { - Log.Error(e); - return (int)ScriptHandleResult.Handled; } + + return result; } } } From 0a85fd78dfb1d663ea68d98c0fe64bbb4f580b5c Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Thu, 23 Nov 2023 23:56:07 +0100 Subject: [PATCH 4/5] ServerUpdateLoop: Rename updateables -> updateItems. --- .../Services/GameLoop/ServerUpdateLoopService.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/NWN.Anvil/src/main/Services/GameLoop/ServerUpdateLoopService.cs b/NWN.Anvil/src/main/Services/GameLoop/ServerUpdateLoopService.cs index ee497799c..6904a8d76 100644 --- a/NWN.Anvil/src/main/Services/GameLoop/ServerUpdateLoopService.cs +++ b/NWN.Anvil/src/main/Services/GameLoop/ServerUpdateLoopService.cs @@ -11,25 +11,25 @@ internal sealed class ServerUpdateLoopService : IDisposable { private static readonly Logger Log = LogManager.GetCurrentClassLogger(); - private IUpdateable[] updateables; + private IUpdateable[] updateItems; - public ServerUpdateLoopService(IEnumerable updateables) + public ServerUpdateLoopService(IEnumerable updateItems) { - this.updateables = updateables.OrderBy(updateable => updateable.GetType().GetServicePriority()).ToArray(); + this.updateItems = updateItems.OrderBy(updateable => updateable.GetType().GetServicePriority()).ToArray(); } public void Dispose() { - updateables = Array.Empty(); + updateItems = Array.Empty(); } internal void Update() { - for (int i = 0; i < updateables.Length; i++) + for (int i = 0; i < updateItems.Length; i++) { try { - updateables[i].Update(); + updateItems[i].Update(); } catch (Exception e) { From 827ced9ef653be30aca241f65610bb4a20b67368 Mon Sep 17 00:00:00 2001 From: Jhett Black <10942655+jhett12321@users.noreply.github.com> Date: Thu, 23 Nov 2023 23:56:37 +0100 Subject: [PATCH 5/5] Optimize OnRunScript calls. --- NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs b/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs index a1b6f62b4..3acded81a 100644 --- a/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs +++ b/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs @@ -77,20 +77,18 @@ private static void OnNWNXSignal(IntPtr signalPtr) [UnmanagedCallersOnly] private static int OnRunScript(IntPtr scriptPtr, uint oidSelf) { + int retVal; string script = scriptPtr.ReadNullTerminatedString(); - int retVal = 0; objectSelf = oidSelf; ScriptContexts.Push(oidSelf); try { - if (ScriptDispatchService != null) - { - retVal = (int)ScriptDispatchService.TryExecuteScript(script, oidSelf); - } + retVal = (int)(ScriptDispatchService?.TryExecuteScript(script, oidSelf) ?? ScriptHandleResult.Handled); } catch (Exception e) { + retVal = 0; Log.Error(e, "An exception occured while executing script {Script}", script); }