From 23b5f3909ab87cb87e066d6cb3506cb124a36442 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Tue, 14 Feb 2023 10:29:28 +0100 Subject: [PATCH] feat(shared): implement support for optional modules and hooks --- .../Hooks/SharedHookManager.cs | 101 ++++++---- .../Hooks/SingleHook.cs | 174 +++++++++--------- .../Hooks/SingleHookManager.cs | 78 ++++++-- .../Hooks/Specific/PeriodicHook.cs | 51 ++--- 4 files changed, 240 insertions(+), 164 deletions(-) diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SharedHookManager.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SharedHookManager.cs index 82527a4..53458db 100644 --- a/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SharedHookManager.cs +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SharedHookManager.cs @@ -44,112 +44,137 @@ public class SharedHookManager /// The browser manager. /// The initial options to be respected. /// The dictionary containing all of the hooks. - public Result> InitializeInstance + public (Dictionary, 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>.FromError(result.Error); - } } var hooks = new Dictionary(); // 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 InitializeSingleHook(SingleHook hook, HookOptions options) + private INostaleHook? InitializeSingleHook + ( + Optional hookOptional, + Func> 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 +201,12 @@ public class SharedHookManager return hook; } + + private void HandleAdd(Dictionary hooks, string name, INostaleHook? hook) + { + if (hook is not null) + { + hooks.Add(name, hook); + } + } } \ No newline at end of file diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHook.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHook.cs index 0906baf..77fd7ed 100644 --- a/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHook.cs +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHook.cs @@ -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; - -/// -/// A hook for a single instance of NosSmooth sharing with the rest of application. -/// -/// The function delegate. -/// A wrapper function that abstracts the call to original function. May get the neccessary object to call the function and accept only relevant arguments. -/// The event args used in case of a call. -public class SingleHook : INostaleHook - where TFunction : Delegate - where TWrapperFunction : Delegate - where TEventArgs : System.EventArgs -{ - private readonly INostaleHook _underlyingHook; - - /// - /// Initializes a new instance of the class. - /// - /// The underlying hook. - public SingleHook(INostaleHook underlyingHook) - { - _underlyingHook = underlyingHook; - } - - /// - /// Called upon Enable or Disable. - /// - public event EventHandler? StateChanged; - - /// - public string Name => _underlyingHook.Name; - - /// - public bool IsEnabled { get; private set; } - - /// - public Result Enable() - { - if (!IsEnabled) - { - IsEnabled = true; - StateChanged?.Invoke(this, new HookStateEventArgs(true)); - _underlyingHook.Called += FireCalled; - } - - return Result.FromSuccess(); - } - - /// - 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); - } - - /// - public TWrapperFunction WrapperFunction => _underlyingHook.WrapperFunction; - - /// - public TFunction OriginalFunction => _underlyingHook.OriginalFunction; - - /// - public event EventHandler? 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; + +/// +/// A hook for a single instance of NosSmooth sharing with the rest of application. +/// +/// The function delegate. +/// A wrapper function that abstracts the call to original function. May get the neccessary object to call the function and accept only relevant arguments. +/// The event args used in case of a call. +public class SingleHook : INostaleHook + where TFunction : Delegate + where TWrapperFunction : Delegate + where TEventArgs : System.EventArgs +{ + private readonly INostaleHook _underlyingHook; + + /// + /// Initializes a new instance of the class. + /// + /// The underlying hook. + public SingleHook(INostaleHook underlyingHook) + { + _underlyingHook = underlyingHook; + } + + /// + /// Called upon Enable or Disable. + /// + public event EventHandler? StateChanged; + + /// + public bool IsUsable => _underlyingHook.IsUsable; + + /// + public string Name => _underlyingHook.Name; + + /// + public bool IsEnabled { get; private set; } + + /// + public Result Enable() + { + if (!IsEnabled) + { + IsEnabled = true; + StateChanged?.Invoke(this, new HookStateEventArgs(true)); + _underlyingHook.Called += FireCalled; + } + + return Result.FromSuccess(); + } + + /// + 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); + } + + /// + public Optional WrapperFunction => _underlyingHook.WrapperFunction; + + /// + public TFunction OriginalFunction => _underlyingHook.OriginalFunction; + + /// + public event EventHandler? Called; } \ No newline at end of file diff --git a/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHookManager.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHookManager.cs index ca36d63..db6aa75 100644 --- a/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHookManager.cs +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHookManager.cs @@ -20,6 +20,7 @@ public class SingleHookManager : IHookManager private readonly SharedHookManager _sharedHookManager; private readonly HookManagerOptions _options; private Dictionary _hooks; + private bool _initialized; /// /// Initializes a new instance of the class. @@ -34,28 +35,28 @@ public class SingleHookManager : 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(); @@ -63,14 +64,10 @@ public class SingleHookManager : IHookManager /// 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; } /// @@ -79,7 +76,7 @@ public class SingleHookManager : IHookManager foreach (var name in names) { var hook = GetHook(name); - hook.Enable(); + hook.TryDo(h => h.Enable()); } } @@ -89,7 +86,7 @@ public class SingleHookManager : IHookManager foreach (var name in names) { var hook = GetHook(name); - hook.Disable(); + hook.TryDo(h => h.Disable()); } } @@ -111,15 +108,56 @@ public class SingleHookManager : 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?"); + ($"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.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/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PeriodicHook.cs b/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PeriodicHook.cs index 1ee8fa7..ed81e41 100644 --- a/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PeriodicHook.cs +++ b/src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PeriodicHook.cs @@ -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; - -/// -/// A hook of a periodic function, -/// preferably called every frame. -/// -internal class PeriodicHook : - SingleHook, IPeriodicHook -{ - /// - /// Initializes a new instance of the class. - /// - /// The underlying hook. - public PeriodicHook(INostaleHook 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; + +/// +/// A hook of a periodic function, +/// preferably called every frame. +/// +internal class PeriodicHook : + SingleHook, IPeriodicHook +{ + /// + /// Initializes a new instance of the class. + /// + /// The underlying hook. + public PeriodicHook(INostaleHook underlyingHook) + : base(underlyingHook) + { + } } \ No newline at end of file -- 2.48.1