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
A src/Core/NosSmooth.LocalBinding/Errors/OptionalNotPresentError.cs => src/Core/NosSmooth.LocalBinding/Errors/OptionalNotPresentError.cs +11 -0
@@ 0,0 1,11 @@
+//
+// OptionalNotPresentError.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;
+
+public record OptionalNotPresentError(string TypeName) : ResultError($"The optional {TypeName} is not present.");<
\ 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 => true;
+
/// <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 => Optional<IPeriodicHook.PeriodicDelegate>.Empty;
/// <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 +113 -193
@@ 53,18 53,14 @@ public class NosBrowserManager
.GetProcesses()
.Where(IsProcessNostaleProcess);
+ private readonly Dictionary<Type, NostaleObject> _modules;
private readonly PlayerManagerOptions _playerManagerOptions;
private readonly SceneManagerOptions _sceneManagerOptions;
private readonly PetManagerOptions _petManagerOptions;
private readonly NetworkManagerOptions _networkManagerOptions;
private readonly UnitManagerOptions _unitManagerOptions;
private readonly NtClientOptions _ntClientOptions;
- private PlayerManager? _playerManager;
- private SceneManager? _sceneManager;
- private PetManagerList? _petManagerList;
- 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 114,7 @@ public class NosBrowserManager
NtClientOptions? ntClientOptions = default
)
{
+ _modules = new Dictionary<Type, NostaleObject>();
_playerManagerOptions = playerManagerOptions ?? new PlayerManagerOptions();
_sceneManagerOptions = sceneManagerOptions ?? new SceneManagerOptions();
_petManagerOptions = petManagerOptions ?? new PetManagerOptions();
@@ 150,139 147,47 @@ public class NosBrowserManager
public bool IsNostaleProcess => NosBrowserManager.IsProcessNostaleProcess(Process);
/// <summary>
- /// Gets the network manager.
+ /// Gets whether the player is currently in game.
/// </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
- {
- get
- {
- if (_networkManager is null)
- {
- throw new InvalidOperationException
- (
- "Could not get network manager. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
- );
- }
-
- return _networkManager;
- }
- }
+ /// <remarks>
+ /// It may be unsafe to access some data if the player is not in game.
+ /// </remarks>
+ public Optional<bool> IsInGame => PlayerManager.Map(manager => manager.Player.Address != nuint.Zero);
/// <summary>
/// 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
- {
- get
- {
- if (_unitManager is null)
- {
- throw new InvalidOperationException
- (
- "Could not get unit manager. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
- );
- }
-
- return _unitManager;
- }
- }
+ public Optional<NetworkManager> NetworkManager => GetModule<NetworkManager>();
/// <summary>
- /// Gets whether the player is currently in game.
+ /// Gets the network manager.
/// </summary>
- /// <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;
- }
- }
+ /// <exception cref="InvalidOperationException">Thrown if the browser is not initialized or there was an error with initialization of unit manager.</exception>
+ public Optional<UnitManager> UnitManager => GetModule<UnitManager>();
/// <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
- {
- get
- {
- if (_ntClient is null)
- {
- throw new InvalidOperationException
- (
- "Could not get nt client. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
- );
- }
-
- return _ntClient;
- }
- }
+ public Optional<NtClient> NtClient => GetModule<NtClient>();
/// <summary>
/// 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
- {
- get
- {
- if (_playerManager is null)
- {
- throw new InvalidOperationException
- (
- "Could not get player manager. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
- );
- }
-
- return _playerManager;
- }
- }
+ public Optional<PlayerManager> PlayerManager => GetModule<PlayerManager>();
/// <summary>
/// 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
- {
- get
- {
- if (_sceneManager is null)
- {
- throw new InvalidOperationException
- (
- "Could not get scene manager. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
- );
- }
-
- return _sceneManager;
- }
- }
+ public Optional<SceneManager> SceneManager => GetModule<SceneManager>();
/// <summary>
/// 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
- {
- get
- {
- if (_petManagerList is null)
- {
- throw new InvalidOperationException
- (
- "Could not get pet manager list. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
- );
- }
-
- return _petManagerList;
- }
- }
+ public Optional<PetManagerList> PetManagerList => GetModule<PetManagerList>();
/// <summary>
/// Initialize the nos browser modules.
@@ 298,120 203,135 @@ public class NosBrowserManager
return (Result)new NotNostaleProcessError(Process);
}
- List<IResult> errorResults = new List<IResult>();
- if (_unitManager is null)
+ NostaleObject Map<T>(T val)
+ where T : NostaleObject
{
- var unitManagerResult = UnitManager.Create(this, _unitManagerOptions);
- if (!unitManagerResult.IsSuccess)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(UnitManager), unitManagerResult.Error),
- unitManagerResult
- )
- );
- }
-
- _unitManager = unitManagerResult.Entity;
+ return val;
}
- if (_networkManager is null)
- {
- var networkManagerResult = NetworkManager.Create(this, _networkManagerOptions);
- if (!networkManagerResult.IsSuccess)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(NetworkManager), networkManagerResult.Error),
- networkManagerResult
- )
- );
- }
+ _initialized = true;
+ return HandleResults
+ (
+ (typeof(UnitManager), () => Structs.UnitManager.Create(this, _unitManagerOptions).Map(Map)),
+ (typeof(NetworkManager), () => Structs.NetworkManager.Create(this, _networkManagerOptions).Map(Map)),
+ (typeof(PlayerManager), () => Structs.PlayerManager.Create(this, _playerManagerOptions).Map(Map)),
+ (typeof(SceneManager), () => Structs.SceneManager.Create(this, _sceneManagerOptions).Map(Map)),
+ (typeof(PetManagerList), () => Structs.PetManagerList.Create(this, _petManagerOptions).Map(Map)),
+ (typeof(NtClient), () => Structs.NtClient.Create(this, _ntClientOptions).Map(Map))
+ );
+ }
- _networkManager = networkManagerResult.Entity;
- }
+ /// <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;
+ }
- if (_playerManager is null)
+ /// <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)
{
- var playerManagerResult = PlayerManager.Create(this, _playerManagerOptions);
- if (!playerManagerResult.IsSuccess)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(PlayerManager), playerManagerResult.Error),
- playerManagerResult
- )
- );
- }
+ throw new InvalidOperationException
+ (
+ $"Could not get {typeof(TModule)}. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
+ );
+ }
- _playerManager = playerManagerResult.Entity;
+ if (!_modules.TryGetValue(typeof(TModule), out var nosObject) || nosObject is not TModule typed)
+ {
+ return Optional<TModule>.Empty;
}
- if (_sceneManager is null)
+ 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)
{
- var sceneManagerResult = SceneManager.Create(this, _sceneManagerOptions);
- if (!sceneManagerResult.IsSuccess)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(SceneManager), sceneManagerResult.Error),
- sceneManagerResult
- )
- );
- }
+ throw new InvalidOperationException
+ (
+ $"Could not get {moduleType.Name}. The browser manager is not initialized. Did you forget to call NosBrowserManager.Initialize?"
+ );
+ }
- _sceneManager = sceneManagerResult.Entity;
+ if (!_modules.TryGetValue(moduleType, out var nosObject))
+ {
+ return Optional<NostaleObject>.Empty;
}
- if (_petManagerList is null)
+ return nosObject;
+ }
+
+ private Result HandleResults(params (Type Type, Func<Result<NostaleObject>> Builder)[] objects)
+ {
+ Result<NostaleObject> HandleSafe(Func<Result<NostaleObject>> builder)
{
- var petManagerResult = PetManagerList.Create(this, _petManagerOptions);
- if (!petManagerResult.IsSuccess)
+ try
{
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(PetManagerList), petManagerResult.Error),
- petManagerResult
- )
- );
+ return builder();
+ }
+ catch (Exception e)
+ {
+ return e;
}
-
- _petManagerList = petManagerResult.Entity;
}
- if (_ntClient is null)
+ List<IResult> errorResults = new List<IResult>();
+ foreach (var obj in objects)
{
- var ntClientResult = NtClient.Create(this, _ntClientOptions);
- if (!ntClientResult.IsSuccess)
+ var createdResult = HandleSafe(obj.Builder);
+
+ if (!createdResult.IsSuccess)
{
errorResults.Add
(
Result.FromError
(
- new CouldNotInitializeModuleError(typeof(NtClient), ntClientResult.Error),
- ntClientResult
+ new CouldNotInitializeModuleError(obj.Type, createdResult.Error),
+ createdResult
)
);
}
-
- _ntClient = ntClientResult.Entity;
+ else if (createdResult.IsDefined(out var created))
+ {
+ _modules.Add(obj.Type, created);
+ }
}
return errorResults.Count switch
{
0 => Result.FromSuccess(),
- 1 => errorResults[0],
- _ => (Result)new AggregateError(errorResults)
+ 1 => (Result)errorResults[0],
+ _ => new AggregateError(errorResults)
};
}
}=
\ 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)
A src/Core/NosSmooth.LocalBinding/Optional.cs => src/Core/NosSmooth.LocalBinding/Optional.cs +211 -0
@@ 0,0 1,211 @@
+//
+// Optional.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 System.Diagnostics.CodeAnalysis;
+using NosSmooth.LocalBinding.Errors;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding;
+
+/// <summary>
+/// An optional, used mainly for hooks and binding modules,
+/// to make it possible to check whether a module is loaded
+/// in runtime.
+/// </summary>
+/// <typeparam name="T">The type of underlying value.</typeparam>
+public class Optional<T>
+ where T : notnull
+{
+ /// <summary>
+ /// An empty optional (no value present).
+ /// </summary>
+ public static readonly Optional<T> Empty = new();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Optional{T}"/> class.
+ /// </summary>
+ /// <param name="value">The underlying value of the optional. Not present when null.</param>
+ public Optional(T? value = default)
+ {
+ Value = value;
+ }
+
+ /// <summary>
+ /// Gets whether the value is present.
+ /// </summary>
+ [MemberNotNullWhen(true, "Value")]
+ public bool IsPresent => Value is not null;
+
+ /// <summary>
+ /// Gets the underlying value.
+ /// </summary>
+ public T? Value { get; }
+
+ /// <summary>
+ /// Tries to get the underlying value, if it's present.
+ /// If it's not present, value won't be set.
+ /// </summary>
+ /// <param name="value">The underlying value.</param>
+ /// <returns>Whether the value is present.</returns>
+ public bool TryGet([NotNullWhen(true)] out T? value)
+ {
+ value = Value;
+ return IsPresent;
+ }
+
+ /// <summary>
+ /// Tries to execute an action on the value, if it exists.
+ /// </summary>
+ /// <remarks>
+ /// Does nothing, if the value does not exist.
+ /// </remarks>
+ /// <param name="action">The action to execute.</param>
+ /// <returns>Whether the value is present.</returns>
+ public bool TryDo(Action<T> action)
+ {
+ if (IsPresent)
+ {
+ action(Value);
+ }
+
+ return IsPresent;
+ }
+
+ /// <summary>
+ /// Gets something from the underlying value,
+ /// exposing it as optional that is empty,
+ /// in case this optional is empty as well.
+ /// </summary>
+ /// <param name="get">The function to obtain something from the value with.</param>
+ /// <typeparam name="TU">The return type.</typeparam>
+ /// <returns>An optional, present if this optional's value is present.</returns>
+ public Optional<TU> Map<TU>(Func<T, TU> get)
+ where TU : notnull
+ {
+ if (IsPresent)
+ {
+ return get(Value);
+ }
+
+ return Optional<TU>.Empty;
+ }
+
+ /// <summary>
+ /// Gets something from the underlying value like <see cref="Map{TU}"/>.
+ /// </summary>
+ /// <remarks>
+ /// Returns <see cref="OptionalNotPresentError"/> in case this value is not present
+ /// instead of returning an optional.
+ /// </remarks>
+ /// <param name="get">The function to obtain something from the value with.</param>
+ /// <typeparam name="TU">The return type.</typeparam>
+ /// <returns>A result, successful in case this optional's value is present.</returns>
+ public Result<TU> MapResult<TU>(Func<T, TU> get)
+ where TU : notnull
+ {
+ if (IsPresent)
+ {
+ return OptionalUtilities.TryGet(() => get(Value));
+ }
+
+ return new OptionalNotPresentError(nameof(T));
+ }
+
+ /// <summary>
+ /// Does something on the underlying value like <see cref="TryDo"/>, but returns a result
+ /// in case the value is not present.
+ /// </summary>
+ /// <param name="get">The function to execute on the value.</param>
+ /// <typeparam name="TU">The return type.</typeparam>
+ /// <returns>A result, successful in case this optional's value is present.</returns>
+ public Result MapResult(Action<T> get)
+ {
+ if (IsPresent)
+ {
+ return OptionalUtilities.Try(() => get(Value));
+ }
+
+ return new OptionalNotPresentError(nameof(T));
+ }
+
+ /// <summary>
+ /// Gets something from the underlying value like <see cref="Map{TU}"/>.
+ /// </summary>
+ /// <remarks>
+ /// Returns <see cref="OptionalNotPresentError"/> in case this value is not present
+ /// instead of returning an optional.
+ ///
+ /// The get function returns a result that will be returned if this optional is present.
+ /// </remarks>
+ /// <param name="get">The function to obtain something from the value with.</param>
+ /// <typeparam name="TU">The return type.</typeparam>
+ /// <returns>A result from the function, <see cref="OptionalNotPresentError"/> in case this optional is not present.</returns>
+ public Result<TU> MapResult<TU>(Func<T, Result<TU>> get)
+ where TU : notnull
+ {
+ if (IsPresent)
+ {
+ return get(Value);
+ }
+
+ return new OptionalNotPresentError(nameof(T));
+ }
+
+ /// <summary>
+ /// Does something on the underlying value like <see cref="TryDo"/>, but returns a result
+ /// in case the value is not present.
+ /// </summary>
+ /// <remarks>
+ /// Returns <see cref="OptionalNotPresentError"/> in case this value is not present
+ /// instead of returning an optional.
+ ///
+ /// The get function returns a result that will be returned if this optional is present.
+ /// </remarks>
+ /// <param name="get">The function to obtain something from the value with.</param>
+ /// <typeparam name="TU">The return type.</typeparam>
+ /// <returns>A result from the function, <see cref="OptionalNotPresentError"/> in case this optional is not present.</returns>
+ public Result MapResult(Func<T, Result> get)
+ {
+ if (IsPresent)
+ {
+ return get(Value);
+ }
+
+ return new OptionalNotPresentError(nameof(T));
+ }
+
+ /// <summary>
+ /// Forcefully gets the underlying value.
+ /// If it's not present, <see cref="InvalidOperationException"/> will be thrown.
+ /// </summary>
+ /// <remarks>
+ /// Try to use other methods that return results where possible as they are easier to handle.
+ /// </remarks>
+ /// <returns>The underlying value.</returns>
+ /// <exception cref="InvalidOperationException">Thrown in case the value is not present.</exception>
+ public T Get()
+ {
+ if (!IsPresent)
+ {
+ throw new InvalidOperationException
+ (
+ $"Could not get {nameof(T)}. Did you forget to call initialization or was there an error?"
+ );
+ }
+
+ return Value;
+ }
+
+ /// <summary>
+ /// Cast a value to optional.
+ /// </summary>
+ /// <param name="val">The value to cast.</param>
+ /// <returns>The created optional.</returns>
+ public static implicit operator Optional<T>(T val)
+ {
+ return new Optional<T>(val);
+ }
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/OptionalUtilities.cs => src/Core/NosSmooth.LocalBinding/OptionalUtilities.cs +69 -0
@@ 0,0 1,69 @@
+//
+// OptionalUtilities.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;
+
+/// <summary>
+/// A utilities that work with members that may not be present or may throw an exception.
+/// </summary>
+public static class OptionalUtilities
+{
+ /// <summary>
+ /// Tries to get value from the function, capturing an exception into Result.
+ /// </summary>
+ /// <param name="get">The function to obtain the value from.</param>
+ /// <typeparam name="T">The type of the value.</typeparam>
+ /// <returns>The value, or exception error if an exception has been thrown.</returns>
+ public static Result<T> TryGet<T>(Func<T> get)
+ {
+ try
+ {
+ return get();
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ }
+
+ /// <summary>
+ /// Tries to get value from the function, capturing an exception into Result.
+ /// </summary>
+ /// <param name="get">The function to obtain the value from.</param>
+ /// <typeparam name="T">The type of the value.</typeparam>
+ /// <returns>The value, or exception error if an exception has been thrown.</returns>
+ public static Result<T> TryIGet<T>(Func<Result<T>> get)
+ {
+ try
+ {
+ return get();
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ }
+
+ /// <summary>
+ /// Tries to execute an action.
+ /// </summary>
+ /// <param name="get">The action.</param>
+ /// <returns>A result, successful if no exception has been thrown.</returns>
+ public static Result Try(Action get)
+ {
+ try
+ {
+ get();
+ return Result.FromSuccess();
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ }
+}<
\ No newline at end of file
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.
M src/Core/NosSmooth.LocalClient/CommandHandlers/Attack/AttackCommandHandler.cs => src/Core/NosSmooth.LocalClient/CommandHandlers/Attack/AttackCommandHandler.cs +4 -1
@@ 56,7 56,10 @@ public class AttackCommandHandler : ICommandHandler<AttackCommand>
var entityResult = _sceneManager.FindEntity(command.TargetId.Value);
if (entityResult.IsDefined(out var entity))
{
- _synchronizer.EnqueueOperation(() => _entityFocusHook.WrapperFunction(entity));
+ if (_entityFocusHook.WrapperFunction.IsPresent)
+ {
+ _synchronizer.EnqueueOperation(() => _entityFocusHook.WrapperFunction.Get()(entity));
+ }
}
}
M src/Core/NosSmooth.LocalClient/CommandHandlers/Walk/PetWalkCommandHandler.cs => src/Core/NosSmooth.LocalClient/CommandHandlers/Walk/PetWalkCommandHandler.cs +3 -2
@@ 78,8 78,9 @@ public class PetWalkCommandHandler : ICommandHandler<PetWalkCommand>
(
() => _userActionDetector.NotUserAction<Result<bool>>
(
- () => _petWalkHook.WrapperFunction(petManager, (ushort)x, (ushort)y)
- )
+ () => _petWalkHook.WrapperFunction.Get()(petManager, (ushort)x, (ushort)y)
+ ),
+ ct
),
petManager,
_options
M src/Core/NosSmooth.LocalClient/NostaleLocalClient.cs => src/Core/NosSmooth.LocalClient/NostaleLocalClient.cs +72 -27
@@ 13,6 13,7 @@ using NosSmooth.Core.Commands.Control;
using NosSmooth.Core.Extensions;
using NosSmooth.Core.Packets;
using NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Errors;
using NosSmooth.LocalBinding.EventArgs;
using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Objects;
@@ 85,15 86,26 @@ public class NostaleLocalClient : BaseNostaleClient
/// <inheritdoc />
public override async Task<Result> RunAsync(CancellationToken stopRequested = default)
{
+ if (!_hookManager.IsHookLoaded<IPacketSendHook>() || !_hookManager.IsHookLoaded<IPacketReceiveHook>())
+ {
+ return new NeededModulesNotInitializedError
+ ("Client cannot run", IHookManager.PacketSendName, IHookManager.PacketReceiveName);
+ }
+
_stopRequested = stopRequested;
_logger.LogInformation("Starting local client");
- _synchronizer.StartSynchronizer();
- _hookManager.PacketSend.Called += SendCallCallback;
- _hookManager.PacketReceive.Called += ReceiveCallCallback;
+ var synchronizerResult = _synchronizer.StartSynchronizer();
+ if (!synchronizerResult.IsSuccess)
+ {
+ return synchronizerResult;
+ }
- _hookManager.EntityFollow.Called += FollowEntity;
- _hookManager.PlayerWalk.Called += Walk;
- _hookManager.PetWalk.Called += PetWalk;
+ _hookManager.PacketSend.Get().Called += SendCallCallback;
+ _hookManager.PacketReceive.Get().Called += ReceiveCallCallback;
+
+ _hookManager.EntityFollow.TryDo(follow => follow.Called += FollowEntity);
+ _hookManager.PlayerWalk.TryDo(walk => walk.Called += Walk);
+ _hookManager.PetWalk.TryDo(walk => walk.Called += PetWalk);
try
{
@@ 104,12 116,12 @@ public class NostaleLocalClient : BaseNostaleClient
// ignored
}
- _hookManager.PacketSend.Called -= SendCallCallback;
- _hookManager.PacketReceive.Called -= ReceiveCallCallback;
+ _hookManager.PacketSend.Get().Called -= SendCallCallback;
+ _hookManager.PacketReceive.Get().Called -= ReceiveCallCallback;
- _hookManager.EntityFollow.Called -= FollowEntity;
- _hookManager.PlayerWalk.Called -= Walk;
- _hookManager.PetWalk.Called -= PetWalk;
+ _hookManager.EntityFollow.TryDo(follow => follow.Called -= FollowEntity);
+ _hookManager.PlayerWalk.TryDo(walk => walk.Called -= Walk);
+ _hookManager.PetWalk.TryDo(walk => walk.Called -= PetWalk);
// the hooks are not needed anymore.
_hookManager.DisableAll();
@@ 120,17 132,59 @@ public class NostaleLocalClient : BaseNostaleClient
/// <inheritdoc />
public override async Task<Result> ReceivePacketAsync(string packetString, CancellationToken ct = default)
{
- ReceivePacket(packetString);
- await ProcessPacketAsync(PacketSource.Server, packetString);
- return Result.FromSuccess();
+ var result = _hookManager.PacketReceive.MapResult
+ (
+ receive => receive.WrapperFunction.MapResult
+ (
+ wrapperFunction =>
+ {
+ _synchronizer.EnqueueOperation(() => wrapperFunction(packetString));
+ return Result.FromSuccess();
+ }
+ )
+ );
+
+ if (result.IsSuccess)
+ {
+ _logger.LogDebug($"Receiving client packet {packetString}");
+ await ProcessPacketAsync(PacketSource.Server, packetString);
+ }
+ else
+ {
+ _logger.LogError("Could not receive packet");
+ _logger.LogResultError(result);
+ }
+
+ return result;
}
/// <inheritdoc />
public override async Task<Result> SendPacketAsync(string packetString, CancellationToken ct = default)
{
- SendPacket(packetString);
- await ProcessPacketAsync(PacketSource.Client, packetString);
- return Result.FromSuccess();
+ var result = _hookManager.PacketSend.MapResult
+ (
+ send => send.WrapperFunction.MapResult
+ (
+ wrapperFunction =>
+ {
+ _synchronizer.EnqueueOperation(() => wrapperFunction(packetString));
+ return Result.FromSuccess();
+ }
+ )
+ );
+
+ if (result.IsSuccess)
+ {
+ _logger.LogDebug($"Sending client packet {packetString}");
+ await ProcessPacketAsync(PacketSource.Server, packetString);
+ }
+ else
+ {
+ _logger.LogError("Could not send packet");
+ _logger.LogResultError(result);
+ }
+
+ return result;
}
private void ReceiveCallCallback(object? owner, PacketEventArgs packetArgs)
@@ 181,20 235,11 @@ public class NostaleLocalClient : BaseNostaleClient
{
_synchronizer.EnqueueOperation
(
- () => _hookManager.PacketSend.WrapperFunction(packetString)
+ () => _hookManager.PacketSend.Get().WrapperFunction.Get()(packetString)
);
_logger.LogDebug($"Sending client packet {packetString}");
}
- private void ReceivePacket(string packetString)
- {
- _synchronizer.EnqueueOperation
- (
- () => _hookManager.PacketReceive.WrapperFunction(packetString)
- );
- _logger.LogDebug($"Receiving client packet {packetString}");
- }
-
private async Task ProcessPacketAsync(PacketSource type, string packetString)
{
try
M src/Core/NosSmooth.LocalClient/UserActionDetector.cs => src/Core/NosSmooth.LocalClient/UserActionDetector.cs +2 -2
@@ 106,7 106,7 @@ public class UserActionDetector
() =>
{
_lastWalkPosition = ((ushort)x, (ushort)y);
- return walkHook.WrapperFunction((ushort)x, (ushort)y);
+ return walkHook.WrapperFunction.MapResult(func => func((ushort)x, (ushort)y));
}
);
@@ 124,7 124,7 @@ public class UserActionDetector
() =>
{
_lastWalkPosition = ((ushort)x, (ushort)y);
- return walkHook.WrapperFunction((ushort)x, (ushort)y);
+ return walkHook.WrapperFunction.MapResult(func => func((ushort)x, (ushort)y));
},
ct
);
M src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SharedHookManager.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SharedHookManager.cs +67 -36
@@ 26,8 26,6 @@ public class SharedHookManager
/// Initializes a new instance of the <see cref="SharedHookManager"/> class.
/// </summary>
/// <param name="underlyingManager">The underlying hook manager.</param>
- /// <param name="bindingManager">The binding manager.</param>
- /// <param name="browserManager">The browser manager.</param>
public SharedHookManager
(
IHookManager underlyingManager
@@ 44,112 42,137 @@ public class SharedHookManager
/// <param name="browserManager">The browser manager.</param>
/// <param name="options">The initial options to be respected.</param>
/// <returns>The dictionary containing all of the hooks.</returns>
- public Result<Dictionary<string, INostaleHook>> InitializeInstance
+ public (Dictionary<string, INostaleHook>, IResult) InitializeInstance
(NosBindingManager bindingManager, NosBrowserManager browserManager, HookManagerOptions options)
{
+ IResult result = Result.FromSuccess();
if (!_initialized)
{
- var result = _underlyingManager.Initialize(bindingManager, browserManager);
+ result = _underlyingManager.Initialize(bindingManager, browserManager);
_initialized = true;
-
- if (!result.IsSuccess)
- {
- return Result<Dictionary<string, INostaleHook>>.FromError(result.Error);
- }
}
var hooks = new Dictionary<string, INostaleHook>();
// TODO: initialize using reflection
- hooks.Add
+ HandleAdd
(
- _underlyingManager.Periodic.Name,
+ hooks,
+ IHookManager.PeriodicName,
InitializeSingleHook
(
- new PeriodicHook(_underlyingManager.Periodic),
+ _underlyingManager.Periodic,
+ u => new PeriodicHook(u),
options.PeriodicHook
)
);
- hooks.Add
+ HandleAdd
(
- _underlyingManager.EntityFocus.Name,
+ hooks,
+ IHookManager.EntityFocusName,
InitializeSingleHook
(
- new EntityFocusHook(_underlyingManager.EntityFocus),
+ _underlyingManager.EntityFocus,
+ u => new EntityFocusHook(u),
options.EntityFocusHook
)
);
- hooks.Add
+ HandleAdd
(
- _underlyingManager.EntityFollow.Name,
+ hooks,
+ IHookManager.EntityFollowName,
InitializeSingleHook
(
- new EntityFollowHook(_underlyingManager.EntityFollow),
+ _underlyingManager.EntityFollow,
+ u => new EntityFollowHook(u),
options.EntityFollowHook
)
);
- hooks.Add
+ HandleAdd
(
- _underlyingManager.EntityUnfollow.Name,
+ hooks,
+ IHookManager.EntityUnfollowName,
InitializeSingleHook
(
- new EntityUnfollowHook(_underlyingManager.EntityUnfollow),
+ _underlyingManager.EntityUnfollow,
+ u => new EntityUnfollowHook(u),
options.EntityUnfollowHook
)
);
- hooks.Add
+ HandleAdd
(
- _underlyingManager.PacketReceive.Name,
+ hooks,
+ IHookManager.PacketReceiveName,
InitializeSingleHook
(
- new PacketReceiveHook(_underlyingManager.PacketReceive),
+ _underlyingManager.PacketReceive,
+ u => new PacketReceiveHook(u),
options.PacketReceiveHook
)
);
- hooks.Add
+ HandleAdd
(
- _underlyingManager.PacketSend.Name,
+ hooks,
+ IHookManager.PacketSendName,
InitializeSingleHook
(
- new PacketSendHook(_underlyingManager.PacketSend),
+ _underlyingManager.PacketSend,
+ u => new PacketSendHook(u),
options.PacketSendHook
)
);
- hooks.Add
+ HandleAdd
(
- _underlyingManager.PetWalk.Name,
+ hooks,
+ IHookManager.PetWalkName,
InitializeSingleHook
(
- new PetWalkHook(_underlyingManager.PetWalk),
+ _underlyingManager.PetWalk,
+ u => new PetWalkHook(u),
options.PetWalkHook
)
);
- hooks.Add
+ HandleAdd
(
- _underlyingManager.PlayerWalk.Name,
+ hooks,
+ IHookManager.CharacterWalkName,
InitializeSingleHook
(
- new PlayerWalkHook(_underlyingManager.PlayerWalk),
+ _underlyingManager.PlayerWalk,
+ u => new PlayerWalkHook(u),
options.PlayerWalkHook
)
);
- return hooks;
+ return (hooks, result);
}
- private INostaleHook<TFunction, TWrapperFunction, TEventArgs> InitializeSingleHook<TFunction, TWrapperFunction,
- TEventArgs>(SingleHook<TFunction, TWrapperFunction, TEventArgs> hook, HookOptions options)
+ private INostaleHook<TFunction, TWrapperFunction, TEventArgs>? InitializeSingleHook<THook, TFunction,
+ TWrapperFunction,
+ TEventArgs>
+ (
+ Optional<THook> hookOptional,
+ Func<THook, SingleHook<TFunction, TWrapperFunction, TEventArgs>> hookCreator,
+ HookOptions options
+ )
+ where THook : notnull
where TFunction : Delegate
where TWrapperFunction : Delegate
where TEventArgs : System.EventArgs
{
+ if (!hookOptional.TryGet(out var underlyingHook))
+ {
+ return null;
+ }
+
+ var hook = hookCreator(underlyingHook);
hook.StateChanged += (_, state) =>
{
if (!_hookedCount.ContainsKey(hook.Name))
@@ 176,4 199,12 @@ public class SharedHookManager
return hook;
}
+
+ private void HandleAdd(Dictionary<string, INostaleHook> hooks, string name, INostaleHook? hook)
+ {
+ if (hook is not null)
+ {
+ hooks.Add(name, hook);
+ }
+ }
}=
\ No newline at end of file
M src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHook.cs +89 -85
@@ 1,86 1,90 @@
-//
-// SingleHook.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 System.ComponentModel;
-using NosSmooth.Extensions.SharedBinding.EventArgs;
-using NosSmooth.LocalBinding.Hooks;
-using Remora.Results;
-
-namespace NosSmooth.Extensions.SharedBinding.Hooks;
-
-/// <summary>
-/// A hook for a single instance of NosSmooth sharing with the rest of application.
-/// </summary>
-/// <typeparam name="TFunction">The function delegate.</typeparam>
-/// <typeparam name="TWrapperFunction">A wrapper function that abstracts the call to original function. May get the neccessary object to call the function and accept only relevant arguments.</typeparam>
-/// <typeparam name="TEventArgs">The event args used in case of a call.</typeparam>
-public class SingleHook<TFunction, TWrapperFunction, TEventArgs> : INostaleHook<TFunction, TWrapperFunction, TEventArgs>
- where TFunction : Delegate
- where TWrapperFunction : Delegate
- where TEventArgs : System.EventArgs
-{
- private readonly INostaleHook<TFunction, TWrapperFunction, TEventArgs> _underlyingHook;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="SingleHook{TFunction, TWrapperFunction, TEventArgs}"/> class.
- /// </summary>
- /// <param name="underlyingHook">The underlying hook.</param>
- public SingleHook(INostaleHook<TFunction, TWrapperFunction, TEventArgs> underlyingHook)
- {
- _underlyingHook = underlyingHook;
- }
-
- /// <summary>
- /// Called upon Enable or Disable.
- /// </summary>
- public event EventHandler<HookStateEventArgs>? StateChanged;
-
- /// <inheritdoc />
- public string Name => _underlyingHook.Name;
-
- /// <inheritdoc />
- public bool IsEnabled { get; private set; }
-
- /// <inheritdoc />
- public Result Enable()
- {
- if (!IsEnabled)
- {
- IsEnabled = true;
- StateChanged?.Invoke(this, new HookStateEventArgs(true));
- _underlyingHook.Called += FireCalled;
- }
-
- return Result.FromSuccess();
- }
-
- /// <inheritdoc />
- public Result Disable()
- {
- if (IsEnabled)
- {
- IsEnabled = true;
- StateChanged?.Invoke(this, new HookStateEventArgs(false));
- _underlyingHook.Called -= FireCalled;
- }
-
- return Result.FromSuccess();
- }
-
- private void FireCalled(object? owner, TEventArgs eventArgs)
- {
- Called?.Invoke(this, eventArgs);
- }
-
- /// <inheritdoc />
- public TWrapperFunction WrapperFunction => _underlyingHook.WrapperFunction;
-
- /// <inheritdoc />
- public TFunction OriginalFunction => _underlyingHook.OriginalFunction;
-
- /// <inheritdoc />
- public event EventHandler<TEventArgs>? Called;
+//
+// SingleHook.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 System.ComponentModel;
+using NosSmooth.Extensions.SharedBinding.EventArgs;
+using NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Hooks;
+using Remora.Results;
+
+namespace NosSmooth.Extensions.SharedBinding.Hooks;
+
+/// <summary>
+/// A hook for a single instance of NosSmooth sharing with the rest of application.
+/// </summary>
+/// <typeparam name="TFunction">The function delegate.</typeparam>
+/// <typeparam name="TWrapperFunction">A wrapper function that abstracts the call to original function. May get the neccessary object to call the function and accept only relevant arguments.</typeparam>
+/// <typeparam name="TEventArgs">The event args used in case of a call.</typeparam>
+public class SingleHook<TFunction, TWrapperFunction, TEventArgs> : INostaleHook<TFunction, TWrapperFunction, TEventArgs>
+ where TFunction : Delegate
+ where TWrapperFunction : Delegate
+ where TEventArgs : System.EventArgs
+{
+ private readonly INostaleHook<TFunction, TWrapperFunction, TEventArgs> _underlyingHook;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SingleHook{TFunction, TWrapperFunction, TEventArgs}"/> class.
+ /// </summary>
+ /// <param name="underlyingHook">The underlying hook.</param>
+ public SingleHook(INostaleHook<TFunction, TWrapperFunction, TEventArgs> underlyingHook)
+ {
+ _underlyingHook = underlyingHook;
+ }
+
+ /// <summary>
+ /// Called upon Enable or Disable.
+ /// </summary>
+ public event EventHandler<HookStateEventArgs>? StateChanged;
+
+ /// <inheritdoc />
+ public bool IsUsable => _underlyingHook.IsUsable;
+
+ /// <inheritdoc />
+ public string Name => _underlyingHook.Name;
+
+ /// <inheritdoc />
+ public bool IsEnabled { get; private set; }
+
+ /// <inheritdoc />
+ public Result Enable()
+ {
+ if (!IsEnabled)
+ {
+ IsEnabled = true;
+ StateChanged?.Invoke(this, new HookStateEventArgs(true));
+ _underlyingHook.Called += FireCalled;
+ }
+
+ return Result.FromSuccess();
+ }
+
+ /// <inheritdoc />
+ public Result Disable()
+ {
+ if (IsEnabled)
+ {
+ IsEnabled = true;
+ StateChanged?.Invoke(this, new HookStateEventArgs(false));
+ _underlyingHook.Called -= FireCalled;
+ }
+
+ return Result.FromSuccess();
+ }
+
+ private void FireCalled(object? owner, TEventArgs eventArgs)
+ {
+ Called?.Invoke(this, eventArgs);
+ }
+
+ /// <inheritdoc />
+ public Optional<TWrapperFunction> WrapperFunction => _underlyingHook.WrapperFunction;
+
+ /// <inheritdoc />
+ public TFunction OriginalFunction => _underlyingHook.OriginalFunction;
+
+ /// <inheritdoc />
+ public event EventHandler<TEventArgs>? Called;
}=
\ No newline at end of file
M src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHookManager.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHookManager.cs +58 -20
@@ 20,6 20,7 @@ public class SingleHookManager : IHookManager
private readonly SharedHookManager _sharedHookManager;
private readonly HookManagerOptions _options;
private Dictionary<string, INostaleHook> _hooks;
+ private bool _initialized;
/// <summary>
/// Initializes a new instance of the <see cref="SingleHookManager"/> class.
@@ 34,28 35,28 @@ public class SingleHookManager : 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();
@@ 63,14 64,10 @@ public class SingleHookManager : IHookManager
/// <inheritdoc />
public IResult Initialize(NosBindingManager bindingManager, NosBrowserManager browserManager)
{
- var hooksResult = _sharedHookManager.InitializeInstance(bindingManager, browserManager, _options);
- if (!hooksResult.IsDefined(out var hooks))
- {
- return hooksResult;
- }
-
+ _initialized = true;
+ var (hooks, result) = _sharedHookManager.InitializeInstance(bindingManager, browserManager, _options);
_hooks = hooks;
- return Result.FromSuccess();
+ return result;
}
/// <inheritdoc />
@@ 79,7 76,7 @@ public class SingleHookManager : IHookManager
foreach (var name in names)
{
var hook = GetHook<INostaleHook>(name);
- hook.Enable();
+ hook.TryDo(h => h.Enable());
}
}
@@ 89,7 86,7 @@ public class SingleHookManager : IHookManager
foreach (var name in names)
{
var hook = GetHook<INostaleHook>(name);
- hook.Disable();
+ hook.TryDo(h => h.Disable());
}
}
@@ 111,15 108,56 @@ public class SingleHookManager : 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?");
+ ($"Could not load hook {typeof(T)}. Did you forget to call IHookManager.Initialize?");
+ }
+
+ var hook = _hooks.Values.FirstOrDefault(x => x is T);
+ if (hook 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(hookType.IsInstanceOfType);
+ if (hook is null)
+ {
+ return Optional<INostaleHook>.Empty;
+ }
+
+ return new Optional<INostaleHook>(hook);
+ }
}=
\ No newline at end of file
M src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PeriodicHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PeriodicHook.cs +26 -25
@@ 1,26 1,27 @@
-//
-// PeriodicHook.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 NosSmooth.LocalBinding.Hooks;
-
-namespace NosSmooth.Extensions.SharedBinding.Hooks.Specific;
-
-/// <summary>
-/// A hook of a periodic function,
-/// preferably called every frame.
-/// </summary>
-internal class PeriodicHook :
- SingleHook<IPeriodicHook.PeriodicDelegate, IPeriodicHook.PeriodicDelegate, System.EventArgs>, IPeriodicHook
-{
- /// <summary>
- /// Initializes a new instance of the <see cref="PeriodicHook"/> class.
- /// </summary>
- /// <param name="underlyingHook">The underlying hook.</param>
- public PeriodicHook(INostaleHook<IPeriodicHook.PeriodicDelegate, IPeriodicHook.PeriodicDelegate, System.EventArgs> underlyingHook)
- : base(underlyingHook)
- {
- }
+//
+// PeriodicHook.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 NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Hooks;
+
+namespace NosSmooth.Extensions.SharedBinding.Hooks.Specific;
+
+/// <summary>
+/// A hook of a periodic function,
+/// preferably called every frame.
+/// </summary>
+internal class PeriodicHook :
+ SingleHook<IPeriodicHook.PeriodicDelegate, IPeriodicHook.PeriodicDelegate, System.EventArgs>, IPeriodicHook
+{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PeriodicHook"/> class.
+ /// </summary>
+ /// <param name="underlyingHook">The underlying hook.</param>
+ public PeriodicHook(INostaleHook<IPeriodicHook.PeriodicDelegate, IPeriodicHook.PeriodicDelegate, System.EventArgs> underlyingHook)
+ : base(underlyingHook)
+ {
+ }
}=
\ No newline at end of file
M src/Samples/External/ExternalBrowser/Program.cs => src/Samples/External/ExternalBrowser/Program.cs +3 -3
@@ 55,17 55,17 @@ public class Program
Console.Error.WriteLine(initializationResult.ToFullString());
}
- var length = externalBrowser.PetManagerList.Length;
+ var length = externalBrowser.PetManagerList.Get().Length;
Console.WriteLine(length);
- if (!externalBrowser.IsInGame)
+ if (!externalBrowser.IsInGame.Get())
{
Console.Error.WriteLine("The player is not in game, cannot get the name of the player.");
continue;
}
Console.WriteLine
- ($"Player in process {process.Id} is named {externalBrowser.PlayerManager.Player.Name}");
+ ($"Player in process {process.Id} is named {externalBrowser.PlayerManager.Get().Player.Name}");
}
}
}
M src/Samples/HighLevel/SimplePiiBot/Commands/EntityCommands.cs => src/Samples/HighLevel/SimplePiiBot/Commands/EntityCommands.cs +13 -16
@@ 107,18 107,17 @@ public class EntityCommands : CommandGroup
public async Task<Result> HandleFocusAsync(int entityId)
{
var entityResult = _sceneManager.FindEntity(entityId);
- if (!entityResult.IsSuccess)
+ if (!entityResult.IsDefined(out var entity))
{
return Result.FromError(entityResult);
}
return await _synchronizer.SynchronizeAsync
(
- () =>
- {
- _hookManager.EntityFocus.WrapperFunction(entityResult.Entity);
- return Result.FromSuccess();
- },
+ () => _hookManager.EntityFocus.MapResult
+ (
+ focus => focus.WrapperFunction.MapResult(wrapper => wrapper(entity))
+ ),
CancellationToken
);
}
@@ 139,11 138,10 @@ public class EntityCommands : CommandGroup
return await _synchronizer.SynchronizeAsync
(
- () =>
- {
- _hookManager.EntityFollow.WrapperFunction(entity);
- return Result.FromSuccess();
- },
+ () => _hookManager.EntityFollow.MapResult
+ (
+ follow => follow.WrapperFunction.MapResult(wrapper => wrapper(entity))
+ ),
CancellationToken
);
}
@@ 157,11 155,10 @@ public class EntityCommands : CommandGroup
{
return await _synchronizer.SynchronizeAsync
(
- () =>
- {
- _hookManager.EntityUnfollow.WrapperFunction();
- return Result.FromSuccess();
- },
+ () => _hookManager.EntityUnfollow.MapResult
+ (
+ unfollow => unfollow.WrapperFunction.MapResult(wrapper => wrapper())
+ ),
CancellationToken
);
}
M src/Samples/HighLevel/SimplePiiBot/HostedService.cs => src/Samples/HighLevel/SimplePiiBot/HostedService.cs +10 -0
@@ 11,6 11,7 @@ using NosSmooth.Core.Client;
using NosSmooth.Core.Extensions;
using NosSmooth.Data.NOSFiles;
using NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.PacketSerializer.Extensions;
using NosSmooth.PacketSerializer.Packets;
using OneOf.Types;
@@ 78,6 79,15 @@ public class HostedService : BackgroundService
if (!bindingResult.IsSuccess)
{
_logger.LogResultError(bindingResult);
+ }
+
+ if (!_bindingManager.IsModulePresent<IPeriodicHook>() || !_bindingManager.IsModulePresent<IPacketSendHook>()
+ || !_bindingManager.IsModulePresent<IPacketReceiveHook>())
+ {
+ _logger.LogError
+ (
+ "At least one of: periodic, packet receive, packet send has not been loaded correctly, the bot may not be used at all. Aborting"
+ );
return;
}
M src/Samples/LowLevel/InterceptNameChanger/NameChanger.cs => src/Samples/LowLevel/InterceptNameChanger/NameChanger.cs +11 -0
@@ 11,6 11,7 @@ using NosSmooth.Core.Client;
using NosSmooth.Core.Extensions;
using NosSmooth.Extensions.SharedBinding.Extensions;
using NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalClient;
using NosSmooth.LocalClient.Extensions;
using NosSmooth.Packets.Enums;
@@ 63,6 64,16 @@ namespace InterceptNameChanger
logger.LogResultError(initializeResult);
}
+ if (!bindingManager.IsModulePresent<IPeriodicHook>() || !bindingManager.IsModulePresent<IPacketSendHook>()
+ || !bindingManager.IsModulePresent<IPacketReceiveHook>())
+ {
+ logger.LogError
+ (
+ "At least one of: periodic, packet receive, packet send has not been loaded correctly, the bot may not be used at all. Aborting"
+ );
+ return;
+ }
+
var packetTypesRepository = provider.GetRequiredService<IPacketTypesRepository>();
var packetAddResult = packetTypesRepository.AddDefaultPackets();
if (!packetAddResult.IsSuccess)
M src/Samples/LowLevel/SimpleChat/SimpleChat.cs => src/Samples/LowLevel/SimpleChat/SimpleChat.cs +11 -0
@@ 10,6 10,7 @@ using NosSmooth.Core.Client;
using NosSmooth.Core.Extensions;
using NosSmooth.Extensions.SharedBinding.Extensions;
using NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalClient.Extensions;
using NosSmooth.Packets.Enums;
using NosSmooth.Packets.Enums.Chat;
@@ 57,6 58,16 @@ public class SimpleChat
logger.LogError($"Could not initialize NosBindingManager.");
logger.LogResultError(initializeResult);
}
+
+ if (!bindingManager.IsModulePresent<IPeriodicHook>() || !bindingManager.IsModulePresent<IPacketSendHook>()
+ || !bindingManager.IsModulePresent<IPacketReceiveHook>())
+ {
+ logger.LogError
+ (
+ "At least one of: periodic, packet receive, packet send has not been loaded correctly, the bot may not be used at all. Aborting"
+ );
+ return;
+ }
var packetTypesRepository = provider.GetRequiredService<IPacketTypesRepository>();
var packetAddResult = packetTypesRepository.AddDefaultPackets();
M src/Samples/LowLevel/WalkCommands/Startup.cs => src/Samples/LowLevel/WalkCommands/Startup.cs +11 -0
@@ 16,6 16,7 @@ using NosSmooth.Extensions.Pathfinding.Extensions;
using NosSmooth.Extensions.SharedBinding.Extensions;
using NosSmooth.LocalBinding;
using NosSmooth.LocalBinding.Extensions;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalClient;
using NosSmooth.LocalClient.Extensions;
using NosSmooth.PacketSerializer.Extensions;
@@ 82,6 83,16 @@ public class Startup
logger.LogError($"Could not initialize NosBindingManager.");
logger.LogResultError(initializeResult);
}
+
+ if (!bindingManager.IsModulePresent<IPeriodicHook>() || !bindingManager.IsModulePresent<IPacketSendHook>()
+ || !bindingManager.IsModulePresent<IPacketReceiveHook>())
+ {
+ logger.LogError
+ (
+ "At least one of: periodic, packet receive, packet send has not been loaded correctly, the bot may not be used at all. Aborting"
+ );
+ return;
+ }
var packetTypesRepository = provider.GetRequiredService<IPacketTypesRepository>();
var packetAddResult = packetTypesRepository.AddDefaultPackets();