From cf76dfcdbe24fa6871034a08eab1bcd8dcc69a7e Mon Sep 17 00:00:00 2001 From: Rutherther Date: Tue, 14 Feb 2023 10:30:04 +0100 Subject: [PATCH] feat(binding): convert hooks and modules to optionals --- .../NeededModulesNotInitializedError.cs | 21 ++++ .../Extensions/ServiceCollectionExtensions.cs | 43 +++++-- .../Hooks/IHookManager.cs | 48 ++++++-- .../Hooks/INostaleHook.cs | 7 +- .../Implementations/CancelableNostaleHook.cs | 5 +- .../Hooks/Implementations/EntityFocusHook.cs | 30 ++--- .../Hooks/Implementations/EntityFollowHook.cs | 27 +++-- .../Implementations/EntityUnfollowHook.cs | 8 +- .../Hooks/Implementations/HookManager.cs | 65 +++++++++-- .../Implementations/PacketReceiveHook.cs | 38 ++++--- .../Hooks/Implementations/PacketSendHook.cs | 36 +++--- .../Hooks/Implementations/PeriodicHook.cs | 5 +- .../Hooks/Implementations/PetWalkHook.cs | 13 ++- .../Hooks/Implementations/PlayerWalkHook.cs | 20 ++-- .../NosBindingManager.cs | 9 ++ .../NosBrowserManager.cs | 107 ++++++++++++++---- .../NosThreadSynchronizer.cs | 14 ++- .../Structs/ControlManager.cs | 1 + .../Structs/MapBaseObj.cs | 1 + .../Structs/NostaleList.cs | 10 +- .../Structs/SceneManager.cs | 5 +- 21 files changed, 382 insertions(+), 131 deletions(-) create mode 100644 src/Core/NosSmooth.LocalBinding/Errors/NeededModulesNotInitializedError.cs diff --git a/src/Core/NosSmooth.LocalBinding/Errors/NeededModulesNotInitializedError.cs b/src/Core/NosSmooth.LocalBinding/Errors/NeededModulesNotInitializedError.cs new file mode 100644 index 0000000..705a54d --- /dev/null +++ b/src/Core/NosSmooth.LocalBinding/Errors/NeededModulesNotInitializedError.cs @@ -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; + +/// +/// A modules that are needed for the given operation are not loaded. +/// +/// The message to show. +/// The modules. +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 diff --git a/src/Core/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs b/src/Core/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs index 84e6b25..1b99dfc 100644 --- a/src/Core/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs +++ b/src/Core/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs @@ -37,14 +37,22 @@ public static class ServiceCollectionExtensions .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(p => p.GetRequiredService().PacketReceive) - .AddSingleton(p => p.GetRequiredService().PacketSend) - .AddSingleton(p => p.GetRequiredService().EntityFollow) - .AddSingleton(p => p.GetRequiredService().EntityUnfollow) - .AddSingleton(p => p.GetRequiredService().EntityFocus) - .AddSingleton(p => p.GetRequiredService().PlayerWalk) - .AddSingleton(p => p.GetRequiredService().PetWalk) - .AddSingleton(p => p.GetRequiredService().Periodic) + .AddSingleton(p => p.GetRequiredService().PacketReceive) + .AddSingleton(p => p.GetRequiredService().PacketSend) + .AddSingleton(p => p.GetRequiredService().EntityFollow) + .AddSingleton(p => p.GetRequiredService().EntityUnfollow) + .AddSingleton(p => p.GetRequiredService().EntityFocus) + .AddSingleton(p => p.GetRequiredService().PlayerWalk) + .AddSingleton(p => p.GetRequiredService().PetWalk) + .AddSingleton(p => p.GetRequiredService().Periodic) + .AddSingleton(p => p.GetRequiredService().PacketReceive.Get()) + .AddSingleton(p => p.GetRequiredService().PacketSend.Get()) + .AddSingleton(p => p.GetRequiredService().EntityFollow.Get()) + .AddSingleton(p => p.GetRequiredService().EntityUnfollow.Get()) + .AddSingleton(p => p.GetRequiredService().EntityFocus.Get()) + .AddSingleton(p => p.GetRequiredService().PlayerWalk.Get()) + .AddSingleton(p => p.GetRequiredService().PetWalk.Get()) + .AddSingleton(p => p.GetRequiredService().Periodic.Get()) .AddSingleton(p => p.GetRequiredService().PlayerManager) .AddSingleton(p => p.GetRequiredService().SceneManager) .AddSingleton(p => p.GetRequiredService().PetManagerList) @@ -61,7 +69,24 @@ public static class ServiceCollectionExtensions .AddSingleton(p => p.GetRequiredService().EntityFocus) .AddSingleton(p => p.GetRequiredService().EntityFollow) .AddSingleton(p => p.GetRequiredService().EntityUnfollow) - .AddSingleton(p => p.GetRequiredService().Periodic); + .AddSingleton(p => p.GetRequiredService().Periodic) + .AddSingleton(p => p.GetRequiredService().PlayerManager.Get()) + .AddSingleton(p => p.GetRequiredService().SceneManager.Get()) + .AddSingleton(p => p.GetRequiredService().PetManagerList.Get()) + .AddSingleton(p => p.GetRequiredService().SceneManager.Get()) + .AddSingleton(p => p.GetRequiredService().PetManagerList.Get()) + .AddSingleton(p => p.GetRequiredService().PlayerManager.Get()) + .AddSingleton(p => p.GetRequiredService().NetworkManager.Get()) + .AddSingleton(p => p.GetRequiredService().UnitManager.Get()) + .AddSingleton(p => p.GetRequiredService().NtClient.Get()) + .AddSingleton(p => p.GetRequiredService().PacketReceive.Get()) + .AddSingleton(p => p.GetRequiredService().PacketSend.Get()) + .AddSingleton(p => p.GetRequiredService().PlayerWalk.Get()) + .AddSingleton(p => p.GetRequiredService().PetWalk.Get()) + .AddSingleton(p => p.GetRequiredService().EntityFocus.Get()) + .AddSingleton(p => p.GetRequiredService().EntityFollow.Get()) + .AddSingleton(p => p.GetRequiredService().EntityUnfollow.Get()) + .AddSingleton(p => p.GetRequiredService().Periodic.Get()); } /// diff --git a/src/Core/NosSmooth.LocalBinding/Hooks/IHookManager.cs b/src/Core/NosSmooth.LocalBinding/Hooks/IHookManager.cs index 7bd6b12..78239c3 100644 --- a/src/Core/NosSmooth.LocalBinding/Hooks/IHookManager.cs +++ b/src/Core/NosSmooth.LocalBinding/Hooks/IHookManager.cs @@ -56,37 +56,37 @@ public interface IHookManager /// /// Gets the packet send hook. /// - public IPacketSendHook PacketSend { get; } + public Optional PacketSend { get; } /// /// Gets the packet receive hook. /// - public IPacketReceiveHook PacketReceive { get; } + public Optional PacketReceive { get; } /// /// Gets the player walk hook. /// - public IPlayerWalkHook PlayerWalk { get; } + public Optional PlayerWalk { get; } /// /// Gets the entity follow hook. /// - public IEntityFollowHook EntityFollow { get; } + public Optional EntityFollow { get; } /// /// Gets the entity unfollow hook. /// - public IEntityUnfollowHook EntityUnfollow { get; } + public Optional EntityUnfollow { get; } /// /// Gets the player walk hook. /// - public IPetWalkHook PetWalk { get; } + public Optional PetWalk { get; } /// /// Gets the entity focus hook. /// - public IEntityFocusHook EntityFocus { get; } + public Optional EntityFocus { get; } /// /// 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 . /// - public IPeriodicHook Periodic { get; } + public Optional Periodic { get; } /// /// Gets all of the hooks. @@ -139,4 +139,36 @@ public interface IHookManager /// Enable all hooks. /// public void EnableAll(); + + /// + /// Checks whether hook of the given type is loaded (there were no errors in finding the function). + /// + /// The type of the hook. + /// Whether the hook is loaded/present. + public bool IsHookLoaded() + where THook : INostaleHook; + + /// + /// 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. + /// + /// The type of the hook. + /// Whether the hook is loaded/present and usable. + public bool IsHookUsable() + where THook : INostaleHook; + + /// + /// Checks whether hook of the given type is loaded (there were no errors in finding the function). + /// + /// The type of the hook. + /// Whether the hook is loaded/present. + public bool IsHookLoaded(Type hookType); + + /// + /// 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. + /// + /// The type of the hook. + /// Whether the hook is loaded/present and usable. + public bool IsHookUsable(Type hookType); } \ No newline at end of file diff --git a/src/Core/NosSmooth.LocalBinding/Hooks/INostaleHook.cs b/src/Core/NosSmooth.LocalBinding/Hooks/INostaleHook.cs index 4890b90..b40f77b 100644 --- a/src/Core/NosSmooth.LocalBinding/Hooks/INostaleHook.cs +++ b/src/Core/NosSmooth.LocalBinding/Hooks/INostaleHook.cs @@ -22,7 +22,7 @@ public interface INostaleHook : INostal /// /// Gets the wrapper function delegate. /// - public TWrapperFunction WrapperFunction { get; } + public Optional WrapperFunction { get; } /// /// Gets the original function delegate. @@ -40,6 +40,11 @@ public interface INostaleHook : INostal /// public interface INostaleHook { + /// + /// Gets whether the wrapper function is present and usable. + /// + public bool IsUsable { get; } + /// /// Gets the name of the function. /// diff --git a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/CancelableNostaleHook.cs b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/CancelableNostaleHook.cs index 8be9524..5cc1745 100644 --- a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/CancelableNostaleHook.cs +++ b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/CancelableNostaleHook.cs @@ -64,7 +64,7 @@ public abstract class CancelableNostaleHook Hook.Hook.IsEnabled; /// - public abstract TWrapperFunction WrapperFunction { get; } + public abstract Optional WrapperFunction { get; } /// public TFunction OriginalFunction @@ -83,6 +83,9 @@ public abstract class CancelableNostaleHook public event EventHandler? Called; + /// + public bool IsUsable => WrapperFunction.IsPresent; + /// public abstract string Name { get; } diff --git a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFocusHook.cs b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFocusHook.cs index cab86a4..ef28a96 100644 --- a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFocusHook.cs +++ b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFocusHook.cs @@ -30,7 +30,7 @@ internal class EntityFocusHook : CancelableNostaleHook new EntityFocusHook(browserManager.UnitManager), + () => new EntityFocusHook(browserManager.Memory, browserManager.UnitManager), hook => hook.Detour, options ); @@ -38,10 +38,12 @@ internal class EntityFocusHook : CancelableNostaleHook _unitManager; + private readonly IMemory _memory; - private EntityFocusHook(UnitManager unitManager) + private EntityFocusHook(IMemory memory, Optional unitManager) { + _memory = memory; _unitManager = unitManager; } @@ -49,18 +51,20 @@ internal class EntityFocusHook : CancelableNostaleHook IHookManager.EntityFocusName; /// - public override IEntityFocusHook.EntityFocusWrapperDelegate WrapperFunction => (entity) => OriginalFunction - (_unitManager.Address, entity?.Address ?? 0); + public override Optional WrapperFunction + => _unitManager.Map + (unitManager => entity => OriginalFunction(unitManager.Address, entity?.Address ?? 0)); /// - 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 new EntityFollowHook(browserManager.PlayerManager), + () => new EntityFollowHook(browserManager.Memory, browserManager.PlayerManager), hook => hook.Detour, options ); @@ -37,10 +38,12 @@ internal class EntityFollowHook : CancelableNostaleHook _playerManager; + private readonly IMemory _memory; - private EntityFollowHook(PlayerManager playerManager) + private EntityFollowHook(IMemory memory, Optional playerManager) { + _memory = memory; _playerManager = playerManager; } @@ -48,12 +51,20 @@ internal class EntityFollowHook : CancelableNostaleHook IHookManager.EntityFollowName; /// - public override IEntityFollowHook.EntityFollowWrapperDelegate WrapperFunction => (entity) => OriginalFunction - (_playerManager.Address, entity?.Address ?? 0); + public override Optional WrapperFunction + => _playerManager.Map + (playerManager => entity => OriginalFunction(playerManager.Address, entity?.Address ?? 0)); /// - 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 _playerManager; - private EntityUnfollowHook(PlayerManager playerManager) + private EntityUnfollowHook(Optional playerManager) { _playerManager = playerManager; } @@ -48,8 +48,8 @@ internal class EntityUnfollowHook : CancelableNostaleHook IHookManager.EntityUnfollowName; /// - public override IEntityUnfollowHook.EntityUnfollowWrapperDelegate WrapperFunction - => () => OriginalFunction(_playerManager.Address); + public override Optional WrapperFunction + => _playerManager.Map(playerManager => () => OriginalFunction(playerManager.Address)); /// protected override IEntityUnfollowHook.EntityUnfollowDelegate WrapWithCalling(IEntityUnfollowHook.EntityUnfollowDelegate function) diff --git a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/HookManager.cs b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/HookManager.cs index 8d94cc7..192c5fc 100644 --- a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/HookManager.cs +++ b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/HookManager.cs @@ -15,7 +15,8 @@ namespace NosSmooth.LocalBinding.Hooks.Implementations; internal class HookManager : IHookManager { private readonly HookManagerOptions _options; - private Dictionary _hooks; + private readonly Dictionary _hooks; + private bool _initialized; /// /// Initializes a new instance of the class. @@ -28,28 +29,29 @@ internal class HookManager : IHookManager } /// - public IPacketSendHook PacketSend => GetHook(IHookManager.PacketSendName); + public Optional PacketSend => GetHook(IHookManager.PacketSendName); /// - public IPacketReceiveHook PacketReceive => GetHook(IHookManager.PacketReceiveName); + public Optional PacketReceive => GetHook(IHookManager.PacketReceiveName); /// - public IPlayerWalkHook PlayerWalk => GetHook(IHookManager.CharacterWalkName); + public Optional PlayerWalk => GetHook(IHookManager.CharacterWalkName); /// - public IEntityFollowHook EntityFollow => GetHook(IHookManager.EntityFollowName); + public Optional EntityFollow => GetHook(IHookManager.EntityFollowName); /// - public IEntityUnfollowHook EntityUnfollow => GetHook(IHookManager.EntityUnfollowName); + public Optional EntityUnfollow => GetHook + (IHookManager.EntityUnfollowName); /// - public IPetWalkHook PetWalk => GetHook(IHookManager.PetWalkName); + public Optional PetWalk => GetHook(IHookManager.PetWalkName); /// - public IEntityFocusHook EntityFocus => GetHook(IHookManager.EntityFocusName); + public Optional EntityFocus => GetHook(IHookManager.EntityFocusName); /// - public IPeriodicHook Periodic => GetHook(IHookManager.PeriodicName); + public Optional Periodic => GetHook(IHookManager.PeriodicName); /// public IReadOnlyList Hooks => _hooks.Values.ToList(); @@ -57,6 +59,7 @@ internal class HookManager : IHookManager /// 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(string name) + /// + public bool IsHookLoaded() + where THook : INostaleHook + => IsHookLoaded(typeof(THook)); + + /// + public bool IsHookUsable() + where THook : INostaleHook + => IsHookUsable(typeof(THook)); + + /// + public bool IsHookLoaded(Type hookType) + => GetHook(hookType).IsPresent; + + /// + public bool IsHookUsable(Type hookType) + => GetHook(hookType).TryGet(out var h) && h.IsUsable; + + private Optional GetHook(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.Empty; + } + return typed; } + + private Optional 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.Empty; + } + + return new Optional(hook); + } } \ No newline at end of file diff --git a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketReceiveHook.cs b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketReceiveHook.cs index add0894..99d8f29 100644 --- a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketReceiveHook.cs +++ b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketReceiveHook.cs @@ -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 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; - private PacketReceiveHook(NetworkManager networkManager) + private PacketReceiveHook(IMemory memory, Optional networkManager) { + _memory = memory; _networkManager = networkManager; } @@ -50,15 +53,20 @@ internal class PacketReceiveHook : CancelableNostaleHook IHookManager.PacketReceiveName; /// - 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 WrapperFunction + => _networkManager.Map + ( + networkManager => (packetString) => + { + var packetObject = networkManager.GetAddressForPacketReceive(); + using var nostaleString = NostaleStringA.Create(_memory, packetString); + OriginalFunction(packetObject, nostaleString.Get()); + } + ); /// - protected override IPacketReceiveHook.PacketReceiveDelegate WrapWithCalling(IPacketReceiveHook.PacketReceiveDelegate function) + protected override IPacketReceiveHook.PacketReceiveDelegate WrapWithCalling + (IPacketReceiveHook.PacketReceiveDelegate function) => (packetObject, packetString) => { CallingFromNosSmooth = true; diff --git a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketSendHook.cs b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketSendHook.cs index ebb6880..8cd09f5 100644 --- a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketSendHook.cs +++ b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketSendHook.cs @@ -30,33 +30,39 @@ internal class PacketSendHook : CancelableNostaleHook 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; + + private PacketSendHook(IMemory memory, Optional networkManager) { + _memory = memory; _networkManager = networkManager; } - private readonly NetworkManager _networkManager; - /// public override string Name => IHookManager.PacketSendName; /// - 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 WrapperFunction => + _networkManager.Map + ( + networkManager => (packetString) => + { + var packetObject = networkManager.GetAddressForPacketSend(); + using var nostaleString = NostaleStringA.Create(_memory, packetString); + OriginalFunction(packetObject, nostaleString.Get()); + } + ); /// protected override IPacketSendHook.PacketSendDelegate WrapWithCalling(IPacketSendHook.PacketSendDelegate function) diff --git a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PeriodicHook.cs b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PeriodicHook.cs index 6f73e78..519c0c5 100644 --- a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PeriodicHook.cs +++ b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PeriodicHook.cs @@ -40,6 +40,9 @@ internal class PeriodicHook : IPeriodicHook private NosAsmHook _hook = null!; + /// + public bool IsUsable => WrapperFunction.IsPresent; + /// public string Name => IHookManager.PeriodicName; @@ -47,7 +50,7 @@ internal class PeriodicHook : IPeriodicHook public bool IsEnabled => _hook.Hook.IsEnabled; /// - public IPeriodicHook.PeriodicDelegate WrapperFunction => OriginalFunction; + public Optional WrapperFunction => OriginalFunction; /// public IPeriodicHook.PeriodicDelegate OriginalFunction => throw new InvalidOperationException diff --git a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PetWalkHook.cs b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PetWalkHook.cs index dee60cb..59f1e50 100644 --- a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PetWalkHook.cs +++ b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PetWalkHook.cs @@ -48,8 +48,8 @@ internal class PetWalkHook : CancelableNostaleHook IHookManager.PetWalkName; /// - public override IPetWalkHook.PetWalkWrapperDelegate WrapperFunction - => (p, x, y) => OriginalFunction(p.Address, (y << 16) | x) == 1; + public override Optional WrapperFunction + => (IPetWalkHook.PetWalkWrapperDelegate)((p, x, y) => OriginalFunction(p.Address, (y << 16) | x) == 1); /// protected override IPetWalkHook.PetWalkDelegate WrapWithCalling(IPetWalkHook.PetWalkDelegate function) @@ -63,7 +63,14 @@ internal class PetWalkHook : CancelableNostaleHook { CallingFromNosSmooth = true; - var res = function(petManagerPtr, position, un0, un1, un2); + var res = function + ( + petManagerPtr, + position, + un0, + un1, + un2 + ); CallingFromNosSmooth = false; return res; }; diff --git a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PlayerWalkHook.cs b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PlayerWalkHook.cs index e1de7d9..2307dd1 100644 --- a/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PlayerWalkHook.cs +++ b/src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PlayerWalkHook.cs @@ -37,22 +37,26 @@ internal class PlayerWalkHook : CancelableNostaleHook _playerManager; + + private PlayerWalkHook(Optional playerManager) { _playerManager = playerManager; } - private PlayerManager _playerManager; - /// public override string Name => IHookManager.CharacterWalkName; /// - public override IPlayerWalkHook.WalkWrapperDelegate WrapperFunction => (x, y) => - { - var playerManagerObject = _playerManager.Address; - return OriginalFunction(playerManagerObject, (y << 16) | x) == 1; - }; + public override Optional WrapperFunction + => _playerManager.Map + ( + playerManager => (x, y) => + { + var playerManagerObject = playerManager.Address; + return OriginalFunction(playerManagerObject, (y << 16) | x) == 1; + } + ); /// protected override IPlayerWalkHook.WalkDelegate WrapWithCalling(IPlayerWalkHook.WalkDelegate function) diff --git a/src/Core/NosSmooth.LocalBinding/NosBindingManager.cs b/src/Core/NosSmooth.LocalBinding/NosBindingManager.cs index 024fb34..ca54d2d 100644 --- a/src/Core/NosSmooth.LocalBinding/NosBindingManager.cs +++ b/src/Core/NosSmooth.LocalBinding/NosBindingManager.cs @@ -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 }; } + /// + /// Gets whether a hook or browser module is present. + /// + /// The type of the module. + /// Whether the module is present. + public bool IsModulePresent() + => _hookManager.IsHookUsable(typeof(TModule)) || _browserManager.IsModuleLoaded(typeof(TModule)); + /// public void Dispose() { diff --git a/src/Core/NosSmooth.LocalBinding/NosBrowserManager.cs b/src/Core/NosSmooth.LocalBinding/NosBrowserManager.cs index fff749a..6256113 100644 --- a/src/Core/NosSmooth.LocalBinding/NosBrowserManager.cs +++ b/src/Core/NosSmooth.LocalBinding/NosBrowserManager.cs @@ -53,6 +53,7 @@ public class NosBrowserManager .GetProcesses() .Where(IsProcessNostaleProcess); + private readonly Dictionary _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; /// /// Initializes a new instance of the class. @@ -118,6 +120,7 @@ public class NosBrowserManager NtClientOptions? ntClientOptions = default ) { + _modules = new Dictionary(); _playerManagerOptions = playerManagerOptions ?? new PlayerManagerOptions(); _sceneManagerOptions = sceneManagerOptions ?? new SceneManagerOptions(); _petManagerOptions = petManagerOptions ?? new PetManagerOptions(); @@ -153,7 +156,7 @@ public class NosBrowserManager /// Gets the network manager. /// /// Thrown if the browser is not initialized or there was an error with initialization of network manager. - public NetworkManager NetworkManager + public Optional NetworkManager { get { @@ -173,7 +176,7 @@ public class NosBrowserManager /// Gets the network manager. /// /// Thrown if the browser is not initialized or there was an error with initialization of unit manager. - public UnitManager UnitManager + public Optional UnitManager { get { @@ -195,20 +198,13 @@ public class NosBrowserManager /// /// It may be unsafe to access some data if the player is not in game. /// - public bool IsInGame - { - get - { - var player = PlayerManager.Player; - return player.Address != nuint.Zero; - } - } + public Optional IsInGame => PlayerManager.Map(manager => manager.Player.Address != nuint.Zero); /// /// Gets the nt client. /// /// Thrown if the browser is not initialized or there was an error with initialization of nt client. - public NtClient NtClient + public Optional NtClient { get { @@ -228,7 +224,7 @@ public class NosBrowserManager /// Gets the player manager. /// /// Thrown if the browser is not initialized or there was an error with initialization of player manager. - public PlayerManager PlayerManager + public Optional PlayerManager { get { @@ -248,7 +244,7 @@ public class NosBrowserManager /// Gets the scene manager. /// /// Thrown if the browser is not initialized or there was an error with initialization of scene manager. - public SceneManager SceneManager + public Optional SceneManager { get { @@ -268,7 +264,7 @@ public class NosBrowserManager /// Gets the pet manager list. /// /// Thrown if the browser is not initialized or there was an error with initialization of pet manager list. - public PetManagerList PetManagerList + public Optional PetManagerList { get { @@ -298,10 +294,11 @@ public class NosBrowserManager return (Result)new NotNostaleProcessError(Process); } + _initialized = true; List errorResults = new List(); 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) }; } + + /// + /// Gets whether a hook or browser module is present/loaded. + /// Returns false in case pattern was not found. + /// + /// The type of the module. + /// Whether the module is present. + public bool IsModuleLoaded() + where TModule : NostaleObject + => IsModuleLoaded(typeof(TModule)); + + /// + /// Gets whether a hook or browser module is present/loaded. + /// Returns false in case pattern was not found. + /// + /// The type of the module. + /// Whether the module is present. + public bool IsModuleLoaded(Type moduleType) + { + return GetModule(moduleType).IsPresent; + } + + /// + /// Get module of the specified type. + /// + /// The type of the module. + /// The module. + /// Thrown in case the manager was not initialized. + public Optional GetModule() + 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.Empty; + } + + return typed; + } + + /// + /// Get module of the specified type. + /// + /// The type of the module. + /// The module. + /// Thrown in case the manager was not initialized. + public Optional 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.Empty; + } + + return nosObject; + } } \ No newline at end of file diff --git a/src/Core/NosSmooth.LocalBinding/NosThreadSynchronizer.cs b/src/Core/NosSmooth.LocalBinding/NosThreadSynchronizer.cs index 657abee..56988fd 100644 --- a/src/Core/NosSmooth.LocalBinding/NosThreadSynchronizer.cs +++ b/src/Core/NosSmooth.LocalBinding/NosThreadSynchronizer.cs @@ -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; /// public class NosThreadSynchronizer { - private readonly IPeriodicHook _periodicHook; + private readonly Optional _periodicHook; private readonly ILogger _logger; private readonly NosThreadSynchronizerOptions _options; private readonly ConcurrentQueue _queuedOperations; @@ -32,7 +33,7 @@ public class NosThreadSynchronizer /// The options. public NosThreadSynchronizer ( - IPeriodicHook periodicHook, + Optional periodicHook, ILogger logger, IOptions options ) @@ -51,9 +52,12 @@ public class NosThreadSynchronizer /// /// Start the synchronizer operation. /// - public void StartSynchronizer() + /// The result, successful if periodic hook is present. + 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); } /// @@ -61,7 +65,7 @@ public class NosThreadSynchronizer /// public void StopSynchronizer() { - _periodicHook.Called -= PeriodicCall; + _periodicHook.TryDo(h => h.Called -= PeriodicCall); } private void PeriodicCall(object? owner, System.EventArgs eventArgs) diff --git a/src/Core/NosSmooth.LocalBinding/Structs/ControlManager.cs b/src/Core/NosSmooth.LocalBinding/Structs/ControlManager.cs index 017e018..b71ee68 100644 --- a/src/Core/NosSmooth.LocalBinding/Structs/ControlManager.cs +++ b/src/Core/NosSmooth.LocalBinding/Structs/ControlManager.cs @@ -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; diff --git a/src/Core/NosSmooth.LocalBinding/Structs/MapBaseObj.cs b/src/Core/NosSmooth.LocalBinding/Structs/MapBaseObj.cs index 6c781a7..33f99bb 100644 --- a/src/Core/NosSmooth.LocalBinding/Structs/MapBaseObj.cs +++ b/src/Core/NosSmooth.LocalBinding/Structs/MapBaseObj.cs @@ -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; diff --git a/src/Core/NosSmooth.LocalBinding/Structs/NostaleList.cs b/src/Core/NosSmooth.LocalBinding/Structs/NostaleList.cs index 469c169..d812afb 100644 --- a/src/Core/NosSmooth.LocalBinding/Structs/NostaleList.cs +++ b/src/Core/NosSmooth.LocalBinding/Structs/NostaleList.cs @@ -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. /// /// The type. -public abstract class NostaleList : IEnumerable +public abstract class NostaleList : NostaleObject, IEnumerable where T : NostaleObject { private readonly IMemory _memory; @@ -27,16 +28,11 @@ public abstract class NostaleList : IEnumerable /// The memory. /// The object list pointer. public NostaleList(IMemory memory, nuint objListPointer) + : base(memory, objListPointer) { _memory = memory; - Address = objListPointer; } - /// - /// Gets the address. - /// - protected nuint Address { get; } - /// /// Gets the element at the given index. /// diff --git a/src/Core/NosSmooth.LocalBinding/Structs/SceneManager.cs b/src/Core/NosSmooth.LocalBinding/Structs/SceneManager.cs index b0ce653..b2628e2 100644 --- a/src/Core/NosSmooth.LocalBinding/Structs/SceneManager.cs +++ b/src/Core/NosSmooth.LocalBinding/Structs/SceneManager.cs @@ -15,7 +15,7 @@ namespace NosSmooth.LocalBinding.Structs; /// /// Represents nostale scene manager struct. /// -public class SceneManager +public class SceneManager : NostaleObject { /// /// Create instance. @@ -51,6 +51,7 @@ public class SceneManager /// The pointer to the scene manager. /// The offsets from the static scene manager address. public SceneManager(IMemory memory, int staticSceneManagerAddress, int[] sceneManagerOffsets) + : base(memory, nuint.Zero) { _memory = memory; _staticSceneManagerAddress = staticSceneManagerAddress; @@ -60,7 +61,7 @@ public class SceneManager /// /// Gets the address of the scene manager. /// - public nuint Address => _memory.FollowStaticAddressOffsets(_staticSceneManagerAddress, _sceneManagerOffsets); + public override nuint Address => _memory.FollowStaticAddressOffsets(_staticSceneManagerAddress, _sceneManagerOffsets); /// /// Gets the player list. -- 2.49.0