A src/Core/NosSmooth.LocalBinding/Errors/NeededModulesNotInitializedError.cs => src/Core/NosSmooth.LocalBinding/Errors/NeededModulesNotInitializedError.cs +21 -0
@@ 0,0 1,21 @@
+//
+// NeededModulesNotInitializedError.cs
+//
+// Copyright (c) František Boháček. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Errors;
+
+/// <summary>
+/// A modules that are needed for the given operation are not loaded.
+/// </summary>
+/// <param name="additionalMessage">The message to show.</param>
+/// <param name="modules">The modules.</param>
+public record NeededModulesNotInitializedError
+ (
+ string additionalMessage,
+ params string[] modules
+ )
+ : ResultError($"{additionalMessage}. Some needed modules ({string.Join(", ", modules)}) are not loaded");<
\ No newline at end of file
M src/Core/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs => src/Core/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs +34 -9
@@ 37,14 37,22 @@ public static class ServiceCollectionExtensions
.AddSingleton<NosBrowserManager>()
.AddSingleton<NosThreadSynchronizer>()
.AddSingleton<IHookManager, HookManager>()
- .AddSingleton<IPacketReceiveHook>(p => p.GetRequiredService<IHookManager>().PacketReceive)
- .AddSingleton<IPacketSendHook>(p => p.GetRequiredService<IHookManager>().PacketSend)
- .AddSingleton<IEntityFollowHook>(p => p.GetRequiredService<IHookManager>().EntityFollow)
- .AddSingleton<IEntityUnfollowHook>(p => p.GetRequiredService<IHookManager>().EntityUnfollow)
- .AddSingleton<IEntityFocusHook>(p => p.GetRequiredService<IHookManager>().EntityFocus)
- .AddSingleton<IPlayerWalkHook>(p => p.GetRequiredService<IHookManager>().PlayerWalk)
- .AddSingleton<IPetWalkHook>(p => p.GetRequiredService<IHookManager>().PetWalk)
- .AddSingleton<IPeriodicHook>(p => p.GetRequiredService<IHookManager>().Periodic)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PacketReceive)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PacketSend)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityFollow)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityUnfollow)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityFocus)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PlayerWalk)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PetWalk)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().Periodic)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PacketReceive.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PacketSend.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityFollow.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityUnfollow.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityFocus.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PlayerWalk.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PetWalk.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().Periodic.Get())
.AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PlayerManager)
.AddSingleton(p => p.GetRequiredService<NosBrowserManager>().SceneManager)
.AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PetManagerList)
@@ 61,7 69,24 @@ public static class ServiceCollectionExtensions
.AddSingleton(p => p.GetRequiredService<IHookManager>().EntityFocus)
.AddSingleton(p => p.GetRequiredService<IHookManager>().EntityFollow)
.AddSingleton(p => p.GetRequiredService<IHookManager>().EntityUnfollow)
- .AddSingleton(p => p.GetRequiredService<IHookManager>().Periodic);
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().Periodic)
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PlayerManager.Get())
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().SceneManager.Get())
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PetManagerList.Get())
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().SceneManager.Get())
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PetManagerList.Get())
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PlayerManager.Get())
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().NetworkManager.Get())
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().UnitManager.Get())
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().NtClient.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PacketReceive.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PacketSend.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PlayerWalk.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PetWalk.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityFocus.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityFollow.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityUnfollow.Get())
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().Periodic.Get());
}
/// <summary>
M src/Core/NosSmooth.LocalBinding/Hooks/IHookManager.cs => src/Core/NosSmooth.LocalBinding/Hooks/IHookManager.cs +40 -8
@@ 56,37 56,37 @@ public interface IHookManager
/// <summary>
/// Gets the packet send hook.
/// </summary>
- public IPacketSendHook PacketSend { get; }
+ public Optional<IPacketSendHook> PacketSend { get; }
/// <summary>
/// Gets the packet receive hook.
/// </summary>
- public IPacketReceiveHook PacketReceive { get; }
+ public Optional<IPacketReceiveHook> PacketReceive { get; }
/// <summary>
/// Gets the player walk hook.
/// </summary>
- public IPlayerWalkHook PlayerWalk { get; }
+ public Optional<IPlayerWalkHook> PlayerWalk { get; }
/// <summary>
/// Gets the entity follow hook.
/// </summary>
- public IEntityFollowHook EntityFollow { get; }
+ public Optional<IEntityFollowHook> EntityFollow { get; }
/// <summary>
/// Gets the entity unfollow hook.
/// </summary>
- public IEntityUnfollowHook EntityUnfollow { get; }
+ public Optional<IEntityUnfollowHook> EntityUnfollow { get; }
/// <summary>
/// Gets the player walk hook.
/// </summary>
- public IPetWalkHook PetWalk { get; }
+ public Optional<IPetWalkHook> PetWalk { get; }
/// <summary>
/// Gets the entity focus hook.
/// </summary>
- public IEntityFocusHook EntityFocus { get; }
+ public Optional<IEntityFocusHook> EntityFocus { get; }
/// <summary>
/// Gets the periodic function hook.
@@ 95,7 95,7 @@ public interface IHookManager
/// May be any function that is called periodically.
/// This is used for synchronizing using <see cref="NosThreadSynchronizer"/>.
/// </remarks>
- public IPeriodicHook Periodic { get; }
+ public Optional<IPeriodicHook> Periodic { get; }
/// <summary>
/// Gets all of the hooks.
@@ 139,4 139,36 @@ public interface IHookManager
/// Enable all hooks.
/// </summary>
public void EnableAll();
+
+ /// <summary>
+ /// Checks whether hook of the given type is loaded (there were no errors in finding the function).
+ /// </summary>
+ /// <typeparam name="THook">The type of the hook.</typeparam>
+ /// <returns>Whether the hook is loaded/present.</returns>
+ public bool IsHookLoaded<THook>()
+ where THook : INostaleHook;
+
+ /// <summary>
+ /// Checks whether hook of the given type is loaded (there were no errors in finding the function)
+ /// and that the wrapper function is present/usable.
+ /// </summary>
+ /// <typeparam name="THook">The type of the hook.</typeparam>
+ /// <returns>Whether the hook is loaded/present and usable.</returns>
+ public bool IsHookUsable<THook>()
+ where THook : INostaleHook;
+
+ /// <summary>
+ /// Checks whether hook of the given type is loaded (there were no errors in finding the function).
+ /// </summary>
+ /// <param name="hookType">The type of the hook.</typeparam>
+ /// <returns>Whether the hook is loaded/present.</returns>
+ public bool IsHookLoaded(Type hookType);
+
+ /// <summary>
+ /// Checks whether hook of the given type is loaded (there were no errors in finding the function)
+ /// and that the wrapper function is present/usable.
+ /// </summary>
+ /// <param name="hookType">The type of the hook.</typeparam>
+ /// <returns>Whether the hook is loaded/present and usable.</returns>
+ public bool IsHookUsable(Type hookType);
}=
\ No newline at end of file
M src/Core/NosSmooth.LocalBinding/Hooks/INostaleHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/INostaleHook.cs +6 -1
@@ 22,7 22,7 @@ public interface INostaleHook<TFunction, TWrapperFunction, TEventArgs> : INostal
/// <summary>
/// Gets the wrapper function delegate.
/// </summary>
- public TWrapperFunction WrapperFunction { get; }
+ public Optional<TWrapperFunction> WrapperFunction { get; }
/// <summary>
/// Gets the original function delegate.
@@ 41,6 41,11 @@ public interface INostaleHook<TFunction, TWrapperFunction, TEventArgs> : INostal
public interface INostaleHook
{
/// <summary>
+ /// Gets whether the wrapper function is present and usable.
+ /// </summary>
+ public bool IsUsable { get; }
+
+ /// <summary>
/// Gets the name of the function.
/// </summary>
/// <remarks>
M src/Core/NosSmooth.LocalBinding/Hooks/Implementations/CancelableNostaleHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/CancelableNostaleHook.cs +4 -1
@@ 64,7 64,7 @@ public abstract class CancelableNostaleHook<TFunction, TWrapperFunction, TEventA
public bool IsEnabled => Hook.Hook.IsEnabled;
/// <inheritdoc />
- public abstract TWrapperFunction WrapperFunction { get; }
+ public abstract Optional<TWrapperFunction> WrapperFunction { get; }
/// <inheritdoc/>
public TFunction OriginalFunction
@@ 84,6 84,9 @@ public abstract class CancelableNostaleHook<TFunction, TWrapperFunction, TEventA
public event EventHandler<TEventArgs>? Called;
/// <inheritdoc />
+ public bool IsUsable => WrapperFunction.IsPresent;
+
+ /// <inheritdoc />
public abstract string Name { get; }
/// <inheritdoc />
M src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFocusHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFocusHook.cs +17 -13
@@ 30,7 30,7 @@ internal class EntityFocusHook : CancelableNostaleHook<IEntityFocusHook.EntityFo
var hook = CreateHook
(
bindingManager,
- () => new EntityFocusHook(browserManager.UnitManager),
+ () => new EntityFocusHook(browserManager.Memory, browserManager.UnitManager),
hook => hook.Detour,
options
);
@@ 38,10 38,12 @@ internal class EntityFocusHook : CancelableNostaleHook<IEntityFocusHook.EntityFo
return hook;
}
- private readonly UnitManager _unitManager;
+ private readonly Optional<UnitManager> _unitManager;
+ private readonly IMemory _memory;
- private EntityFocusHook(UnitManager unitManager)
+ private EntityFocusHook(IMemory memory, Optional<UnitManager> unitManager)
{
+ _memory = memory;
_unitManager = unitManager;
}
@@ 49,18 51,20 @@ internal class EntityFocusHook : CancelableNostaleHook<IEntityFocusHook.EntityFo
public override string Name => IHookManager.EntityFocusName;
/// <inheritdoc />
- public override IEntityFocusHook.EntityFocusWrapperDelegate WrapperFunction => (entity) => OriginalFunction
- (_unitManager.Address, entity?.Address ?? 0);
+ public override Optional<IEntityFocusHook.EntityFocusWrapperDelegate> WrapperFunction
+ => _unitManager.Map<IEntityFocusHook.EntityFocusWrapperDelegate>
+ (unitManager => entity => OriginalFunction(unitManager.Address, entity?.Address ?? 0));
/// <inheritdoc />
- protected override IEntityFocusHook.EntityFocusDelegate WrapWithCalling(IEntityFocusHook.EntityFocusDelegate function)
+ protected override IEntityFocusHook.EntityFocusDelegate WrapWithCalling
+ (IEntityFocusHook.EntityFocusDelegate function)
=> (unitManagerPtr, entityPtr) =>
- {
- CallingFromNosSmooth = true;
- var res = function(unitManagerPtr, entityPtr);
- CallingFromNosSmooth = false;
- return res;
- };
+ {
+ CallingFromNosSmooth = true;
+ var res = function(unitManagerPtr, entityPtr);
+ CallingFromNosSmooth = false;
+ return res;
+ };
private nuint Detour
(
@@ 68,7 72,7 @@ internal class EntityFocusHook : CancelableNostaleHook<IEntityFocusHook.EntityFo
nuint entityPtr
)
{
- var entity = new MapBaseObj(_unitManager.Memory, entityPtr);
+ var entity = new MapBaseObj(_memory, entityPtr);
var entityArgs = new EntityEventArgs(entity);
return HandleCall(entityArgs);
}
M src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFollowHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFollowHook.cs +19 -8
@@ 6,6 6,7 @@
using NosSmooth.LocalBinding.EventArgs;
using NosSmooth.LocalBinding.Structs;
+using Reloaded.Memory.Sources;
using Remora.Results;
namespace NosSmooth.LocalBinding.Hooks.Implementations;
@@ 29,7 30,7 @@ internal class EntityFollowHook : CancelableNostaleHook<IEntityFollowHook.Entity
var hook = CreateHook
(
bindingManager,
- () => new EntityFollowHook(browserManager.PlayerManager),
+ () => new EntityFollowHook(browserManager.Memory, browserManager.PlayerManager),
hook => hook.Detour,
options
);
@@ 37,10 38,12 @@ internal class EntityFollowHook : CancelableNostaleHook<IEntityFollowHook.Entity
return hook;
}
- private readonly PlayerManager _playerManager;
+ private readonly Optional<PlayerManager> _playerManager;
+ private readonly IMemory _memory;
- private EntityFollowHook(PlayerManager playerManager)
+ private EntityFollowHook(IMemory memory, Optional<PlayerManager> playerManager)
{
+ _memory = memory;
_playerManager = playerManager;
}
@@ 48,12 51,20 @@ internal class EntityFollowHook : CancelableNostaleHook<IEntityFollowHook.Entity
public override string Name => IHookManager.EntityFollowName;
/// <inheritdoc />
- public override IEntityFollowHook.EntityFollowWrapperDelegate WrapperFunction => (entity) => OriginalFunction
- (_playerManager.Address, entity?.Address ?? 0);
+ public override Optional<IEntityFollowHook.EntityFollowWrapperDelegate> WrapperFunction
+ => _playerManager.Map<IEntityFollowHook.EntityFollowWrapperDelegate>
+ (playerManager => entity => OriginalFunction(playerManager.Address, entity?.Address ?? 0));
/// <inheritdoc />
- protected override IEntityFollowHook.EntityFollowDelegate WrapWithCalling(IEntityFollowHook.EntityFollowDelegate function)
- => (playerManagerPtr, entityPtr, un0, un1) =>
+ protected override IEntityFollowHook.EntityFollowDelegate WrapWithCalling
+ (IEntityFollowHook.EntityFollowDelegate function)
+ =>
+ (
+ playerManagerPtr,
+ entityPtr,
+ un0,
+ un1
+ ) =>
{
CallingFromNosSmooth = true;
var res = function(playerManagerPtr, entityPtr, un0, un1);
@@ 69,7 80,7 @@ internal class EntityFollowHook : CancelableNostaleHook<IEntityFollowHook.Entity
int unknown2 = 1
)
{
- var entity = new MapBaseObj(_playerManager.Memory, entityPtr);
+ var entity = new MapBaseObj(_memory, entityPtr);
var entityArgs = new EntityEventArgs(entity);
return HandleCall(entityArgs);
}
M src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityUnfollowHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityUnfollowHook.cs +4 -4
@@ 37,9 37,9 @@ internal class EntityUnfollowHook : CancelableNostaleHook<IEntityUnfollowHook.En
return hook;
}
- private readonly PlayerManager _playerManager;
+ private readonly Optional<PlayerManager> _playerManager;
- private EntityUnfollowHook(PlayerManager playerManager)
+ private EntityUnfollowHook(Optional<PlayerManager> playerManager)
{
_playerManager = playerManager;
}
@@ 48,8 48,8 @@ internal class EntityUnfollowHook : CancelableNostaleHook<IEntityUnfollowHook.En
public override string Name => IHookManager.EntityUnfollowName;
/// <inheritdoc />
- public override IEntityUnfollowHook.EntityUnfollowWrapperDelegate WrapperFunction
- => () => OriginalFunction(_playerManager.Address);
+ public override Optional<IEntityUnfollowHook.EntityUnfollowWrapperDelegate> WrapperFunction
+ => _playerManager.Map<IEntityUnfollowHook.EntityUnfollowWrapperDelegate>(playerManager => () => OriginalFunction(playerManager.Address));
/// <inheritdoc />
protected override IEntityUnfollowHook.EntityUnfollowDelegate WrapWithCalling(IEntityUnfollowHook.EntityUnfollowDelegate function)
M src/Core/NosSmooth.LocalBinding/Hooks/Implementations/HookManager.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/HookManager.cs +54 -11
@@ 15,7 15,8 @@ namespace NosSmooth.LocalBinding.Hooks.Implementations;
internal class HookManager : IHookManager
{
private readonly HookManagerOptions _options;
- private Dictionary<string, INostaleHook> _hooks;
+ private readonly Dictionary<string, INostaleHook> _hooks;
+ private bool _initialized;
/// <summary>
/// Initializes a new instance of the <see cref="HookManager"/> class.
@@ 28,28 29,29 @@ internal class HookManager : IHookManager
}
/// <inheritdoc/>
- public IPacketSendHook PacketSend => GetHook<IPacketSendHook>(IHookManager.PacketSendName);
+ public Optional<IPacketSendHook> PacketSend => GetHook<IPacketSendHook>(IHookManager.PacketSendName);
/// <inheritdoc/>
- public IPacketReceiveHook PacketReceive => GetHook<IPacketReceiveHook>(IHookManager.PacketReceiveName);
+ public Optional<IPacketReceiveHook> PacketReceive => GetHook<IPacketReceiveHook>(IHookManager.PacketReceiveName);
/// <inheritdoc/>
- public IPlayerWalkHook PlayerWalk => GetHook<IPlayerWalkHook>(IHookManager.CharacterWalkName);
+ public Optional<IPlayerWalkHook> PlayerWalk => GetHook<IPlayerWalkHook>(IHookManager.CharacterWalkName);
/// <inheritdoc/>
- public IEntityFollowHook EntityFollow => GetHook<IEntityFollowHook>(IHookManager.EntityFollowName);
+ public Optional<IEntityFollowHook> EntityFollow => GetHook<IEntityFollowHook>(IHookManager.EntityFollowName);
/// <inheritdoc/>
- public IEntityUnfollowHook EntityUnfollow => GetHook<IEntityUnfollowHook>(IHookManager.EntityUnfollowName);
+ public Optional<IEntityUnfollowHook> EntityUnfollow => GetHook<IEntityUnfollowHook>
+ (IHookManager.EntityUnfollowName);
/// <inheritdoc/>
- public IPetWalkHook PetWalk => GetHook<IPetWalkHook>(IHookManager.PetWalkName);
+ public Optional<IPetWalkHook> PetWalk => GetHook<IPetWalkHook>(IHookManager.PetWalkName);
/// <inheritdoc/>
- public IEntityFocusHook EntityFocus => GetHook<IEntityFocusHook>(IHookManager.EntityFocusName);
+ public Optional<IEntityFocusHook> EntityFocus => GetHook<IEntityFocusHook>(IHookManager.EntityFocusName);
/// <inheritdoc/>
- public IPeriodicHook Periodic => GetHook<IPeriodicHook>(IHookManager.PeriodicName);
+ public Optional<IPeriodicHook> Periodic => GetHook<IPeriodicHook>(IHookManager.PeriodicName);
/// <inheritdoc/>
public IReadOnlyList<INostaleHook> Hooks => _hooks.Values.ToList();
@@ 57,6 59,7 @@ internal class HookManager : IHookManager
/// <inheritdoc/>
public IResult Initialize(NosBindingManager bindingManager, NosBrowserManager browserManager)
{
+ _initialized = true;
if (_hooks.Count > 0)
{ // already initialized
return Result.FromSuccess();
@@ 151,15 154,55 @@ internal class HookManager : IHookManager
}
}
- private T GetHook<T>(string name)
+ /// <inheritdoc/>
+ public bool IsHookLoaded<THook>()
+ where THook : INostaleHook
+ => IsHookLoaded(typeof(THook));
+
+ /// <inheritdoc/>
+ public bool IsHookUsable<THook>()
+ where THook : INostaleHook
+ => IsHookUsable(typeof(THook));
+
+ /// <inheritdoc/>
+ public bool IsHookLoaded(Type hookType)
+ => GetHook(hookType).IsPresent;
+
+ /// <inheritdoc/>
+ public bool IsHookUsable(Type hookType)
+ => GetHook(hookType).TryGet(out var h) && h.IsUsable;
+
+ private Optional<T> GetHook<T>(string name)
where T : INostaleHook
{
- if (!_hooks.ContainsKey(name) || _hooks[name] is not T typed)
+ if (!_initialized)
{
throw new InvalidOperationException
($"Could not load hook {name}. Did you forget to call IHookManager.Initialize?");
}
+ if (!_hooks.ContainsKey(name) || _hooks[name] is not T typed)
+ {
+ return Optional<T>.Empty;
+ }
+
return typed;
}
+
+ private Optional<INostaleHook> GetHook(Type hookType)
+ {
+ if (!_initialized)
+ {
+ throw new InvalidOperationException
+ ($"Could not load hook {hookType.Name}. Did you forget to call IHookManager.Initialize?");
+ }
+
+ var hook = _hooks.Values.FirstOrDefault(x => x.GetType() == hookType);
+ if (hook is null)
+ {
+ return Optional<INostaleHook>.Empty;
+ }
+
+ return new Optional<INostaleHook>(hook);
+ }
}=
\ No newline at end of file
M src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketReceiveHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketReceiveHook.cs +23 -15
@@ 8,6 8,7 @@ using System.Runtime.InteropServices;
using NosSmooth.LocalBinding.EventArgs;
using NosSmooth.LocalBinding.Objects;
using NosSmooth.LocalBinding.Structs;
+using Reloaded.Memory.Sources;
using Remora.Results;
namespace NosSmooth.LocalBinding.Hooks.Implementations;
@@ 29,20 30,22 @@ internal class PacketReceiveHook : CancelableNostaleHook<IPacketReceiveHook.Pack
(NosBindingManager bindingManager, NosBrowserManager browserManager, HookOptions<IPacketReceiveHook> options)
{
var hook = CreateHook
- (
- bindingManager,
- () => new PacketReceiveHook(browserManager.NetworkManager),
- (hook) => hook.Detour,
- options
- );
+ (
+ bindingManager,
+ () => new PacketReceiveHook(browserManager.Memory, browserManager.NetworkManager),
+ (hook) => hook.Detour,
+ options
+ );
return hook;
}
- private NetworkManager _networkManager;
+ private readonly IMemory _memory;
+ private Optional<NetworkManager> _networkManager;
- private PacketReceiveHook(NetworkManager networkManager)
+ private PacketReceiveHook(IMemory memory, Optional<NetworkManager> networkManager)
{
+ _memory = memory;
_networkManager = networkManager;
}
@@ 50,15 53,20 @@ internal class PacketReceiveHook : CancelableNostaleHook<IPacketReceiveHook.Pack
public override string Name => IHookManager.PacketReceiveName;
/// <inheritdoc />
- public override IPacketReceiveHook.PacketReceiveWrapperDelegate WrapperFunction => (packetString) =>
- {
- var packetObject = _networkManager.GetAddressForPacketReceive();
- using var nostaleString = NostaleStringA.Create(_networkManager.Memory, packetString);
- OriginalFunction(packetObject, nostaleString.Get());
- };
+ public override Optional<IPacketReceiveHook.PacketReceiveWrapperDelegate> WrapperFunction
+ => _networkManager.Map<IPacketReceiveHook.PacketReceiveWrapperDelegate>
+ (
+ networkManager => (packetString) =>
+ {
+ var packetObject = networkManager.GetAddressForPacketReceive();
+ using var nostaleString = NostaleStringA.Create(_memory, packetString);
+ OriginalFunction(packetObject, nostaleString.Get());
+ }
+ );
/// <inheritdoc />
- protected override IPacketReceiveHook.PacketReceiveDelegate WrapWithCalling(IPacketReceiveHook.PacketReceiveDelegate function)
+ protected override IPacketReceiveHook.PacketReceiveDelegate WrapWithCalling
+ (IPacketReceiveHook.PacketReceiveDelegate function)
=> (packetObject, packetString) =>
{
CallingFromNosSmooth = true;
M src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketSendHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketSendHook.cs +21 -15
@@ 30,33 30,39 @@ internal class PacketSendHook : CancelableNostaleHook<IPacketSendHook.PacketSend
(NosBindingManager bindingManager, NosBrowserManager browserManager, HookOptions<IPacketSendHook> options)
{
var hook = CreateHook
- (
- bindingManager,
- () => new PacketSendHook(browserManager.NetworkManager),
- (sendHook) => sendHook.Detour,
- options
- );
+ (
+ bindingManager,
+ () => new PacketSendHook(browserManager.Memory, browserManager.NetworkManager),
+ (sendHook) => sendHook.Detour,
+ options
+ );
return hook;
}
- private PacketSendHook(NetworkManager networkManager)
+ private readonly IMemory _memory;
+ private readonly Optional<NetworkManager> _networkManager;
+
+ private PacketSendHook(IMemory memory, Optional<NetworkManager> networkManager)
{
+ _memory = memory;
_networkManager = networkManager;
}
- private readonly NetworkManager _networkManager;
-
/// <inheritdoc />
public override string Name => IHookManager.PacketSendName;
/// <inheritdoc />
- public override IPacketSendHook.PacketSendWrapperDelegate WrapperFunction => (packetString) =>
- {
- var packetObject = _networkManager.GetAddressForPacketSend();
- using var nostaleString = NostaleStringA.Create(_networkManager.Memory, packetString);
- OriginalFunction(packetObject, nostaleString.Get());
- };
+ public override Optional<IPacketSendHook.PacketSendWrapperDelegate> WrapperFunction =>
+ _networkManager.Map<IPacketSendHook.PacketSendWrapperDelegate>
+ (
+ networkManager => (packetString) =>
+ {
+ var packetObject = networkManager.GetAddressForPacketSend();
+ using var nostaleString = NostaleStringA.Create(_memory, packetString);
+ OriginalFunction(packetObject, nostaleString.Get());
+ }
+ );
/// <inheritdoc />
protected override IPacketSendHook.PacketSendDelegate WrapWithCalling(IPacketSendHook.PacketSendDelegate function)
M src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PeriodicHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PeriodicHook.cs +4 -1
@@ 40,6 40,9 @@ internal class PeriodicHook : IPeriodicHook
private NosAsmHook<IPeriodicHook.PeriodicDelegate> _hook = null!;
+ /// <inheritdoc/>
+ public bool IsUsable => WrapperFunction.IsPresent;
+
/// <inheritdoc />
public string Name => IHookManager.PeriodicName;
@@ 47,7 50,7 @@ internal class PeriodicHook : IPeriodicHook
public bool IsEnabled => _hook.Hook.IsEnabled;
/// <inheritdoc />
- public IPeriodicHook.PeriodicDelegate WrapperFunction => OriginalFunction;
+ public Optional<IPeriodicHook.PeriodicDelegate> WrapperFunction => OriginalFunction;
/// <inheritdoc/>
public IPeriodicHook.PeriodicDelegate OriginalFunction => throw new InvalidOperationException
M src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PetWalkHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PetWalkHook.cs +10 -3
@@ 48,8 48,8 @@ internal class PetWalkHook : CancelableNostaleHook<IPetWalkHook.PetWalkDelegate,
public override string Name => IHookManager.PetWalkName;
/// <inheritdoc />
- public override IPetWalkHook.PetWalkWrapperDelegate WrapperFunction
- => (p, x, y) => OriginalFunction(p.Address, (y << 16) | x) == 1;
+ public override Optional<IPetWalkHook.PetWalkWrapperDelegate> WrapperFunction
+ => (IPetWalkHook.PetWalkWrapperDelegate)((p, x, y) => OriginalFunction(p.Address, (y << 16) | x) == 1);
/// <inheritdoc />
protected override IPetWalkHook.PetWalkDelegate WrapWithCalling(IPetWalkHook.PetWalkDelegate function)
@@ 63,7 63,14 @@ internal class PetWalkHook : CancelableNostaleHook<IPetWalkHook.PetWalkDelegate,
) =>
{
CallingFromNosSmooth = true;
- var res = function(petManagerPtr, position, un0, un1, un2);
+ var res = function
+ (
+ petManagerPtr,
+ position,
+ un0,
+ un1,
+ un2
+ );
CallingFromNosSmooth = false;
return res;
};
M src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PlayerWalkHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PlayerWalkHook.cs +12 -8
@@ 37,22 37,26 @@ internal class PlayerWalkHook : CancelableNostaleHook<IPlayerWalkHook.WalkDelega
return hook;
}
- private PlayerWalkHook(PlayerManager playerManager)
+ private readonly Optional<PlayerManager> _playerManager;
+
+ private PlayerWalkHook(Optional<PlayerManager> playerManager)
{
_playerManager = playerManager;
}
- private PlayerManager _playerManager;
-
/// <inheritdoc />
public override string Name => IHookManager.CharacterWalkName;
/// <inheritdoc />
- public override IPlayerWalkHook.WalkWrapperDelegate WrapperFunction => (x, y) =>
- {
- var playerManagerObject = _playerManager.Address;
- return OriginalFunction(playerManagerObject, (y << 16) | x) == 1;
- };
+ public override Optional<IPlayerWalkHook.WalkWrapperDelegate> WrapperFunction
+ => _playerManager.Map<IPlayerWalkHook.WalkWrapperDelegate>
+ (
+ playerManager => (x, y) =>
+ {
+ var playerManagerObject = playerManager.Address;
+ return OriginalFunction(playerManagerObject, (y << 16) | x) == 1;
+ }
+ );
/// <inheritdoc />
protected override IPlayerWalkHook.WalkDelegate WrapWithCalling(IPlayerWalkHook.WalkDelegate function)
M src/Core/NosSmooth.LocalBinding/NosBindingManager.cs => src/Core/NosSmooth.LocalBinding/NosBindingManager.cs +9 -0
@@ 12,6 12,7 @@ using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Hooks.Implementations;
using NosSmooth.LocalBinding.Objects;
using NosSmooth.LocalBinding.Options;
+using NosSmooth.LocalBinding.Structs;
using Reloaded.Hooks;
using Reloaded.Hooks.Definitions;
using Reloaded.Hooks.Definitions.Helpers;
@@ 98,6 99,14 @@ public class NosBindingManager : IDisposable
};
}
+ /// <summary>
+ /// Gets whether a hook or browser module is present.
+ /// </summary>
+ /// <typeparam name="TModule">The type of the module.</typeparam>
+ /// <returns>Whether the module is present.</returns>
+ public bool IsModulePresent<TModule>()
+ => _hookManager.IsHookUsable(typeof(TModule)) || _browserManager.IsModuleLoaded(typeof(TModule));
+
/// <inheritdoc />
public void Dispose()
{
M src/Core/NosSmooth.LocalBinding/NosBrowserManager.cs => src/Core/NosSmooth.LocalBinding/NosBrowserManager.cs +87 -20
@@ 53,6 53,7 @@ public class NosBrowserManager
.GetProcesses()
.Where(IsProcessNostaleProcess);
+ private readonly Dictionary<Type, NostaleObject> _modules;
private readonly PlayerManagerOptions _playerManagerOptions;
private readonly SceneManagerOptions _sceneManagerOptions;
private readonly PetManagerOptions _petManagerOptions;
@@ 65,6 66,7 @@ public class NosBrowserManager
private NetworkManager? _networkManager;
private UnitManager? _unitManager;
private NtClient? _ntClient;
+ private bool _initialized;
/// <summary>
/// Initializes a new instance of the <see cref="NosBrowserManager"/> class.
@@ 118,6 120,7 @@ public class NosBrowserManager
NtClientOptions? ntClientOptions = default
)
{
+ _modules = new Dictionary<Type, NostaleObject>();
_playerManagerOptions = playerManagerOptions ?? new PlayerManagerOptions();
_sceneManagerOptions = sceneManagerOptions ?? new SceneManagerOptions();
_petManagerOptions = petManagerOptions ?? new PetManagerOptions();
@@ 153,7 156,7 @@ public class NosBrowserManager
/// Gets the network manager.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the browser is not initialized or there was an error with initialization of network manager.</exception>
- public NetworkManager NetworkManager
+ public Optional<NetworkManager> NetworkManager
{
get
{
@@ 173,7 176,7 @@ public class NosBrowserManager
/// Gets the network manager.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the browser is not initialized or there was an error with initialization of unit manager.</exception>
- public UnitManager UnitManager
+ public Optional<UnitManager> UnitManager
{
get
{
@@ 195,20 198,13 @@ public class NosBrowserManager
/// <remarks>
/// It may be unsafe to access some data if the player is not in game.
/// </remarks>
- public bool IsInGame
- {
- get
- {
- var player = PlayerManager.Player;
- return player.Address != nuint.Zero;
- }
- }
+ public Optional<bool> IsInGame => PlayerManager.Map(manager => manager.Player.Address != nuint.Zero);
/// <summary>
/// Gets the nt client.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the browser is not initialized or there was an error with initialization of nt client.</exception>
- public NtClient NtClient
+ public Optional<NtClient> NtClient
{
get
{
@@ 228,7 224,7 @@ public class NosBrowserManager
/// Gets the player manager.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the browser is not initialized or there was an error with initialization of player manager.</exception>
- public PlayerManager PlayerManager
+ public Optional<PlayerManager> PlayerManager
{
get
{
@@ 248,7 244,7 @@ public class NosBrowserManager
/// Gets the scene manager.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the browser is not initialized or there was an error with initialization of scene manager.</exception>
- public SceneManager SceneManager
+ public Optional<SceneManager> SceneManager
{
get
{
@@ 268,7 264,7 @@ public class NosBrowserManager
/// Gets the pet manager list.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the browser is not initialized or there was an error with initialization of pet manager list.</exception>
- public PetManagerList PetManagerList
+ public Optional<PetManagerList> PetManagerList
{
get
{
@@ 298,10 294,11 @@ public class NosBrowserManager
return (Result)new NotNostaleProcessError(Process);
}
+ _initialized = true;
List<IResult> errorResults = new List<IResult>();
if (_unitManager is null)
{
- var unitManagerResult = UnitManager.Create(this, _unitManagerOptions);
+ var unitManagerResult = Structs.UnitManager.Create(this, _unitManagerOptions);
if (!unitManagerResult.IsSuccess)
{
errorResults.Add
@@ 319,7 316,7 @@ public class NosBrowserManager
if (_networkManager is null)
{
- var networkManagerResult = NetworkManager.Create(this, _networkManagerOptions);
+ var networkManagerResult = Structs.NetworkManager.Create(this, _networkManagerOptions);
if (!networkManagerResult.IsSuccess)
{
errorResults.Add
@@ 337,7 334,7 @@ public class NosBrowserManager
if (_playerManager is null)
{
- var playerManagerResult = PlayerManager.Create(this, _playerManagerOptions);
+ var playerManagerResult = Structs.PlayerManager.Create(this, _playerManagerOptions);
if (!playerManagerResult.IsSuccess)
{
errorResults.Add
@@ 355,7 352,7 @@ public class NosBrowserManager
if (_sceneManager is null)
{
- var sceneManagerResult = SceneManager.Create(this, _sceneManagerOptions);
+ var sceneManagerResult = Structs.SceneManager.Create(this, _sceneManagerOptions);
if (!sceneManagerResult.IsSuccess)
{
errorResults.Add
@@ 373,7 370,7 @@ public class NosBrowserManager
if (_petManagerList is null)
{
- var petManagerResult = PetManagerList.Create(this, _petManagerOptions);
+ var petManagerResult = Structs.PetManagerList.Create(this, _petManagerOptions);
if (!petManagerResult.IsSuccess)
{
errorResults.Add
@@ 391,7 388,7 @@ public class NosBrowserManager
if (_ntClient is null)
{
- var ntClientResult = NtClient.Create(this, _ntClientOptions);
+ var ntClientResult = Structs.NtClient.Create(this, _ntClientOptions);
if (!ntClientResult.IsSuccess)
{
errorResults.Add
@@ 414,4 411,74 @@ public class NosBrowserManager
_ => (Result)new AggregateError(errorResults)
};
}
+
+ /// <summary>
+ /// Gets whether a hook or browser module is present/loaded.
+ /// Returns false in case pattern was not found.
+ /// </summary>
+ /// <typeparam name="TModule">The type of the module.</typeparam>
+ /// <returns>Whether the module is present.</returns>
+ public bool IsModuleLoaded<TModule>()
+ where TModule : NostaleObject
+ => IsModuleLoaded(typeof(TModule));
+
+ /// <summary>
+ /// Gets whether a hook or browser module is present/loaded.
+ /// Returns false in case pattern was not found.
+ /// </summary>
+ /// <param name="moduleType">The type of the module.</typeparam>
+ /// <returns>Whether the module is present.</returns>
+ public bool IsModuleLoaded(Type moduleType)
+ {
+ return GetModule(moduleType).IsPresent;
+ }
+
+ /// <summary>
+ /// Get module of the specified type.
+ /// </summary>
+ /// <typeparam name="TModule">The type of the module.</typeparam>
+ /// <returns>The module.</returns>
+ /// <exception cref="InvalidOperationException">Thrown in case the manager was not initialized.</exception>
+ public Optional<TModule> GetModule<TModule>()
+ where TModule : NostaleObject
+ {
+ if (!_initialized)
+ {
+ throw new InvalidOperationException
+ (
+ $"Could not get {typeof(TModule)}. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
+ );
+ }
+
+ if (!_modules.TryGetValue(typeof(TModule), out var nosObject) || nosObject is not TModule typed)
+ {
+ return Optional<TModule>.Empty;
+ }
+
+ return typed;
+ }
+
+ /// <summary>
+ /// Get module of the specified type.
+ /// </summary>
+ /// <param name="moduleType">The type of the module.</typeparam>
+ /// <returns>The module.</returns>
+ /// <exception cref="InvalidOperationException">Thrown in case the manager was not initialized.</exception>
+ public Optional<NostaleObject> GetModule(Type moduleType)
+ {
+ if (!_initialized)
+ {
+ throw new InvalidOperationException
+ (
+ $"Could not get {moduleType.Name}. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
+ );
+ }
+
+ if (!_modules.TryGetValue(moduleType, out var nosObject))
+ {
+ return Optional<NostaleObject>.Empty;
+ }
+
+ return nosObject;
+ }
}=
\ No newline at end of file
M src/Core/NosSmooth.LocalBinding/NosThreadSynchronizer.cs => src/Core/NosSmooth.LocalBinding/NosThreadSynchronizer.cs +9 -5
@@ 7,6 7,7 @@
using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
+using NosSmooth.LocalBinding.Errors;
using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Options;
using Remora.Results;
@@ 18,7 19,7 @@ namespace NosSmooth.LocalBinding;
/// </summary>
public class NosThreadSynchronizer
{
- private readonly IPeriodicHook _periodicHook;
+ private readonly Optional<IPeriodicHook> _periodicHook;
private readonly ILogger<NosThreadSynchronizer> _logger;
private readonly NosThreadSynchronizerOptions _options;
private readonly ConcurrentQueue<SyncOperation> _queuedOperations;
@@ 32,7 33,7 @@ public class NosThreadSynchronizer
/// <param name="options">The options.</param>
public NosThreadSynchronizer
(
- IPeriodicHook periodicHook,
+ Optional<IPeriodicHook> periodicHook,
ILogger<NosThreadSynchronizer> logger,
IOptions<NosThreadSynchronizerOptions> options
)
@@ 51,9 52,12 @@ public class NosThreadSynchronizer
/// <summary>
/// Start the synchronizer operation.
/// </summary>
- public void StartSynchronizer()
+ /// <returns>The result, successful if periodic hook is present.</returns>
+ public Result StartSynchronizer()
{
- _periodicHook.Called += PeriodicCall;
+ return _periodicHook.TryDo(h => h.Called += PeriodicCall)
+ ? Result.FromSuccess()
+ : new NeededModulesNotInitializedError("Could not start synchronizer, because the periodic hook is not present", IHookManager.PeriodicName);
}
/// <summary>
@@ 61,7 65,7 @@ public class NosThreadSynchronizer
/// </summary>
public void StopSynchronizer()
{
- _periodicHook.Called -= PeriodicCall;
+ _periodicHook.TryDo(h => h.Called -= PeriodicCall);
}
private void PeriodicCall(object? owner, System.EventArgs eventArgs)
M src/Core/NosSmooth.LocalBinding/Structs/ControlManager.cs => src/Core/NosSmooth.LocalBinding/Structs/ControlManager.cs +1 -0
@@ 5,6 5,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Reloaded.Memory.Sources;
+using Remora.Results;
namespace NosSmooth.LocalBinding.Structs;
M src/Core/NosSmooth.LocalBinding/Structs/MapBaseObj.cs => src/Core/NosSmooth.LocalBinding/Structs/MapBaseObj.cs +1 -0
@@ 5,6 5,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Reloaded.Memory.Sources;
+using Remora.Results;
namespace NosSmooth.LocalBinding.Structs;
M src/Core/NosSmooth.LocalBinding/Structs/NostaleList.cs => src/Core/NosSmooth.LocalBinding/Structs/NostaleList.cs +3 -7
@@ 9,6 9,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.ObjectiveC;
using Reloaded.Memory.Pointers;
using Reloaded.Memory.Sources;
+using Remora.Results;
namespace NosSmooth.LocalBinding.Structs;
@@ 16,7 17,7 @@ namespace NosSmooth.LocalBinding.Structs;
/// A class representing a list from nostale.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
-public abstract class NostaleList<T> : IEnumerable<T>
+public abstract class NostaleList<T> : NostaleObject, IEnumerable<T>
where T : NostaleObject
{
private readonly IMemory _memory;
@@ 27,17 28,12 @@ public abstract class NostaleList<T> : IEnumerable<T>
/// <param name="memory">The memory.</param>
/// <param name="objListPointer">The object list pointer.</param>
public NostaleList(IMemory memory, nuint objListPointer)
+ : base(memory, objListPointer)
{
_memory = memory;
- Address = objListPointer;
}
/// <summary>
- /// Gets the address.
- /// </summary>
- protected nuint Address { get; }
-
- /// <summary>
/// Gets the element at the given index.
/// </summary>
/// <param name="index">The index of the element.</param>
M src/Core/NosSmooth.LocalBinding/Structs/SceneManager.cs => src/Core/NosSmooth.LocalBinding/Structs/SceneManager.cs +3 -2
@@ 15,7 15,7 @@ namespace NosSmooth.LocalBinding.Structs;
/// <summary>
/// Represents nostale scene manager struct.
/// </summary>
-public class SceneManager
+public class SceneManager : NostaleObject
{
/// <summary>
/// Create <see cref="PlayerManager"/> instance.
@@ 51,6 51,7 @@ public class SceneManager
/// <param name="staticSceneManagerAddress">The pointer to the scene manager.</param>
/// <param name="sceneManagerOffsets">The offsets from the static scene manager address.</param>
public SceneManager(IMemory memory, int staticSceneManagerAddress, int[] sceneManagerOffsets)
+ : base(memory, nuint.Zero)
{
_memory = memory;
_staticSceneManagerAddress = staticSceneManagerAddress;
@@ 60,7 61,7 @@ public class SceneManager
/// <summary>
/// Gets the address of the scene manager.
/// </summary>
- public nuint Address => _memory.FollowStaticAddressOffsets(_staticSceneManagerAddress, _sceneManagerOffsets);
+ public override nuint Address => _memory.FollowStaticAddressOffsets(_staticSceneManagerAddress, _sceneManagerOffsets);
/// <summary>
/// Gets the player list.