~ruther/NosSmooth.Local

69c46f6f0b73dd4475ee75d44f8783c9a45c262e — Rutherther 2 years ago 85edfba
feat(shared): add sharing of NosSmooth hooks
A src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/HookStateEventArgs.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/HookStateEventArgs.cs +25 -0
@@ 0,0 1,25 @@
//
//  HookStateEventArgs.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.

namespace NosSmooth.Extensions.SharedBinding.EventArgs;

/// <inheritdoc />
public class HookStateEventArgs : System.EventArgs
{
    /// <summary>
    /// Initializes a new instance of the <see cref="HookStateEventArgs"/> class.
    /// </summary>
    /// <param name="enabled">The new state.</param>
    public HookStateEventArgs(bool enabled)
    {
        Enabled = enabled;
    }

    /// <summary>
    /// Gets the new state.
    /// </summary>
    public bool Enabled { get; }
}
\ No newline at end of file

M src/Extensions/NosSmooth.Extensions.SharedBinding/Extensions/ServiceCollectionExtensions.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Extensions/ServiceCollectionExtensions.cs +42 -40
@@ 4,10 4,13 @@
//  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.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NosSmooth.Data.NOSFiles;
using NosSmooth.Extensions.SharedBinding.Hooks;
using NosSmooth.LocalBinding;
using NosSmooth.LocalBinding.Hooks;
using NosSmooth.PacketSerializer.Packets;

namespace NosSmooth.Extensions.SharedBinding.Extensions;


