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 @@
-
+