diff --git a/.env b/.env index 9d6336f7d..2a1350b61 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -NWNX_VERSION=30482a7 +NWNX_VERSION=a6c5f09 diff --git a/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj b/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj index 994edc42a..acc3a4f7b 100644 --- a/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj +++ b/NWN.Anvil.TestRunner/NWN.Anvil.TestRunner.csproj @@ -69,8 +69,8 @@ - - + + diff --git a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj index cd48486f9..2c4d0f51a 100644 --- a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj +++ b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj @@ -43,8 +43,8 @@ - - + + diff --git a/NWN.Anvil.Tests/src/main/API/TwoDimArray/TwoDimArrayTests.cs b/NWN.Anvil.Tests/src/main/API/TwoDimArray/TwoDimArrayTests.cs index 3c502ab80..962059c2b 100644 --- a/NWN.Anvil.Tests/src/main/API/TwoDimArray/TwoDimArrayTests.cs +++ b/NWN.Anvil.Tests/src/main/API/TwoDimArray/TwoDimArrayTests.cs @@ -3,6 +3,7 @@ using Anvil.Services; using NUnit.Framework; using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.Tests.API { @@ -27,7 +28,7 @@ 2 Test2 "Test 2" 0x00000002 2.0f """; string resourceName = "testtemp.2da"; - ResourceManager.WriteTempResource(resourceName, StringHelper.Encoding.GetBytes(twoDimArray)); + ResourceManager.WriteTempResource(resourceName, StringUtils.Encoding.GetBytes(twoDimArray)); createdTempResources.Add(resourceName); TwoDimArray array = NwGameTables.GetTable(resourceName)!; diff --git a/NWN.Anvil/NWN.Anvil.csproj b/NWN.Anvil/NWN.Anvil.csproj index 89380a790..e81f0a83c 100644 --- a/NWN.Anvil/NWN.Anvil.csproj +++ b/NWN.Anvil/NWN.Anvil.csproj @@ -66,8 +66,8 @@ - - + + diff --git a/NWN.Anvil/src/main/API/Color.cs b/NWN.Anvil/src/main/API/Color.cs index dc58e8b8c..dd482041c 100644 --- a/NWN.Anvil/src/main/API/Color.cs +++ b/NWN.Anvil/src/main/API/Color.cs @@ -1,6 +1,6 @@ using System; using Newtonsoft.Json; -using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.API { @@ -154,7 +154,7 @@ public string ToColorToken() { const byte tokenMinVal = 1; ReadOnlySpan tokenBytes = [Math.Max(Red, tokenMinVal), Math.Max(Green, tokenMinVal), Math.Max(Blue, tokenMinVal)]; - return StringHelper.Encoding.GetString(tokenBytes); + return StringUtils.Encoding.GetString(tokenBytes); } /// diff --git a/NWN.Anvil/src/main/API/EngineStructures/EngineStructure.cs b/NWN.Anvil/src/main/API/EngineStructures/EngineStructure.cs index fc9f5c1c3..b5e40b65b 100644 --- a/NWN.Anvil/src/main/API/EngineStructures/EngineStructure.cs +++ b/NWN.Anvil/src/main/API/EngineStructures/EngineStructure.cs @@ -1,5 +1,5 @@ using System; -using NWN.Core; +using NWNX.NET; namespace Anvil.API { @@ -40,7 +40,7 @@ private void ReleaseUnmanagedResources() if (memoryOwn) { memoryOwn = false; - VM.FreeGameDefinedStructure(StructureId, handle); + NWNXAPI.FreeGameDefinedStructure(StructureId, handle); handle = IntPtr.Zero; } } diff --git a/NWN.Anvil/src/main/API/Events/Native/BarterEvents/OnBarterEnd.cs b/NWN.Anvil/src/main/API/Events/Native/BarterEvents/OnBarterEnd.cs index 78c72aec3..051f92713 100644 --- a/NWN.Anvil/src/main/API/Events/Native/BarterEvents/OnBarterEnd.cs +++ b/NWN.Anvil/src/main/API/Events/Native/BarterEvents/OnBarterEnd.cs @@ -108,7 +108,7 @@ private static IReadOnlyList GetBarterItems(CNWSBarter barter) [UnmanagedCallersOnly] private static int OnSendServerToPlayerBarterCloseBarter(void* pMessage, uint nInitiatorId, uint nRecipientId, int bAccepted) { - NwPlayer? player = LowLevel.ServerExoApp.GetClientObjectByPlayerId(nInitiatorId).AsNWSPlayer().ToNwPlayer(); + NwPlayer? player = LowLevel.ServerExoApp.GetClientObjectByPlayerId(nInitiatorId).ToNwPlayer(); if (player == null) { return sendServerToPlayerBarterCloseBarterHook.CallOriginal(pMessage, nInitiatorId, nRecipientId, bAccepted); diff --git a/NWN.Anvil/src/main/API/Events/Native/ClientEvents/OnClientConnect.cs b/NWN.Anvil/src/main/API/Events/Native/ClientEvents/OnClientConnect.cs index 78f71cb95..43e29f11a 100644 --- a/NWN.Anvil/src/main/API/Events/Native/ClientEvents/OnClientConnect.cs +++ b/NWN.Anvil/src/main/API/Events/Native/ClientEvents/OnClientConnect.cs @@ -100,7 +100,7 @@ private static unsafe int OnSendServerToPlayerCharList(void* pMessage, void* pPl PlayerName = playerInfo.m_sPlayerName.ToString()!, ClientVersion = new Version(playerInfo.m_nBuildVersion, playerInfo.m_nPatchRevision), ClientPlatform = (PlayerPlatform)playerInfo.m_nPlatformId, - CDKey = playerInfo.m_lstKeys[0].sPublic.ToString()!, + CDKey = playerInfo.m_cCDKey.sPublic.ToString()!, DM = playerInfo.m_bGameMasterPrivileges.ToBool(), IP = ipAddress, }); diff --git a/NWN.Anvil/src/main/API/Events/Native/CombatEvents/OnCombatStatusChange.cs b/NWN.Anvil/src/main/API/Events/Native/CombatEvents/OnCombatStatusChange.cs index e38ac6760..39fc894c5 100644 --- a/NWN.Anvil/src/main/API/Events/Native/CombatEvents/OnCombatStatusChange.cs +++ b/NWN.Anvil/src/main/API/Events/Native/CombatEvents/OnCombatStatusChange.cs @@ -31,7 +31,7 @@ protected override IDisposable[] RequestHooks() [UnmanagedCallersOnly] private static int OnSendServerToPlayerAmbientBattleMusicPlay(void* pMessage, uint nPlayer, int bPlay) { - NwPlayer? player = ServerExoApp.GetClientObjectByPlayerId(nPlayer).AsNWSPlayer().ToNwPlayer(); + NwPlayer? player = ServerExoApp.GetClientObjectByPlayerId(nPlayer).ToNwPlayer(); OnCombatStatusChange? eventData = null; if (player != null) diff --git a/NWN.Anvil/src/main/API/Events/Native/ItemEvents/OnItemUnequip.cs b/NWN.Anvil/src/main/API/Events/Native/ItemEvents/OnItemUnequip.cs index b75bc1e32..23891c8e6 100644 --- a/NWN.Anvil/src/main/API/Events/Native/ItemEvents/OnItemUnequip.cs +++ b/NWN.Anvil/src/main/API/Events/Native/ItemEvents/OnItemUnequip.cs @@ -31,12 +31,12 @@ public sealed class OnItemUnequip : IEvent public sealed unsafe class Factory : HookEventFactory { - private static FunctionHook Hook { get; set; } = null!; + private static FunctionHook Hook { get; set; } = null!; protected override IDisposable[] RequestHooks() { delegate* unmanaged pHook = &OnUnequipItem; - Hook = HookService.RequestHook(pHook, HookOrder.Early); + Hook = HookService.RequestHook(pHook, HookOrder.Early); return [Hook]; } diff --git a/NWN.Anvil/src/main/API/Extensions/IntegerExtensions.cs b/NWN.Anvil/src/main/API/Extensions/IntegerExtensions.cs index 94661bd82..03fe3d152 100644 --- a/NWN.Anvil/src/main/API/Extensions/IntegerExtensions.cs +++ b/NWN.Anvil/src/main/API/Extensions/IntegerExtensions.cs @@ -146,34 +146,35 @@ public static int ToInt(this bool value) /// The object ID to convert. /// Methods to use to resolve the player. /// The associated player for this object, otherwise null. - public static unsafe NwPlayer? ToNwPlayer(this uint objectId, PlayerSearch playerSearch = PlayerSearch.All) + public static NwPlayer? ToNwPlayer(this uint objectId, PlayerSearch playerSearch = PlayerSearch.All) { if (objectId == NwObject.Invalid) { return null; } - CNWSPlayer? player = null; if (playerSearch.HasFlag(PlayerSearch.Controlled)) { - player = LowLevel.ServerExoApp.GetClientObjectByObjectId(objectId); + CNWSPlayer? player = LowLevel.ServerExoApp.GetClientObjectByObjectId(objectId); + if (player != null) + { + return new NwPlayer(player); + } } - if ((player == null || player.Pointer == IntPtr.Zero) && playerSearch.HasFlag(PlayerSearch.Login)) + if (playerSearch.HasFlag(PlayerSearch.Login)) { - CExoLinkedListInternal players = LowLevel.ServerExoApp.m_pcExoAppInternal.m_pNWSPlayerList.m_pcExoLinkedListInternal; - for (CExoLinkedListNode node = players.pHead; node != null; node = node.pNext) + CExoArrayListCNWSPlayerPtr? players = LowLevel.ServerExoApp.m_pcExoAppInternal.m_lstPlayerList; + foreach (CNWSPlayer player in players) { - CNWSPlayer current = CNWSPlayer.FromPointer(node.pObject); - if (current.m_oidPCObject == objectId) + if (player.m_oidPCObject == objectId) { - player = current; - break; + return new NwPlayer(player); } } } - return player != null && player.Pointer != IntPtr.Zero ? new NwPlayer(player) : null; + return null; } } } diff --git a/NWN.Anvil/src/main/API/Objects/NwArea.cs b/NWN.Anvil/src/main/API/Objects/NwArea.cs index 320e51603..1fbfee596 100644 --- a/NWN.Anvil/src/main/API/Objects/NwArea.cs +++ b/NWN.Anvil/src/main/API/Objects/NwArea.cs @@ -4,6 +4,7 @@ using Anvil.Services; using NWN.Core; using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.API { diff --git a/NWN.Anvil/src/main/API/Objects/NwModule.cs b/NWN.Anvil/src/main/API/Objects/NwModule.cs index 3724f046a..1cdae03a4 100644 --- a/NWN.Anvil/src/main/API/Objects/NwModule.cs +++ b/NWN.Anvil/src/main/API/Objects/NwModule.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Anvil.Internal; -using Anvil.Native; using NWN.Core; using NWN.Native.API; @@ -131,7 +130,7 @@ public int MaxHenchmen /// /// Gets the current player count. /// - public uint PlayerCount => LowLevel.ServerExoApp.m_pcExoAppInternal.m_pNWSPlayerList.Count(); + public int PlayerCount => LowLevel.ServerExoApp.m_pcExoAppInternal.m_lstPlayerList.Count; /// /// Gets all current online players. @@ -140,12 +139,10 @@ public IEnumerable Players { get { - CExoLinkedListCNWSClient playerList = LowLevel.ServerExoApp.m_pcExoAppInternal.m_pNWSPlayerList; - - for (CExoLinkedListNode node = playerList.GetHeadPos(); node != null; node = node.pNext) + CExoArrayListCNWSPlayerPtr? playerList = LowLevel.ServerExoApp.m_pcExoAppInternal.m_lstPlayerList; + foreach (CNWSPlayer player in playerList) { - CNWSPlayer player = playerList.GetAtPos(node).AsNWSPlayer(); - yield return player.ToNwPlayer()!; + yield return new NwPlayer(player); } } } diff --git a/NWN.Anvil/src/main/API/Objects/NwPlayer.cs b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs index 36b2af184..f6a756767 100644 --- a/NWN.Anvil/src/main/API/Objects/NwPlayer.cs +++ b/NWN.Anvil/src/main/API/Objects/NwPlayer.cs @@ -237,8 +237,8 @@ public bool IsValid { get { - CNWSClient client = LowLevel.ServerExoApp.GetClientObjectByPlayerId(PlayerId); - return client != null && client.AsNWSPlayer() == player; + CNWSPlayer? playerById = LowLevel.ServerExoApp.GetClientObjectByPlayerId(PlayerId); + return playerById == player; } } @@ -1687,7 +1687,7 @@ private void AssertPlayerValid() internal static NwPlayer? FromPlayerId(uint playerId) { - CNWSPlayer? player = LowLevel.ServerExoApp.GetClientObjectByPlayerId(playerId, 0)?.AsNWSPlayer(); + CNWSPlayer? player = LowLevel.ServerExoApp.GetClientObjectByPlayerId(playerId); return player != null ? new NwPlayer(player) : null; } } diff --git a/NWN.Anvil/src/main/API/Objects/PlayerQuickBarButton.cs b/NWN.Anvil/src/main/API/Objects/PlayerQuickBarButton.cs index 21e4693e7..aa8761153 100644 --- a/NWN.Anvil/src/main/API/Objects/PlayerQuickBarButton.cs +++ b/NWN.Anvil/src/main/API/Objects/PlayerQuickBarButton.cs @@ -1,4 +1,5 @@ using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.API { diff --git a/NWN.Anvil/src/main/API/Resources/Gff/Fields/GffResourceField.cs b/NWN.Anvil/src/main/API/Resources/Gff/Fields/GffResourceField.cs index 0a93714be..610651ff5 100644 --- a/NWN.Anvil/src/main/API/Resources/Gff/Fields/GffResourceField.cs +++ b/NWN.Anvil/src/main/API/Resources/Gff/Fields/GffResourceField.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.API { diff --git a/NWN.Anvil/src/main/API/Resources/Gff/Fields/GffResourceFieldStruct.cs b/NWN.Anvil/src/main/API/Resources/Gff/Fields/GffResourceFieldStruct.cs index 1ed4d50ee..e4696b8b0 100644 --- a/NWN.Anvil/src/main/API/Resources/Gff/Fields/GffResourceFieldStruct.cs +++ b/NWN.Anvil/src/main/API/Resources/Gff/Fields/GffResourceFieldStruct.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.API { @@ -22,10 +23,10 @@ internal GffResourceFieldStruct(CResGFF resGff, CResStruct resStruct) : base(res for (uint i = 0; i < fieldCount; i++) { byte* fieldIdPtr = ResGff.GetFieldLabel(resStruct, i); - string key = StringHelper.ReadNullTerminatedString(fieldIdPtr); + string? key = StringUtils.ReadNullTerminatedString(fieldIdPtr); GffResourceField? value = Create(resGff, resStruct, i, fieldIdPtr); - if (value == null) + if (key == null || value == null) { continue; } diff --git a/NWN.Anvil/src/main/API/Resources/Gff/GffResource.cs b/NWN.Anvil/src/main/API/Resources/Gff/GffResource.cs index 582814215..c8ccb567d 100644 --- a/NWN.Anvil/src/main/API/Resources/Gff/GffResource.cs +++ b/NWN.Anvil/src/main/API/Resources/Gff/GffResource.cs @@ -1,5 +1,6 @@ using System; using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.API { @@ -8,11 +9,12 @@ public sealed class GffResource : IDisposable private readonly CResGFF resGff; private readonly CResStruct rootStruct; - internal GffResource(string name, CResGFF resGff) + internal unsafe GffResource(string name, CResGFF resGff) { this.resGff = resGff; + NativeArray? fileType = this.resGff.m_pFileType; - FileType = resGff.m_pFileType.ReadFixedLengthString().Trim(); + FileType = StringUtils.ReadFixedLengthString(fileType.Pointer, fileType.Length).Trim(); rootStruct = new CResStruct(); if (!resGff.GetTopLevelStruct(rootStruct).ToBool()) diff --git a/NWN.Anvil/src/main/API/Ruleset/NwBaseItem.cs b/NWN.Anvil/src/main/API/Ruleset/NwBaseItem.cs index 265392201..324f5c26c 100644 --- a/NWN.Anvil/src/main/API/Ruleset/NwBaseItem.cs +++ b/NWN.Anvil/src/main/API/Ruleset/NwBaseItem.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.API { @@ -189,7 +190,14 @@ internal NwBaseItem(uint baseItemId, CNWBaseItem baseItemInfo) /// Gets the ResRef of the item's model, or the base part of the resref.
/// This property is dependent on . See https://nwn.wiki/display/NWN1/baseitems.2da for more info. ///
- public string? ItemClass => BaseItemInfo.m_ItemClassResRefChunk.ReadFixedLengthString(); + public string ItemClass + { + get + { + NativeArray? resRefChunk = BaseItemInfo.m_ItemClassResRefChunk; + return StringUtils.ReadFixedLengthString(resRefChunk.Pointer, resRefChunk.Length); + } + } /// /// Gets the maximum number of "cast spell" properties items of this type can be given when designed in the Toolset. diff --git a/NWN.Anvil/src/main/API/Utils/NativeUtils.cs b/NWN.Anvil/src/main/API/Utils/NativeUtils.cs index 0b298366f..70298cfbd 100644 --- a/NWN.Anvil/src/main/API/Utils/NativeUtils.cs +++ b/NWN.Anvil/src/main/API/Utils/NativeUtils.cs @@ -7,6 +7,7 @@ using Anvil.Services; using NLog; using NWN.Native.API; +using NWNX.NET.Native; using Vector = NWN.Native.API.Vector; namespace Anvil.API @@ -132,13 +133,13 @@ public static T PeekMessage(this CNWSMessage message, int offset) where T : u public static string PeekMessageResRef(this CNWSMessage message, int offset) { byte* ptr = message.m_pnReadBuffer + message.m_nReadBufferPtr + offset; - return StringHelper.ReadFixedLengthString(ptr, 16); + return StringUtils.ReadFixedLengthString(ptr, 16); } public static string PeekMessageString(this CNWSMessage message, int offset) { byte* ptr = message.m_pnReadBuffer + message.m_nReadBufferPtr + offset; - return StringHelper.ReadNullTerminatedString(ptr); + return StringUtils.ReadNullTerminatedString(ptr)!; } public static byte[]? SerializeGff(string fileType, string version, Func serializeAction) diff --git a/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs b/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs index 59a3c14d9..1c52b7e66 100644 --- a/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs +++ b/NWN.Anvil/src/main/AnvilCore.FunctionHandlers.cs @@ -4,7 +4,8 @@ using System.Runtime.InteropServices; using Anvil.Services; using NWN.Core; -using NWN.Native.API; +using NWNX.NET; +using NWNX.NET.Native; using Action = System.Action; namespace Anvil @@ -27,7 +28,7 @@ public sealed partial class AnvilCore : ICoreFunctionHandler void ICoreFunctionHandler.ClosureActionDoCommand(uint obj, Action func) { - if (VM.ClosureActionDoCommand(obj, nextEventId) != 0) + if (NWNXAPI.ClosureActionDoCommand(obj, nextEventId) != 0) { Closures.Add(nextEventId++, func); } @@ -35,7 +36,7 @@ void ICoreFunctionHandler.ClosureActionDoCommand(uint obj, Action func) void ICoreFunctionHandler.ClosureAssignCommand(uint obj, Action func) { - if (VM.ClosureAssignCommand(obj, nextEventId) != 0) + if (NWNXAPI.ClosureAssignCommand(obj, nextEventId) != 0) { Closures.Add(nextEventId++, func); } @@ -43,7 +44,7 @@ void ICoreFunctionHandler.ClosureAssignCommand(uint obj, Action func) void ICoreFunctionHandler.ClosureDelayCommand(uint obj, float duration, Action func) { - if (VM.ClosureDelayCommand(obj, duration, nextEventId) != 0) + if (NWNXAPI.ClosureDelayCommand(obj, duration, nextEventId) != 0) { Closures.Add(nextEventId++, func); } @@ -52,7 +53,7 @@ void ICoreFunctionHandler.ClosureDelayCommand(uint obj, float duration, Action f [UnmanagedCallersOnly] private static void OnNWNXSignal(IntPtr signalPtr) { - string signal = signalPtr.ReadNullTerminatedString(); + string? signal = signalPtr.ReadNullTerminatedString(); switch (signal) { @@ -75,8 +76,13 @@ private static void OnNWNXSignal(IntPtr signalPtr) [UnmanagedCallersOnly] private static int OnRunScript(IntPtr scriptPtr, uint oidSelf) { + string? script = scriptPtr.ReadNullTerminatedString(); + if (script == null) + { + return 0; + } + int retVal; - string script = scriptPtr.ReadNullTerminatedString(); objectSelf = oidSelf; ScriptContexts.Push(oidSelf); @@ -123,8 +129,8 @@ private static void OnLoop(ulong _) [UnmanagedCallersOnly] private static void OnAssertFail(IntPtr messagePtr, IntPtr nativeStackTracePtr) { - string message = messagePtr.ReadNullTerminatedString(); - string nativeStackTrace = nativeStackTracePtr.ReadNullTerminatedString(); + string? message = messagePtr.ReadNullTerminatedString(); + string? nativeStackTrace = nativeStackTracePtr.ReadNullTerminatedString(); StackTrace stackTrace = new StackTrace(true); Log.Error("An assertion failure occurred in native code.\n" + diff --git a/NWN.Anvil/src/main/AnvilCore.cs b/NWN.Anvil/src/main/AnvilCore.cs index b3a3658c7..aac34edf7 100644 --- a/NWN.Anvil/src/main/AnvilCore.cs +++ b/NWN.Anvil/src/main/AnvilCore.cs @@ -9,12 +9,13 @@ using NLog; using NWN.Core; using NWN.Native.API; +using NWNX.NET; 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. + /// Until is called, all APIs are unavailable for usage. ///
public sealed partial class AnvilCore { @@ -42,28 +43,20 @@ private AnvilCore(IServiceManager serviceManager) } /// - /// Entrypoint to start Anvil. + /// Entrypoint to start Anvil. This should be specified in the "NWNX_DOTNET_METHOD" environment variable to be triggered by NWNX. /// - /// The NativeHandles pointer, provided by the NWNX bootstrap entry point. - /// The size of the NativeHandles bootstrap structure, provided by the NWNX entry point. - /// 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 unsafe int Init(IntPtr arg, int argLength, IServiceManager? serviceManager = default) + internal static unsafe void Bootstrap() { - serviceManager ??= new AnvilServiceManager(); + AnvilServiceManager serviceManager = new AnvilServiceManager(); instance = new AnvilCore(serviceManager); - NWNCore.NativeEventHandlesUnmanaged eventHandles = new NWNCore.NativeEventHandlesUnmanaged - { - Signal = &OnNWNXSignal, - RunScript = &OnRunScript, - Closure = &OnClosure, - MainLoop = &OnLoop, - AssertFail = &OnAssertFail, - }; - - return NWNCore.Init(arg, argLength, instance, eventHandles); + NWNCore.Init(instance); + NWNXAPI.RegisterSignalHandler(&OnNWNXSignal); + NWNXAPI.RegisterRunScriptHandler(&OnRunScript); + NWNXAPI.RegisterClosureHandler(&OnClosure); + NWNXAPI.RegisterMainLoopHandler(&OnLoop); + NWNXAPI.RegisterAssertHandler(&OnAssertFail); } /// diff --git a/NWN.Anvil/src/main/Bootstrap.cs b/NWN.Anvil/src/main/Bootstrap.cs deleted file mode 100644 index 4ce5ef42c..000000000 --- a/NWN.Anvil/src/main/Bootstrap.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using JetBrains.Annotations; - -// ReSharper disable once CheckNamespace -namespace NWN -{ - // @cond INTERNAL - public static class Internal - { - [UsedImplicitly] // Called by NWNX - public static int Bootstrap(IntPtr arg, int argLength) - { - return Anvil.AnvilCore.Init(arg, argLength); - } - } - - // @endcond -} diff --git a/NWN.Anvil/src/main/Native/CExoStringData.cs b/NWN.Anvil/src/main/Native/CExoStringData.cs index d46b6f390..03345286d 100644 --- a/NWN.Anvil/src/main/Native/CExoStringData.cs +++ b/NWN.Anvil/src/main/Native/CExoStringData.cs @@ -1,6 +1,6 @@ using System; using System.Runtime.InteropServices; -using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.Native { diff --git a/NWN.Anvil/src/main/Native/Functions/Functions.CExoDebugInternal.cs b/NWN.Anvil/src/main/Native/Functions/Functions.CExoDebugInternal.cs index 9369d4ebf..46e41d406 100644 --- a/NWN.Anvil/src/main/Native/Functions/Functions.CExoDebugInternal.cs +++ b/NWN.Anvil/src/main/Native/Functions/Functions.CExoDebugInternal.cs @@ -7,9 +7,6 @@ internal static unsafe partial class Functions { public static class CExoDebugInternal { - [NativeFunction("_ZN17CExoDebugInternal16WriteToErrorFileERK10CExoString", "?WriteToErrorFile@CExoDebugInternal@@QEAAXAEBVCExoString@@@Z")] - public delegate void WriteToErrorFile(void* pExoDebugInternal, void* pMessage); - [NativeFunction("_ZN17CExoDebugInternal14WriteToLogFileERK10CExoString", "?WriteToLogFile@CExoDebugInternal@@QEAAXAEBVCExoString@@@Z")] public delegate void WriteToLogFile(void* pExoDebugInternal, void* pMessage); } diff --git a/NWN.Anvil/src/main/Native/Functions/Functions.CNWSCreature.cs b/NWN.Anvil/src/main/Native/Functions/Functions.CNWSCreature.cs index 4ede33a11..385c51045 100644 --- a/NWN.Anvil/src/main/Native/Functions/Functions.CNWSCreature.cs +++ b/NWN.Anvil/src/main/Native/Functions/Functions.CNWSCreature.cs @@ -76,6 +76,9 @@ public delegate int AddCastSpellActions(void* pCreature, uint nSpellId, int nMul [NativeFunction("_ZN12CNWSCreature8RunEquipEjjj", "?RunEquip@CNWSCreature@@QEAAHIII@Z")] public delegate int RunEquip(void* pCreature, uint oidItemToEquip, uint nInventorySlot, uint oidFeedbackPlayer); + [NativeFunction("_ZN12CNWSCreature10RunUnequipEjjhhij", "?RunUnequip@CNWSCreature@@QEAAHIIEEHI@Z")] + public delegate int RunUnequip(void* pCreature, uint oidItemToUnequip, uint oidTargetRepository, byte x, byte y, int bMergeIntoRepository, uint oidFeedbackPlayer); + [NativeFunction("_ZN12CNWSCreature19SendFeedbackMessageEtP16CNWCCMessageDataP10CNWSPlayer", "?SendFeedbackMessage@CNWSCreature@@QEAAXGPEAVCNWCCMessageData@@PEAVCNWSPlayer@@@Z")] public delegate void SendFeedbackMessage(void* pCreature, ushort nFeedbackId, void* pMessageData, void* pFeedbackPlayer); @@ -94,9 +97,6 @@ public delegate int AddCastSpellActions(void* pCreature, uint nSpellId, int nMul [NativeFunction("_ZN12CNWSCreature18SignalRangedDamageEP10CNWSObjecti", "?SignalRangedDamage@CNWSCreature@@IEAAXPEAVCNWSObject@@H@Z")] public delegate void SignalRangedDamage(void* pCreature, void* pTarget, int nAttacks); - [NativeFunction("_ZN12CNWSCreature10RunUnequipEjjhhij", "?UnequipItem@CNWSCreature@@QEAAHPEAVCNWSItem@@H@Z")] - public delegate int UnequipItem(void* pCreature, uint oidItemToUnequip, uint oidTargetRepository, byte x, byte y, int bMergeIntoRepository, uint oidFeedbackPlayer); - [NativeFunction("_ZN12CNWSCreature17UnpossessFamiliarEv", "?UnpossessFamiliar@CNWSCreature@@QEAAXXZ")] public delegate void UnpossessFamiliar(void* pCreature); diff --git a/NWN.Anvil/src/main/Services/Chat/ChatService.cs b/NWN.Anvil/src/main/Services/Chat/ChatService.cs index efc59deab..f45895d01 100644 --- a/NWN.Anvil/src/main/Services/Chat/ChatService.cs +++ b/NWN.Anvil/src/main/Services/Chat/ChatService.cs @@ -161,7 +161,7 @@ private int OnSendServerToPlayerChatMessage(void* pMessage, ChatChannel nChatMes return retVal; } - CExoLinkedListInternal playerList = LowLevel.ServerExoApp.m_pcExoAppInternal.m_pNWSPlayerList.m_pcExoLinkedListInternal; + CExoArrayListCNWSPlayerPtr? playerList = LowLevel.ServerExoApp.m_pcExoAppInternal.m_lstPlayerList; if (playerList == null) { return false.ToInt(); diff --git a/NWN.Anvil/src/main/Services/Core/EncodingService.cs b/NWN.Anvil/src/main/Services/Core/EncodingService.cs index 6d1cabd74..3794e9083 100644 --- a/NWN.Anvil/src/main/Services/Core/EncodingService.cs +++ b/NWN.Anvil/src/main/Services/Core/EncodingService.cs @@ -1,8 +1,7 @@ using System; using System.Text; using Anvil.Internal; -using NWN.Core; -using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.Services { @@ -16,7 +15,7 @@ public sealed class EncodingService : ICoreService /// public Encoding Encoding { - get => NWNCore.Encoding; + get => StringUtils.Encoding; set { if (value == null) @@ -24,8 +23,7 @@ public Encoding Encoding throw new NullReferenceException("Encoding must not be null."); } - NWNCore.Encoding = value; - StringHelper.Encoding = value; + StringUtils.Encoding = value; } } diff --git a/NWN.Anvil/src/main/Services/Core/Hooking/FunctionHook.cs b/NWN.Anvil/src/main/Services/Core/Hooking/FunctionHook.cs index 5edba1ee3..75546cb97 100644 --- a/NWN.Anvil/src/main/Services/Core/Hooking/FunctionHook.cs +++ b/NWN.Anvil/src/main/Services/Core/Hooking/FunctionHook.cs @@ -1,11 +1,12 @@ using System; using System.Runtime.InteropServices; using JetBrains.Annotations; -using NWN.Core; +using NWNX.NET; +using NWNX.NET.Native; namespace Anvil.Services { - public sealed class FunctionHook : IDisposable where T : Delegate + public sealed unsafe class FunctionHook : IDisposable where T : Delegate { /// /// The original function call - invoke this to run the standard game behaviour. @@ -17,14 +18,14 @@ public sealed class FunctionHook : IDisposable where T : Delegate private readonly T? handler; private readonly HookService hookService; - private readonly IntPtr nativeFuncPtr; + private readonly FunctionHook* functionHook; - internal FunctionHook(HookService hookService, IntPtr nativeFuncPtr, T? handler = null) + internal FunctionHook(HookService hookService, FunctionHook* functionHook, T? handler = null) { this.hookService = hookService; - this.nativeFuncPtr = nativeFuncPtr; + this.functionHook = functionHook; this.handler = handler; - CallOriginal = Marshal.GetDelegateForFunctionPointer(nativeFuncPtr); + CallOriginal = Marshal.GetDelegateForFunctionPointer((IntPtr)functionHook->m_trampoline); } /// @@ -32,7 +33,7 @@ internal FunctionHook(HookService hookService, IntPtr nativeFuncPtr, T? handler /// public void Dispose() { - VM.ReturnHook(nativeFuncPtr); + NWNXAPI.ReturnFunctionHook(functionHook); hookService.RemoveHook(this); } } diff --git a/NWN.Anvil/src/main/Services/Core/Hooking/HookService.cs b/NWN.Anvil/src/main/Services/Core/Hooking/HookService.cs index 9e645a0ff..94b8b2471 100644 --- a/NWN.Anvil/src/main/Services/Core/Hooking/HookService.cs +++ b/NWN.Anvil/src/main/Services/Core/Hooking/HookService.cs @@ -4,7 +4,8 @@ using System.Reflection; using System.Runtime.InteropServices; using NLog; -using NWN.Core; +using NWNX.NET; +using NWNX.NET.Native; namespace Anvil.Services { @@ -27,8 +28,8 @@ public sealed unsafe class HookService : ICoreService public FunctionHook RequestHook(T handler, int order = HookOrder.Default) where T : Delegate { IntPtr managedFuncPtr = Marshal.GetFunctionPointerForDelegate(handler); - IntPtr nativeFuncPtr = CreateHook(managedFuncPtr, order); - FunctionHook hook = new FunctionHook(this, nativeFuncPtr, handler); + FunctionHook* functionHook = CreateHook(managedFuncPtr, order); + FunctionHook hook = new FunctionHook(this, functionHook, handler); hooks.Add(hook); return hook; @@ -43,14 +44,14 @@ public FunctionHook RequestHook(T handler, int order = HookOrder.Default) /// A wrapper object containing a delegate to the original function. The wrapped object can be disposed to release the hook. public FunctionHook RequestHook(void* handler, int order = HookOrder.Default) where T : Delegate { - IntPtr nativeFuncPtr = CreateHook((IntPtr)handler, order); - FunctionHook hook = new FunctionHook(this, nativeFuncPtr); + FunctionHook* functionHook = CreateHook((IntPtr)handler, order); + FunctionHook hook = new FunctionHook(this, functionHook); hooks.Add(hook); return hook; } - private IntPtr CreateHook(IntPtr handler, int order) + private FunctionHook* CreateHook(IntPtr handler, int order) { NativeFunctionAttribute? info = typeof(T).GetCustomAttribute(); if (info == null) @@ -59,7 +60,7 @@ private IntPtr CreateHook(IntPtr handler, int order) } Log.Debug("Requesting function hook for {HookType}, address {Address}", typeof(T).Name, $"0x{info.Address:X}"); - return VM.RequestHook(info.Address, handler, order); + return NWNXAPI.RequestFunctionHook(info.Address, handler, order); } void ICoreService.Init() {} diff --git a/NWN.Anvil/src/main/Services/Feedback/FeedbackService.cs b/NWN.Anvil/src/main/Services/Feedback/FeedbackService.cs index c9865211f..8282ee8ef 100644 --- a/NWN.Anvil/src/main/Services/Feedback/FeedbackService.cs +++ b/NWN.Anvil/src/main/Services/Feedback/FeedbackService.cs @@ -166,7 +166,7 @@ private void OnSendFeedbackMessage(void* pCreature, ushort nFeedbackId, void* pM private int OnSendServerToPlayerCCMessage(void* pMessage, uint nPlayerId, byte nMinor, void* pMessageData, void* pAttackData) { - CNWSPlayer player = ServerExoApp.GetClientObjectByPlayerId(nPlayerId, 0).AsNWSPlayer(); + CNWSPlayer player = ServerExoApp.GetClientObjectByPlayerId(nPlayerId); if (IsMessageHidden(globalFilterListCombatMessage, playerFilterListCombatMessage, player.m_oidPCObject, (CombatLogMessage)nMinor, CombatMessageFilterMode)) { return false.ToInt(); diff --git a/NWN.Anvil/src/main/Services/LogRedirector/ServerLogRedirectorService.cs b/NWN.Anvil/src/main/Services/LogRedirector/ServerLogRedirectorService.cs index bcc6e088e..e1e1c4ebc 100644 --- a/NWN.Anvil/src/main/Services/LogRedirector/ServerLogRedirectorService.cs +++ b/NWN.Anvil/src/main/Services/LogRedirector/ServerLogRedirectorService.cs @@ -16,8 +16,6 @@ internal sealed unsafe class ServerLogRedirectorService : IDisposable private readonly bool callOriginal; private readonly FunctionHook? executeCommandPrintStringHook; - private readonly FunctionHook? writeToErrorFileHook; - private readonly FunctionHook? writeToLogFileHook; private bool printString; @@ -36,14 +34,12 @@ public ServerLogRedirectorService(HookService hookService) } writeToLogFileHook = hookService.RequestHook(OnWriteToLogFile, HookOrder.VeryEarly); - writeToErrorFileHook = hookService.RequestHook(OnWriteToErrorFile, HookOrder.VeryEarly); executeCommandPrintStringHook = hookService.RequestHook(OnExecuteCommandPrintString, HookOrder.VeryEarly); } public void Dispose() { writeToLogFileHook?.Dispose(); - writeToErrorFileHook?.Dispose(); executeCommandPrintStringHook?.Dispose(); } @@ -55,17 +51,6 @@ private int OnExecuteCommandPrintString(void* pVirtualMachineCommands, int nComm return retVal; } - private void OnWriteToErrorFile(void* pExoDebugInternal, void* pMessage) - { - CExoString message = CExoString.FromPointer(pMessage); - Log.Error(TrimMessage(message)); - - if (callOriginal) - { - writeToErrorFileHook!.CallOriginal(pExoDebugInternal, pMessage); - } - } - private void OnWriteToLogFile(void* pExoDebugInternal, void* pMessage) { CExoString message = CExoString.FromPointer(pMessage); diff --git a/NWN.Anvil/src/main/Services/ObjectStorage/ObjectStorageService.cs b/NWN.Anvil/src/main/Services/ObjectStorage/ObjectStorageService.cs index 1ada2abce..b0769b225 100644 --- a/NWN.Anvil/src/main/Services/ObjectStorage/ObjectStorageService.cs +++ b/NWN.Anvil/src/main/Services/ObjectStorage/ObjectStorageService.cs @@ -5,6 +5,7 @@ using Anvil.Native; using NLog; using NWN.Native.API; +using NWNX.NET.Native; namespace Anvil.Services { diff --git a/NWN.Anvil/src/main/Services/Resources/ResourceManager.cs b/NWN.Anvil/src/main/Services/Resources/ResourceManager.cs index c3c71dc9f..89a2947c1 100644 --- a/NWN.Anvil/src/main/Services/Resources/ResourceManager.cs +++ b/NWN.Anvil/src/main/Services/Resources/ResourceManager.cs @@ -8,6 +8,7 @@ using NLog; using NWN.Core; using NWN.Native.API; +using NWNX.NET.Native; using ResRefType = Anvil.API.ResRefType; namespace Anvil.Services @@ -132,7 +133,7 @@ public string GetNSSContents(string scriptName) { case ResRefType.NSS: string source = GetNSSContents(name); - return StringHelper.Encoding.GetBytes(source); + return StringUtils.Encoding.GetBytes(source); case ResRefType.NCS: return null; default: @@ -156,7 +157,7 @@ public string GetNSSContents(string scriptName) return null; default: byte[]? data = GetStandardResourceData(name, type); - return data != null ? StringHelper.Encoding.GetString(data) : null; + return data != null ? StringUtils.Encoding.GetString(data) : null; } } @@ -190,7 +191,7 @@ public void WriteTempResource(string resourceName, byte[] data) /// The text to populate in the resource. public void WriteTempResource(string resourceName, string text) { - WriteTempResource(resourceName, StringHelper.Encoding.GetBytes(text)); + WriteTempResource(resourceName, StringUtils.Encoding.GetBytes(text)); } private byte[]? GetStandardResourceData(string name, ResRefType type) diff --git a/NWN.Anvil/src/main/Services/Services/IServiceManager.cs b/NWN.Anvil/src/main/Services/Services/IServiceManager.cs index 4b0ad1136..2cbb097aa 100644 --- a/NWN.Anvil/src/main/Services/Services/IServiceManager.cs +++ b/NWN.Anvil/src/main/Services/Services/IServiceManager.cs @@ -5,8 +5,7 @@ namespace Anvil.Services { /// - /// The interface that manages all core, anvil and plugin services.
- /// Advanced: implement in your own class and specify in to implement your own service manager. + /// The interface that manages all core, anvil and plugin services. ///
public interface IServiceManager { diff --git a/dockerfile b/dockerfile index 3353a1998..069c8d3dd 100644 --- a/dockerfile +++ b/dockerfile @@ -56,6 +56,9 @@ ENV NWN_LD_PRELOAD="/nwn/nwnx/NWNX_Core.so" ENV NWNX_DOTNET_SKIP=n ENV NWNX_SWIG_DOTNET_SKIP=n ENV NWNX_DOTNET_ASSEMBLY=/nwn/anvil/NWN.Anvil +ENV NWNX_DOTNET_ENTRYPOINT=Anvil.AnvilCore +ENV NWNX_DOTNET_METHOD=Bootstrap +ENV NWNX_DOTNET_NEW_BOOTSTRAP=true # Use NWNX_ServerLogRedirector as default log manager ENV NWNX_SERVERLOGREDIRECTOR_SKIP=n diff --git a/docs/NWN.Anvil.Samples.csproj b/docs/NWN.Anvil.Samples.csproj index a53c0bcd8..346cda3da 100644 --- a/docs/NWN.Anvil.Samples.csproj +++ b/docs/NWN.Anvil.Samples.csproj @@ -30,7 +30,7 @@ - +