M NosSmooth.Local.sln => NosSmooth.Local.sln +7 -0
@@ 43,6 43,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HighLevel", "HighLevel", "{
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimplePiiBot", "src\Samples\HighLevel\SimplePiiBot\SimplePiiBot.csproj", "{A93B5A9F-68FC-4A17-AFAC-DAB22B617A50}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NosSmooth.Extensions.SharedBinding", "src\Extensions\NosSmooth.Extensions.SharedBinding\NosSmooth.Extensions.SharedBinding.csproj", "{59A3D740-88E1-49F4-8AED-859A866F3D7E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ 63,6 65,7 @@ Global
{7190312B-CFEE-49D3-8DAB-542C31071E87} = {D19F45EC-8E59-4F7C-A4C4-D0BD11C8C32B}
{8B99B1BD-0738-40C1-9961-4DBC7AF3E8CE} = {004AF2D8-8D02-4BDF-BD18-752C54D65F1C}
{A93B5A9F-68FC-4A17-AFAC-DAB22B617A50} = {8B99B1BD-0738-40C1-9961-4DBC7AF3E8CE}
+ {59A3D740-88E1-49F4-8AED-859A866F3D7E} = {987E4CD9-DBFE-40FF-90F7-AAD060CFC781}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CA2873D8-BD0B-4583-818D-B94A3C2ABBA3}.Debug|Any CPU.ActiveCfg = Debug|Win32
@@ 109,5 112,9 @@ Global
{A93B5A9F-68FC-4A17-AFAC-DAB22B617A50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A93B5A9F-68FC-4A17-AFAC-DAB22B617A50}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A93B5A9F-68FC-4A17-AFAC-DAB22B617A50}.Release|Any CPU.Build.0 = Release|Any CPU
+ {59A3D740-88E1-49F4-8AED-859A866F3D7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {59A3D740-88E1-49F4-8AED-859A866F3D7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {59A3D740-88E1-49F4-8AED-859A866F3D7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {59A3D740-88E1-49F4-8AED-859A866F3D7E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
A README.md => README.md +20 -0
@@ 0,0 1,20 @@
+# NosSmooth.Local
+NosSmooth is a multi-platform library for NosTale
+packets, data, game state and such.
+See the main repository at [NosSmooth](https://github.com/Rutherther/NosSmooth).
+NosSmooth.Local contains libraries for the regular NosTale client
+such as memory mapping and NosSmooth client implementation for
+handling the packets.
+
+See the samples in the `src/Samples` folder.
+
+## Features
+
+### Bindings (hooks, memory mapping)
+Hooks, options, mapping, errors
+
+### Client
+commands implementations, initialization
+
+### Injector
+copy Inject, regular injector cannot be used bcs .NET 5<
\ No newline at end of file
M src/Core/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs => src/Core/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs +27 -8
@@ 5,7 5,10 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Extensions.DependencyInjection;
+using NosSmooth.LocalBinding.Hooks;
+using NosSmooth.LocalBinding.Hooks.Implementations;
using NosSmooth.LocalBinding.Objects;
+using NosSmooth.LocalBinding.Options;
using NosSmooth.LocalBinding.Structs;
namespace NosSmooth.LocalBinding.Extensions;
@@ 19,8 22,9 @@ public static class ServiceCollectionExtensions
/// Adds bindings to Nostale objects along with <see cref="NosBindingManager"/> to initialize those.
/// </summary>
/// <remarks>
- /// Adds <see cref="PlayerManagerBinding"/> and <see cref="NetworkBinding"/>.
- /// You have to initialize these using <see cref="NosBindingManager"/>
+ /// This adds <see cref="NosBindingManager"/>, <see cref="NosBrowserManager"/>,
+ /// <see cref="IHookManager"/> and their siblings.
+ /// You have to initialize the bindings using <see cref="NosBindingManager"/>
/// prior to requesting them from the provider, otherwise an exception
/// will be thrown.
/// </remarks>
@@ 32,16 36,31 @@ public static class ServiceCollectionExtensions
.AddSingleton<NosBindingManager>()
.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<NosBrowserManager>().PlayerManager)
.AddSingleton(p => p.GetRequiredService<NosBrowserManager>().SceneManager)
.AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PetManagerList)
.AddSingleton(p => p.GetRequiredService<NosBrowserManager>().SceneManager)
.AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PetManagerList)
- .AddSingleton(p => p.GetRequiredService<NosBindingManager>().PlayerManager)
- .AddSingleton(p => p.GetRequiredService<NosBindingManager>().Periodic)
- .AddSingleton(p => p.GetRequiredService<NosBindingManager>().PetManager)
- .AddSingleton(p => p.GetRequiredService<NosBindingManager>().UnitManager)
- .AddSingleton(p => p.GetRequiredService<NosBindingManager>().Network);
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().PlayerManager)
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().NetworkManager)
+ .AddSingleton(p => p.GetRequiredService<NosBrowserManager>().UnitManager)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PacketReceive)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PacketSend)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PlayerWalk)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().PetWalk)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityFocus)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityFollow)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().EntityUnfollow)
+ .AddSingleton(p => p.GetRequiredService<IHookManager>().Periodic);
}
/// <summary>
@@ 52,7 71,7 @@ public static class ServiceCollectionExtensions
/// <returns>The collection.</returns>
public static IServiceCollection ConfigureHooks(this IServiceCollection serviceCollection, Action<HooksConfigBuilder> configure)
{
- var builder = new HooksConfigBuilder();
+ var builder = new HooksConfigBuilder(new HookManagerOptions());
configure(builder);
builder.Apply(serviceCollection);
return serviceCollection;
R src/Core/NosSmooth.LocalBinding/HookOptions.cs => src/Core/NosSmooth.LocalBinding/Hooks/HookOptions.cs +24 -2
@@ 4,12 4,34 @@
// 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.LocalBinding;
+namespace NosSmooth.LocalBinding.Hooks;
/// <summary>
/// A configuration for hooking a function.
/// </summary>
+/// <param name="Name">The name of the hook.</param>
/// <param name="Hook">Whether to hook the function.</param>
/// <param name="MemoryPattern">The memory pattern in hex. Use ?? for any bytes.</param>
/// <param name="Offset">The offset to find the function at.</param>
-public record HookOptions(bool Hook, string MemoryPattern, int Offset);>
\ No newline at end of file
+public record HookOptions
+(
+ string Name,
+ bool Hook,
+ string MemoryPattern,
+ int Offset
+);
+
+/// <summary>
+/// A configuration for hooking a function.
+/// </summary>
+/// <param name="Name">The name of the hook.</param>
+/// <param name="Hook">Whether to hook the function.</param>
+/// <param name="MemoryPattern">The memory pattern in hex. Use ?? for any bytes.</param>
+/// <param name="Offset">The offset to find the function at.</param>
+public record HookOptions<TFunction>
+(
+ string Name,
+ bool Hook,
+ string MemoryPattern,
+ int Offset
+) : HookOptions(Name, Hook, MemoryPattern, Offset);<
\ No newline at end of file
R src/Core/NosSmooth.LocalBinding/HookOptionsBuilder.cs => src/Core/NosSmooth.LocalBinding/Hooks/HookOptionsBuilder.cs +12 -22
@@ 4,48 4,38 @@
// 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.LocalBinding;
+namespace NosSmooth.LocalBinding.Hooks;
/// <summary>
/// Builder for <see cref="HookOptions"/>.
/// </summary>
-public class HookOptionsBuilder
+/// <typeparam name="T">The type of hook options.</typeparam>
+public class HookOptionsBuilder<T>
{
+ private string _name;
private bool _hook;
private string _pattern;
private int _offset;
/// <summary>
- /// Initializes a new instance of the <see cref="HookOptionsBuilder"/> class.
+ /// Initializes a new instance of the <see cref="HookOptionsBuilder{T}"/> class.
/// </summary>
/// <param name="options">The options.</param>
- internal HookOptionsBuilder(HookOptions options)
+ internal HookOptionsBuilder(HookOptions<T> options)
{
+ _name = options.Name;
_hook = options.Hook;
_pattern = options.MemoryPattern;
_offset = options.Offset;
}
/// <summary>
- /// Initializes a new instance of the <see cref="HookOptionsBuilder"/> class.
- /// </summary>
- /// <param name="hook">Whether to hook the function.</param>
- /// <param name="pattern">The default pattern.</param>
- /// <param name="offset">The default offset.</param>
- internal HookOptionsBuilder(bool hook, string pattern, int offset)
- {
- _offset = offset;
- _pattern = pattern;
- _hook = hook;
- }
-
- /// <summary>
/// Configure whether to hook this function.
/// Default true.
/// </summary>
/// <param name="hook">Whether to hook the function.</param>
/// <returns>This builder.</returns>
- public HookOptionsBuilder Hook(bool hook = true)
+ public HookOptionsBuilder<T> Hook(bool hook = true)
{
_hook = hook;
return this;
@@ 57,7 47,7 @@ public class HookOptionsBuilder
/// </summary>
/// <param name="pattern">The memory pattern.</param>
/// <returns>This builder.</returns>
- public HookOptionsBuilder MemoryPattern(string pattern)
+ public HookOptionsBuilder<T> MemoryPattern(string pattern)
{
_pattern = pattern;
return this;
@@ 68,7 58,7 @@ public class HookOptionsBuilder
/// </summary>
/// <param name="offset">The offset.</param>
/// <returns>This builder.</returns>
- public HookOptionsBuilder Offset(int offset)
+ public HookOptionsBuilder<T> Offset(int offset)
{
_offset = offset;
return this;
@@ 78,6 68,6 @@ public class HookOptionsBuilder
/// Create hook options from this builder.
/// </summary>
/// <returns>The options.</returns>
- internal HookOptions Build()
- => new HookOptions(_hook, _pattern, _offset);
+ internal HookOptions<T> Build()
+ => new HookOptions<T>(_name, _hook, _pattern, _offset);
}=
\ No newline at end of file
R src/Core/NosSmooth.LocalBinding/HooksConfigBuilder.cs => src/Core/NosSmooth.LocalBinding/Hooks/HooksConfigBuilder.cs +37 -56
@@ 7,7 7,7 @@
using Microsoft.Extensions.DependencyInjection;
using NosSmooth.LocalBinding.Options;
-namespace NosSmooth.LocalBinding;
+namespace NosSmooth.LocalBinding.Hooks;
/// <summary>
/// Provides user-friendly builder for configuring
@@ 27,28 27,29 @@ namespace NosSmooth.LocalBinding;
/// </remarks>
public class HooksConfigBuilder
{
- private readonly HookOptionsBuilder _packetSendHook;
- private readonly HookOptionsBuilder _packetReceiveHook;
- private readonly HookOptionsBuilder _playerWalkHook;
- private readonly HookOptionsBuilder _petWalkHook;
- private readonly HookOptionsBuilder _entityFocusHook;
- private readonly HookOptionsBuilder _entityFollowHook;
- private readonly HookOptionsBuilder _entityUnfollowHook;
- private readonly HookOptionsBuilder _periodicHook;
+ private readonly HookOptionsBuilder<IPacketSendHook> _packetSendHook;
+ private readonly HookOptionsBuilder<IPacketReceiveHook> _packetReceiveHook;
+ private readonly HookOptionsBuilder<IPlayerWalkHook> _playerWalkHook;
+ private readonly HookOptionsBuilder<IPetWalkHook> _petWalkHook;
+ private readonly HookOptionsBuilder<IEntityFocusHook> _entityFocusHook;
+ private readonly HookOptionsBuilder<IEntityFollowHook> _entityFollowHook;
+ private readonly HookOptionsBuilder<IEntityUnfollowHook> _entityUnfollowHook;
+ private readonly HookOptionsBuilder<IPeriodicHook> _periodicHook;
/// <summary>
/// Initializes a new instance of the <see cref="HooksConfigBuilder"/> class.
/// </summary>
- internal HooksConfigBuilder()
+ /// <param name="options">The default options to build from.</param>
+ internal HooksConfigBuilder(HookManagerOptions options)
{
- _playerWalkHook = new HookOptionsBuilder(new CharacterBindingOptions().WalkHook);
- _packetSendHook = new HookOptionsBuilder(new NetworkBindingOptions().PacketSendHook);
- _packetReceiveHook = new HookOptionsBuilder(new NetworkBindingOptions().PacketReceiveHook);
- _petWalkHook = new HookOptionsBuilder(new PetManagerBindingOptions().PetWalkHook);
- _entityFocusHook = new HookOptionsBuilder(new UnitManagerBindingOptions().EntityFocusHook);
- _entityFollowHook = new HookOptionsBuilder(new CharacterBindingOptions().EntityFollowHook);
- _entityUnfollowHook = new HookOptionsBuilder(new CharacterBindingOptions().EntityUnfollowHook);
- _periodicHook = new HookOptionsBuilder(new PeriodicBindingOptions().PeriodicHook);
+ _packetSendHook = new HookOptionsBuilder<IPacketSendHook>(options.PacketSendHook);
+ _packetReceiveHook = new HookOptionsBuilder<IPacketReceiveHook>(options.PacketReceiveHook);
+ _playerWalkHook = new HookOptionsBuilder<IPlayerWalkHook>(options.PlayerWalkHook);
+ _petWalkHook = new HookOptionsBuilder<IPetWalkHook>(options.PetWalkHook);
+ _entityFocusHook = new HookOptionsBuilder<IEntityFocusHook>(options.EntityFocusHook);
+ _entityFollowHook = new HookOptionsBuilder<IEntityFollowHook>(options.EntityFollowHook);
+ _entityUnfollowHook = new HookOptionsBuilder<IEntityUnfollowHook>(options.EntityUnfollowHook);
+ _periodicHook = new HookOptionsBuilder<IPeriodicHook>(options.PeriodicHook);
}
/// <summary>
@@ 103,7 104,7 @@ public class HooksConfigBuilder
/// </summary>
/// <param name="configure">The configuring action.</param>
/// <returns>This builder.</returns>
- public HooksConfigBuilder HookPeriodic(Action<HookOptionsBuilder>? configure = default)
+ public HooksConfigBuilder HookPeriodic(Action<HookOptionsBuilder<IPeriodicHook>>? configure = default)
{
if (configure is not null)
{
@@ 125,7 126,7 @@ public class HooksConfigBuilder
/// </summary>
/// <param name="configure">The configuring action.</param>
/// <returns>This builder.</returns>
- public HooksConfigBuilder HookPacketSend(Action<HookOptionsBuilder>? configure = default)
+ public HooksConfigBuilder HookPacketSend(Action<HookOptionsBuilder<IPacketSendHook>>? configure = default)
{
if (configure is not null)
{
@@ 147,7 148,7 @@ public class HooksConfigBuilder
/// </summary>
/// <param name="configure">The configuring action.</param>
/// <returns>This builder.</returns>
- public HooksConfigBuilder HookPacketReceive(Action<HookOptionsBuilder>? configure = default)
+ public HooksConfigBuilder HookPacketReceive(Action<HookOptionsBuilder<IPacketReceiveHook>>? configure = default)
{
if (configure is not null)
{
@@ 169,7 170,7 @@ public class HooksConfigBuilder
/// </summary>
/// <param name="configure">The configuring action.</param>
/// <returns>This builder.</returns>
- public HooksConfigBuilder HookPlayerWalk(Action<HookOptionsBuilder>? configure = default)
+ public HooksConfigBuilder HookPlayerWalk(Action<HookOptionsBuilder<IPlayerWalkHook>>? configure = default)
{
if (configure is not null)
{
@@ 191,7 192,7 @@ public class HooksConfigBuilder
/// </summary>
/// <param name="configure">The configuring action.</param>
/// <returns>This builder.</returns>
- public HooksConfigBuilder HookPetWalk(Action<HookOptionsBuilder>? configure = default)
+ public HooksConfigBuilder HookPetWalk(Action<HookOptionsBuilder<IPetWalkHook>>? configure = default)
{
if (configure is not null)
{
@@ 213,7 214,7 @@ public class HooksConfigBuilder
/// </summary>
/// <param name="configure">The configuring action.</param>
/// <returns>This builder.</returns>
- public HooksConfigBuilder HookEntityFocus(Action<HookOptionsBuilder>? configure = default)
+ public HooksConfigBuilder HookEntityFocus(Action<HookOptionsBuilder<IEntityFocusHook>>? configure = default)
{
if (configure is not null)
{
@@ 235,7 236,7 @@ public class HooksConfigBuilder
/// </summary>
/// <param name="configure">The configuring action.</param>
/// <returns>This builder.</returns>
- public HooksConfigBuilder HookEntityFollow(Action<HookOptionsBuilder>? configure = default)
+ public HooksConfigBuilder HookEntityFollow(Action<HookOptionsBuilder<IEntityFollowHook>>? configure = default)
{
if (configure is not null)
{
@@ 257,7 258,7 @@ public class HooksConfigBuilder
/// </summary>
/// <param name="configure">The configuring action.</param>
/// <returns>This builder.</returns>
- public HooksConfigBuilder HookEntityUnfollow(Action<HookOptionsBuilder>? configure = default)
+ public HooksConfigBuilder HookEntityUnfollow(Action<HookOptionsBuilder<IEntityUnfollowHook>>? configure = default)
{
if (configure is not null)
{
@@ 278,38 279,18 @@ public class HooksConfigBuilder
/// <param name="serviceCollection">The service collection.</param>
internal void Apply(IServiceCollection serviceCollection)
{
- serviceCollection.Configure<CharacterBindingOptions>
+ serviceCollection.Configure<HookManagerOptions>
(
- characterOptions =>
+ o =>
{
- characterOptions.WalkHook = _playerWalkHook.Build();
- characterOptions.EntityFollowHook = _entityFollowHook.Build();
- characterOptions.EntityUnfollowHook = _entityUnfollowHook.Build();
- }
- );
-
- serviceCollection.Configure<NetworkBindingOptions>
- (
- characterOptions =>
- {
- characterOptions.PacketReceiveHook = _packetReceiveHook.Build();
- characterOptions.PacketSendHook = _packetSendHook.Build();
- }
- );
-
- serviceCollection.Configure<PetManagerBindingOptions>
- (
- characterOptions =>
- {
- characterOptions.PetWalkHook = _petWalkHook.Build();
- }
- );
-
- serviceCollection.Configure<UnitManagerBindingOptions>
- (
- characterOptions =>
- {
- characterOptions.EntityFocusHook = _entityFocusHook.Build();
+ o.PeriodicHook = _periodicHook.Build();
+ o.EntityFocusHook = _entityFocusHook.Build();
+ o.EntityFollowHook = _entityFollowHook.Build();
+ o.EntityUnfollowHook = _entityUnfollowHook.Build();
+ o.PacketSendHook = _packetSendHook.Build();
+ o.PacketReceiveHook = _packetReceiveHook.Build();
+ o.PetWalkHook = _petWalkHook.Build();
+ o.PlayerWalkHook = _playerWalkHook.Build();
}
);
}
A src/Core/NosSmooth.LocalBinding/Hooks/IEntityFocusHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/IEntityFocusHook.cs +42 -0
@@ 0,0 1,42 @@
+//
+// IEntityFocusHook.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.Structs;
+using Reloaded.Hooks.Definitions.X86;
+
+namespace NosSmooth.LocalBinding.Hooks;
+
+/// <summary>
+/// A hook of UnitManager.EntityFocus.
+/// </summary>
+public interface IEntityFocusHook : INostaleHook<IEntityFocusHook.EntityFocusDelegate,
+ IEntityFocusHook.EntityFocusWrapperDelegate, EntityEventArgs>
+{
+ /// <summary>
+ /// NosTale entity focus function to hook.
+ /// </summary>
+ /// <param name="unitManagerPtr">The unit manager object.</param>
+ /// <param name="entityPtr">The entity object.</param>
+ /// <returns>1 to proceed to NosTale function, 0 to block the call.</returns>
+ [Function
+ (
+ new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
+ FunctionAttribute.Register.eax,
+ FunctionAttribute.StackCleanup.Callee,
+ new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
+ )]
+ public delegate nuint EntityFocusDelegate(nuint unitManagerPtr, nuint entityPtr);
+
+ /// <summary>
+ /// Entity focus function.
+ /// </summary>
+ /// <remarks>
+ /// In case entity is null, unfocus any entity.
+ /// </remarks>
+ /// <param name="entity">The entity object.</param>
+ public delegate void EntityFocusWrapperDelegate(MapBaseObj? entity);
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/IEntityFollowHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/IEntityFollowHook.cs +50 -0
@@ 0,0 1,50 @@
+//
+// IEntityFollowHook.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.Structs;
+using Reloaded.Hooks.Definitions.X86;
+
+namespace NosSmooth.LocalBinding.Hooks;
+
+/// <summary>
+/// A hook of CharacterManager.EntityFollow.
+/// </summary>
+public interface IEntityFollowHook : INostaleHook<IEntityFollowHook.EntityFollowDelegate,
+ IEntityFollowHook.EntityFollowWrapperDelegate, EntityEventArgs>
+{
+ /// <summary>
+ /// NosTale entity follow function to hook.
+ /// </summary>
+ /// <param name="playerManagerPtr">The player manager object.</param>
+ /// <param name="entityPtr">The entity object.</param>
+ /// <param name="unknown1">Unknown 1. TODO.</param>
+ /// <param name="unknown2">Unknown 2. TODO.</param>
+ /// <returns>1 to proceed to NosTale function, 0 to block the call.</returns>
+ [Function
+ (
+ new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx, FunctionAttribute.Register.ecx },
+ FunctionAttribute.Register.eax,
+ FunctionAttribute.StackCleanup.Callee,
+ new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
+ )]
+ public delegate nuint EntityFollowDelegate
+ (
+ nuint playerManagerPtr,
+ nuint entityPtr,
+ int unknown1 = 0,
+ int unknown2 = 1
+ );
+
+ /// <summary>
+ /// Entity follow function.
+ /// </summary>
+ /// <remarks>
+ /// Does not support unfollow. For unfollow, use <see cref="IEntityUnfollowHook"/>.
+ /// </remarks>
+ /// <param name="entity">The entity object.</param>
+ public delegate void EntityFollowWrapperDelegate(MapBaseObj entity);
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/IEntityUnfollowHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/IEntityUnfollowHook.cs +37 -0
@@ 0,0 1,37 @@
+//
+// IEntityUnfollowHook.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 Reloaded.Hooks.Definitions.X86;
+
+namespace NosSmooth.LocalBinding.Hooks;
+
+/// <summary>
+/// A hook of CharacterManager.EntityFollow.
+/// </summary>
+public interface IEntityUnfollowHook : INostaleHook<IEntityUnfollowHook.EntityUnfollowDelegate,
+ IEntityUnfollowHook.EntityUnfollowWrapperDelegate, EntityEventArgs>
+{
+ /// <summary>
+ /// NosTale entity follow function to hook.
+ /// </summary>
+ /// <param name="playerManagerPtr">The player manager object.</param>
+ /// <param name="unknown">Unknown. TODO.</param>
+ /// <returns>1 to proceed to NosTale function, 0 to block the call.</returns>
+ [Function
+ (
+ new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
+ FunctionAttribute.Register.eax,
+ FunctionAttribute.StackCleanup.Callee,
+ new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
+ )]
+ public delegate nuint EntityUnfollowDelegate(nuint playerManagerPtr, int unknown = 0);
+
+ /// <summary>
+ /// Entity unfollow function.
+ /// </summary>
+ public delegate void EntityUnfollowWrapperDelegate();
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/IHookManager.cs => src/Core/NosSmooth.LocalBinding/Hooks/IHookManager.cs +142 -0
@@ 0,0 1,142 @@
+//
+// IHookManager.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.Hooks;
+
+/// <summary>
+/// A manager holding all NosTale hooks with actions to execute on all of them.
+/// </summary>
+public interface IHookManager
+{
+ /// <summary>
+ /// A name of packet send hook.
+ /// </summary>
+ public const string PacketSendName = "NetworkManager.PacketSend";
+
+ /// <summary>
+ /// A name of packet receive hook.
+ /// </summary>
+ public const string PacketReceiveName = "NetworkManager.PacketReceive";
+
+ /// <summary>
+ /// A name of character walk hook.
+ /// </summary>
+ public const string CharacterWalkName = "CharacterManager.Walk";
+
+ /// <summary>
+ /// A name of pet walk hook.
+ /// </summary>
+ public const string PetWalkName = "PetManager.Walk";
+
+ /// <summary>
+ /// A name of entity follow hook.
+ /// </summary>
+ public const string EntityFollowName = "CharacterManager.EntityFollow";
+
+ /// <summary>
+ /// A name of entity unfollow hook.
+ /// </summary>
+ public const string EntityUnfollowName = "CharacterManager.EntityUnfollow";
+
+ /// <summary>
+ /// A name of entity focus hook.
+ /// </summary>
+ public const string EntityFocusName = "UnitManager.EntityFocus";
+
+ /// <summary>
+ /// A name of periodic hook.
+ /// </summary>
+ public const string PeriodicName = "Periodic";
+
+ /// <summary>
+ /// Gets the packet send hook.
+ /// </summary>
+ public IPacketSendHook PacketSend { get; }
+
+ /// <summary>
+ /// Gets the packet receive hook.
+ /// </summary>
+ public IPacketReceiveHook PacketReceive { get; }
+
+ /// <summary>
+ /// Gets the player walk hook.
+ /// </summary>
+ public IPlayerWalkHook PlayerWalk { get; }
+
+ /// <summary>
+ /// Gets the entity follow hook.
+ /// </summary>
+ public IEntityFollowHook EntityFollow { get; }
+
+ /// <summary>
+ /// Gets the entity unfollow hook.
+ /// </summary>
+ public IEntityUnfollowHook EntityUnfollow { get; }
+
+ /// <summary>
+ /// Gets the player walk hook.
+ /// </summary>
+ public IPetWalkHook PetWalk { get; }
+
+ /// <summary>
+ /// Gets the entity focus hook.
+ /// </summary>
+ public IEntityFocusHook EntityFocus { get; }
+
+ /// <summary>
+ /// Gets the periodic function hook.
+ /// </summary>
+ /// <remarks>
+ /// May be any function that is called periodically.
+ /// This is used for synchronizing using <see cref="NosThreadSynchronizer"/>.
+ /// </remarks>
+ public IPeriodicHook Periodic { get; }
+
+ /// <summary>
+ /// Gets all of the hooks.
+ /// </summary>
+ public IReadOnlyList<INostaleHook> Hooks { get; }
+
+ /// <summary>
+ /// Initializes all hooks.
+ /// </summary>
+ /// <param name="bindingManager">The binding manager.</param>
+ /// <param name="browserManager">The browser manager.</param>
+ /// <returns>A result that may or may not have failed.</returns>
+ public IResult Initialize(NosBindingManager bindingManager, NosBrowserManager browserManager);
+
+ /// <summary>
+ /// Enable hooks from the given list.
+ /// </summary>
+ /// <remarks>
+ /// Use constants from <see cref="IHookManager"/>,
+ /// such as IHookManager.PacketSendName.
+ /// </remarks>
+ /// <param name="names">The hooks to enable.</param>
+ public void Enable(IEnumerable<string> names);
+
+ /// <summary>
+ /// Disable hooks from the given list.
+ /// </summary>
+ /// <remarks>
+ /// Use constants from <see cref="IHookManager"/>,
+ /// such as IHookManager.PacketSendName.
+ /// </remarks>
+ /// <param name="names">The hooks to disable.</param>
+ public void Disable(IEnumerable<string> names);
+
+ /// <summary>
+ /// Disable all hooks.
+ /// </summary>
+ public void DisableAll();
+
+ /// <summary>
+ /// Enable all hooks.
+ /// </summary>
+ public void EnableAll();
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/INostaleHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/INostaleHook.cs +74 -0
@@ 0,0 1,74 @@
+//
+// INostaleHook.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.Hooks;
+
+/// <summary>
+/// A hook of a NosTale function.
+/// </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 interface INostaleHook<TFunction, TWrapperFunction, TEventArgs> : INostaleHook
+ where TFunction : Delegate
+ where TWrapperFunction : Delegate
+ where TEventArgs : System.EventArgs
+{
+ /// <summary>
+ /// Gets the wrapper function delegate.
+ /// </summary>
+ public TWrapperFunction WrapperFunction { get; }
+
+ /// <summary>
+ /// Gets the original function delegate.
+ /// </summary>
+ public TFunction OriginalFunction { get; }
+
+ /// <summary>
+ /// An event fired in case the function is called from NosTale.
+ /// </summary>
+ public event EventHandler<TEventArgs>? Called;
+}
+
+/// <summary>
+/// A hook of a NosTale function.
+/// </summary>
+public interface INostaleHook
+{
+ /// <summary>
+ /// Gets the name of the function.
+ /// </summary>
+ /// <remarks>
+ /// Usually denoted as Type.Method,
+ /// for example PlayerManager.Walk.
+ /// </remarks>
+ public string Name { get; }
+
+ /// <summary>
+ /// Gets whether the hook is currently enabled.
+ /// </summary>
+ public bool IsEnabled { get; }
+
+ /// <summary>
+ /// Enable the hook.
+ /// </summary>
+ /// <remarks>
+ /// If it already is enabled, does nothing.
+ /// </remarks>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Result Enable();
+
+ /// <summary>
+ /// Disable the hook.
+ /// </summary>
+ /// <remarks>
+ /// If it already is disabled, does nothing.
+ /// </remarks>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Result Disable();
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/IPacketReceiveHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/IPacketReceiveHook.cs +39 -0
@@ 0,0 1,39 @@
+//
+// IPacketReceiveHook.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.Objects;
+using Reloaded.Hooks.Definitions.X86;
+
+namespace NosSmooth.LocalBinding.Hooks;
+
+/// <summary>
+/// A hook of NetworkManager.PacketSend.
+/// </summary>
+public interface IPacketReceiveHook : INostaleHook<IPacketReceiveHook.PacketReceiveDelegate,
+ IPacketReceiveHook.PacketReceiveWrapperDelegate, PacketEventArgs>
+{
+ /// <summary>
+ /// NosTale packet send function to hook.
+ /// </summary>
+ /// <param name="packetObject">The packet object.</param>
+ /// <param name="packetString">Pointer to <see cref="NostaleStringA"/>.Get() object.</param>
+ /// <returns>1 to proceed to NosTale function, 0 to block the call.</returns>
+ [Function
+ (
+ new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
+ FunctionAttribute.Register.eax,
+ FunctionAttribute.StackCleanup.Callee,
+ new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
+ )]
+ public delegate nuint PacketReceiveDelegate(nuint packetObject, nuint packetString);
+
+ /// <summary>
+ /// Packet send function.
+ /// </summary>
+ /// <param name="packetString">The string to send.</param>
+ public delegate void PacketReceiveWrapperDelegate(string packetString);
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/IPacketSendHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/IPacketSendHook.cs +38 -0
@@ 0,0 1,38 @@
+//
+// IPacketSendHook.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.Objects;
+using Reloaded.Hooks.Definitions.X86;
+
+namespace NosSmooth.LocalBinding.Hooks;
+
+/// <summary>
+/// A hook of NetworkManager.PacketSend.
+/// </summary>
+public interface IPacketSendHook : INostaleHook<IPacketSendHook.PacketSendDelegate, IPacketSendHook.PacketSendWrapperDelegate, PacketEventArgs>
+{
+ /// <summary>
+ /// NosTale packet send function to hook.
+ /// </summary>
+ /// <param name="packetObject">The packet object.</param>
+ /// <param name="packetString">Pointer to <see cref="NostaleStringA"/>.Get() object.</param>
+ /// <returns>1 to proceed to NosTale function, 0 to block the call.</returns>
+ [Function
+ (
+ new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
+ FunctionAttribute.Register.eax,
+ FunctionAttribute.StackCleanup.Callee,
+ new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
+ )]
+ public delegate nuint PacketSendDelegate(nuint packetObject, nuint packetString);
+
+ /// <summary>
+ /// Packet send function.
+ /// </summary>
+ /// <param name="packetString">The string to send.</param>
+ public delegate void PacketSendWrapperDelegate(string packetString);
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/IPeriodicHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/IPeriodicHook.cs +29 -0
@@ 0,0 1,29 @@
+//
+// IPeriodicHook.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 Reloaded.Hooks.Definitions.X86;
+
+namespace NosSmooth.LocalBinding.Hooks;
+
+/// <summary>
+/// A hook of a periodic function,
+/// preferably called every frame.
+/// </summary>
+public interface
+ IPeriodicHook : INostaleHook<IPeriodicHook.PeriodicDelegate, IPeriodicHook.PeriodicDelegate, System.EventArgs>
+{
+ /// <summary>
+ /// NosTale periodic function to hook.
+ /// </summary>
+ [Function
+ (
+ new FunctionAttribute.Register[0],
+ FunctionAttribute.Register.eax,
+ FunctionAttribute.StackCleanup.Callee,
+ new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
+ )]
+ public delegate void PeriodicDelegate();
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/IPetWalkHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/IPetWalkHook.cs +43 -0
@@ 0,0 1,43 @@
+//
+// IPetWalkHook.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.Structs;
+using Reloaded.Hooks.Definitions.X86;
+
+namespace NosSmooth.LocalBinding.Hooks;
+
+/// <summary>
+/// A hook of PetManager.Walk.
+/// </summary>
+public interface IPetWalkHook : INostaleHook<IPetWalkHook.PetWalkDelegate, IPetWalkHook.PetWalkWrapperDelegate, PetWalkEventArgs>
+{
+ /// <summary>
+ /// NosTale walk function to hook.
+ /// </summary>
+ /// <param name="petManagerPtr">The pointer to a pet manager object.</param>
+ /// <param name="position">The position to walk to. First 4 bits are x (most significant), next 4 bits are y.</param>
+ /// <param name="unknown0">Unknown 1. TODO.</param>
+ /// <param name="unknown1">Unknown 2. TODO.</param>
+ /// <returns>1 to proceed to NosTale function, 0 to block the call.</returns>
+ [Function
+ (
+ new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx, FunctionAttribute.Register.ecx },
+ FunctionAttribute.Register.eax,
+ FunctionAttribute.StackCleanup.Callee,
+ new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
+ )]
+ public delegate nuint PetWalkDelegate(nuint petManagerPtr, int position, short unknown0 = 0, int unknown1 = 1);
+
+ /// <summary>
+ /// Pet walk function.
+ /// </summary>
+ /// <param name="manager">The pet manager.</param>
+ /// <param name="x">The x coordinate to walk to.</param>
+ /// <param name="y">The y coordinate to walk to.</param>
+ /// <returns>A bool signaling whether the operation was successful.</returns>
+ public delegate bool PetWalkWrapperDelegate(PetManager manager, ushort x, ushort y);
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/IPlayerWalkHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/IPlayerWalkHook.cs +41 -0
@@ 0,0 1,41 @@
+//
+// IPlayerWalkHook.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 Reloaded.Hooks.Definitions.X86;
+
+namespace NosSmooth.LocalBinding.Hooks;
+
+/// <summary>
+/// A hook of CharacterManager.Walk.
+/// </summary>
+public interface IPlayerWalkHook : INostaleHook<IPlayerWalkHook.WalkDelegate, IPlayerWalkHook.WalkWrapperDelegate, WalkEventArgs>
+{
+ /// <summary>
+ /// NosTale walk function to hook.
+ /// </summary>
+ /// <param name="playerManagerPtr">The pointer to a player manager object.</param>
+ /// <param name="position">The position to walk to. First 4 bits are x (most significant), next 4 bits are y.</param>
+ /// <param name="unknown0">Unknown 1. TODO.</param>
+ /// <param name="unknown1">Unknown 2. TODO.</param>
+ /// <returns>1 to proceed to NosTale function, 0 to block the call.</returns>
+ [Function
+ (
+ new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx, FunctionAttribute.Register.ecx },
+ FunctionAttribute.Register.eax,
+ FunctionAttribute.StackCleanup.Callee,
+ new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
+ )]
+ public delegate nuint WalkDelegate(nuint playerManagerPtr, int position, short unknown0 = 0, int unknown1 = 1);
+
+ /// <summary>
+ /// Player walk function.
+ /// </summary>
+ /// <param name="x">The x coordinate to go to.</param>
+ /// <param name="y">The y coordinate to go to.</param>
+ /// <returns>A bool signaling whether the operation was successful.</returns>
+ public delegate bool WalkWrapperDelegate(ushort x, ushort y);
+}
A src/Core/NosSmooth.LocalBinding/Hooks/Implementations/CancelableNostaleHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/CancelableNostaleHook.cs +135 -0
@@ 0,0 1,135 @@
+//
+// CancelableNostaleHook.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 System.Diagnostics;
+using NosSmooth.LocalBinding.Extensions;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Hooks.Implementations;
+
+/// <summary>
+/// A hook of a NosTale function
+/// that may be cancelled (not propagated to NosTale) when fired.
+/// </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 abstract class CancelableNostaleHook<TFunction, TWrapperFunction, TEventArgs> : INostaleHook<TFunction, TWrapperFunction, TEventArgs>
+ where TFunction : Delegate
+ where TWrapperFunction : Delegate
+ where TEventArgs : CancelEventArgs
+{
+ /// <summary>
+ /// Creates the hook instance.
+ /// </summary>
+ /// <param name="bindingManager">The binding manager to create the hook.</param>
+ /// <param name="new">Create new hook object.</param>
+ /// <param name="detour">The function that obtains detour.</param>
+ /// <param name="options">The options.</param>
+ /// <typeparam name="T">The hook type.</typeparam>
+ /// <returns>The hook, or failed result.</returns>
+ protected static Result<T> CreateHook<T>(NosBindingManager bindingManager, Func<T> @new, Func<T, TFunction> detour, HookOptions options)
+ where T : CancelableNostaleHook<TFunction, TWrapperFunction, TEventArgs>
+ {
+ var nosHook = @new();
+
+ var hookResult = bindingManager.CreateCustomAsmHookFromPattern<TFunction>
+ (nosHook.Name, detour(nosHook), options);
+ if (!hookResult.IsDefined(out var hook))
+ {
+ return Result<T>.FromError(hookResult);
+ }
+
+ nosHook.Hook = hook;
+ return nosHook;
+ }
+
+ private TFunction? _originalFunction;
+
+ /// <summary>
+ /// Gets the hook.
+ /// </summary>
+ protected NosAsmHook<TFunction> Hook { get; set; } = null!;
+
+ /// <summary>
+ /// Set to true at start of WrapWithCalling, set to false at end of WrapWithCalling.
+ /// </summary>
+ protected bool CallingFromNosSmooth { get; set; }
+
+ /// <inheritdoc />
+ public bool IsEnabled => Hook.Hook.IsEnabled;
+
+ /// <inheritdoc />
+ public abstract TWrapperFunction WrapperFunction { get; }
+
+ /// <inheritdoc/>
+ public TFunction OriginalFunction
+ {
+ get
+ {
+ if (_originalFunction is null)
+ {
+ _originalFunction = WrapWithCalling(Hook.OriginalFunction.GetWrapper());
+ }
+
+ return _originalFunction;
+ }
+ }
+
+ /// <inheritdoc />
+ public event EventHandler<TEventArgs>? Called;
+
+ /// <inheritdoc />
+ public abstract string Name { get; }
+
+ /// <inheritdoc />
+ public Result Enable()
+ {
+ Hook.Hook.EnableOrActivate();
+ return Result.FromSuccess();
+ }
+
+ /// <inheritdoc />
+ public Result Disable()
+ {
+ Hook.Hook.Disable();
+ return Result.FromSuccess();
+ }
+
+ /// <summary>
+ /// Wrap the target function with setting CallingFromNosSmooth to true.
+ /// </summary>
+ /// <example>
+ /// protected MyFun WrapWithCalling(MyFun fun) {
+ /// return (a, b) => {
+ /// CallingFromNosSmooth = true;
+ /// var res = fun(a, b);
+ /// CallingFromNosSmooth = false;
+ /// return res;
+ /// }
+ /// }.
+ /// </example>
+ /// <param name="function">The function to wrap.</param>
+ /// <returns>The wrapped function.</returns>
+ protected abstract TFunction WrapWithCalling(TFunction function);
+
+ /// <summary>
+ /// Calls the event, returns whether to proceed.
+ /// </summary>
+ /// <param name="args">The event arguments.</param>
+ /// <returns>Whether to proceed.</returns>
+ protected nuint HandleCall(TEventArgs args)
+ {
+ if (CallingFromNosSmooth)
+ { // this is a call from NosSmooth, do not invoke the event.
+ return 1;
+ }
+
+ Called?.Invoke(this, args);
+ return args.Cancel ? 0 : (nuint)1;
+ }
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFocusHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFocusHook.cs +75 -0
@@ 0,0 1,75 @@
+//
+// 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.Structs;
+using Reloaded.Memory.Sources;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Hooks.Implementations;
+
+/// <summary>
+/// A hook of NetworkManager.EntityFocus.
+/// </summary>
+internal class EntityFocusHook : CancelableNostaleHook<IEntityFocusHook.EntityFocusDelegate,
+ IEntityFocusHook.EntityFocusWrapperDelegate, EntityEventArgs>, IEntityFocusHook
+{
+ /// <summary>
+ /// Create the packet send hook.
+ /// </summary>
+ /// <param name="bindingManager">The binding manager.</param>
+ /// <param name="browserManager">The browser manager.</param>
+ /// <param name="options">The options for the binding.</param>
+ /// <returns>A packet send hook or an error.</returns>
+ public static Result<EntityFocusHook> Create
+ (NosBindingManager bindingManager, NosBrowserManager browserManager, HookOptions<IEntityFocusHook> options)
+ {
+ var hook = CreateHook
+ (
+ bindingManager,
+ () => new EntityFocusHook(browserManager.UnitManager),
+ hook => hook.Detour,
+ options
+ );
+
+ return hook;
+ }
+
+ private readonly UnitManager _unitManager;
+
+ private EntityFocusHook(UnitManager unitManager)
+ {
+ _unitManager = unitManager;
+ }
+
+ /// <inheritdoc />
+ public override string Name => IHookManager.EntityFocusName;
+
+ /// <inheritdoc />
+ public override IEntityFocusHook.EntityFocusWrapperDelegate WrapperFunction => (entity) => OriginalFunction
+ (_unitManager.Address, entity?.Address ?? 0);
+
+ /// <inheritdoc />
+ protected override IEntityFocusHook.EntityFocusDelegate WrapWithCalling(IEntityFocusHook.EntityFocusDelegate function)
+ => (unitManagerPtr, entityPtr) =>
+ {
+ CallingFromNosSmooth = true;
+ var res = function(unitManagerPtr, entityPtr);
+ CallingFromNosSmooth = false;
+ return res;
+ };
+
+ private nuint Detour
+ (
+ nuint unitManagerPtr,
+ nuint entityPtr
+ )
+ {
+ var entity = new MapBaseObj(_unitManager.Memory, entityPtr);
+ var entityArgs = new EntityEventArgs(entity);
+ return HandleCall(entityArgs);
+ }
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFollowHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityFollowHook.cs +76 -0
@@ 0,0 1,76 @@
+//
+// 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.Structs;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Hooks.Implementations;
+
+/// <summary>
+/// A hook of NetworkManager.EntityFollow.
+/// </summary>
+internal class EntityFollowHook : CancelableNostaleHook<IEntityFollowHook.EntityFollowDelegate,
+ IEntityFollowHook.EntityFollowWrapperDelegate, EntityEventArgs>, IEntityFollowHook
+{
+ /// <summary>
+ /// Create the packet send hook.
+ /// </summary>
+ /// <param name="bindingManager">The binding manager.</param>
+ /// <param name="browserManager">The browser manager.</param>
+ /// <param name="options">The options for the binding.</param>
+ /// <returns>A packet send hook or an error.</returns>
+ public static Result<EntityFollowHook> Create
+ (NosBindingManager bindingManager, NosBrowserManager browserManager, HookOptions<IEntityFollowHook> options)
+ {
+ var hook = CreateHook
+ (
+ bindingManager,
+ () => new EntityFollowHook(browserManager.PlayerManager),
+ hook => hook.Detour,
+ options
+ );
+
+ return hook;
+ }
+
+ private readonly PlayerManager _playerManager;
+
+ private EntityFollowHook(PlayerManager playerManager)
+ {
+ _playerManager = playerManager;
+ }
+
+ /// <inheritdoc />
+ public override string Name => IHookManager.EntityFollowName;
+
+ /// <inheritdoc />
+ public override IEntityFollowHook.EntityFollowWrapperDelegate WrapperFunction => (entity) => OriginalFunction
+ (_playerManager.Address, entity?.Address ?? 0);
+
+ /// <inheritdoc />
+ protected override IEntityFollowHook.EntityFollowDelegate WrapWithCalling(IEntityFollowHook.EntityFollowDelegate function)
+ => (playerManagerPtr, entityPtr, un0, un1) =>
+ {
+ CallingFromNosSmooth = true;
+ var res = function(playerManagerPtr, entityPtr, un0, un1);
+ CallingFromNosSmooth = false;
+ return res;
+ };
+
+ private nuint Detour
+ (
+ nuint playerManagerPtr,
+ nuint entityPtr,
+ int unknown1 = 0,
+ int unknown2 = 1
+ )
+ {
+ var entity = new MapBaseObj(_playerManager.Memory, entityPtr);
+ var entityArgs = new EntityEventArgs(entity);
+ return HandleCall(entityArgs);
+ }
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityUnfollowHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/EntityUnfollowHook.cs +70 -0
@@ 0,0 1,70 @@
+//
+// 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.Structs;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Hooks.Implementations;
+
+/// <summary>
+/// A hook of NetworkManager.EntityUnfollow.
+/// </summary>
+internal class EntityUnfollowHook : CancelableNostaleHook<IEntityUnfollowHook.EntityUnfollowDelegate,
+ IEntityUnfollowHook.EntityUnfollowWrapperDelegate, EntityEventArgs>, IEntityUnfollowHook
+{
+ /// <summary>
+ /// Create the packet send hook.
+ /// </summary>
+ /// <param name="bindingManager">The binding manager.</param>
+ /// <param name="browserManager">The browser manager.</param>
+ /// <param name="options">The options for the binding.</param>
+ /// <returns>A packet send hook or an error.</returns>
+ public static Result<EntityUnfollowHook> Create
+ (NosBindingManager bindingManager, NosBrowserManager browserManager, HookOptions<IEntityUnfollowHook> options)
+ {
+ var hook = CreateHook
+ (
+ bindingManager,
+ () => new EntityUnfollowHook(browserManager.PlayerManager),
+ hook => hook.Detour,
+ options
+ );
+
+ return hook;
+ }
+
+ private readonly PlayerManager _playerManager;
+
+ private EntityUnfollowHook(PlayerManager playerManager)
+ {
+ _playerManager = playerManager;
+ }
+
+ /// <inheritdoc />
+ public override string Name => IHookManager.EntityUnfollowName;
+
+ /// <inheritdoc />
+ public override IEntityUnfollowHook.EntityUnfollowWrapperDelegate WrapperFunction
+ => () => OriginalFunction(_playerManager.Address);
+
+ /// <inheritdoc />
+ protected override IEntityUnfollowHook.EntityUnfollowDelegate WrapWithCalling(IEntityUnfollowHook.EntityUnfollowDelegate function)
+ => (playerManagerPtr, un) =>
+ {
+ CallingFromNosSmooth = true;
+ var res = function(playerManagerPtr, un);
+ CallingFromNosSmooth = false;
+ return res;
+ };
+
+ private nuint Detour
+ (
+ nuint playerManagerPtr,
+ int unknown = 0
+ )
+ => HandleCall(new EntityEventArgs(null));
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/Implementations/HookManager.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/HookManager.cs +167 -0
@@ 0,0 1,167 @@
+//
+// 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;
+
+/// <inheritdoc />
+internal class HookManager : IHookManager
+{
+ private readonly HookManagerOptions _options;
+ private List<INostaleHook> _hooks;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HookManager"/> class.
+ /// </summary>
+ /// <param name="options">The hook manager options.</param>
+ public HookManager(IOptions<HookManagerOptions> options)
+ {
+ _options = options.Value;
+ _hooks = new List<INostaleHook>();
+ }
+
+ /// <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.AsReadOnly();
+
+ /// <inheritdoc/>
+ public IResult Initialize(NosBindingManager bindingManager, NosBrowserManager browserManager)
+ {
+ 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>(T original)
+ where T : INostaleHook
+ {
+ return original;
+ }
+
+ private IResult HandleResults(params Func<Result<INostaleHook>>[] functions)
+ {
+ List<IResult> errorResults = new List<IResult>();
+ foreach (var func in functions)
+ {
+ try
+ {
+ var result = func();
+ if (result.IsSuccess)
+ {
+ _hooks.Add(result.Entity);
+ }
+ else
+ {
+ errorResults.Add(result);
+ }
+ }
+ catch (Exception e)
+ {
+ errorResults.Add((Result)e);
+ }
+
+ }
+
+ return errorResults.Count switch
+ {
+ 0 => Result.FromSuccess(),
+ 1 => errorResults[0],
+ _ => (Result)new AggregateError(errorResults)
+ };
+ }
+
+ /// <inheritdoc/>
+ public void Enable(IEnumerable<string> names)
+ {
+ foreach (var hook in Hooks
+ .Where(x => names.Contains(x.Name)))
+ {
+ hook.Enable();
+ }
+ }
+
+ /// <inheritdoc/>
+ public void Disable(IEnumerable<string> names)
+ {
+ foreach (var hook in Hooks
+ .Where(x => names.Contains(x.Name)))
+ {
+ hook.Disable();
+ }
+ }
+
+ /// <inheritdoc/>
+ public void DisableAll()
+ {
+ foreach (var hook in Hooks)
+ {
+ hook.Disable();
+ }
+ }
+
+ /// <inheritdoc/>
+ public void EnableAll()
+ {
+ foreach (var hook in Hooks)
+ {
+ hook.Enable();
+ }
+ }
+
+ private T GetHook<T>(string name)
+ where T : INostaleHook
+ {
+ var hook = _hooks.FirstOrDefault(x => x.Name == name);
+
+ if (hook is null || hook 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
R src/Core/NosSmooth.LocalBinding/NosAsmHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/NosAsmHook.cs +1 -1
@@ 6,7 6,7 @@
using Reloaded.Hooks.Definitions;
-namespace NosSmooth.LocalBinding;
+namespace NosSmooth.LocalBinding.Hooks.Implementations;
/// <summary>
/// An assembly hook data.
A src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketReceiveHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketReceiveHook.cs +81 -0
@@ 0,0 1,81 @@
+//
+// 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 System.Runtime.InteropServices;
+using NosSmooth.LocalBinding.EventArgs;
+using NosSmooth.LocalBinding.Objects;
+using NosSmooth.LocalBinding.Structs;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Hooks.Implementations;
+
+/// <summary>
+/// A hook of NetworkManager.PacketSend.
+/// </summary>
+internal class PacketReceiveHook : CancelableNostaleHook<IPacketReceiveHook.PacketReceiveDelegate,
+ IPacketReceiveHook.PacketReceiveWrapperDelegate, PacketEventArgs>, IPacketReceiveHook
+{
+ /// <summary>
+ /// Create the packet send hook.
+ /// </summary>
+ /// <param name="bindingManager">The binding manager.</param>
+ /// <param name="browserManager">The browser manager.</param>
+ /// <param name="options">The options for the binding.</param>
+ /// <returns>A packet send hook or an error.</returns>
+ public static Result<PacketReceiveHook> Create
+ (NosBindingManager bindingManager, NosBrowserManager browserManager, HookOptions<IPacketReceiveHook> options)
+ {
+ var hook = CreateHook
+ (
+ bindingManager,
+ () => new PacketReceiveHook(browserManager.NetworkManager),
+ (hook) => hook.Detour,
+ options
+ );
+
+ return hook;
+ }
+
+ private NetworkManager _networkManager;
+
+ private PacketReceiveHook(NetworkManager networkManager)
+ {
+ _networkManager = networkManager;
+ }
+
+ /// <inheritdoc />
+ 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());
+ };
+
+ /// <inheritdoc />
+ protected override IPacketReceiveHook.PacketReceiveDelegate WrapWithCalling(IPacketReceiveHook.PacketReceiveDelegate function)
+ => (packetObject, packetString) =>
+ {
+ CallingFromNosSmooth = true;
+ var res = function(packetObject, packetString);
+ CallingFromNosSmooth = false;
+ return res;
+ };
+
+ private nuint Detour(nuint packetObject, nuint packetString)
+ {
+ var packet = Marshal.PtrToStringAnsi((IntPtr)packetString);
+ if (packet is null)
+ { // ?
+ return 1;
+ }
+
+ var packetArgs = new PacketEventArgs(packet);
+ return HandleCall(packetArgs);
+ }
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketSendHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PacketSendHook.cs +82 -0
@@ 0,0 1,82 @@
+//
+// 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 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;
+
+/// <summary>
+/// A hook of NetworkManager.PacketSend.
+/// </summary>
+internal class PacketSendHook : CancelableNostaleHook<IPacketSendHook.PacketSendDelegate,
+ IPacketSendHook.PacketSendWrapperDelegate, PacketEventArgs>, IPacketSendHook
+{
+ /// <summary>
+ /// Create the packet send hook.
+ /// </summary>
+ /// <param name="bindingManager">The binding manager.</param>
+ /// <param name="browserManager">The browser manager.</param>
+ /// <param name="options">The options for the binding.</param>
+ /// <returns>A packet send hook or an error.</returns>
+ public static Result<PacketSendHook> Create
+ (NosBindingManager bindingManager, NosBrowserManager browserManager, HookOptions<IPacketSendHook> options)
+ {
+ var hook = CreateHook
+ (
+ bindingManager,
+ () => new PacketSendHook(browserManager.NetworkManager),
+ (sendHook) => sendHook.Detour,
+ options
+ );
+
+ return hook;
+ }
+
+ private PacketSendHook(NetworkManager networkManager)
+ {
+ _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());
+ };
+
+ /// <inheritdoc />
+ protected override IPacketSendHook.PacketSendDelegate WrapWithCalling(IPacketSendHook.PacketSendDelegate function)
+ => (packetObject, packetString) =>
+ {
+ CallingFromNosSmooth = true;
+ var res = function(packetObject, packetString);
+ CallingFromNosSmooth = false;
+ return res;
+ };
+
+ private nuint Detour(nuint packetObject, nuint packetString)
+ {
+ var packet = Marshal.PtrToStringAnsi((IntPtr)packetString);
+ if (packet is null)
+ { // ?
+ return 1;
+ }
+
+ var packetArgs = new PacketEventArgs(packet);
+ return HandleCall(packetArgs);
+ }
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PeriodicHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PeriodicHook.cs +77 -0
@@ 0,0 1,77 @@
+//
+// 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 System.Diagnostics;
+using NosSmooth.LocalBinding.Extensions;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Hooks.Implementations;
+
+/// <inheritdoc />
+internal class PeriodicHook : IPeriodicHook
+{
+ /// <summary>
+ /// Create the periodic hook.
+ /// </summary>
+ /// <param name="bindingManager">The binding manager.</param>
+ /// <param name="options">The options for the binding.</param>
+ /// <returns>A packet send hook or an error.</returns>
+ public static Result<PeriodicHook> Create(NosBindingManager bindingManager, HookOptions<IPeriodicHook> options)
+ {
+ var periodicHook = new PeriodicHook();
+
+ var hookResult = bindingManager.CreateCustomAsmHookFromPattern<IPeriodicHook.PeriodicDelegate>
+ (periodicHook.Name, periodicHook.Detour, options, false);
+ if (!hookResult.IsDefined(out var hook))
+ {
+ return Result<PeriodicHook>.FromError(hookResult);
+ }
+
+ periodicHook._hook = hook;
+ return periodicHook;
+ }
+
+ private PeriodicHook()
+ {
+ }
+
+ private NosAsmHook<IPeriodicHook.PeriodicDelegate> _hook = null!;
+
+ /// <inheritdoc />
+ public string Name => IHookManager.PeriodicName;
+
+ /// <inheritdoc />
+ public bool IsEnabled => _hook.Hook.IsEnabled;
+
+ /// <inheritdoc />
+ public IPeriodicHook.PeriodicDelegate WrapperFunction => OriginalFunction;
+
+ /// <inheritdoc/>
+ public IPeriodicHook.PeriodicDelegate OriginalFunction => throw new InvalidOperationException
+ ("Calling NosTale periodic function from NosSmooth is not allowed.");
+
+ /// <inheritdoc />
+ public event EventHandler<System.EventArgs>? Called;
+
+ /// <inheritdoc />
+ public Result Enable()
+ {
+ _hook.Hook.EnableOrActivate();
+ return Result.FromSuccess();
+ }
+
+ /// <inheritdoc />
+ public Result Disable()
+ {
+ _hook.Hook.Disable();
+ return Result.FromSuccess();
+ }
+
+ private void Detour()
+ {
+ Called?.Invoke(this, System.EventArgs.Empty);
+ }
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PetWalkHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PetWalkHook.cs +83 -0
@@ 0,0 1,83 @@
+//
+// 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.Structs;
+using Reloaded.Memory.Sources;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Hooks.Implementations;
+
+/// <summary>
+/// A hook of NetworkManager.PetWalk.
+/// </summary>
+internal class PetWalkHook : CancelableNostaleHook<IPetWalkHook.PetWalkDelegate,
+ IPetWalkHook.PetWalkWrapperDelegate, PetWalkEventArgs>, IPetWalkHook
+{
+ /// <summary>
+ /// Create the packet send hook.
+ /// </summary>
+ /// <param name="bindingManager">The binding manager.</param>
+ /// <param name="options">The options for the binding.</param>
+ /// <returns>A packet send hook or an error.</returns>
+ public static Result<PetWalkHook> Create
+ (NosBindingManager bindingManager, HookOptions<IPetWalkHook> options)
+ {
+ var hook = CreateHook
+ (
+ bindingManager,
+ () => new PetWalkHook(bindingManager.Memory),
+ hook => hook.Detour,
+ options
+ );
+
+ return hook;
+ }
+
+ private IMemory _memory;
+
+ private PetWalkHook(IMemory memory)
+ {
+ _memory = memory;
+ }
+
+ /// <inheritdoc />
+ public override string Name => IHookManager.PetWalkName;
+
+ /// <inheritdoc />
+ public override IPetWalkHook.PetWalkWrapperDelegate WrapperFunction
+ => (p, x, y) => OriginalFunction(p.Address, (y << 16) | x) == 1;
+
+ /// <inheritdoc />
+ protected override IPetWalkHook.PetWalkDelegate WrapWithCalling(IPetWalkHook.PetWalkDelegate function)
+ =>
+ (
+ petManagerPtr,
+ position,
+ un0,
+ un1
+ ) =>
+ {
+ CallingFromNosSmooth = true;
+ var res = function(petManagerPtr, position, un0, un1);
+ CallingFromNosSmooth = false;
+ return res;
+ };
+
+ private nuint Detour
+ (
+ nuint petManagerPtr,
+ int position,
+ short un0,
+ int un1
+ )
+ {
+ var petManager = new PetManager(_memory, petManagerPtr);
+ var walkArgs = new PetWalkEventArgs
+ (petManager, (ushort)(position & 0xFFFF), (ushort)((position >> 16) & 0xFFFF));
+ return HandleCall(walkArgs);
+ }
+}<
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PlayerWalkHook.cs => src/Core/NosSmooth.LocalBinding/Hooks/Implementations/PlayerWalkHook.cs +84 -0
@@ 0,0 1,84 @@
+//
+// 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.Structs;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Hooks.Implementations;
+
+/// <summary>
+/// A hook of NetworkManager.PlayerWalk.
+/// </summary>
+internal class PlayerWalkHook : CancelableNostaleHook<IPlayerWalkHook.WalkDelegate,
+ IPlayerWalkHook.WalkWrapperDelegate, WalkEventArgs>, IPlayerWalkHook
+{
+ /// <summary>
+ /// Create the packet send hook.
+ /// </summary>
+ /// <param name="bindingManager">The binding manager.</param>
+ /// <param name="browserManager">The browser manager.</param>
+ /// <param name="options">The options for the binding.</param>
+ /// <returns>A packet send hook or an error.</returns>
+ public static Result<PlayerWalkHook> Create
+ (NosBindingManager bindingManager, NosBrowserManager browserManager, HookOptions<IPlayerWalkHook> options)
+ {
+ var hook = CreateHook
+ (
+ bindingManager,
+ () => new PlayerWalkHook(browserManager.PlayerManager),
+ hook => hook.Detour,
+ options
+ );
+
+ return hook;
+ }
+
+ private PlayerWalkHook(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;
+ };
+
+ /// <inheritdoc />
+ protected override IPlayerWalkHook.WalkDelegate WrapWithCalling(IPlayerWalkHook.WalkDelegate function)
+ =>
+ (
+ playerManagerPtr,
+ position,
+ un0,
+ un1
+ ) =>
+ {
+ CallingFromNosSmooth = true;
+ var res = function(playerManagerPtr, position, un0, un1);
+ CallingFromNosSmooth = false;
+ return res;
+ };
+
+ private nuint Detour
+ (
+ nuint playerManagerPtr,
+ int position,
+ short un0,
+ int un1
+ )
+ {
+ var walkArgs = new WalkEventArgs((ushort)(position & 0xFFFF), (ushort)((position >> 16) & 0xFFFF));
+ return HandleCall(walkArgs);
+ }
+}<
\ No newline at end of file
M src/Core/NosSmooth.LocalBinding/IsExternalInit.cs => src/Core/NosSmooth.LocalBinding/IsExternalInit.cs +1 -1
@@ 4,7 4,7 @@
// 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 System.Runtime.CompilerServices
+namespace NosSmooth.LocalBinding
{
/// <summary>
/// Dummy.
M src/Core/NosSmooth.LocalBinding/NosBindingManager.cs => src/Core/NosSmooth.LocalBinding/NosBindingManager.cs +74 -341
@@ 5,13 5,18 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Diagnostics;
+using System.Reflection;
using Microsoft.Extensions.Options;
using NosSmooth.LocalBinding.Errors;
+using NosSmooth.LocalBinding.Hooks;
+using NosSmooth.LocalBinding.Hooks.Implementations;
using NosSmooth.LocalBinding.Objects;
using NosSmooth.LocalBinding.Options;
using Reloaded.Hooks;
using Reloaded.Hooks.Definitions;
using Reloaded.Hooks.Definitions.Helpers;
+using Reloaded.Hooks.Definitions.Internal;
+using Reloaded.Hooks.Definitions.X86;
using Reloaded.Hooks.Tools;
using Reloaded.Hooks.X86;
using Reloaded.Memory.Sigscan;
@@ 26,46 31,24 @@ namespace NosSmooth.LocalBinding;
public class NosBindingManager : IDisposable
{
private readonly NosBrowserManager _browserManager;
- private readonly PetManagerBindingOptions _petManagerBindingOptions;
- private readonly CharacterBindingOptions _characterBindingOptions;
- private readonly NetworkBindingOptions _networkBindingOptions;
- private readonly UnitManagerBindingOptions _unitManagerBindingOptions;
- private readonly PeriodicBindingOptions _periodicBindingOptions;
-
- private NetworkBinding? _networkBinding;
- private PlayerManagerBinding? _characterBinding;
- private UnitManagerBinding? _unitManagerBinding;
- private PetManagerBinding? _petManagerBinding;
- private PeriodicBinding? _periodicBinding;
+ private readonly IHookManager _hookManager;
/// <summary>
/// Initializes a new instance of the <see cref="NosBindingManager"/> class.
/// </summary>
/// <param name="browserManager">The NosTale browser manager.</param>
- /// <param name="characterBindingOptions">The character binding options.</param>
- /// <param name="networkBindingOptions">The network binding options.</param>
- /// <param name="sceneManagerBindingOptions">The scene manager binding options.</param>
- /// <param name="petManagerBindingOptions">The pet manager binding options.</param>
- /// <param name="periodicBindingOptions">The periodic binding options.</param>
+ /// <param name="hookManager">The hook manager.</param>
public NosBindingManager
(
NosBrowserManager browserManager,
- IOptions<CharacterBindingOptions> characterBindingOptions,
- IOptions<NetworkBindingOptions> networkBindingOptions,
- IOptions<UnitManagerBindingOptions> sceneManagerBindingOptions,
- IOptions<PetManagerBindingOptions> petManagerBindingOptions,
- IOptions<PeriodicBindingOptions> periodicBindingOptions
+ IHookManager hookManager
)
{
_browserManager = browserManager;
+ _hookManager = hookManager;
Hooks = new ReloadedHooks();
Memory = new Memory();
Scanner = new Scanner(Process.GetCurrentProcess(), Process.GetCurrentProcess().MainModule);
- _characterBindingOptions = characterBindingOptions.Value;
- _networkBindingOptions = networkBindingOptions.Value;
- _unitManagerBindingOptions = sceneManagerBindingOptions.Value;
- _petManagerBindingOptions = petManagerBindingOptions.Value;
- _periodicBindingOptions = periodicBindingOptions.Value;
}
/// <summary>
@@ 84,116 67,11 @@ public class NosBindingManager : IDisposable
internal IMemory Memory { get; }
/// <summary>
- /// Gets the periodic binding.
- /// </summary>
- /// <exception cref="InvalidOperationException">Thrown if the manager is not initialized yet.</exception>
- public PeriodicBinding Periodic
- {
- get
- {
- if (_periodicBinding is null)
- {
- throw new InvalidOperationException
- (
- "Could not get periodic. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?"
- );
- }
-
- return _periodicBinding;
- }
- }
-
- /// <summary>
- /// Gets the network binding.
- /// </summary>
- /// <exception cref="InvalidOperationException">Thrown if the manager is not initialized yet.</exception>
- public NetworkBinding Network
- {
- get
- {
- if (_networkBinding is null)
- {
- throw new InvalidOperationException
- (
- "Could not get network. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?"
- );
- }
-
- return _networkBinding;
- }
- }
-
- /// <summary>
- /// Gets the character binding.
- /// </summary>
- /// <exception cref="InvalidOperationException">Thrown if the manager is not initialized yet.</exception>
- public PlayerManagerBinding PlayerManager
- {
- get
- {
- if (_characterBinding is null)
- {
- throw new InvalidOperationException
- (
- "Could not get character. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?"
- );
- }
-
- return _characterBinding;
- }
- }
-
- /// <summary>
- /// Gets the character binding.
- /// </summary>
- /// <exception cref="InvalidOperationException">Thrown if the manager is not initialized yet.</exception>
- public UnitManagerBinding UnitManager
- {
- get
- {
- if (_unitManagerBinding is null)
- {
- throw new InvalidOperationException
- (
- "Could not get scene manager. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?"
- );
- }
-
- return _unitManagerBinding;
- }
- }
-
- /// <summary>
- /// Gets the pet manager binding.
- /// </summary>
- /// <exception cref="InvalidOperationException">Thrown if the manager is not initialized yet.</exception>
- public PetManagerBinding PetManager
- {
- get
- {
- if (_petManagerBinding is null)
- {
- throw new InvalidOperationException
- (
- "Could not get pet manager. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?"
- );
- }
-
- return _petManagerBinding;
- }
- }
-
- /// <summary>
/// Initialize the existing bindings and hook NosTale functions.
/// </summary>
/// <returns>A result that may or may not have succeeded.</returns>
public IResult Initialize()
{
- if (_networkBinding is not null)
- { // already initialized
- return Result.FromSuccess();
- }
-
List<IResult> errorResults = new List<IResult>();
var browserInitializationResult = _browserManager.Initialize();
if (!browserInitializationResult.IsSuccess)
@@ 206,163 84,10 @@ public class NosBindingManager : IDisposable
errorResults.Add(browserInitializationResult);
}
- try
- {
- var periodicBinding = PeriodicBinding.Create
- (
- this,
- _periodicBindingOptions
- );
- if (!periodicBinding.IsSuccess)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(PeriodicBinding), periodicBinding.Error),
- periodicBinding
- )
- );
- }
- _periodicBinding = periodicBinding.Entity;
- }
- catch (Exception e)
+ var hookManagerInitializationResult = _hookManager.Initialize(this, _browserManager);
+ if (!hookManagerInitializationResult.IsSuccess)
{
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(PeriodicBinding), new ExceptionError(e)),
- (Result)new ExceptionError(e)
- )
- );
- }
-
- try
- {
- var network = NetworkBinding.Create(this, _networkBindingOptions);
- if (!network.IsSuccess)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(NetworkBinding), network.Error),
- network
- )
- );
- }
-
- _networkBinding = network.Entity;
- }
- catch (Exception e)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(NetworkBinding), new ExceptionError(e)),
- (Result)new ExceptionError(e)
- )
- );
- }
-
- try
- {
- var playerManagerBinding = PlayerManagerBinding.Create
- (
- this,
- _browserManager.PlayerManager,
- _characterBindingOptions
- );
- if (!playerManagerBinding.IsSuccess)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(PlayerManagerBinding), playerManagerBinding.Error),
- playerManagerBinding
- )
- );
- }
- _characterBinding = playerManagerBinding.Entity;
- }
- catch (Exception e)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(PlayerManagerBinding), new ExceptionError(e)),
- (Result)new ExceptionError(e)
- )
- );
- }
-
- try
- {
- var unitManagerBinding = UnitManagerBinding.Create
- (
- this,
- _unitManagerBindingOptions
- );
- if (!unitManagerBinding.IsSuccess)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(UnitManagerBinding), unitManagerBinding.Error),
- unitManagerBinding
- )
- );
- }
- _unitManagerBinding = unitManagerBinding.Entity;
- }
- catch (Exception e)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(UnitManagerBinding), new ExceptionError(e)),
- (Result)new ExceptionError(e)
- )
- );
- }
-
- try
- {
- var petManagerBinding = PetManagerBinding.Create
- (
- this,
- _browserManager.PetManagerList,
- _petManagerBindingOptions
- );
- if (!petManagerBinding.IsSuccess)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(PetManagerBinding), petManagerBinding.Error),
- petManagerBinding
- )
- );
- }
- _petManagerBinding = petManagerBinding.Entity;
- }
- catch (Exception e)
- {
- errorResults.Add
- (
- Result.FromError
- (
- new CouldNotInitializeModuleError(typeof(UnitManagerBinding), new ExceptionError(e)),
- (Result)new ExceptionError(e)
- )
- );
+ errorResults.Add(hookManagerInitializationResult);
}
return errorResults.Count switch
@@ 373,33 98,11 @@ public class NosBindingManager : IDisposable
};
}
- /// <summary>
- /// Disable the currently enabled NosTale hooks.
- /// </summary>
- public void DisableHooks()
- {
- _networkBinding?.DisableHooks();
- _characterBinding?.DisableHooks();
- _unitManagerBinding?.DisableHooks();
- _petManagerBinding?.DisableHooks();
- }
-
- /// <summary>
- /// Enable all NosTale hooks.
- /// </summary>
- public void EnableHooks()
- {
- _networkBinding?.EnableHooks();
- _characterBinding?.EnableHooks();
- _unitManagerBinding?.EnableHooks();
- _petManagerBinding?.EnableHooks();
- }
-
/// <inheritdoc />
public void Dispose()
{
Scanner.Dispose();
- DisableHooks();
+ _hookManager.DisableAll();
}
/// <summary>
@@ 457,6 160,7 @@ public class NosBindingManager : IDisposable
/// <param name="name">The name of the binding.</param>
/// <param name="callbackFunction">The callback function to call instead of the original one.</param>
/// <param name="options">The options for the function hook. (pattern, offset, whether to activate).</param>
+ /// <param name="cancellable">Whether the call may be cancelled.</param>
/// <typeparam name="TFunction">The type of the function.</typeparam>
/// <returns>The hook object or an error.</returns>
internal Result<NosAsmHook<TFunction>>
@@ 464,8 168,10 @@ public class NosBindingManager : IDisposable
(
string name,
TFunction callbackFunction,
- HookOptions options
+ HookOptions options,
+ bool cancellable = true
)
+ where TFunction : Delegate
{
var walkFunctionAddress = Scanner.FindPattern(options.MemoryPattern);
if (!walkFunctionAddress.Found)
@@ 481,42 187,69 @@ public class NosBindingManager : IDisposable
var reverseWrapper = Hooks.CreateReverseWrapper<TFunction>(callbackFunction);
var callDetour = Utilities.GetAbsoluteCallMnemonics
(reverseWrapper.WrapperPointer.ToUnsigned(), IntPtr.Size == 8);
+ if (!Misc.TryGetAttribute<TFunction, FunctionAttribute>(out var attribute))
+ {
+ return new ArgumentInvalidError(nameof(TFunction), "The function does not have a function attribute.");
+ }
+ var stackArgumentsCount = Utilities.GetNumberofParameters<TFunction>() - attribute.SourceRegisters.Length;
+ if (stackArgumentsCount < 0)
+ {
+ stackArgumentsCount = 0;
+ }
- var hook = Hooks.CreateAsmHook
- (
- new[]
+ var asmInstructions = new List<string>();
+
+ asmInstructions.AddRange(new[]
+ {
+ "use32",
+ "pushad",
+ "pushfd",
+ });
+
+ for (int i = 0; i < stackArgumentsCount; i++)
+ { // TODO make it into asm loop
+ asmInstructions.AddRange(new[]
{
- "use32",
- "pushad",
- "pushfd",
- "push edx",
- "push eax",
+ $"add esp, {36 + (4 * stackArgumentsCount)}",
+ "pop ebx",
+ "sub esp, 0x04",
+ $"sub esp, {36 + (4 * stackArgumentsCount)}",
+ "push ebx",
+ });
+ }
- // call managed function
- callDetour,
+ asmInstructions.Add(callDetour);
- // check result
- // 1 means continue executing
- // 0 means do not permit the call
- "test eax, eax",
- "jnz rest",
+ if (cancellable)
+ {
+ asmInstructions.AddRange
+ (
+ new[]
+ {
+ // check result
+ // 1 means continue executing
+ // 0 means do not permit the call
+ "test eax, eax",
+ "jnz rest",
+
+ // returned 0, going to return early
+ "popfd",
+ "popad",
+
+ $"ret {4 * stackArgumentsCount}", // return early
+ }
+ );
+ }
- // returned 0, going to return early
- "pop eax",
- "pop edx",
- "popfd",
- "popad",
- "ret", // return early
+ asmInstructions.AddRange(new[]
+ {
+ // returned 1, going to execute the function
+ "rest:",
+ "popfd",
+ "popad"
+ });
- // returned 1, going to execute the function
- "rest:",
- "pop eax",
- "pop edx",
- "popfd",
- "popad"
- },
- address
- );
+ var hook = Hooks.CreateAsmHook(asmInstructions.ToArray(), address);
if (options.Hook)
{
M src/Core/NosSmooth.LocalBinding/NosBrowserManager.cs => src/Core/NosSmooth.LocalBinding/NosBrowserManager.cs +96 -4
@@ 56,9 56,13 @@ public class NosBrowserManager
private readonly PlayerManagerOptions _playerManagerOptions;
private readonly SceneManagerOptions _sceneManagerOptions;
private readonly PetManagerOptions _petManagerOptions;
+ private readonly NetworkManagerOptions _networkManagerOptions;
+ private readonly UnitManagerOptions _unitManagerOptions;
private PlayerManager? _playerManager;
private SceneManager? _sceneManager;
private PetManagerList? _petManagerList;
+ private NetworkManager? _networkManager;
+ private UnitManager? _unitManager;
/// <summary>
/// Initializes a new instance of the <see cref="NosBrowserManager"/> class.
@@ 66,18 70,24 @@ public class NosBrowserManager
/// <param name="playerManagerOptions">The options for obtaining player manager.</param>
/// <param name="sceneManagerOptions">The scene manager options.</param>
/// <param name="petManagerOptions">The pet manager options.</param>
+ /// <param name="networkManagerOptions">The network manager options.</param>
+ /// <param name="unitManagerOptions">The unit manager options.</param>
public NosBrowserManager
(
IOptions<PlayerManagerOptions> playerManagerOptions,
IOptions<SceneManagerOptions> sceneManagerOptions,
- IOptions<PetManagerOptions> petManagerOptions
+ IOptions<PetManagerOptions> petManagerOptions,
+ IOptions<NetworkManagerOptions> networkManagerOptions,
+ IOptions<UnitManagerOptions> unitManagerOptions
)
: this
(
Process.GetCurrentProcess(),
playerManagerOptions.Value,
sceneManagerOptions.Value,
- petManagerOptions.Value
+ petManagerOptions.Value,
+ networkManagerOptions.Value,
+ unitManagerOptions.Value
)
{
}
@@ 89,19 99,25 @@ public class NosBrowserManager
/// <param name="playerManagerOptions">The options for obtaining player manager.</param>
/// <param name="sceneManagerOptions">The scene manager options.</param>
/// <param name="petManagerOptions">The pet manager options.</param>
+ /// <param name="networkManagerOptions">The network manager options.</param>
+ /// <param name="unitManagerOptions">The unit manager options.</param>
public NosBrowserManager
(
Process process,
PlayerManagerOptions playerManagerOptions,
SceneManagerOptions sceneManagerOptions,
- PetManagerOptions petManagerOptions
+ PetManagerOptions petManagerOptions,
+ NetworkManagerOptions networkManagerOptions,
+ UnitManagerOptions unitManagerOptions
)
{
_playerManagerOptions = playerManagerOptions;
_sceneManagerOptions = sceneManagerOptions;
_petManagerOptions = petManagerOptions;
+ _networkManagerOptions = networkManagerOptions;
+ _unitManagerOptions = unitManagerOptions;
Process = process;
- Memory = new ExternalMemory(process);
+ Memory = Process.Id == Process.GetCurrentProcess().Id ? new Memory() : new ExternalMemory(process);
Scanner = new Scanner(process, process.MainModule);
}
@@ 126,6 142,46 @@ public class NosBrowserManager
public bool IsNostaleProcess => NosBrowserManager.IsProcessNostaleProcess(Process);
/// <summary>
+ /// Gets the network manager.
+ /// </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;
+ }
+ }
+
+ /// <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;
+ }
+ }
+
+ /// <summary>
/// Gets whether the player is currently in game.
/// </summary>
/// <remarks>
@@ 215,6 271,42 @@ public class NosBrowserManager
}
List<IResult> errorResults = new List<IResult>();
+ if (_unitManager is null)
+ {
+ var unitManagerResult = UnitManager.Create(this, _unitManagerOptions);
+ if (!unitManagerResult.IsSuccess)
+ {
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(UnitManager), unitManagerResult.Error),
+ unitManagerResult
+ )
+ );
+ }
+
+ _unitManager = unitManagerResult.Entity;
+ }
+
+ if (_networkManager is null)
+ {
+ var networkManagerResult = NetworkManager.Create(this, _networkManagerOptions);
+ if (!networkManagerResult.IsSuccess)
+ {
+ errorResults.Add
+ (
+ Result.FromError
+ (
+ new CouldNotInitializeModuleError(typeof(NetworkManager), networkManagerResult.Error),
+ networkManagerResult
+ )
+ );
+ }
+
+ _networkManager = networkManagerResult.Entity;
+ }
+
if (_playerManager is null)
{
var playerManagerResult = PlayerManager.Create(this, _playerManagerOptions);
M src/Core/NosSmooth.LocalBinding/NosSmooth.LocalBinding.csproj => src/Core/NosSmooth.LocalBinding/NosSmooth.LocalBinding.csproj +1 -0
@@ 9,6 9,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
<PackageReference Include="Reloaded.Hooks" Version="4.2.1" />
<PackageReference Include="Reloaded.Memory.Sigscan" Version="3.1.6" />
M src/Core/NosSmooth.LocalBinding/NosThreadSynchronizer.cs => src/Core/NosSmooth.LocalBinding/NosThreadSynchronizer.cs +39 -15
@@ 5,10 5,9 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Concurrent;
-using System.Net;
-using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
-using NosSmooth.LocalBinding.Objects;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Options;
using Remora.Results;
@@ 19,18 18,26 @@ namespace NosSmooth.LocalBinding;
/// </summary>
public class NosThreadSynchronizer
{
- private readonly PeriodicBinding _periodicBinding;
+ private readonly IPeriodicHook _periodicHook;
+ private readonly ILogger<NosThreadSynchronizer> _logger;
private readonly NosThreadSynchronizerOptions _options;
private readonly ConcurrentQueue<SyncOperation> _queuedOperations;
/// <summary>
/// Initializes a new instance of the <see cref="NosThreadSynchronizer"/> class.
/// </summary>
- /// <param name="periodicBinding">The periodic function binding.</param>
+ /// <param name="periodicHook">The periodic hook.</param>
+ /// <param name="logger">The logger.</param>
/// <param name="options">The options.</param>
- public NosThreadSynchronizer(PeriodicBinding periodicBinding, IOptions<NosThreadSynchronizerOptions> options)
+ public NosThreadSynchronizer
+ (
+ IPeriodicHook periodicHook,
+ ILogger<NosThreadSynchronizer> logger,
+ IOptions<NosThreadSynchronizerOptions> options
+ )
{
- _periodicBinding = periodicBinding;
+ _periodicHook = periodicHook;
+ _logger = logger;
_options = options.Value;
_queuedOperations = new ConcurrentQueue<SyncOperation>();
}
@@ 40,7 47,7 @@ public class NosThreadSynchronizer
/// </summary>
public void StartSynchronizer()
{
- _periodicBinding.PeriodicCall += PeriodicCall;
+ _periodicHook.Called += PeriodicCall;
}
/// <summary>
@@ 48,7 55,7 @@ public class NosThreadSynchronizer
/// </summary>
public void StopSynchronizer()
{
- _periodicBinding.PeriodicCall -= PeriodicCall;
+ _periodicHook.Called -= PeriodicCall;
}
private void PeriodicCall(object? owner, System.EventArgs eventArgs)
@@ 65,13 72,13 @@ public class NosThreadSynchronizer
{
try
{
- var result = operation.action();
+ var result = operation.Action();
operation.Result = result;
}
catch (Exception e)
{
- // TODO: log?
- operation.Result = e;
+ _logger.LogError(e, "Synchronizer obtained an exception");
+ operation.Result = (Result)e;
}
if (operation.CancellationTokenSource is not null)
@@ 115,6 122,23 @@ public class NosThreadSynchronizer
/// <returns>The result of the action.</returns>
public async Task<Result> SynchronizeAsync(Func<Result> action, CancellationToken ct = default)
{
+ return (Result)await CommonSynchronizeAsync(() => action(), ct);
+ }
+
+ /// <summary>
+ /// Synchronizes to NosTale thread, executes the given action and returns its result.
+ /// </summary>
+ /// <param name="action">The action to execute.</param>
+ /// <param name="ct">The cancellation token used for cancelling the operation.</param>
+ /// <returns>The result of the action.</returns>
+ /// <typeparam name="T">The type of the result.</typeparam>
+ public async Task<Result<T>> SynchronizeAsync<T>(Func<Result<T>> action, CancellationToken ct = default)
+ {
+ return (Result<T>)await CommonSynchronizeAsync(() => action(), ct);
+ }
+
+ private async Task<IResult> CommonSynchronizeAsync(Func<IResult> action, CancellationToken ct = default)
+ {
var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(ct);
var syncOperation = new SyncOperation(action, linkedSource);
_queuedOperations.Enqueue(syncOperation);
@@ 132,14 156,14 @@ public class NosThreadSynchronizer
}
catch (Exception e)
{
- return new ExceptionError(e);
+ return (Result)new ExceptionError(e);
}
return syncOperation.Result ?? Result.FromSuccess();
}
- private record SyncOperation(Func<Result> action, CancellationTokenSource? CancellationTokenSource)
+ private record SyncOperation(Func<IResult> Action, CancellationTokenSource? CancellationTokenSource)
{
- public Result? Result { get; set; }
+ public IResult? Result { get; set; }
}
}=
\ No newline at end of file
D src/Core/NosSmooth.LocalBinding/Objects/NetworkBinding.cs => src/Core/NosSmooth.LocalBinding/Objects/NetworkBinding.cs +0 -227
@@ 1,227 0,0 @@
-//
-// NetworkBinding.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.Collections.Concurrent;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using NosSmooth.LocalBinding.Errors;
-using NosSmooth.LocalBinding.EventArgs;
-using NosSmooth.LocalBinding.Extensions;
-using NosSmooth.LocalBinding.Options;
-using Reloaded.Hooks.Definitions;
-using Reloaded.Hooks.Definitions.X86;
-using Remora.Results;
-
-namespace NosSmooth.LocalBinding.Objects;
-
-/// <summary>
-/// The binding to nostale network object.
-/// </summary>
-public class NetworkBinding
-{
- [Function
- (
- new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
- FunctionAttribute.Register.eax,
- FunctionAttribute.StackCleanup.Callee,
- new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
- )]
- private delegate nuint PacketSendDelegate(nuint packetObject, nuint packetString);
-
- [Function
- (
- new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
- FunctionAttribute.Register.eax,
- FunctionAttribute.StackCleanup.Callee,
- new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
- )]
- private delegate nuint PacketReceiveDelegate(nuint packetObject, nuint packetString);
-
- /// <summary>
- /// Create the network binding with finding the network object and functions.
- /// </summary>
- /// <param name="bindingManager">The binding manager.</param>
- /// <param name="options">The options for the binding.</param>
- /// <returns>A network binding or an error.</returns>
- public static Result<NetworkBinding> Create(NosBindingManager bindingManager, NetworkBindingOptions options)
- {
- var process = Process.GetCurrentProcess();
- var networkObjectAddress = bindingManager.Scanner.FindPattern(options.NetworkObjectPattern);
- if (!networkObjectAddress.Found)
- {
- return new BindingNotFoundError(options.NetworkObjectPattern, "NetworkBinding");
- }
-
- var binding = new NetworkBinding
- (
- bindingManager,
- (nuint)(networkObjectAddress.Offset + (int)process.MainModule!.BaseAddress + 0x01)
- );
-
- var sendHookResult = bindingManager.CreateCustomAsmHookFromPattern<PacketSendDelegate>
- ("NetworkBinding.SendPacket", binding.SendPacketDetour, options.PacketSendHook);
- if (!sendHookResult.IsDefined(out var sendHook))
- {
- return Result<NetworkBinding>.FromError(sendHookResult);
- }
-
- var receiveHookResult = bindingManager.CreateCustomAsmHookFromPattern<PacketReceiveDelegate>
- ("NetworkBinding.ReceivePacket", binding.ReceivePacketDetour, options.PacketReceiveHook);
- if (!receiveHookResult.IsDefined(out var receiveHook))
- {
- return Result<NetworkBinding>.FromError(receiveHookResult);
- }
-
- binding._sendHook = sendHook;
- binding._receiveHook = receiveHook;
- return binding;
- }
-
- private readonly NosBindingManager _bindingManager;
- private readonly nuint _networkManagerAddress;
- private NosAsmHook<PacketSendDelegate> _sendHook = null!;
- private NosAsmHook<PacketReceiveDelegate> _receiveHook = null!;
- private bool _callingReceive;
- private bool _callingSend;
-
- private NetworkBinding
- (
- NosBindingManager bindingManager,
- nuint networkManagerAddress
- )
- {
- _bindingManager = bindingManager;
- _networkManagerAddress = networkManagerAddress;
- }
-
- /// <summary>
- /// Event that is called when packet send was called by NosTale.
- /// </summary>
- /// <remarks>
- /// The send must be hooked for this event to be called.
- /// </remarks>
- public event EventHandler<PacketEventArgs>? PacketSendCall;
-
- /// <summary>
- /// Event that is called when packet receive was called by NosTale.
- /// </summary>
- /// <remarks>
- /// The receive must be hooked for this event to be called.
- /// </remarks>
- public event EventHandler<PacketEventArgs>? PacketReceiveCall;
-
- /// <summary>
- /// Send the given packet.
- /// </summary>
- /// <param name="packet">The packet to send.</param>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result SendPacket(string packet)
- {
- try
- {
- _callingSend = true;
- using var nostaleString = NostaleStringA.Create(_bindingManager.Memory, packet);
- _sendHook.OriginalFunction.GetWrapper()(GetManagerAddress(false), nostaleString.Get());
- _callingSend = false;
- }
- catch (Exception e)
- {
- return e;
- }
-
- return Result.FromSuccess();
- }
-
- /// <summary>
- /// Receive the given packet.
- /// </summary>
- /// <param name="packet">The packet to receive.</param>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result ReceivePacket(string packet)
- {
- try
- {
- _callingReceive = true;
- using var nostaleString = NostaleStringA.Create(_bindingManager.Memory, packet);
- _receiveHook.OriginalFunction.GetWrapper()(GetManagerAddress(true), nostaleString.Get());
- _callingReceive = false;
- }
- catch (Exception e)
- {
- return e;
- }
-
- return Result.FromSuccess();
- }
-
- /// <summary>
- /// Enable all networking hooks.
- /// </summary>
- public void EnableHooks()
- {
- _receiveHook.Hook.EnableOrActivate();
- _sendHook.Hook.EnableOrActivate();
- }
-
- /// <summary>
- /// Disable all the hooks that are currently enabled.
- /// </summary>
- public void DisableHooks()
- {
- _receiveHook.Hook.Disable();
- _sendHook.Hook.Disable();
- }
-
- private nuint GetManagerAddress(bool third)
- {
- nuint networkManager = _networkManagerAddress;
- _bindingManager.Memory.Read(networkManager, out networkManager);
- _bindingManager.Memory.Read(networkManager, out networkManager);
- _bindingManager.Memory.Read(networkManager, out networkManager);
-
- if (third)
- {
- _bindingManager.Memory.Read(networkManager + 0x34, out networkManager);
- }
-
- return networkManager;
- }
-
- private nuint SendPacketDetour(nuint packetObject, nuint packetString)
- {
- if (_callingSend)
- {
- return 1;
- }
-
- var packet = Marshal.PtrToStringAnsi((IntPtr)packetString);
- if (packet is null)
- { // ?
- return 1;
- }
- var packetArgs = new PacketEventArgs(packet);
- PacketSendCall?.Invoke(this, packetArgs);
- return packetArgs.Cancel ? 0 : (nuint)1;
- }
-
- private nuint ReceivePacketDetour(nuint packetObject, nuint packetString)
- {
- if (_callingReceive)
- {
- return 1;
- }
-
- var packet = Marshal.PtrToStringAnsi((IntPtr)packetString);
- if (packet is null)
- { // ?
- return 1;
- }
-
- var packetArgs = new PacketEventArgs(packet);
- PacketReceiveCall?.Invoke(this, packetArgs);
- return packetArgs.Cancel ? 0 : (nuint)1;
- }
-}>
\ No newline at end of file
D src/Core/NosSmooth.LocalBinding/Objects/PeriodicBinding.cs => src/Core/NosSmooth.LocalBinding/Objects/PeriodicBinding.cs +0 -82
@@ 1,82 0,0 @@
-//
-// PeriodicBinding.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 NosSmooth.LocalBinding.Errors;
-using NosSmooth.LocalBinding.Extensions;
-using NosSmooth.LocalBinding.Options;
-using Reloaded.Hooks.Definitions.X86;
-using Remora.Results;
-
-namespace NosSmooth.LocalBinding.Objects;
-
-/// <summary>
-/// Binds to a periodic function to allow synchronizing.
-/// </summary>
-public class PeriodicBinding
-{
- [Function
- (
- new FunctionAttribute.Register[0],
- FunctionAttribute.Register.eax,
- FunctionAttribute.StackCleanup.Callee,
- new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp, FunctionAttribute.Register.eax, FunctionAttribute.Register.edx, FunctionAttribute.Register.ecx }
- )]
- private delegate void PeriodicDelegate();
-
- /// <summary>
- /// Create the periodic binding with finding the periodic function.
- /// </summary>
- /// <param name="bindingManager">The binding manager.</param>
- /// <param name="options">The options for the binding.</param>
- /// <returns>A periodic binding or an error.</returns>
- public static Result<PeriodicBinding> Create(NosBindingManager bindingManager, PeriodicBindingOptions options)
- {
- var binding = new PeriodicBinding();
-
- var periodicHookResult = bindingManager.CreateCustomAsmHookFromPattern<PeriodicDelegate>
- ("PeriodicBinding.Periodic", binding.PeriodicDetour, options.PeriodicHook);
- if (!periodicHookResult.IsDefined(out var periodicHook))
- {
- return Result<PeriodicBinding>.FromError(periodicHookResult);
- }
-
- binding._periodicHook = periodicHook;
- return binding;
- }
-
- private NosAsmHook<PeriodicDelegate>? _periodicHook;
-
- private PeriodicBinding()
- {
- }
-
- /// <summary>
- /// An action called on every period.
- /// </summary>
- public event EventHandler? PeriodicCall;
-
- /// <summary>
- /// Enable all networking hooks.
- /// </summary>
- public void EnableHooks()
- {
- _periodicHook?.Hook.EnableOrActivate();
- }
-
- /// <summary>
- /// Disable all the hooks that are currently enabled.
- /// </summary>
- public void DisableHooks()
- {
- _periodicHook?.Hook.Disable();
- }
-
- private void PeriodicDetour()
- {
- PeriodicCall?.Invoke(this, System.EventArgs.Empty);
- }
-}>
\ No newline at end of file
D src/Core/NosSmooth.LocalBinding/Objects/PetManagerBinding.cs => src/Core/NosSmooth.LocalBinding/Objects/PetManagerBinding.cs +0 -168
@@ 1,168 0,0 @@
-//
-// PetManagerBinding.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.Extensions;
-using NosSmooth.LocalBinding.Options;
-using NosSmooth.LocalBinding.Structs;
-using Reloaded.Hooks.Definitions;
-using Reloaded.Hooks.Definitions.X86;
-using Reloaded.Memory.Sources;
-using Remora.Results;
-
-namespace NosSmooth.LocalBinding.Objects;
-
-/// <summary>
-/// The binding to NosTale pet manager.
-/// </summary>
-public class PetManagerBinding
-{
- private readonly IMemory _memory;
-
- /// <summary>
- /// Create nostale pet manager binding.
- /// </summary>
- /// <param name="bindingManager">The binding manager.</param>
- /// <param name="petManagerList">The list of the pet managers.</param>
- /// <param name="options">The options.</param>
- /// <returns>A pet manager binding or and error.</returns>
- public static Result<PetManagerBinding> Create
- (NosBindingManager bindingManager, PetManagerList petManagerList, PetManagerBindingOptions options)
- {
- var petManager = new PetManagerBinding(bindingManager.Memory, petManagerList);
- var hookResult = bindingManager.CreateHookFromPattern<PetWalkDelegate>
- (
- "PetManagerBinding.PetWalk",
- petManager.PetWalkDetour,
- options.PetWalkHook
- );
-
- if (!hookResult.IsSuccess)
- {
- return Result<PetManagerBinding>.FromError(hookResult);
- }
-
- petManager._petWalkHook = hookResult.Entity;
- return petManager;
- }
-
- [Function
- (
- new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx, FunctionAttribute.Register.ecx },
- FunctionAttribute.Register.eax,
- FunctionAttribute.StackCleanup.Callee,
- new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
- )]
- private delegate bool PetWalkDelegate
- (
- nuint petManagerPtr,
- uint position,
- short unknown0 = 0,
- int unknown1 = 1,
- int unknown2 = 1
- );
-
- private IHook<PetWalkDelegate> _petWalkHook = null!;
-
- private PetManagerBinding(IMemory memory, PetManagerList petManagerList)
- {
- _memory = memory;
- PetManagerList = petManagerList;
- }
-
- /// <summary>
- /// Event that is called when walk was called by NosTale.
- /// </summary>
- /// <remarks>
- /// The walk must be hooked for this event to be called.
- /// </remarks>
- public event EventHandler<PetWalkEventArgs>? PetWalkCall;
-
- /// <summary>
- /// Gets the hook of the pet walk function.
- /// </summary>
- public IHook PetWalkHook => _petWalkHook;
-
- /// <summary>
- /// Gets pet manager list.
- /// </summary>
- public PetManagerList PetManagerList { get; }
-
- /// <summary>
- /// Disable all PetManager hooks.
- /// </summary>
- public void DisableHooks()
- {
- _petWalkHook.Disable();
- }
-
- /// <summary>
- /// Enable all PetManager hooks.
- /// </summary>
- public void EnableHooks()
- {
- _petWalkHook.EnableOrActivate();
- }
-
- /// <summary>
- /// Walk the given pet to the given location.
- /// </summary>
- /// <param name="selector">Index of the pet to walk. -1 for every pet currently available.</param>
- /// <param name="x">The x coordinate.</param>
- /// <param name="y">The y coordinate.</param>
- /// <returns>A result returned from NosTale or an error.</returns>
- public Result<bool> PetWalk(int selector, short x, short y)
- {
- uint position = Convert.ToUInt32(((ushort)y << 16) | (ushort)x);
- if (PetManagerList.Length < selector + 1)
- {
- return new NotFoundError("Could not find the pet using the given selector.");
- }
-
- if (selector == -1)
- {
- bool lastResult = true;
- for (int i = 0; i < PetManagerList.Length; i++)
- {
- lastResult = _petWalkHook.OriginalFunction(PetManagerList[i].Address, position);
- }
-
- return lastResult;
- }
-
- return _petWalkHook.OriginalFunction(PetManagerList[selector].Address, position);
- }
-
- private bool PetWalkDetour
- (
- nuint petManagerPtr,
- uint position,
- short unknown0 = 0,
- int unknown1 = 1,
- int unknown2 = 1
- )
- {
- var x = (ushort)(position & 0xFFFF);
- var y = (ushort)((position >> 16) & 0xFFFF);
- var petManager = new PetManager(_memory, petManagerPtr);
- var petWalkEventArgs = new PetWalkEventArgs(petManager, x, y);
- PetWalkCall?.Invoke(this, petWalkEventArgs);
-
- if (!petWalkEventArgs.Cancel)
- {
- return _petWalkHook.OriginalFunction
- (
- petManagerPtr,
- position,
- unknown0,
- unknown1,
- unknown2
- );
- }
-
- return false;
- }
-}>
\ No newline at end of file
D src/Core/NosSmooth.LocalBinding/Objects/PlayerManagerBinding.cs => src/Core/NosSmooth.LocalBinding/Objects/PlayerManagerBinding.cs +0 -260
@@ 1,260 0,0 @@
-//
-// PlayerManagerBinding.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 System.Diagnostics;
-using NosSmooth.LocalBinding.Errors;
-using NosSmooth.LocalBinding.EventArgs;
-using NosSmooth.LocalBinding.Extensions;
-using NosSmooth.LocalBinding.Options;
-using NosSmooth.LocalBinding.Structs;
-using Reloaded.Hooks.Definitions;
-using Reloaded.Hooks.Definitions.X86;
-using Remora.Results;
-
-namespace NosSmooth.LocalBinding.Objects;
-
-/// <summary>
-/// The nostale binding of a character.
-/// </summary>
-public class PlayerManagerBinding
-{
- [Function
- (
- new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx, FunctionAttribute.Register.ecx },
- FunctionAttribute.Register.eax,
- FunctionAttribute.StackCleanup.Callee,
- new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
- )]
- private delegate bool WalkDelegate(nuint playerManagerPtr, int position, short unknown0 = 0, int unknown1 = 1);
-
- [Function
- (
- new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx, FunctionAttribute.Register.ecx },
- FunctionAttribute.Register.eax,
- FunctionAttribute.StackCleanup.Callee,
- new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
- )]
- private delegate bool FollowEntityDelegate
- (
- nuint playerManagerPtr,
- nuint entityPtr,
- int unknown1 = 0,
- int unknown2 = 1
- );
-
- [Function
- (
- new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
- FunctionAttribute.Register.eax,
- FunctionAttribute.StackCleanup.Callee,
- new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
- )]
- private delegate void UnfollowEntityDelegate(nuint playerManagerPtr, int unknown = 0);
-
- /// <summary>
- /// Create the network binding with finding the network object and functions.
- /// </summary>
- /// <param name="bindingManager">The binding manager.</param>
- /// <param name="playerManager">The player manager.</param>
- /// <param name="options">The options for the binding.</param>
- /// <returns>A network binding or an error.</returns>
- public static Result<PlayerManagerBinding> Create(NosBindingManager bindingManager, PlayerManager playerManager, CharacterBindingOptions options)
- {
- var binding = new PlayerManagerBinding
- (
- bindingManager,
- playerManager
- );
-
- var walkHookResult = bindingManager.CreateHookFromPattern<WalkDelegate>
- ("CharacterBinding.Walk", binding.WalkDetour, options.WalkHook);
- if (!walkHookResult.IsDefined(out var walkHook))
- {
- return Result<PlayerManagerBinding>.FromError(walkHookResult);
- }
-
- var entityFollowHookResult = bindingManager.CreateHookFromPattern<FollowEntityDelegate>
- ("CharacterBinding.EntityFollow", binding.FollowEntityDetour, options.EntityFollowHook);
- if (!entityFollowHookResult.IsDefined(out var entityFollowHook))
- {
- return Result<PlayerManagerBinding>.FromError(entityFollowHookResult);
- }
-
- var entityUnfollowHookResult = bindingManager.CreateHookFromPattern<UnfollowEntityDelegate>
- ("CharacterBinding.EntityUnfollow", binding.UnfollowEntityDetour, options.EntityUnfollowHook);
- if (!entityUnfollowHookResult.IsDefined(out var entityUnfollowHook))
- {
- return Result<PlayerManagerBinding>.FromError(entityUnfollowHookResult);
- }
-
- binding._walkHook = walkHook;
- binding._followHook = entityFollowHook;
- binding._unfollowHook = entityUnfollowHook;
- return binding;
- }
-
- private readonly NosBindingManager _bindingManager;
-
- private IHook<WalkDelegate> _walkHook = null!;
- private IHook<FollowEntityDelegate> _followHook = null!;
- private IHook<UnfollowEntityDelegate> _unfollowHook = null!;
-
- private PlayerManagerBinding
- (
- NosBindingManager bindingManager,
- PlayerManager playerManager
- )
- {
- PlayerManager = playerManager;
- _bindingManager = bindingManager;
- }
-
- /// <summary>
- /// Gets the player manager.
- /// </summary>
- public PlayerManager PlayerManager { get; }
-
- /// <summary>
- /// Event that is called when walk was called by NosTale.
- /// </summary>
- /// <remarks>
- /// The walk must be hooked for this event to be called.
- /// </remarks>
- public event EventHandler<WalkEventArgs>? WalkCall;
-
- /// <summary>
- /// Event that is called when entity follow or unfollow was called.
- /// </summary>
- /// <remarks>
- /// The follow/unfollow entity must be hooked for this event to be called.
- /// </remarks>
- public event EventHandler<EntityEventArgs>? FollowEntityCall;
-
- /// <summary>
- /// Disable all PlayerManager hooks.
- /// </summary>
- public void DisableHooks()
- {
- _followHook.Disable();
- _unfollowHook.Disable();
- _walkHook.Disable();
- }
-
- /// <summary>
- /// Enable all PlayerManager hooks.
- /// </summary>
- public void EnableHooks()
- {
- _followHook.EnableOrActivate();
- _unfollowHook.EnableOrActivate();
- _walkHook.EnableOrActivate();
- }
-
- /// <summary>
- /// Walk to the given position.
- /// </summary>
- /// <param name="x">The x coordinate.</param>
- /// <param name="y">The y coordinate.</param>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result<bool> Walk(short x, short y)
- {
- int param = ((ushort)y << 16) | (ushort)x;
- try
- {
- return _walkHook.OriginalFunction(PlayerManager.Address, param);
- }
- catch (Exception e)
- {
- return e;
- }
- }
-
- private bool WalkDetour(nuint characterObject, int position, short unknown0, int unknown1)
- {
- var walkEventArgs = new WalkEventArgs((ushort)(position & 0xFFFF), (ushort)((position >> 16) & 0xFFFF));
- WalkCall?.Invoke(this, walkEventArgs);
- if (!walkEventArgs.Cancel)
- {
- return _walkHook.OriginalFunction(characterObject, position, unknown0, unknown1);
- }
-
- return false;
- }
-
- /// <summary>
- /// Follow the entity.
- /// </summary>
- /// <param name="entity">The entity.</param>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result FollowEntity(MapBaseObj? entity)
- => FollowEntity(entity?.Address ?? nuint.Zero);
-
- /// <summary>
- /// Follow the entity.
- /// </summary>
- /// <param name="entityAddress">The entity address.</param>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result FollowEntity(nuint entityAddress)
- {
- try
- {
- _followHook.OriginalFunction(PlayerManager.Address, entityAddress);
- }
- catch (Exception e)
- {
- return e;
- }
-
- return Result.FromSuccess();
- }
-
- /// <summary>
- /// Stop following entity.
- /// </summary>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result UnfollowEntity()
- {
- try
- {
- _unfollowHook.OriginalFunction(PlayerManager.Address);
- }
- catch (Exception e)
- {
- return e;
- }
-
- return Result.FromSuccess();
- }
-
- private bool FollowEntityDetour
- (
- nuint playerManagerPtr,
- nuint entityPtr,
- int unknown1,
- int unknown2
- )
- {
- var entityEventArgs = new EntityEventArgs(new MapBaseObj(_bindingManager.Memory, entityPtr));
- FollowEntityCall?.Invoke(this, entityEventArgs);
- if (!entityEventArgs.Cancel)
- {
- return _followHook.OriginalFunction(playerManagerPtr, entityPtr, unknown1, unknown2);
- }
-
- return false;
- }
-
- private void UnfollowEntityDetour(nuint playerManagerPtr, int unknown)
- {
- var entityEventArgs = new EntityEventArgs(null);
- FollowEntityCall?.Invoke(this, entityEventArgs);
- if (!entityEventArgs.Cancel)
- {
- _unfollowHook.OriginalFunction(playerManagerPtr, unknown);
- }
- }
-}>
\ No newline at end of file
D src/Core/NosSmooth.LocalBinding/Objects/UnitManagerBinding.cs => src/Core/NosSmooth.LocalBinding/Objects/UnitManagerBinding.cs +0 -172
@@ 1,172 0,0 @@
-//
-// UnitManagerBinding.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 NosSmooth.LocalBinding.Errors;
-using NosSmooth.LocalBinding.EventArgs;
-using NosSmooth.LocalBinding.Extensions;
-using NosSmooth.LocalBinding.Options;
-using NosSmooth.LocalBinding.Structs;
-using Reloaded.Hooks.Definitions;
-using Reloaded.Hooks.Definitions.X86;
-using Reloaded.Hooks.Internal.Testing;
-using Remora.Results;
-
-namespace NosSmooth.LocalBinding.Objects;
-
-/// <summary>
-/// The nostale binding of a scene manager.
-/// </summary>
-/// <remarks>
-/// The scene manager holds addresses to entities, mouse position, ....
-/// </remarks>
-public class UnitManagerBinding
-{
- [Function
- (
- new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
- FunctionAttribute.Register.eax,
- FunctionAttribute.StackCleanup.Callee,
- new[] { FunctionAttribute.Register.ebx, FunctionAttribute.Register.esi, FunctionAttribute.Register.edi, FunctionAttribute.Register.ebp }
- )]
- private delegate nuint FocusEntityDelegate(nuint unitManagerPtr, nuint entityPtr);
-
- /// <summary>
- /// Create the scene manager binding.
- /// </summary>
- /// <param name="bindingManager">The binding manager.</param>
- /// <param name="bindingOptions">The options for the binding.</param>
- /// <returns>A network binding or an error.</returns>
- public static Result<UnitManagerBinding> Create
- (NosBindingManager bindingManager, UnitManagerBindingOptions bindingOptions)
- {
- var process = Process.GetCurrentProcess();
-
- var unitManagerStaticAddress = bindingManager.Scanner.FindPattern(bindingOptions.UnitManagerPattern);
- if (!unitManagerStaticAddress.Found)
- {
- return new BindingNotFoundError(bindingOptions.UnitManagerPattern, "UnitManagerBinding.UnitManager");
- }
-
- var binding = new UnitManagerBinding
- (
- bindingManager,
- (int)process.MainModule!.BaseAddress + unitManagerStaticAddress.Offset,
- bindingOptions.UnitManagerOffsets
- );
-
- var entityFocusHookResult = bindingManager.CreateCustomAsmHookFromPattern<FocusEntityDelegate>
- ("UnitManager.EntityFocus", binding.FocusEntityDetour, bindingOptions.EntityFocusHook);
- if (!entityFocusHookResult.IsDefined(out var entityFocusHook))
- {
- return Result<UnitManagerBinding>.FromError(entityFocusHookResult);
- }
-
- binding._focusHook = entityFocusHook;
- return binding;
- }
-
- private readonly int _staticUnitManagerAddress;
- private readonly int[] _unitManagerOffsets;
-
- private readonly NosBindingManager _bindingManager;
-
- private NosAsmHook<FocusEntityDelegate> _focusHook = null!;
-
- private bool _callingFocus;
-
- private UnitManagerBinding
- (
- NosBindingManager bindingManager,
- int staticUnitManagerAddress,
- int[] unitManagerOffsets
- )
- {
- _bindingManager = bindingManager;
- _staticUnitManagerAddress = staticUnitManagerAddress;
- _unitManagerOffsets = unitManagerOffsets;
- }
-
- /// <summary>
- /// Gets the address of unit manager.
- /// </summary>
- public nuint Address => _bindingManager.Memory.FollowStaticAddressOffsets
- (_staticUnitManagerAddress, _unitManagerOffsets);
-
- /// <summary>
- /// Event that is called when entity focus was called by NosTale.
- /// </summary>
- /// <remarks>
- /// The focus entity must be hooked for this event to be called.
- /// </remarks>
- public event EventHandler<EntityEventArgs>? EntityFocusCall;
-
- /// <summary>
- /// Focus the entity.
- /// </summary>
- /// <param name="entity">The entity.</param>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result FocusEntity(MapBaseObj? entity)
- => FocusEntity(entity?.Address ?? nuint.Zero);
-
- /// <summary>
- /// Disable all UnitManager hooks.
- /// </summary>
- public void DisableHooks()
- {
- _focusHook.Hook.Disable();
- }
-
- /// <summary>
- /// Enable all UnitManager hooks.
- /// </summary>
- public void EnableHooks()
- {
- _focusHook.Hook.EnableOrActivate();
- }
-
- /// <summary>
- /// Focus the entity.
- /// </summary>
- /// <param name="entityAddress">The entity address.</param>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result FocusEntity(nuint entityAddress)
- {
- try
- {
- _callingFocus = true;
- _focusHook.OriginalFunction.GetWrapper()(Address, entityAddress);
- _callingFocus = false;
- }
- catch (Exception e)
- {
- return e;
- }
-
- return Result.FromSuccess();
- }
-
- private nuint FocusEntityDetour(nuint unitManagerPtr, nuint entityId)
- {
- if (_callingFocus)
- {
- // in case this is being called from UnitManagerBinding.FocusEntity,
- // do not handle.
- return 1;
- }
-
- MapBaseObj? obj = null;
- if (entityId != nuint.Zero)
- {
- obj = new MapBaseObj(_bindingManager.Memory, entityId);
- }
-
- var entityEventArgs = new EntityEventArgs(obj);
- EntityFocusCall?.Invoke(this, entityEventArgs);
-
- return entityEventArgs.Cancel ? 0 : (nuint)1;
- }
-}>
\ No newline at end of file
D src/Core/NosSmooth.LocalBinding/Options/CharacterBindingOptions.cs => src/Core/NosSmooth.LocalBinding/Options/CharacterBindingOptions.cs +0 -32
@@ 1,32 0,0 @@
-//
-// CharacterBindingOptions.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.Objects;
-
-namespace NosSmooth.LocalBinding.Options;
-
-/// <summary>
-/// Options for <see cref="PlayerManagerBinding"/>.
-/// </summary>
-public class CharacterBindingOptions
-{
- /// <summary>
- /// Gets or sets the configuration for player walk function hook.
- /// </summary>
- public HookOptions WalkHook { get; set; } = new HookOptions(false, "55 8B EC 83 C4 EC 53 56 57 66 89 4D FA", 0);
-
- /// <summary>
- /// Gets or sets the configuration for entity follow function hook.
- /// </summary>
- public HookOptions EntityFollowHook { get; set; }
- = new HookOptions(false, "55 8B EC 51 53 56 57 88 4D FF 8B F2 8B F8", 0);
-
- /// <summary>
- /// Gets or sets the configuration for entity unfollow function hook.
- /// </summary>
- public HookOptions EntityUnfollowHook { get; set; }
- = new HookOptions(false, "80 78 14 00 74 1A", 0);
-}>
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Options/HookManagerOptions.cs => src/Core/NosSmooth.LocalBinding/Options/HookManagerOptions.cs +69 -0
@@ 0,0 1,69 @@
+//
+// HookManagerOptions.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.LocalBinding.Options;
+
+/// <summary>
+/// Options for <see cref="IHookManager"/>.
+/// </summary>
+public class HookManagerOptions
+{
+ /// <summary>
+ /// Gets or sets the configuration for player walk function hook.
+ /// </summary>
+ public HookOptions<IPlayerWalkHook> PlayerWalkHook { get; set; }
+ = new(IHookManager.CharacterWalkName, false, "55 8B EC 83 C4 EC 53 56 57 66 89 4D FA", 0);
+
+ /// <summary>
+ /// Gets or sets the configuration for entity follow function hook.
+ /// </summary>
+ public HookOptions<IEntityFollowHook> EntityFollowHook { get; set; }
+ = new(IHookManager.EntityFollowName, false, "55 8B EC 51 53 56 57 88 4D FF 8B F2 8B F8", 0);
+
+ /// <summary>
+ /// Gets or sets the configuration for entity unfollow function hook.
+ /// </summary>
+ public HookOptions<IEntityUnfollowHook> EntityUnfollowHook { get; set; }
+ = new(IHookManager.EntityUnfollowName, false, "80 78 14 00 74 1A", 0);
+
+ /// <summary>
+ /// Gets or sets the configuration for packet receive function hook.
+ /// </summary>
+ public HookOptions<IPacketReceiveHook> PacketReceiveHook { get; set; }
+ = new
+ (
+ IHookManager.PacketReceiveName,
+ true,
+ "55 8B EC 83 C4 ?? 53 56 57 33 C9 89 4D ?? 89 4D ?? 89 55 ?? 8B D8 8B 45 ??",
+ 0
+ );
+
+ /// <summary>
+ /// Gets or sets the configuration for packet send function hook.
+ /// </summary>
+ public HookOptions<IPacketSendHook> PacketSendHook { get; set; }
+ = new(IHookManager.PacketSendName, true, "53 56 8B F2 8B D8 EB 04", 0);
+
+ /// <summary>
+ /// Gets or sets the configuration for pet walk function hook.
+ /// </summary>
+ public HookOptions<IPetWalkHook> PetWalkHook { get; set; }
+ = new(IHookManager.PetWalkName, false, "55 8b ec 83 c4 e4 53 56 57 8b f9 89 55 fc 8b d8 c6 45 fb 00", 0);
+
+ /// <summary>
+ /// Gets or sets the configuration for any periodic function hook.
+ /// </summary>
+ public HookOptions<IPeriodicHook> PeriodicHook { get; set; }
+ = new(IHookManager.PeriodicName, true, "55 8B EC 53 56 83 C4", 0);
+
+ /// <summary>
+ /// Gets or sets the configuration for entity focus function hook.
+ /// </summary>
+ public HookOptions<IEntityFocusHook> EntityFocusHook { get; set; }
+ = new(IHookManager.EntityFocusName, false, "73 00 00 00 55 8b ec b9 05 00 00 00", 4);
+}<
\ No newline at end of file
R src/Core/NosSmooth.LocalBinding/Options/NetworkBindingOptions.cs => src/Core/NosSmooth.LocalBinding/Options/NetworkManagerOptions.cs +10 -15
@@ 1,31 1,21 @@
//
-// NetworkBindingOptions.cs
+// NetworkManagerOptions.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;
using NosSmooth.LocalBinding.Objects;
+using NosSmooth.LocalBinding.Structs;
namespace NosSmooth.LocalBinding.Options;
/// <summary>
-/// Options for <see cref="NetworkBinding"/>.
+/// Options for <see cref="NetworkManager"/>.
/// </summary>
-public class NetworkBindingOptions
+public class NetworkManagerOptions
{
/// <summary>
- /// Gets or sets the configuration for packet receive function hook.
- /// </summary>
- public HookOptions PacketReceiveHook { get; set; }
- = new HookOptions(true, "55 8B EC 83 C4 ?? 53 56 57 33 C9 89 4D ?? 89 4D ?? 89 55 ?? 8B D8 8B 45 ??", 0);
-
- /// <summary>
- /// Gets or sets the configuration for packet send function hook.
- /// </summary>
- public HookOptions PacketSendHook { get; set; }
- = new HookOptions(true, "53 56 8B F2 8B D8 EB 04", 0);
-
- /// <summary>
/// Gets or sets the pattern to find the network object at.
/// </summary>
/// <remarks>
@@ 33,4 23,9 @@ public class NetworkBindingOptions
/// </remarks>
public string NetworkObjectPattern { get; set; }
= "A1 ?? ?? ?? ?? 8B 00 BA ?? ?? ?? ?? E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? A1 ?? ?? ?? ?? 8B 00 8B 40 40";
+
+ /// <summary>
+ /// Gets or sets the offset of NetworkObject.
+ /// </summary>
+ public int NetworkObjectOffset { get; set; } = 1;
}=
\ No newline at end of file
D src/Core/NosSmooth.LocalBinding/Options/PetManagerBindingOptions.cs => src/Core/NosSmooth.LocalBinding/Options/PetManagerBindingOptions.cs +0 -21
@@ 1,21 0,0 @@
-//
-// PetManagerBindingOptions.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.Objects;
-
-namespace NosSmooth.LocalBinding.Options;
-
-/// <summary>
-/// Options for <see cref="PetManagerBinding"/>.
-/// </summary>
-public class PetManagerBindingOptions
-{
- /// <summary>
- /// Gets or sets the configuration for pet walk function hook.
- /// </summary>
- public HookOptions PetWalkHook { get; set; }
- = new HookOptions(false, "55 8b ec 83 c4 e4 53 56 57 8b f9 89 55 fc 8b d8 c6 45 fb 00", 0);
-}>
\ No newline at end of file
R src/Core/NosSmooth.LocalBinding/Options/UnitManagerBindingOptions.cs => src/Core/NosSmooth.LocalBinding/Options/UnitManagerOptions.cs +5 -9
@@ 1,17 1,19 @@
//
-// UnitManagerBindingOptions.cs
+// UnitManagerOptions.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;
using NosSmooth.LocalBinding.Objects;
+using NosSmooth.LocalBinding.Structs;
namespace NosSmooth.LocalBinding.Options;
/// <summary>
-/// Options for <see cref="UnitManagerBinding"/>.
+/// Options for <see cref="UnitManager"/>.
/// </summary>
-public class UnitManagerBindingOptions
+public class UnitManagerOptions
{
/// <summary>
/// Gets or sets the pattern to static address of unit manager.
@@ 24,10 26,4 @@ public class UnitManagerBindingOptions
/// </summary>
public int[] UnitManagerOffsets { get; set; }
= { 1, 0 };
-
- /// <summary>
- /// Gets or sets the configuration for entity focus function hook.
- /// </summary>
- public HookOptions EntityFocusHook { get; set; }
- = new HookOptions(false, "73 00 00 00 55 8b ec b9 05 00 00 00", 4);
}=
\ No newline at end of file
A src/Core/NosSmooth.LocalBinding/Structs/MapBaseList.cs => src/Core/NosSmooth.LocalBinding/Structs/MapBaseList.cs +29 -0
@@ 0,0 1,29 @@
+//
+// MapBaseList.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 Reloaded.Memory.Sources;
+
+namespace NosSmooth.LocalBinding.Structs;
+
+/// <inheritdoc />
+public class MapBaseList : NostaleList<MapBaseObj>
+{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MapBaseList"/> class.
+ /// </summary>
+ /// <param name="memory">The memory.</param>
+ /// <param name="objListPointer">The list pointer.</param>
+ public MapBaseList(IMemory memory, nuint objListPointer)
+ : base(memory, objListPointer)
+ {
+ }
+
+ /// <inheritdoc />
+ protected override MapBaseObj CreateNew(IMemory memory, nuint address)
+ {
+ return new MapBaseObj(memory, address);
+ }
+}<
\ No newline at end of file
M src/Core/NosSmooth.LocalBinding/Structs/MapBaseObj.cs => src/Core/NosSmooth.LocalBinding/Structs/MapBaseObj.cs +0 -7
@@ 16,13 16,6 @@ public class MapBaseObj : NostaleObject
/// <summary>
/// Initializes a new instance of the <see cref="MapBaseObj"/> class.
/// </summary>
- public MapBaseObj()
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MapBaseObj"/> class.
- /// </summary>
/// <param name="memory">The memory.</param>
/// <param name="mapObjPointer">The map object pointer.</param>
public MapBaseObj(IMemory memory, nuint mapObjPointer)
A src/Core/NosSmooth.LocalBinding/Structs/NetworkManager.cs => src/Core/NosSmooth.LocalBinding/Structs/NetworkManager.cs +92 -0
@@ 0,0 1,92 @@
+//
+// NetworkManager.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.Errors;
+using NosSmooth.LocalBinding.Extensions;
+using NosSmooth.LocalBinding.Options;
+using Reloaded.Memory.Sources;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Structs;
+
+/// <summary>
+/// An object used for PacketSend and PacketReceive functions.
+/// </summary>
+public class NetworkManager : NostaleObject
+{
+ /// <summary>
+ /// Create <see cref="PlayerManager"/> instance.
+ /// </summary>
+ /// <param name="nosBrowserManager">The NosTale process browser.</param>
+ /// <param name="options">The options.</param>
+ /// <returns>The player manager or an error.</returns>
+ public static Result<NetworkManager> Create(NosBrowserManager nosBrowserManager, NetworkManagerOptions options)
+ {
+ var networkObjectAddress = nosBrowserManager.Scanner.FindPattern(options.NetworkObjectPattern);
+ if (!networkObjectAddress.Found)
+ {
+ return new BindingNotFoundError(options.NetworkObjectPattern, "NetworkBinding");
+ }
+
+ if (nosBrowserManager.Process.MainModule is null)
+ {
+ return new NotFoundError("Cannot find the main module of the target process.");
+ }
+
+ var staticAddress = (nuint)(nosBrowserManager.Process.MainModule.BaseAddress + networkObjectAddress.Offset
+ + options.NetworkObjectOffset);
+ return new NetworkManager(nosBrowserManager.Memory, staticAddress);
+ }
+
+ private readonly IMemory _memory;
+ private readonly nuint _staticNetworkManagerAddress;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NetworkManager"/> class.
+ /// </summary>
+ /// <param name="memory">The memory.</param>
+ /// <param name="staticNetworkManagerAddress">The pointer to the beginning of the player manager structure.</param>
+ public NetworkManager(IMemory memory, nuint staticNetworkManagerAddress)
+ : base(memory, staticNetworkManagerAddress)
+ {
+ _memory = memory;
+ _staticNetworkManagerAddress = staticNetworkManagerAddress;
+ }
+
+ /// <summary>
+ /// Gets the address to the player manager.
+ /// </summary>
+ public override nuint Address => GetManagerAddress(false);
+
+ private nuint GetManagerAddress(bool third)
+ {
+ nuint networkManager = _staticNetworkManagerAddress;
+ _memory.Read(networkManager, out networkManager);
+ _memory.Read(networkManager, out networkManager);
+ _memory.Read(networkManager, out networkManager);
+
+ if (third)
+ {
+ _memory.Read(networkManager + 0x34, out networkManager);
+ }
+
+ return networkManager;
+ }
+
+ /// <summary>
+ /// Gets an address pointer used in PacketSend function.
+ /// </summary>
+ /// <returns>Pointer to the object.</returns>
+ public nuint GetAddressForPacketSend()
+ => GetManagerAddress(false);
+
+ /// <summary>
+ /// Gets an address pointer used in PacketReceive function.
+ /// </summary>
+ /// <returns>Pointer to the object.</returns>
+ public nuint GetAddressForPacketReceive()
+ => GetManagerAddress(true);
+}<
\ No newline at end of file
M src/Core/NosSmooth.LocalBinding/Structs/NostaleList.cs => src/Core/NosSmooth.LocalBinding/Structs/NostaleList.cs +13 -7
@@ 5,6 5,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices.ObjectiveC;
using Reloaded.Memory.Pointers;
using Reloaded.Memory.Sources;
@@ 14,8 16,8 @@ namespace NosSmooth.LocalBinding.Structs;
/// A class representing a list from nostale.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
-public class NostaleList<T> : IEnumerable<T>
- where T : NostaleObject, new()
+public abstract class NostaleList<T> : IEnumerable<T>
+ where T : NostaleObject
{
private readonly IMemory _memory;
@@ 52,11 54,7 @@ public class NostaleList<T> : IEnumerable<T>
_memory.SafeRead(Address + 0x04, out int arrayAddress);
_memory.SafeRead((nuint)arrayAddress + (nuint)(0x04 * index), out int objectAddress);
- return new T
- {
- Memory = _memory,
- Address = (nuint)objectAddress
- };
+ return CreateNew(_memory, (nuint)objectAddress);
}
}
@@ 72,6 70,14 @@ public class NostaleList<T> : IEnumerable<T>
}
}
+ /// <summary>
+ /// Create a new instance of <typeparamref name="T"/> with the given memory and address.
+ /// </summary>
+ /// <param name="memory">The memory.</param>
+ /// <param name="address">The address.</param>
+ /// <returns>The new object.</returns>
+ protected abstract T CreateNew(IMemory memory, nuint address);
+
/// <inheritdoc/>
public IEnumerator<T> GetEnumerator()
{
M src/Core/NosSmooth.LocalBinding/Structs/NostaleObject.cs => src/Core/NosSmooth.LocalBinding/Structs/NostaleObject.cs +0 -7
@@ 16,13 16,6 @@ public abstract class NostaleObject
/// <summary>
/// Initializes a new instance of the <see cref="NostaleObject"/> class.
/// </summary>
- internal NostaleObject()
- {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="NostaleObject"/> class.
- /// </summary>
/// <param name="memory">The memory.</param>
/// <param name="address">The address in the memory.</param>
protected NostaleObject(IMemory memory, nuint address)
M src/Core/NosSmooth.LocalBinding/Structs/PetManagerList.cs => src/Core/NosSmooth.LocalBinding/Structs/PetManagerList.cs +6 -0
@@ 50,4 50,10 @@ public class PetManagerList : NostaleList<PetManager>
: base(memory, memory.FollowStaticAddressOffsets(staticPetManagerListAddress, staticPetManagerOffsets))
{
}
+
+ /// <inheritdoc />
+ protected override PetManager CreateNew(IMemory memory, nuint address)
+ {
+ return new PetManager(memory, address);
+ }
}=
\ No newline at end of file
M src/Core/NosSmooth.LocalBinding/Structs/SceneManager.cs => src/Core/NosSmooth.LocalBinding/Structs/SceneManager.cs +4 -4
@@ 65,22 65,22 @@ public class SceneManager
/// <summary>
/// Gets the player list.
/// </summary>
- public NostaleList<MapBaseObj> PlayerList => new NostaleList<MapBaseObj>(_memory, ReadPtr(Address + 0xC));
+ public NostaleList<MapBaseObj> PlayerList => new MapBaseList(_memory, ReadPtr(Address + 0xC));
/// <summary>
/// Gets the monster list.
/// </summary>
- public NostaleList<MapBaseObj> MonsterList => new NostaleList<MapBaseObj>(_memory, ReadPtr(Address + 0x10));
+ public NostaleList<MapBaseObj> MonsterList => new MapBaseList(_memory, ReadPtr(Address + 0x10));
/// <summary>
/// Gets the npc list.
/// </summary>
- public NostaleList<MapBaseObj> NpcList => new NostaleList<MapBaseObj>(_memory, ReadPtr(Address + 0x14));
+ public NostaleList<MapBaseObj> NpcList => new MapBaseList(_memory, ReadPtr(Address + 0x14));
/// <summary>
/// Gets the item list.
/// </summary>
- public NostaleList<MapBaseObj> ItemList => new NostaleList<MapBaseObj>(_memory, ReadPtr(Address + 0x18));
+ public NostaleList<MapBaseObj> ItemList => new MapBaseList(_memory, ReadPtr(Address + 0x18));
/// <summary>
/// Gets the entity that is currently being followed by the player.
A src/Core/NosSmooth.LocalBinding/Structs/UnitManager.cs => src/Core/NosSmooth.LocalBinding/Structs/UnitManager.cs +66 -0
@@ 0,0 1,66 @@
+//
+// UnitManager.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.Errors;
+using NosSmooth.LocalBinding.Extensions;
+using NosSmooth.LocalBinding.Options;
+using Reloaded.Memory.Sources;
+using Remora.Results;
+
+namespace NosSmooth.LocalBinding.Structs;
+
+/// <summary>
+/// An object used for PacketSend and PacketReceive functions.
+/// </summary>
+public class UnitManager : NostaleObject
+{
+ /// <summary>
+ /// Create <see cref="PlayerManager"/> instance.
+ /// </summary>
+ /// <param name="nosBrowserManager">The NosTale process browser.</param>
+ /// <param name="options">The options.</param>
+ /// <returns>The player manager or an error.</returns>
+ public static Result<UnitManager> Create(NosBrowserManager nosBrowserManager, UnitManagerOptions options)
+ {
+ var unitObjectAddress = nosBrowserManager.Scanner.FindPattern(options.UnitManagerPattern);
+ if (!unitObjectAddress.Found)
+ {
+ return new BindingNotFoundError(options.UnitManagerPattern, "UnitManager");
+ }
+
+ if (nosBrowserManager.Process.MainModule is null)
+ {
+ return new NotFoundError("Cannot find the main module of the target process.");
+ }
+
+ var staticAddress = (int)(nosBrowserManager.Process.MainModule.BaseAddress + unitObjectAddress.Offset);
+ return new UnitManager(nosBrowserManager.Memory, staticAddress, options.UnitManagerOffsets);
+ }
+
+ private readonly IMemory _memory;
+ private readonly int _staticUnitManagerAddress;
+ private readonly int[] _optionsUnitManagerOffsets;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnitManager"/> class.
+ /// </summary>
+ /// <param name="memory">The memory.</param>
+ /// <param name="staticUnitManagerAddress">The pointer to the beginning of the player manager structure.</param>
+ /// <param name="optionsUnitManagerOffsets">The unit manager offsets.</param>
+ public UnitManager(IMemory memory, int staticUnitManagerAddress, int[] optionsUnitManagerOffsets)
+ : base(memory, (nuint)staticUnitManagerAddress)
+ {
+ _memory = memory;
+ _staticUnitManagerAddress = staticUnitManagerAddress;
+ _optionsUnitManagerOffsets = optionsUnitManagerOffsets;
+ }
+
+ /// <summary>
+ /// Gets the address to the player manager.
+ /// </summary>
+ public override nuint Address => _memory.FollowStaticAddressOffsets
+ (_staticUnitManagerAddress, _optionsUnitManagerOffsets);
+}<
\ No newline at end of file
M src/Core/NosSmooth.LocalClient/CommandHandlers/Attack/AttackCommandHandler.cs => src/Core/NosSmooth.LocalClient/CommandHandlers/Attack/AttackCommandHandler.cs +6 -5
@@ 10,6 10,7 @@ using NosSmooth.Core.Commands.Attack;
using NosSmooth.Core.Commands.Control;
using NosSmooth.Core.Extensions;
using NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Objects;
using NosSmooth.LocalBinding.Structs;
using Remora.Results;
@@ 23,7 24,7 @@ public class AttackCommandHandler : ICommandHandler<AttackCommand>
{
private readonly INostaleClient _nostaleClient;
private readonly NosThreadSynchronizer _synchronizer;
- private readonly UnitManagerBinding _unitManagerBinding;
+ private readonly IEntityFocusHook _entityFocusHook;
private readonly SceneManager _sceneManager;
/// <summary>
@@ 31,19 32,19 @@ public class AttackCommandHandler : ICommandHandler<AttackCommand>
/// </summary>
/// <param name="nostaleClient">The NosTale client.</param>
/// <param name="synchronizer">The thread synchronizer.</param>
- /// <param name="unitManagerBinding">The unit manager binding.</param>
+ /// <param name="entityFocusHook">The entity focus hook.</param>
/// <param name="sceneManager">The scene manager.</param>
public AttackCommandHandler
(
INostaleClient nostaleClient,
NosThreadSynchronizer synchronizer,
- UnitManagerBinding unitManagerBinding,
+ IEntityFocusHook entityFocusHook,
SceneManager sceneManager
)
{
_nostaleClient = nostaleClient;
_synchronizer = synchronizer;
- _unitManagerBinding = unitManagerBinding;
+ _entityFocusHook = entityFocusHook;
_sceneManager = sceneManager;
}
@@ 55,7 56,7 @@ public class AttackCommandHandler : ICommandHandler<AttackCommand>
var entityResult = _sceneManager.FindEntity(command.TargetId.Value);
if (entityResult.IsDefined(out var entity))
{
- _synchronizer.EnqueueOperation(() => _unitManagerBinding.FocusEntity(entity));
+ _synchronizer.EnqueueOperation(() => _entityFocusHook.WrapperFunction(entity));
}
}
M src/Core/NosSmooth.LocalClient/CommandHandlers/Walk/PetWalkCommandHandler.cs => src/Core/NosSmooth.LocalClient/CommandHandlers/Walk/PetWalkCommandHandler.cs +25 -8
@@ 9,7 9,10 @@ using NosSmooth.Core.Client;
using NosSmooth.Core.Commands;
using NosSmooth.Core.Commands.Control;
using NosSmooth.Core.Commands.Walking;
+using NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Objects;
+using NosSmooth.LocalBinding.Structs;
using Remora.Results;
namespace NosSmooth.LocalClient.CommandHandlers.Walk;
@@ 24,7 27,9 @@ public class PetWalkCommandHandler : ICommandHandler<PetWalkCommand>
/// </summary>
public const string PetWalkControlGroup = "PetWalk";
- private readonly PetManagerBinding _petManagerBinding;
+ private readonly IPetWalkHook _petWalkHook;
+ private readonly PetManagerList _petManagerList;
+ private readonly NosThreadSynchronizer _threadSynchronizer;
private readonly UserActionDetector _userActionDetector;
private readonly INostaleClient _nostaleClient;
private readonly WalkCommandHandlerOptions _options;
@@ 32,20 37,26 @@ public class PetWalkCommandHandler : ICommandHandler<PetWalkCommand>
/// <summary>
/// Initializes a new instance of the <see cref="PetWalkCommandHandler"/> class.
/// </summary>
- /// <param name="petManagerBinding">The character object binding.</param>
+ /// <param name="petWalkHook">The pet walk hook.</param>
+ /// <param name="petManagerList">The pet manager list.</param>
+ /// <param name="threadSynchronizer">The thread synchronizer.</param>
/// <param name="userActionDetector">The user action detector.</param>
/// <param name="nostaleClient">The nostale client.</param>
/// <param name="options">The options.</param>
public PetWalkCommandHandler
(
- PetManagerBinding petManagerBinding,
+ IPetWalkHook petWalkHook,
+ PetManagerList petManagerList,
+ NosThreadSynchronizer threadSynchronizer,
UserActionDetector userActionDetector,
INostaleClient nostaleClient,
IOptions<WalkCommandHandlerOptions> options
)
{
_options = options.Value;
- _petManagerBinding = petManagerBinding;
+ _petWalkHook = petWalkHook;
+ _petManagerList = petManagerList;
+ _threadSynchronizer = threadSynchronizer;
_userActionDetector = userActionDetector;
_nostaleClient = nostaleClient;
}
@@ 53,17 64,23 @@ public class PetWalkCommandHandler : ICommandHandler<PetWalkCommand>
/// <inheritdoc/>
public async Task<Result> HandleCommand(PetWalkCommand command, CancellationToken ct = default)
{
- if (_petManagerBinding.PetManagerList.Length < command.PetSelector + 1)
+ if (_petManagerList.Length < command.PetSelector + 1)
{
return new NotFoundError("Could not find the pet using the given selector.");
}
- var petManager = _petManagerBinding.PetManagerList[command.PetSelector];
+ var petManager = _petManagerList[command.PetSelector];
var handler = new ControlCommandWalkHandler
(
_nostaleClient,
- async (x, y, ct) => await _userActionDetector.NotUserActionAsync
- (() => _petManagerBinding.PetWalk(command.PetSelector, x, y), ct),
+ async (x, y, ct) =>
+ await _threadSynchronizer.SynchronizeAsync
+ (
+ () => _userActionDetector.NotUserAction<Result<bool>>
+ (
+ () => _petWalkHook.WrapperFunction(petManager, (ushort)x, (ushort)y)
+ )
+ ),
petManager,
_options
);
M src/Core/NosSmooth.LocalClient/CommandHandlers/Walk/PlayerWalkCommandHandler.cs => src/Core/NosSmooth.LocalClient/CommandHandlers/Walk/PlayerWalkCommandHandler.cs +22 -7
@@ 10,7 10,10 @@ using NosSmooth.Core.Commands;
using NosSmooth.Core.Commands.Control;
using NosSmooth.Core.Commands.Walking;
using NosSmooth.Core.Extensions;
+using NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Objects;
+using NosSmooth.LocalBinding.Structs;
using Remora.Results;
namespace NosSmooth.LocalClient.CommandHandlers.Walk;
@@ 25,7 28,9 @@ public class PlayerWalkCommandHandler : ICommandHandler<PlayerWalkCommand>
/// </summary>
public const string PlayerWalkControlGroup = "PlayerWalk";
- private readonly PlayerManagerBinding _playerManagerBinding;
+ private readonly PlayerManager _playerManager;
+ private readonly IPlayerWalkHook _playerWalkHook;
+ private readonly NosThreadSynchronizer _threadSynchronizer;
private readonly UserActionDetector _userActionDetector;
private readonly INostaleClient _nostaleClient;
private readonly WalkCommandHandlerOptions _options;
@@ 33,20 38,26 @@ public class PlayerWalkCommandHandler : ICommandHandler<PlayerWalkCommand>
/// <summary>
/// Initializes a new instance of the <see cref="PlayerWalkCommandHandler"/> class.
/// </summary>
- /// <param name="playerManagerBinding">The character object binding.</param>
+ /// <param name="playerManager">The player manager.</param>
+ /// <param name="playerWalkHook">The player walk hook.</param>
+ /// <param name="threadSynchronizer">The thread synchronizer.</param>
/// <param name="userActionDetector">The user action detector.</param>
/// <param name="nostaleClient">The nostale client.</param>
/// <param name="options">The options.</param>
public PlayerWalkCommandHandler
(
- PlayerManagerBinding playerManagerBinding,
+ PlayerManager playerManager,
+ IPlayerWalkHook playerWalkHook,
+ NosThreadSynchronizer threadSynchronizer,
UserActionDetector userActionDetector,
INostaleClient nostaleClient,
IOptions<WalkCommandHandlerOptions> options
)
{
_options = options.Value;
- _playerManagerBinding = playerManagerBinding;
+ _playerManager = playerManager;
+ _playerWalkHook = playerWalkHook;
+ _threadSynchronizer = threadSynchronizer;
_userActionDetector = userActionDetector;
_nostaleClient = nostaleClient;
}
@@ 57,9 68,13 @@ public class PlayerWalkCommandHandler : ICommandHandler<PlayerWalkCommand>
var handler = new ControlCommandWalkHandler
(
_nostaleClient,
- async (x, y, ct) =>
- await _userActionDetector.NotUserWalkAsync(_playerManagerBinding, x, y, ct),
- _playerManagerBinding.PlayerManager,
+ async (x, y, ct)
+ => await _threadSynchronizer.SynchronizeAsync
+ (
+ () => _userActionDetector.NotUserWalk(_playerWalkHook, x, y),
+ ct
+ ),
+ _playerManager,
_options
);
M src/Core/NosSmooth.LocalClient/NostaleLocalClient.cs => src/Core/NosSmooth.LocalClient/NostaleLocalClient.cs +21 -24
@@ 14,6 14,7 @@ using NosSmooth.Core.Extensions;
using NosSmooth.Core.Packets;
using NosSmooth.LocalBinding;
using NosSmooth.LocalBinding.EventArgs;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Objects;
using NosSmooth.LocalBinding.Structs;
using NosSmooth.Packets;
@@ 34,9 35,7 @@ namespace NosSmooth.LocalClient;
public class NostaleLocalClient : BaseNostaleClient
{
private readonly NosThreadSynchronizer _synchronizer;
- private readonly NetworkBinding _networkBinding;
- private readonly PlayerManagerBinding _playerManagerBinding;
- private readonly PetManagerBinding _petManagerBinding;
+ private readonly IHookManager _hookManager;
private readonly ControlCommands _controlCommands;
private readonly IPacketSerializer _packetSerializer;
private readonly PacketHandler _packetHandler;
@@ 51,9 50,7 @@ public class NostaleLocalClient : BaseNostaleClient
/// Initializes a new instance of the <see cref="NostaleLocalClient"/> class.
/// </summary>
/// <param name="synchronizer">The thread synchronizer.</param>
- /// <param name="networkBinding">The network binding.</param>
- /// <param name="playerManagerBinding">The player manager binding.</param>
- /// <param name="petManagerBinding">The pet manager binding.</param>
+ /// <param name="hookManager">The hook manager.</param>
/// <param name="controlCommands">The control commands.</param>
/// <param name="commandProcessor">The command processor.</param>
/// <param name="packetSerializer">The packet serializer.</param>
@@ 65,9 62,7 @@ public class NostaleLocalClient : BaseNostaleClient
public NostaleLocalClient
(
NosThreadSynchronizer synchronizer,
- NetworkBinding networkBinding,
- PlayerManagerBinding playerManagerBinding,
- PetManagerBinding petManagerBinding,
+ IHookManager hookManager,
ControlCommands controlCommands,
CommandProcessor commandProcessor,
IPacketSerializer packetSerializer,
@@ 81,9 76,7 @@ public class NostaleLocalClient : BaseNostaleClient
{
_options = options.Value;
_synchronizer = synchronizer;
- _networkBinding = networkBinding;
- _playerManagerBinding = playerManagerBinding;
- _petManagerBinding = petManagerBinding;
+ _hookManager = hookManager;
_controlCommands = controlCommands;
_packetSerializer = packetSerializer;
_packetHandler = packetHandler;
@@ 98,12 91,12 @@ public class NostaleLocalClient : BaseNostaleClient
_stopRequested = stopRequested;
_logger.LogInformation("Starting local client");
_synchronizer.StartSynchronizer();
- _networkBinding.PacketSendCall += SendCallCallback;
- _networkBinding.PacketReceiveCall += ReceiveCallCallback;
+ _hookManager.PacketSend.Called += SendCallCallback;
+ _hookManager.PacketReceive.Called += ReceiveCallCallback;
- _playerManagerBinding.FollowEntityCall += FollowEntity;
- _playerManagerBinding.WalkCall += Walk;
- _petManagerBinding.PetWalkCall += PetWalk;
+ _hookManager.EntityFollow.Called += FollowEntity;
+ _hookManager.PlayerWalk.Called += Walk;
+ _hookManager.PetWalk.Called += PetWalk;
try
{
@@ 114,11 107,15 @@ public class NostaleLocalClient : BaseNostaleClient
// ignored
}
- _networkBinding.PacketSendCall -= SendCallCallback;
- _networkBinding.PacketReceiveCall -= ReceiveCallCallback;
- _playerManagerBinding.FollowEntityCall -= FollowEntity;
- _playerManagerBinding.WalkCall -= Walk;
- _petManagerBinding.PetWalkCall -= PetWalk;
+ _hookManager.PacketSend.Called -= SendCallCallback;
+ _hookManager.PacketReceive.Called -= ReceiveCallCallback;
+
+ _hookManager.EntityFollow.Called -= FollowEntity;
+ _hookManager.PlayerWalk.Called -= Walk;
+ _hookManager.PetWalk.Called -= PetWalk;
+
+ // the hooks are not needed anymore.
+ _hookManager.DisableAll();
return Result.FromSuccess();
}
@@ 187,7 184,7 @@ public class NostaleLocalClient : BaseNostaleClient
{
_synchronizer.EnqueueOperation
(
- () => _networkBinding.SendPacket(packetString)
+ () => _hookManager.PacketSend.WrapperFunction(packetString)
);
_logger.LogDebug($"Sending client packet {packetString}");
}
@@ 196,7 193,7 @@ public class NostaleLocalClient : BaseNostaleClient
{
_synchronizer.EnqueueOperation
(
- () => _networkBinding.ReceivePacket(packetString)
+ () => _hookManager.PacketReceive.WrapperFunction(packetString)
);
_logger.LogDebug($"Receiving client packet {packetString}");
}
M src/Core/NosSmooth.LocalClient/UserActionDetector.cs => src/Core/NosSmooth.LocalClient/UserActionDetector.cs +52 -6
@@ 5,6 5,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Data.SqlTypes;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Objects;
using NosSmooth.LocalBinding.Structs;
using Remora.Results;
@@ 50,6 51,36 @@ public class UserActionDetector
/// Execute an action that makes sure walk won't be treated as a user action.
/// </summary>
/// <param name="action">The action to execute.</param>
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>Return value of the action.</returns>
+ public async Task NotUserActionAsync(Action action, CancellationToken ct = default)
+ {
+ await _semaphore.WaitAsync(ct);
+ _handlingDisabled = true;
+ action();
+ _handlingDisabled = false;
+ _semaphore.Release();
+ }
+
+ /// <summary>
+ /// Execute an action that makes sure walk won't be treated as a user action.
+ /// </summary>
+ /// <param name="action">The action to execute.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Result NotUserAction(Func<Result> action)
+ {
+ _semaphore.Wait();
+ _handlingDisabled = true;
+ var result = action();
+ _handlingDisabled = false;
+ _semaphore.Release();
+ return result;
+ }
+
+ /// <summary>
+ /// Execute an action that makes sure walk won't be treated as a user action.
+ /// </summary>
+ /// <param name="action">The action to execute.</param>
/// <typeparam name="T">The return type.</typeparam>
/// <returns>Return value of the action.</returns>
public T NotUserAction<T>(Func<T> action)
@@ 65,23 96,38 @@ public class UserActionDetector
/// <summary>
/// Execute walk action and make sure walk won't be treated as a user action.
/// </summary>
- /// <param name="playerManager">The player manager.</param>
+ /// <param name="walkHook">The walk hook.</param>
+ /// <param name="x">The x coordinate.</param>
+ /// <param name="y">The y coordinate.</param>
+ /// <returns>The result of the walk.</returns>
+ public Result<bool> NotUserWalk(IPlayerWalkHook walkHook, short x, short y)
+ => NotUserAction
+ (
+ () =>
+ {
+ _lastWalkPosition = ((ushort)x, (ushort)y);
+ return walkHook.WrapperFunction((ushort)x, (ushort)y);
+ }
+ );
+
+ /// <summary>
+ /// Execute walk action and make sure walk won't be treated as a user action.
+ /// </summary>
+ /// <param name="walkHook">The walk hook.</param>
/// <param name="x">The x coordinate.</param>
/// <param name="y">The y coordinate.</param>
/// <param name="ct">The cancellation token used for cancelling the operation.</param>
/// <returns>The result of the walk.</returns>
- public async Task<Result<bool>> NotUserWalkAsync(PlayerManagerBinding playerManager, short x, short y, CancellationToken ct = default)
- {
- return await NotUserActionAsync
+ public async Task<Result<bool>> NotUserWalkAsync(IPlayerWalkHook walkHook, short x, short y, CancellationToken ct = default)
+ => await NotUserActionAsync
(
() =>
{
_lastWalkPosition = ((ushort)x, (ushort)y);
- return playerManager.Walk(x, y);
+ return walkHook.WrapperFunction((ushort)x, (ushort)y);
},
ct
);
- }
/// <summary>
/// Checks whether the given Walk call
R src/Core/NosSmooth.LocalBinding/Options/PeriodicBindingOptions.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/EventArgs/HookStateEventArgs.cs +15 -11
@@ 1,21 1,25 @@
//
-// PeriodicBindingOptions.cs
+// 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.
-using NosSmooth.LocalBinding.Objects;
+namespace NosSmooth.Extensions.SharedBinding.EventArgs;
-namespace NosSmooth.LocalBinding.Options;
-
-/// <summary>
-/// Options for <see cref="PeriodicBinding"/>.
-/// </summary>
-public class PeriodicBindingOptions
+/// <inheritdoc />
+public class HookStateEventArgs : System.EventArgs
{
/// <summary>
- /// Gets or sets the configuration for any periodic function hook.
+ /// 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 HookOptions PeriodicHook { get; set; }
- = new HookOptions(true, "55 8B EC 53 56 83 C4", 0);
+ public bool Enabled { get; }
}=
\ No newline at end of file
A src/Extensions/NosSmooth.Extensions.SharedBinding/Extensions/ServiceCollectionExtensions.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/Extensions/ServiceCollectionExtensions.cs +104 -0
@@ 0,0 1,104 @@
+//
+// ServiceCollectionExtensions.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.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using NosSmooth.Data.NOSFiles;
+using NosSmooth.Extensions.SharedBinding.Hooks;
+using NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Extensions;
+using NosSmooth.LocalBinding.Hooks;
+using NosSmooth.PacketSerializer.Packets;
+
+namespace NosSmooth.Extensions.SharedBinding.Extensions;
+
+/// <summary>
+/// Extension methods for <see cref="IServiceProvider"/>.
+/// </summary>
+public static class ServiceCollectionExtensions
+{
+ /// <summary>
+ /// Adds shared <see cref="IHookManager"/>.
+ /// </summary>
+ /// <param name="serviceCollection">The collection.</param>
+ /// <returns>The same collection.</returns>
+ public static IServiceCollection ShareHooks(this IServiceCollection serviceCollection)
+ {
+ var originalHookManager = serviceCollection
+ .Last(x => x.ServiceType == typeof(IHookManager));
+
+ var sharedHookManager = ServiceDescriptor.Singleton<SharedHookManager>
+ (
+ p =>
+ {
+ var sharedHookManager = p.GetRequiredService<SharedManager>().GetShared<IHookManager>(p);
+ return new SharedHookManager(sharedHookManager);
+ }
+ );
+
+ return serviceCollection
+ .Configure<SharedOptions>(o => o.AddDescriptor(originalHookManager))
+ .Configure<SharedOptions>(o => o.AddDescriptor(sharedHookManager))
+ .AddSingleton<SharedHookManager>(p => SharedManager.Instance.GetShared<SharedHookManager>(p))
+ .Replace(ServiceDescriptor.Singleton<IHookManager, SingleHookManager>());
+ }
+
+ /// <summary>
+ /// 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 Share<T>(this IServiceCollection serviceCollection)
+ where T : class
+ {
+ var original = serviceCollection
+ .Last(x => x.ServiceType == typeof(T));
+
+ return serviceCollection
+ .Configure<SharedOptions>(o => o.AddDescriptor(original))
+ .Replace
+ (
+ ServiceDescriptor.Singleton<T>(p => SharedManager.Instance.GetShared<T>(p))
+ );
+ }
+
+ /// <summary>
+ /// 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 TryShare<T>(this IServiceCollection serviceCollection)
+ where T : class
+ {
+ if (serviceCollection.Any(x => x.ServiceType == typeof(T)))
+ {
+ return serviceCollection.Share<T>();
+ }
+
+ return serviceCollection;
+ }
+
+ /// <summary>
+ /// 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)
+ {
+ 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
A src/Extensions/NosSmooth.Extensions.SharedBinding/NosSmooth.Extensions.SharedBinding.csproj => src/Extensions/NosSmooth.Extensions.SharedBinding/NosSmooth.Extensions.SharedBinding.csproj +19 -0
@@ 0,0 1,19 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net7.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+ </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>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\Core\NosSmooth.LocalBinding\NosSmooth.LocalBinding.csproj" />
+ </ItemGroup>
+
+</Project>
A src/Extensions/NosSmooth.Extensions.SharedBinding/SharedManager.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/SharedManager.cs +93 -0
@@ 0,0 1,93 @@
+//
+// SharedManager.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.DependencyInjection;
+using Microsoft.Extensions.Options;
+using NosSmooth.Data.NOSFiles;
+using NosSmooth.LocalBinding;
+using NosSmooth.PacketSerializer.Packets;
+
+namespace NosSmooth.Extensions.SharedBinding;
+
+/// <summary>
+/// Manager for sharing <see cref="NosBindingManager"/>,
+/// <see cref="NostaleDataFilesManager"/> and
+/// <see cref="IPacketTypesRepository"/>.
+/// </summary>
+public class SharedManager
+{
+ private static SharedManager? _instance;
+ private Dictionary<Type, object> _sharedData = new();
+
+ /// <summary>
+ /// A singleton instance.
+ /// One per process.
+ /// </summary>
+ public static SharedManager Instance
+ {
+ get
+ {
+ if (_instance is null)
+ {
+ _instance = new SharedManager();
+ }
+
+ return _instance;
+ }
+ }
+
+ private SharedManager()
+ {
+ }
+
+ /// <summary>
+ /// Get shared equivalent of the given type.
+ /// </summary>
+ /// <param name="services">The service provider.</param>
+ /// <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 (!_sharedData.ContainsKey(typeof(T)))
+ {
+ _sharedData[typeof(T)] = CreateShared<T>(services);
+ }
+
+ return (T)_sharedData[typeof(T)];
+ }
+
+ private T CreateShared<T>(IServiceProvider services)
+ where T : class
+ {
+ var options = services.GetRequiredService<IOptions<SharedOptions>>();
+ var descriptor = options.Value.GetDescriptor(typeof(T));
+
+ if (descriptor is null)
+ {
+ throw new InvalidOperationException
+ ($"Could not find {typeof(T)} in the service provider when trying to make a shared instance.");
+ }
+
+ if (descriptor.ImplementationInstance is not null)
+ {
+ return (T)descriptor.ImplementationInstance;
+ }
+
+ if (descriptor.ImplementationFactory is not null)
+ {
+ return (T)descriptor.ImplementationFactory(services);
+ }
+
+ if (descriptor.ImplementationType is not null)
+ {
+ return (T)ActivatorUtilities.CreateInstance(services, descriptor.ImplementationType);
+ }
+
+ return ActivatorUtilities.CreateInstance<T>(services);
+ }
+}<
\ No newline at end of file
A src/Extensions/NosSmooth.Extensions.SharedBinding/SharedOptions.cs => src/Extensions/NosSmooth.Extensions.SharedBinding/SharedOptions.cs +45 -0
@@ 0,0 1,45 @@
+//
+// SharedOptions.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.DependencyInjection;
+using NosSmooth.Data.NOSFiles;
+using NosSmooth.LocalBinding;
+using NosSmooth.PacketSerializer.Packets;
+
+namespace NosSmooth.Extensions.SharedBinding;
+
+/// <summary>
+/// Options for <see cref="SharedManager"/>.
+/// </summary>
+internal class SharedOptions
+{
+ private Dictionary<Type, ServiceDescriptor> _descriptors = new();
+
+ /// <summary>
+ /// Add service descriptor for given type.
+ /// </summary>
+ /// <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>
+ /// Get descriptor for the given type.
+ /// </summary>
+ /// <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
M src/Samples/External/ExternalBrowser/Program.cs => src/Samples/External/ExternalBrowser/Program.cs +13 -2
@@ 8,6 8,7 @@ using System.Diagnostics;
using NosSmooth.Core.Extensions;
using NosSmooth.LocalBinding;
using NosSmooth.LocalBinding.Options;
+using NosSmooth.LocalBinding.Structs;
namespace ExternalBrowser;
@@ 25,6 26,8 @@ public class Program
var playerManagerOptions = new PlayerManagerOptions();
var sceneManagerOptions = new SceneManagerOptions();
var petManagerOptions = new PetManagerOptions();
+ var networkManagerOptions = new NetworkManagerOptions();
+ var unitManagerOptions = new UnitManagerOptions();
foreach (var argument in arguments)
{
@@ 45,7 48,14 @@ public class Program
foreach (var process in processes)
{
var externalBrowser = new NosBrowserManager
- (process, playerManagerOptions, sceneManagerOptions, petManagerOptions);
+ (
+ process,
+ playerManagerOptions,
+ sceneManagerOptions,
+ petManagerOptions,
+ networkManagerOptions,
+ unitManagerOptions
+ );
if (!externalBrowser.IsNostaleProcess)
{
@@ 68,7 78,8 @@ public class Program
continue;
}
- Console.WriteLine($"Player in process {process.Id} is named {externalBrowser.PlayerManager.Player.Name}");
+ Console.WriteLine
+ ($"Player in process {process.Id} is named {externalBrowser.PlayerManager.Player.Name}");
}
}
}
M src/Samples/HighLevel/SimplePiiBot/Commands/EntityCommands.cs => src/Samples/HighLevel/SimplePiiBot/Commands/EntityCommands.cs +21 -12
@@ 9,6 9,7 @@ using NosSmooth.Extensions.Combat.Errors;
using NosSmooth.Game;
using NosSmooth.Game.Apis;
using NosSmooth.LocalBinding;
+using NosSmooth.LocalBinding.Hooks;
using NosSmooth.LocalBinding.Objects;
using NosSmooth.LocalBinding.Structs;
using NosSmooth.Packets.Enums.Chat;
@@ 24,36 25,32 @@ namespace SimplePiiBot.Commands;
public class EntityCommands : CommandGroup
{
private readonly Game _game;
+ private readonly IHookManager _hookManager;
private readonly NosThreadSynchronizer _synchronizer;
- private readonly UnitManagerBinding _unitManagerBinding;
private readonly SceneManager _sceneManager;
- private readonly PlayerManagerBinding _playerManagerBinding;
private readonly FeedbackService _feedbackService;
/// <summary>
/// Initializes a new instance of the <see cref="EntityCommands"/> class.
/// </summary>
/// <param name="game">The game.</param>
+ /// <param name="hookManager">The hook manager.</param>
/// <param name="synchronizer">The thread synchronizer.</param>
- /// <param name="unitManagerBinding">The scene manager binding.</param>
/// <param name="sceneManager">The scene manager.</param>
- /// <param name="playerManagerBinding">The character binding.</param>
/// <param name="feedbackService">The feedback service.</param>
public EntityCommands
(
Game game,
+ IHookManager hookManager,
NosThreadSynchronizer synchronizer,
- UnitManagerBinding unitManagerBinding,
SceneManager sceneManager,
- PlayerManagerBinding playerManagerBinding,
FeedbackService feedbackService
)
{
_game = game;
+ _hookManager = hookManager;
_synchronizer = synchronizer;
- _unitManagerBinding = unitManagerBinding;
_sceneManager = sceneManager;
- _playerManagerBinding = playerManagerBinding;
_feedbackService = feedbackService;
}
@@ 117,7 114,11 @@ public class EntityCommands : CommandGroup
return await _synchronizer.SynchronizeAsync
(
- () => _unitManagerBinding.FocusEntity(entityResult.Entity),
+ () =>
+ {
+ _hookManager.EntityFocus.WrapperFunction(entityResult.Entity);
+ return Result.FromSuccess();
+ },
CancellationToken
);
}
@@ 131,14 132,18 @@ public class EntityCommands : CommandGroup
public async Task<Result> HandleFollowAsync(int entityId)
{
var entityResult = _sceneManager.FindEntity(entityId);
- if (!entityResult.IsSuccess)
+ if (!entityResult.IsDefined(out var entity))
{
return Result.FromError(entityResult);
}
return await _synchronizer.SynchronizeAsync
(
- () => _playerManagerBinding.FollowEntity(entityResult.Entity),
+ () =>
+ {
+ _hookManager.EntityFollow.WrapperFunction(entity);
+ return Result.FromSuccess();
+ },
CancellationToken
);
}
@@ 152,7 157,11 @@ public class EntityCommands : CommandGroup
{
return await _synchronizer.SynchronizeAsync
(
- () => _playerManagerBinding.UnfollowEntity(),
+ () =>
+ {
+ _hookManager.EntityUnfollow.WrapperFunction();
+ return Result.FromSuccess();
+ },
CancellationToken
);
}
M src/Samples/HighLevel/SimplePiiBot/DllMain.cs => src/Samples/HighLevel/SimplePiiBot/DllMain.cs +2 -0
@@ 13,6 13,7 @@ using NosSmooth.Core.Extensions;
using NosSmooth.Data.NOSFiles.Extensions;
using NosSmooth.Extensions.Combat.Extensions;
using NosSmooth.Extensions.Pathfinding.Extensions;
+using NosSmooth.Extensions.SharedBinding.Extensions;
using NosSmooth.Game.Extensions;
using NosSmooth.LocalClient.Extensions;
using Remora.Commands.Extensions;
@@ 81,6 82,7 @@ public class DllMain
.AddNostaleGame()
.AddLocalClient()
.AddNostaleDataFiles()
+ .ShareNosSmooth()
.AddNostaleCombat()
.AddNostalePathfinding()
.AddSingleton<Bot>()
M src/Samples/HighLevel/SimplePiiBot/SimplePiiBot.csproj => src/Samples/HighLevel/SimplePiiBot/SimplePiiBot.csproj +1 -0
@@ 31,6 31,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\Extensions\NosSmooth.ChatCommands\NosSmooth.ChatCommands.csproj" />
+ <ProjectReference Include="..\..\..\Extensions\NosSmooth.Extensions.SharedBinding\NosSmooth.Extensions.SharedBinding.csproj" />
</ItemGroup>
</Project>
M src/Samples/LowLevel/InterceptNameChanger/InterceptNameChanger.csproj => src/Samples/LowLevel/InterceptNameChanger/InterceptNameChanger.csproj +4 -0
@@ 4,6 4,7 @@
<Nullable>enable</Nullable>
<LangVersion>10</LangVersion>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
+ <EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Costura.Fody" Version="5.7.0">
@@ 18,8 19,11 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
<PackageReference Include="NosSmooth.Core" Version="3.0.1" />
+ <PackageReference Include="NosSmooth.Packets" Version="2.3.4" />
+ <PackageReference Include="Remora.Results" Version="7.2.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Core\NosSmooth.LocalClient\NosSmooth.LocalClient.csproj" />
+ <ProjectReference Include="..\..\..\Extensions\NosSmooth.Extensions.SharedBinding\NosSmooth.Extensions.SharedBinding.csproj" />
</ItemGroup>
</Project>=
\ No newline at end of file
M src/Samples/LowLevel/InterceptNameChanger/NameChangeInterceptor.cs => src/Samples/LowLevel/InterceptNameChanger/NameChangeInterceptor.cs +3 -2
@@ 4,6 4,7 @@
// 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;
using Microsoft.Extensions.Logging;
using NosSmooth.Core.Client;
using NosSmooth.LocalClient;
@@ 61,10 62,10 @@ namespace InterceptNameChanger
/// <inheritdoc/>
public bool InterceptReceive(ref string packet)
{
- if (packet.StartsWith("c_info"))
+ if (packet.StartsWith("c_info "))
{
var oldPart = packet.Substring(packet.IndexOf(' ', 7));
- var result = _client.ReceivePacketAsync($"c_info {_name} " + oldPart)
+ var result = _client.ReceivePacketAsync($"c_info {_name}" + oldPart)
.GetAwaiter().GetResult(); // Change the name
if (!result.IsSuccess)
M src/Samples/LowLevel/InterceptNameChanger/NameChanger.cs => src/Samples/LowLevel/InterceptNameChanger/NameChanger.cs +2 -0
@@ 9,6 9,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NosSmooth.Core.Client;
using NosSmooth.Core.Extensions;
+using NosSmooth.Extensions.SharedBinding.Extensions;
using NosSmooth.LocalBinding;
using NosSmooth.LocalClient;
using NosSmooth.LocalClient.Extensions;
@@ 33,6 34,7 @@ namespace InterceptNameChanger
{
var provider = new ServiceCollection()
.AddLocalClient()
+ .ShareNosSmooth()
// .AddPacketResponder<SayResponder>()
.AddLogging
D src/Samples/LowLevel/InterceptNameChanger/Properties/AssemblyInfo.cs => src/Samples/LowLevel/InterceptNameChanger/Properties/AssemblyInfo.cs +0 -41
@@ 1,41 0,0 @@
-//
-// AssemblyInfo.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.Reflection;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("InterceptNameChanger")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("InterceptNameChanger")]
-[assembly: AssemblyCopyright("Copyright © 2021")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("F96F3AA0-131E-4B6B-AB21-BBE2DEBCEF3A")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]>
\ No newline at end of file
M src/Samples/LowLevel/SimpleChat/SimpleChat.cs => src/Samples/LowLevel/SimpleChat/SimpleChat.cs +2 -0
@@ 8,6 8,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NosSmooth.Core.Client;
using NosSmooth.Core.Extensions;
+using NosSmooth.Extensions.SharedBinding.Extensions;
using NosSmooth.LocalBinding;
using NosSmooth.LocalClient.Extensions;
using NosSmooth.Packets.Enums;
@@ 31,6 32,7 @@ public class SimpleChat
{
var provider = new ServiceCollection()
.AddLocalClient()
+ .ShareNosSmooth()
.AddPacketResponder<SayResponder>()
.AddLogging
(
M src/Samples/LowLevel/SimpleChat/SimpleChat.csproj => src/Samples/LowLevel/SimpleChat/SimpleChat.csproj +1 -0
@@ 22,5 22,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Core\NosSmooth.LocalClient\NosSmooth.LocalClient.csproj" />
+ <ProjectReference Include="..\..\..\Extensions\NosSmooth.Extensions.SharedBinding\NosSmooth.Extensions.SharedBinding.csproj" />
</ItemGroup>
</Project>=
\ No newline at end of file
M src/Samples/LowLevel/WalkCommands/Startup.cs => src/Samples/LowLevel/WalkCommands/Startup.cs +2 -0
@@ 12,6 12,7 @@ using NosSmooth.Core.Extensions;
using NosSmooth.Data.NOSFiles;
using NosSmooth.Data.NOSFiles.Extensions;
using NosSmooth.Extensions.Pathfinding.Extensions;
+using NosSmooth.Extensions.SharedBinding.Extensions;
using NosSmooth.LocalBinding;
using NosSmooth.LocalBinding.Extensions;
using NosSmooth.LocalClient;
@@ 32,6 33,7 @@ public class Startup
{
var collection = new ServiceCollection()
.AddLocalClient()
+ .ShareNosSmooth()
// hook pet and player walk to
// recognize user action's and
M src/Samples/LowLevel/WalkCommands/WalkCommands.csproj => src/Samples/LowLevel/WalkCommands/WalkCommands.csproj +4 -0
@@ 39,6 39,9 @@
<PackageReference Include="NosSmooth.Extensions.Pathfinding">
<Version>1.1.1</Version>
</PackageReference>
+ <PackageReference Include="NosSmooth.Packets">
+ <Version>2.3.4</Version>
+ </PackageReference>
<PackageReference Include="Remora.Results">
<Version>7.2.3</Version>
</PackageReference>
@@ 46,5 49,6 @@
<ItemGroup>
<ProjectReference Include="..\..\..\Core\NosSmooth.LocalClient\NosSmooth.LocalClient.csproj" />
<ProjectReference Include="..\..\..\Extensions\NosSmooth.ChatCommands\NosSmooth.ChatCommands.csproj" />
+ <ProjectReference Include="..\..\..\Extensions\NosSmooth.Extensions.SharedBinding\NosSmooth.Extensions.SharedBinding.csproj" />
</ItemGroup>
</Project>=
\ No newline at end of file