// // HookManager.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; using Microsoft.Extensions.Options; using NosSmooth.LocalBinding.Options; using Remora.Results; namespace NosSmooth.LocalBinding.Hooks.Implementations; /// internal class HookManager : IHookManager { private readonly HookManagerOptions _options; private readonly Dictionary _hooks; private bool _initialized; /// /// Initializes a new instance of the class. /// /// The hook manager options. public HookManager(IOptionsSnapshot options) { _options = options.Value; _hooks = new Dictionary(); } /// public Optional PacketSend => GetHook(IHookManager.PacketSendName); /// public Optional PacketReceive => GetHook(IHookManager.PacketReceiveName); /// public Optional PlayerWalk => GetHook(IHookManager.CharacterWalkName); /// public Optional EntityFollow => GetHook(IHookManager.EntityFollowName); /// public Optional EntityUnfollow => GetHook (IHookManager.EntityUnfollowName); /// public Optional PetWalk => GetHook(IHookManager.PetWalkName); /// public Optional EntityFocus => GetHook(IHookManager.EntityFocusName); /// public Optional Periodic => GetHook(IHookManager.PeriodicName); /// public IReadOnlyList Hooks => _hooks.Values.ToList(); /// public Result Initialize(NosBindingManager bindingManager, NosBrowserManager browserManager) { _initialized = true; if (_hooks.Count > 0) { // already initialized return Result.FromSuccess(); } return HandleResults ( () => PeriodicHook.Create(bindingManager, _options.PeriodicHook).Map(MapHook), () => EntityFocusHook.Create(bindingManager, browserManager, _options.EntityFocusHook).Map(MapHook), () => EntityFollowHook.Create(bindingManager, browserManager, _options.EntityFollowHook).Map(MapHook), () => EntityUnfollowHook.Create(bindingManager, browserManager, _options.EntityUnfollowHook).Map(MapHook), () => PlayerWalkHook.Create(bindingManager, browserManager, _options.PlayerWalkHook).Map(MapHook), () => PetWalkHook.Create(bindingManager, _options.PetWalkHook).Map(MapHook), () => PacketSendHook.Create(bindingManager, browserManager, _options.PacketSendHook).Map(MapHook), () => PacketReceiveHook.Create(bindingManager, browserManager, _options.PacketReceiveHook).Map(MapHook) ); } private INostaleHook MapHook(T original) where T : INostaleHook { return original; } private Result HandleResults(params Func>[] functions) { List errorResults = new List(); foreach (var func in functions) { try { var result = func(); if (result.IsSuccess) { _hooks.Add(result.Entity.Name, result.Entity); } else { errorResults.Add(Result.FromError(result)); } } catch (Exception e) { errorResults.Add((Result)e); } } return errorResults.Count switch { 0 => Result.FromSuccess(), 1 => (Result)errorResults[0], _ => new AggregateError(errorResults) }; } /// public void Enable(IEnumerable names) { foreach (var hook in Hooks .Where(x => names.Contains(x.Name))) { hook.Enable(); } } /// public void Disable(IEnumerable names) { foreach (var hook in Hooks .Where(x => names.Contains(x.Name))) { hook.Disable(); } } /// public void DisableAll() { foreach (var hook in Hooks) { hook.Disable(); } } /// public void EnableAll() { foreach (var hook in Hooks) { hook.Enable(); } } /// 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 (!_initialized) { throw new InvalidOperationException ($"Could not load hook {name}. Did you forget to call IHookManager.Initialize?"); } if (!_hooks.ContainsKey(name) || _hooks[name] is not T typed) { return Optional.Empty; } return typed; } private Optional GetHook(Type hookType) { if (!_initialized) { throw new InvalidOperationException ($"Could not load hook {hookType.Name}. Did you forget to call IHookManager.Initialize?"); } var hook = _hooks.Values.FirstOrDefault(x => x.GetType() == hookType); if (hook is null) { return Optional.Empty; } return new Optional(hook); } }