@@ 18,81 21,80 @@ namespace NosSmooth.Extensions.SharedBinding.Extensions;
public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Replaces <see cref="NosBindingManager"/>
    /// with shared equivalent. That allows for multiple programs injected inside NosTale.
    /// Adds shared <see cref="IHookManager"/>.
    /// </summary>
    /// <param name="serviceCollection">The collection.</param>
    /// <returns>The same collection.</returns>
    public static IServiceCollection ShareBinding(this IServiceCollection serviceCollection)
    public static IServiceCollection ShareHooks(this IServiceCollection serviceCollection)
    {
        var original = serviceCollection
            .Last(x => x.ServiceType == typeof(NosBindingManager));
            .Last(x => x.ServiceType == typeof(IHookManager));
        serviceCollection.Configure<SharedOptions>(o => o.AddDescriptor(original));

        return serviceCollection
            .Configure<SharedOptions>(o => o.BindingDescriptor = original)
            .Replace
                (ServiceDescriptor.Singleton<NosBindingManager>(p => SharedManager.Instance.GetNosBindingManager(p)));
            .AddSingleton<SharedHookManager>
            (
                p =>
                {
                    var sharedHookManager = p.GetRequiredService<SharedManager>().GetShared<IHookManager>(p);
                    return new SharedHookManager(sharedHookManager);
                }
            )
            .Replace(ServiceDescriptor.Singleton<IHookManager, SingleHookManager>());
    }

    /// <summary>
    /// Replaces <see cref="NostaleDataFilesManager"/>
    /// Replaces <typeparamref name="T"/>
    /// with shared equivalent. That allows for multiple programs injected inside NosTale.
    /// </summary>
    /// <param name="serviceCollection">The collection.</param>
    /// <typeparam name="T">The shared type.</typeparam>
    /// <returns>The same collection.</returns>
    public static IServiceCollection ShareFileManager(this IServiceCollection serviceCollection)
    public static IServiceCollection Share<T>(this IServiceCollection serviceCollection)
        where T : class
    {
        var original = serviceCollection
            .Last(x => x.ServiceType == typeof(NostaleDataFilesManager));
            .Last(x => x.ServiceType == typeof(T));

        return serviceCollection
            .Configure<SharedOptions>(o => o.FileDescriptor = original)
            .Configure<SharedOptions>(o => o.AddDescriptor(original))
            .Replace
                (ServiceDescriptor.Singleton<NostaleDataFilesManager>(p => SharedManager.Instance.GetFilesManager(p)));
            (
                ServiceDescriptor.Singleton<T>(p => SharedManager.Instance.GetShared<T>(p))
            );
    }

    /// <summary>
    /// Replaces <see cref="IPacketTypesRepository"/>
    /// Tries to replace <see cref="T"/>
    /// with shared equivalent. That allows for multiple programs injected inside NosTale.
    /// </summary>
    /// <param name="serviceCollection">The collection.</param>
    /// <typeparam name="T">The shared type.</typeparam>
    /// <returns>The same collection.</returns>
    public static IServiceCollection SharePacketRepository(this IServiceCollection serviceCollection)
    public static IServiceCollection TryShare<T>(this IServiceCollection serviceCollection)
        where T : class
    {
        var original = serviceCollection
            .Last(x => x.ServiceType == typeof(IPacketTypesRepository));
        if (serviceCollection.Any(x => x.ServiceType == typeof(T)))
        {
            return serviceCollection.Share<T>();
        }

        return serviceCollection
            .Configure<SharedOptions>(o => o.PacketRepositoryDescriptor = original)
            .Replace
            (
                ServiceDescriptor.Singleton<IPacketTypesRepository>(p => SharedManager.Instance.GetPacketRepository(p))
            );
        return serviceCollection;
    }

    /// <summary>
    /// Replaces <see cref="NosBindingManager"/>, <see cref="NostaleDataFilesManager"/> and <see cref="IPacketTypesRepository"/>
    /// with their shared equvivalents. That allows for multiple programs injected inside NosTale.
    /// Replaces some NosSmooth types with their shared equivalents.
    /// That allows for multiple programs injected inside NosTale.
    /// </summary>
    /// <param name="serviceCollection">The collection.</param>
    /// <returns>The same collection.</returns>
    public static IServiceCollection ShareNosSmooth(this IServiceCollection serviceCollection)
    {
        if (serviceCollection.Any(x => x.ServiceType == typeof(NosBindingManager)))
        {
            serviceCollection.ShareBinding();
        }

        if (serviceCollection.Any(x => x.ServiceType == typeof(NostaleDataFilesManager)))
        {
            serviceCollection.ShareFileManager();
        }

        if (serviceCollection.Any(x => x.ServiceType == typeof(IPacketTypesRepository)))
        {
            serviceCollection.SharePacketRepository();
        }

        return serviceCollection;
        return serviceCollection
            .AddSingleton<SharedManager>(p => SharedManager.Instance)
            .ShareHooks()
            .TryShare<NosBrowserManager>()
            .TryShare<IPacketTypesRepository>()
            .TryShare<NostaleDataFilesManager>();
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SharedHookManager.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SharedHookManager.cs +174 -0
@@ 0,0 1,174 @@
//
//  SharedHookManager.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.Extensions.SharedBinding.Hooks.Specific;
using NosSmooth.LocalBinding;
using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Options;
using Remora.Results;

namespace NosSmooth.Extensions.SharedBinding.Hooks;

/// <summary>
/// A hook manager managing <see cref="SingleHookManager"/>s of all of the instances.
/// </summary>
public class SharedHookManager
{
    private readonly IHookManager _underlyingManager;

    private bool _initialized;
    private Dictionary<string, int> _hookedCount;

    /// <summary>
    /// 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
    )
    {
        _hookedCount = new Dictionary<string, int>();
        _underlyingManager = underlyingManager;
    }

    /// <summary>
    /// Initialize a shared NosSmooth instance.
    /// </summary>
    /// <param name="bindingManager">The binding manager.</param>
    /// <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
        (NosBindingManager bindingManager, NosBrowserManager browserManager, HookManagerOptions options)
    {
        if (!_initialized)
        {
            _underlyingManager.Initialize(bindingManager, browserManager);
            _initialized = true;
        }

        var hooks = new Dictionary<string, INostaleHook>();

        // TODO: initialize using reflection
        hooks.Add
        (
            _underlyingManager.Periodic.Name,
            InitializeSingleHook
            (
                new PeriodicHook(_underlyingManager.Periodic),
                options.PeriodicHook
            )
        );

        hooks.Add
        (
            _underlyingManager.EntityFocus.Name,
            InitializeSingleHook
            (
                new EntityFocusHook(_underlyingManager.EntityFocus),
                options.EntityFocusHook
            )
        );

        hooks.Add
        (
            _underlyingManager.EntityFollow.Name,
            InitializeSingleHook
            (
                new EntityFollowHook(_underlyingManager.EntityFollow),
                options.EntityFollowHook
            )
        );

        hooks.Add
        (
            _underlyingManager.EntityUnfollow.Name,
            InitializeSingleHook
            (
                new EntityUnfollowHook(_underlyingManager.EntityUnfollow),
                options.EntityUnfollowHook
            )
        );

        hooks.Add
        (
            _underlyingManager.PacketReceive.Name,
            InitializeSingleHook
            (
                new PacketReceiveHook(_underlyingManager.PacketReceive),
                options.PacketReceiveHook
            )
        );

        hooks.Add
        (
            _underlyingManager.PacketSend.Name,
            InitializeSingleHook
            (
                new PacketSendHook(_underlyingManager.PacketSend),
                options.PacketSendHook
            )
        );

        hooks.Add
        (
            _underlyingManager.PetWalk.Name,
            InitializeSingleHook
            (
                new PetWalkHook(_underlyingManager.PetWalk),
                options.PetWalkHook
            )
        );

        hooks.Add
        (
            _underlyingManager.PlayerWalk.Name,
            InitializeSingleHook
            (
                new PlayerWalkHook(_underlyingManager.PlayerWalk),
                options.PlayerWalkHook
            )
        );

        return hooks;
    }

    private INostaleHook<TFunction, TWrapperFunction, TEventArgs> InitializeSingleHook<TFunction, TWrapperFunction,
        TEventArgs>(SingleHook<TFunction, TWrapperFunction, TEventArgs> hook, HookOptions options)
        where TFunction : Delegate
        where TWrapperFunction : Delegate
        where TEventArgs : System.EventArgs
    {
        hook.StateChanged += (_, state) =>
        {
            if (!_hookedCount.ContainsKey(hook.Name))
            {
                _hookedCount[hook.Name] = 0;
            }

            _hookedCount[hook.Name] += state.Enabled ? 1 : -1;

            if (state.Enabled)
            {
                _underlyingManager.Enable(new[] { hook.Name });
            }
            else if (_hookedCount[hook.Name] == 0)
            {
                _underlyingManager.Disable(new[] { hook.Name });
            }
        };

        if (options.Hook)
        {
            hook.Enable();
        }

        return hook;
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHook.cs +86 -0
@@ 0,0 1,86 @@
//
//  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;
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHookManager.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/SingleHookManager.cs +125 -0
@@ 0,0 1,125 @@
//
//  SingleHookManager.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 Microsoft.Extensions.Options;
using NosSmooth.LocalBinding;
using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Options;
using Remora.Results;

namespace NosSmooth.Extensions.SharedBinding.Hooks;

/// <summary>
/// A hook manager for a single NosSmooth instance using shared data.
/// </summary>
public class SingleHookManager : IHookManager
{
    private readonly SharedHookManager _sharedHookManager;
    private readonly HookManagerOptions _options;
    private Dictionary<string, INostaleHook> _hooks;

    /// <summary>
    /// Initializes a new instance of the <see cref="SingleHookManager"/> class.
    /// </summary>
    /// <param name="sharedHookManager">The shared hook manager.</param>
    /// <param name="options">The hook options.</param>
    public SingleHookManager(SharedHookManager sharedHookManager, IOptions<HookManagerOptions> options)
    {
        _hooks = new Dictionary<string, INostaleHook>();
        _sharedHookManager = sharedHookManager;
        _options = options.Value;
    }

    /// <inheritdoc />
    public IPacketSendHook PacketSend => GetHook<IPacketSendHook>(IHookManager.PacketSendName);

    /// <inheritdoc />
    public IPacketReceiveHook PacketReceive => GetHook<IPacketReceiveHook>(IHookManager.PacketReceiveName);

    /// <inheritdoc />
    public IPlayerWalkHook PlayerWalk => GetHook<IPlayerWalkHook>(IHookManager.CharacterWalkName);

    /// <inheritdoc />
    public IEntityFollowHook EntityFollow => GetHook<IEntityFollowHook>(IHookManager.EntityFollowName);

    /// <inheritdoc />
    public IEntityUnfollowHook EntityUnfollow => GetHook<IEntityUnfollowHook>(IHookManager.EntityUnfollowName);

    /// <inheritdoc />
    public IPetWalkHook PetWalk => GetHook<IPetWalkHook>(IHookManager.PetWalkName);

    /// <inheritdoc />
    public IEntityFocusHook EntityFocus => GetHook<IEntityFocusHook>(IHookManager.EntityFocusName);

    /// <inheritdoc />
    public IPeriodicHook Periodic => GetHook<IPeriodicHook>(IHookManager.PeriodicName);

    /// <inheritdoc />
    public IReadOnlyList<INostaleHook> Hooks => _hooks.Values.ToList();

    /// <inheritdoc />
    public IResult Initialize(NosBindingManager bindingManager, NosBrowserManager browserManager)
    {
        var hooksResult = _sharedHookManager.InitializeInstance(bindingManager, browserManager, _options);
        if (!hooksResult.IsDefined(out var hooks))
        {
            return hooksResult;
        }

        _hooks = hooks;
        return Result.FromSuccess();
    }

    /// <inheritdoc />
    public void Enable(IEnumerable<string> names)
    {
        foreach (var name in names)
        {
            var hook = GetHook<INostaleHook>(name);
            hook.Enable();
        }
    }

    /// <inheritdoc />
    public void Disable(IEnumerable<string> names)
    {
        foreach (var name in names)
        {
            var hook = GetHook<INostaleHook>(name);
            hook.Disable();
        }
    }

    /// <inheritdoc />
    public void DisableAll()
    {
        foreach (var hook in _hooks.Values)
        {
            hook.Disable();
        }
    }

    /// <inheritdoc />
    public void EnableAll()
    {
        foreach (var hook in _hooks.Values)
        {
            hook.Enable();
        }
    }

    private T GetHook<T>(string name)
        where T : INostaleHook
    {
        if (!_hooks.ContainsKey(name) || _hooks[name] is not T typed)
        {
            throw new InvalidOperationException
                ($"Could not load hook {name}. Did you forget to call IHookManager.Initialize?");
        }

        return typed;
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/EntityFocusHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/EntityFocusHook.cs +24 -0
@@ 0,0 1,24 @@
//
//  EntityFocusHook.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.EventArgs;
using NosSmooth.LocalBinding.Hooks;

namespace NosSmooth.Extensions.SharedBinding.Hooks.Specific;

/// <inheritdoc />
internal class EntityFocusHook : SingleHook<IEntityFocusHook.EntityFocusDelegate,
    IEntityFocusHook.EntityFocusWrapperDelegate, EntityEventArgs>, IEntityFocusHook
{
    /// <summary>
    /// Initializes a new instance of the <see cref="EntityFocusHook"/> class.
    /// </summary>
    /// <param name="underlyingHook">The underlying hook.</param>
    public EntityFocusHook(INostaleHook<IEntityFocusHook.EntityFocusDelegate, IEntityFocusHook.EntityFocusWrapperDelegate, EntityEventArgs> underlyingHook)
        : base(underlyingHook)
    {
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/EntityFollowHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/EntityFollowHook.cs +24 -0
@@ 0,0 1,24 @@
//
//  EntityFollowHook.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.EventArgs;
using NosSmooth.LocalBinding.Hooks;

namespace NosSmooth.Extensions.SharedBinding.Hooks.Specific;

/// <inheritdoc />
internal class EntityFollowHook : SingleHook<IEntityFollowHook.EntityFollowDelegate,
    IEntityFollowHook.EntityFollowWrapperDelegate, EntityEventArgs>, IEntityFollowHook
{
    /// <summary>
    /// Initializes a new instance of the <see cref="EntityFollowHook"/> class.
    /// </summary>
    /// <param name="underlyingHook">The underlying hook.</param>
    public EntityFollowHook(INostaleHook<IEntityFollowHook.EntityFollowDelegate, IEntityFollowHook.EntityFollowWrapperDelegate, EntityEventArgs> underlyingHook)
        : base(underlyingHook)
    {
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/EntityUnfollowHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/EntityUnfollowHook.cs +24 -0
@@ 0,0 1,24 @@
//
//  EntityUnfollowHook.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.EventArgs;
using NosSmooth.LocalBinding.Hooks;

namespace NosSmooth.Extensions.SharedBinding.Hooks.Specific;

/// <inheritdoc />
internal class EntityUnfollowHook : SingleHook<IEntityUnfollowHook.EntityUnfollowDelegate,
    IEntityUnfollowHook.EntityUnfollowWrapperDelegate, EntityEventArgs>, IEntityUnfollowHook
{
    /// <summary>
    /// Initializes a new instance of the <see cref="EntityUnfollowHook"/> class.
    /// </summary>
    /// <param name="underlyingHook">The underlying hook.</param>
    public EntityUnfollowHook(INostaleHook<IEntityUnfollowHook.EntityUnfollowDelegate, IEntityUnfollowHook.EntityUnfollowWrapperDelegate, EntityEventArgs> underlyingHook)
        : base(underlyingHook)
    {
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PacketReceiveHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PacketReceiveHook.cs +24 -0
@@ 0,0 1,24 @@
//
//  PacketReceiveHook.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.EventArgs;
using NosSmooth.LocalBinding.Hooks;

namespace NosSmooth.Extensions.SharedBinding.Hooks.Specific;

/// <inheritdoc />
internal class PacketReceiveHook : SingleHook<IPacketReceiveHook.PacketReceiveDelegate,
    IPacketReceiveHook.PacketReceiveWrapperDelegate, PacketEventArgs>, IPacketReceiveHook
{
    /// <summary>
    /// Initializes a new instance of the <see cref="PacketReceiveHook"/> class.
    /// </summary>
    /// <param name="underlyingHook">The underlying hook.</param>
    public PacketReceiveHook(INostaleHook<IPacketReceiveHook.PacketReceiveDelegate, IPacketReceiveHook.PacketReceiveWrapperDelegate, PacketEventArgs> underlyingHook)
        : base(underlyingHook)
    {
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PacketSendHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PacketSendHook.cs +24 -0
@@ 0,0 1,24 @@
//
//  PacketSendHook.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.EventArgs;
using NosSmooth.LocalBinding.Hooks;

namespace NosSmooth.Extensions.SharedBinding.Hooks.Specific;

/// <inheritdoc />
internal class PacketSendHook : SingleHook<IPacketSendHook.PacketSendDelegate,
    IPacketSendHook.PacketSendWrapperDelegate, PacketEventArgs>, IPacketSendHook
{
    /// <summary>
    /// Initializes a new instance of the <see cref="PacketSendHook"/> class.
    /// </summary>
    /// <param name="underlyingHook">The underlying hook.</param>
    public PacketSendHook(INostaleHook<IPacketSendHook.PacketSendDelegate, IPacketSendHook.PacketSendWrapperDelegate, PacketEventArgs> underlyingHook)
        : base(underlyingHook)
    {
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PeriodicHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PeriodicHook.cs +26 -0
@@ 0,0 1,26 @@
//
//  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)
    {
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PetWalkHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PetWalkHook.cs +23 -0
@@ 0,0 1,23 @@
//
//  PetWalkHook.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.EventArgs;
using NosSmooth.LocalBinding.Hooks;

namespace NosSmooth.Extensions.SharedBinding.Hooks.Specific;

/// <inheritdoc />
internal class PetWalkHook : SingleHook<IPetWalkHook.PetWalkDelegate, IPetWalkHook.PetWalkWrapperDelegate, PetWalkEventArgs>, IPetWalkHook
{
    /// <summary>
    /// Initializes a new instance of the <see cref="PetWalkHook"/> class.
    /// </summary>
    /// <param name="underlyingHook">The underlying hook.</param>
    public PetWalkHook(INostaleHook<IPetWalkHook.PetWalkDelegate, IPetWalkHook.PetWalkWrapperDelegate, PetWalkEventArgs> underlyingHook)
        : base(underlyingHook)
    {
    }
}
\ No newline at end of file

A src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PlayerWalkHook.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Hooks/Specific/PlayerWalkHook.cs +23 -0
@@ 0,0 1,23 @@
//
//  PlayerWalkHook.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.EventArgs;
using NosSmooth.LocalBinding.Hooks;

namespace NosSmooth.Extensions.SharedBinding.Hooks.Specific;

/// <inheritdoc />
internal class PlayerWalkHook : SingleHook<IPlayerWalkHook.WalkDelegate, IPlayerWalkHook.WalkWrapperDelegate, WalkEventArgs>, IPlayerWalkHook
{
    /// <summary>
    /// Initializes a new instance of the <see cref="PlayerWalkHook"/> class.
    /// </summary>
    /// <param name="underlyingHook">The underlying hook.</param>
    public PlayerWalkHook(INostaleHook<IPlayerWalkHook.WalkDelegate, IPlayerWalkHook.WalkWrapperDelegate, WalkEventArgs> underlyingHook)
        : base(underlyingHook)
    {
    }
}
\ No newline at end of file

M src/Extensions/NosSmooth.Extensions.SharedBinding/NosSmooth.Extensions.SharedBinding.csproj => src/Extensions/NosSmooth.Extensions.SharedBinding/NosSmooth.Extensions.SharedBinding.csproj +1 -0
@@ 7,6 7,7 @@
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="NosSmooth.Core" Version="3.0.1" />
      <PackageReference Include="NosSmooth.Data.NOSFiles" Version="2.0.2" />
      <PackageReference Include="NosSmooth.PacketSerializer" Version="2.0.0" />
    </ItemGroup>

M src/Extensions/NosSmooth.Extensions.SharedBinding/SharedManager.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/SharedManager.cs +14 -41
@@ 20,9 20,7 @@ namespace NosSmooth.Extensions.SharedBinding;
public class SharedManager
{
    private static SharedManager? _instance;
    private NosBindingManager? _bindingManager;
    private NostaleDataFilesManager? _filesManager;
    private IPacketTypesRepository? _packetRepository;
    private Dictionary<Type, object> _sharedData = new();

    /// <summary>
    /// A singleton instance.


@@ 41,58 39,33 @@ public class SharedManager
        }
    }

    /// <summary>
    /// Gets the shared nos binding manager.
    /// </summary>
    /// <param name="services">The service provider.</param>
    /// <returns>The shared manager.</returns>
    public NosBindingManager GetNosBindingManager(IServiceProvider services)
    {
        if (_bindingManager is null)
        {
            _bindingManager = GetFromDescriptor<NosBindingManager>(services, o => o.BindingDescriptor);
        }

        return _bindingManager;

    }

    /// <summary>
    /// Gets the shared file manager.
    /// </summary>
    /// <param name="services">The service provider.</param>
    /// <returns>The shared manager.</returns>
    public NostaleDataFilesManager GetFilesManager(IServiceProvider services)
    private SharedManager()
    {
        if (_filesManager is null)
        {
            _filesManager = GetFromDescriptor<NostaleDataFilesManager>(services, o => o.FileDescriptor);
        }

        return _filesManager;

    }

    /// <summary>
    /// Gets the shared packet type repository.
    /// Get shared equivalent of the given type.
    /// </summary>
    /// <param name="services">The service provider.</param>
    /// <returns>The shared repository.</returns>
    public IPacketTypesRepository GetPacketRepository(IServiceProvider services)
    /// <typeparam name="T">The type to get shared instance of.</typeparam>
    /// <returns>The shared instance.</returns>
    /// <exception cref="InvalidOperationException">Thrown in case the type is not shared.</exception>
    public T GetShared<T>(IServiceProvider services)
        where T : class
    {
        if (_packetRepository is null)
        if (!_sharedData.ContainsKey(typeof(T)))
        {
            _packetRepository = GetFromDescriptor<IPacketTypesRepository>(services, o => o.PacketRepositoryDescriptor);
            _sharedData[typeof(T)] = CreateShared<T>(services);
        }

        return _packetRepository;

        return (T)_sharedData[typeof(T)];
    }

    private T GetFromDescriptor<T>(IServiceProvider services, Func<SharedOptions, ServiceDescriptor?> getDescriptor)
    private T CreateShared<T>(IServiceProvider services)
        where T : class
    {
        var options = services.GetRequiredService<IOptions<SharedOptions>>();
        var descriptor = getDescriptor(options.Value);
        var descriptor = options.Value.GetDescriptor(typeof(T));

        if (descriptor is null)
        {

M src/Extensions/NosSmooth.Extensions.SharedBinding/SharedOptions.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/SharedOptions.cs +20 -8
@@ 16,18 16,30 @@ namespace NosSmooth.Extensions.SharedBinding;
/// </summary>
internal class SharedOptions
{
    /// <summary>
    /// Gets or sets the original descriptor of <see cref="NosBindingManager"/>.
    /// </summary>
    public ServiceDescriptor? BindingDescriptor { get; set; }
    private Dictionary<Type, ServiceDescriptor> _descriptors = new();

    /// <summary>
    /// Gets or sets the original descriptor of <see cref="NostaleDataFilesManager"/>.
    /// Add service descriptor for given type.
    /// </summary>
    public ServiceDescriptor? FileDescriptor { get; set; }
    /// <param name="descriptor">The service descriptor.</param>
    public void AddDescriptor(ServiceDescriptor descriptor)
    {
        var type = descriptor.ServiceType;
        if (_descriptors.ContainsKey(type))
        {
            return;
        }

        _descriptors[type] = descriptor;
    }

    /// <summary>
    /// Gets or sets the original descriptor of <see cref="IPacketTypesRepository"/>.
    /// Get descriptor for the given type.
    /// </summary>
    public ServiceDescriptor? PacketRepositoryDescriptor { get; set; }
    /// <param name="type">The type of the descriptor.</param>
    /// <returns>A descriptor.</returns>
    public ServiceDescriptor GetDescriptor(Type type)
    {
        return _descriptors[type];
    }
}
\ No newline at end of file

Do not follow this link