M Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs => Local/NosSmooth.LocalBinding/Extensions/ServiceCollectionExtensions.cs +1 -0
@@ 30,6 30,7 @@ public static class ServiceCollectionExtensions
return serviceCollection
.AddSingleton<NosBindingManager>()
.AddSingleton(p => p.GetRequiredService<NosBindingManager>().Character)
+ .AddSingleton(p => p.GetRequiredService<NosBindingManager>().SceneManager)
.AddSingleton(p => p.GetRequiredService<NosBindingManager>().Network);
}
}=
\ No newline at end of file
M Local/NosSmooth.LocalBinding/NosBindingManager.cs => Local/NosSmooth.LocalBinding/NosBindingManager.cs +31 -1
@@ 23,19 23,23 @@ public class NosBindingManager : IDisposable
{
private readonly CharacterBindingOptions _characterBindingOptions;
private readonly NetworkBindingOptions _networkBindingOptions;
+ private SceneManagerBindingOptions _sceneManagerBindingOptions;
private NetworkBinding? _networkBinding;
private CharacterBinding? _characterBinding;
+ private SceneManagerBinding? _sceneManagerBinding;
/// <summary>
/// Initializes a new instance of the <see cref="NosBindingManager"/> class.
/// </summary>
/// <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>
public NosBindingManager
(
IOptions<CharacterBindingOptions> characterBindingOptions,
- IOptions<NetworkBindingOptions> networkBindingOptions
+ IOptions<NetworkBindingOptions> networkBindingOptions,
+ IOptions<SceneManagerBindingOptions> sceneManagerBindingOptions
)
{
Hooks = new ReloadedHooks();
@@ 43,6 47,7 @@ public class NosBindingManager : IDisposable
Scanner = new Scanner(Process.GetCurrentProcess(), Process.GetCurrentProcess().MainModule);
_characterBindingOptions = characterBindingOptions.Value;
_networkBindingOptions = networkBindingOptions.Value;
+ _sceneManagerBindingOptions = sceneManagerBindingOptions.Value;
}
/// <summary>
@@ 97,6 102,24 @@ public class NosBindingManager : IDisposable
}
/// <summary>
+ /// Gets the character binding.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown if the manager is not initialized yet.</exception>
+ public SceneManagerBinding SceneManager
+ {
+ get
+ {
+ if (_sceneManagerBinding is null)
+ {
+ throw new InvalidOperationException
+ ("Could not get scene manager. The binding manager is not initialized. Did you forget to call NosBindingManager.Initialize?");
+ }
+
+ return _sceneManagerBinding;
+ }
+ }
+
+ /// <summary>
/// Initialize the existing bindings and hook NosTale functions.
/// </summary>
/// <returns>A result that may or may not have succeeded.</returns>
@@ 116,6 139,13 @@ public class NosBindingManager : IDisposable
}
_characterBinding = character.Entity;
+ var sceneManager = SceneManagerBinding.Create(this, _sceneManagerBindingOptions);
+ if (!sceneManager.IsSuccess)
+ {
+ return Result.FromError(sceneManager);
+ }
+ _sceneManagerBinding = sceneManager.Entity;
+
return Result.FromSuccess();
}
A Local/NosSmooth.LocalBinding/Objects/SceneManagerBinding.cs => Local/NosSmooth.LocalBinding/Objects/SceneManagerBinding.cs +216 -0
@@ 0,0 1,216 @@
+//
+// SceneManagerBinding.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.Options;
+using NosSmooth.LocalBinding.Structs;
+using Reloaded.Hooks;
+using Reloaded.Hooks.Definitions;
+using Reloaded.Hooks.Definitions.X86;
+using Reloaded.Memory.Buffers.Internal.Kernel32;
+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 SceneManagerBinding
+{
+ [Function
+ (
+ new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx },
+ FunctionAttribute.Register.ecx,
+ FunctionAttribute.StackCleanup.Callee
+ )]
+ private delegate void FocusEntityDelegate(IntPtr outputStringPtr, IntPtr 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<SceneManagerBinding> Create
+ (NosBindingManager bindingManager, SceneManagerBindingOptions bindingOptions)
+ {
+ var process = Process.GetCurrentProcess();
+ var sceneManagerObjectAddress = bindingManager.Scanner.CompiledFindPattern
+ (bindingOptions.SceneManagerObjectPattern);
+ if (!sceneManagerObjectAddress.Found)
+ {
+ return new BindingNotFoundError(bindingOptions.SceneManagerObjectPattern, "SceneManagerBinding");
+ }
+
+ var focusEntityAddress = bindingManager.Scanner.CompiledFindPattern(bindingOptions.FocusEntityPattern);
+ if (!focusEntityAddress.Found)
+ {
+ return new BindingNotFoundError(bindingOptions.FocusEntityPattern, "SceneManagerBinding.FocusEntity");
+ }
+
+ var focusEntityFunction = bindingManager.Hooks.CreateFunction<FocusEntityDelegate>
+ (focusEntityAddress.Offset + (int)process.MainModule!.BaseAddress + 0x04);
+ var focusEntityWrapper = focusEntityFunction.GetWrapper();
+
+ var binding = new SceneManagerBinding
+ (
+ bindingManager,
+ (IntPtr)(sceneManagerObjectAddress.Offset + (int)process.MainModule!.BaseAddress + 0x01),
+ focusEntityWrapper
+ );
+
+ if (bindingOptions.HookFocusEntity)
+ {
+ binding._focusHook = focusEntityFunction.Hook(binding.FocusEntityDetour);
+ binding._originalFocusEntity = binding._focusHook.OriginalFunction;
+ }
+
+ binding._focusHook?.Activate();
+ return binding;
+ }
+
+ private readonly NosBindingManager _bindingManager;
+ private readonly IntPtr _sceneManagerAddress;
+ private FocusEntityDelegate _originalFocusEntity;
+
+ private IHook<FocusEntityDelegate>? _focusHook;
+
+ private SceneManagerBinding
+ (
+ NosBindingManager bindingManager,
+ IntPtr sceneManagerAddress,
+ FocusEntityDelegate originalFocusEntity
+ )
+ {
+ _originalFocusEntity = originalFocusEntity;
+ _bindingManager = bindingManager;
+ _sceneManagerAddress = sceneManagerAddress;
+ }
+
+ /// <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 Func<MapBaseObj?, bool>? EntityFocus;
+
+ /// <summary>
+ /// Gets the scene manager object.
+ /// </summary>
+ public SceneManager SceneManager => new SceneManager(_bindingManager.Memory, GetSceneManagerAddress());
+
+ private IntPtr GetSceneManagerAddress()
+ {
+ IntPtr sceneManagerAddress = _sceneManagerAddress;
+ _bindingManager.Memory.Read(sceneManagerAddress, out sceneManagerAddress);
+
+ return sceneManagerAddress;
+ }
+
+ /// <summary>
+ /// Focus the entity with the given id.
+ /// </summary>
+ /// <param name="id">The id of the entity.</param>
+ /// <returns>A result that may or may not have succeeded.</returns>
+ public Result FocusEntity(int id)
+ {
+ var entityResult = FindEntity(id);
+ if (!entityResult.IsSuccess)
+ {
+ return Result.FromError(entityResult);
+ }
+
+ return FocusEntity(entityResult.Entity);
+ }
+
+ /// <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 ?? IntPtr.Zero);
+
+ /// <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(IntPtr entityAddress)
+ {
+ try
+ {
+ _originalFocusEntity(IntPtr.Zero, entityAddress);
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+
+ return Result.FromSuccess();
+ }
+
+ /// <summary>
+ /// Find the given entity address.
+ /// </summary>
+ /// <param name="id">The id of the entity.</param>
+ /// <returns>The pointer to the entity or an error.</returns>
+ public Result<MapBaseObj?> FindEntity(int id)
+ {
+ if (id == 0)
+ {
+ return Result<MapBaseObj?>.FromSuccess(null);
+ }
+
+ var manager = SceneManager;
+
+ var item = manager.ItemList.FirstOrDefault(x => x.Id == id);
+ if (item is not null)
+ {
+ return item;
+ }
+
+ var monster = manager.MonsterList.FirstOrDefault(x => x.Id == id);
+ if (monster is not null)
+ {
+ return monster;
+ }
+
+ var npc = manager.NpcList.FirstOrDefault(x => x.Id == id);
+ if (npc is not null)
+ {
+ return npc;
+ }
+
+ var player = manager.PlayerList.FirstOrDefault(x => x.Id == id);
+ if (player is not null)
+ {
+ return player;
+ }
+
+ return new NotFoundError($"Could not find entity with id {id}");
+ }
+
+ private void FocusEntityDetour(IntPtr outputStringPtr, IntPtr entityId)
+ {
+ MapBaseObj? obj = null;
+ if (entityId != IntPtr.Zero)
+ {
+ obj = new MapBaseObj(_bindingManager.Memory, entityId);
+ }
+
+ var result = EntityFocus?.Invoke(obj);
+ if (result ?? true)
+ {
+ _originalFocusEntity(outputStringPtr, entityId);
+ }
+ }
+}<
\ No newline at end of file
A Local/NosSmooth.LocalBinding/Options/SceneManagerBindingOptions.cs => Local/NosSmooth.LocalBinding/Options/SceneManagerBindingOptions.cs +35 -0
@@ 0,0 1,35 @@
+//
+// SceneManagerBindingOptions.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="SceneManagerBinding"/>.
+/// </summary>
+public class SceneManagerBindingOptions
+{
+ /// <summary>
+ /// Gets or sets the pattern to find the scene manager object at.
+ /// </summary>
+ /// <remarks>
+ /// The address of the object is direct pointer to the scene manager.
+ /// </remarks>
+ public string SceneManagerObjectPattern { get; set; }
+ = "FF ?? ?? ?? ?? ?? FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF";
+
+ /// <summary>
+ /// Gets or sets the pattern to find the focus entity method at.
+ /// </summary>
+ public string FocusEntityPattern { get; set; }
+ = "73 00 00 00 55 8b ec b9 05 00 00 00";
+
+ /// <summary>
+ /// Gets or sets whether to hook the Focus entity function.
+ /// </summary>
+ public bool HookFocusEntity { get; set; } = true;
+}<
\ No newline at end of file
A Local/NosSmooth.LocalBinding/Structs/MapBaseObj.cs => Local/NosSmooth.LocalBinding/Structs/MapBaseObj.cs +46 -0
@@ 0,0 1,46 @@
+//
+// MapBaseObj.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;
+
+/// <summary>
+/// Base map object. Common for players, monsters, npcs.
+/// </summary>
+public class MapBaseObj
+{
+ private readonly IMemory _memory;
+ private readonly IntPtr _mapObjPointer;
+
+ /// <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, IntPtr mapObjPointer)
+ {
+ _memory = memory;
+ _mapObjPointer = mapObjPointer;
+ }
+
+ /// <summary>
+ /// Gets the pointer to the object.
+ /// </summary>
+ public IntPtr Address => _mapObjPointer;
+
+ /// <summary>
+ /// Gets the id of the entity.
+ /// </summary>
+ public long Id
+ {
+ get
+ {
+ _memory.SafeRead(_mapObjPointer + 0x08, out int id);
+ return id;
+ }
+ }
+}<
\ No newline at end of file
A Local/NosSmooth.LocalBinding/Structs/MapObjBaseList.cs => Local/NosSmooth.LocalBinding/Structs/MapObjBaseList.cs +112 -0
@@ 0,0 1,112 @@
+//
+// MapObjBaseList.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;
+using Reloaded.Memory.Pointers;
+using Reloaded.Memory.Sources;
+
+namespace NosSmooth.LocalBinding.Structs;
+
+/// <summary>
+/// List of map objects.
+/// </summary>
+public class MapObjBaseList : IEnumerable<MapBaseObj>
+{
+ private readonly IMemory _memory;
+ private readonly IntPtr _objListPointer;
+ private readonly ArrayPtr<IntPtr> _objList;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MapObjBaseList"/> class.
+ /// </summary>
+ /// <param name="memory">The memory.</param>
+ /// <param name="objListPointer">The object list pointer.</param>
+ public MapObjBaseList(IMemory memory, IntPtr objListPointer)
+ {
+ memory.Read(objListPointer + 0x04, out IntPtr arrayFirst);
+ _objList = new ArrayPtr<IntPtr>((ulong)arrayFirst, source: memory);
+ _memory = memory;
+ _objListPointer = objListPointer;
+ }
+
+ /// <summary>
+ /// Gets the element at the given index.
+ /// </summary>
+ /// <param name="index">The index of the element.</param>
+ /// <exception cref="IndexOutOfRangeException">Thrown if the index is not in the bounds of the array.</exception>
+ public MapBaseObj this[int index]
+ {
+ get
+ {
+ if (index >= Length || index < 0)
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ return new MapBaseObj(_memory, _objList[index]);
+ }
+ }
+
+ /// <summary>
+ /// Gets the length of the array.
+ /// </summary>
+ public int Length
+ {
+ get
+ {
+ _memory.SafeRead(_objListPointer + 0x08, out int length);
+ return length;
+ }
+ }
+
+ /// <inheritdoc/>
+ public IEnumerator<MapBaseObj> GetEnumerator()
+ {
+ return new MapObjBaseEnumerator(this);
+ }
+
+ /// <inheritdoc/>
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ private class MapObjBaseEnumerator : IEnumerator<MapBaseObj>
+ {
+ private readonly MapObjBaseList _list;
+ private int _index;
+
+ public MapObjBaseEnumerator(MapObjBaseList list)
+ {
+ _index = -1;
+ _list = list;
+ }
+
+ public bool MoveNext()
+ {
+ if (_list.Length > _index + 1)
+ {
+ _index++;
+ return true;
+ }
+
+ return false;
+ }
+
+ public void Reset()
+ {
+ _index = -1;
+ }
+
+ public MapBaseObj Current => _list[_index];
+
+ object IEnumerator.Current => Current;
+
+ public void Dispose()
+ {
+ }
+ }
+}<
\ No newline at end of file
A Local/NosSmooth.LocalBinding/Structs/SceneManager.cs => Local/NosSmooth.LocalBinding/Structs/SceneManager.cs +55 -0
@@ 0,0 1,55 @@
+//
+// SceneManager.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;
+
+/// <summary>
+/// Represents nostale scene manager struct.
+/// </summary>
+public class SceneManager
+{
+ private readonly IMemory _memory;
+ private readonly IntPtr _sceneManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SceneManager"/> class.
+ /// </summary>
+ /// <param name="memory">The memory.</param>
+ /// <param name="sceneManager">The pointer to the scene manager.</param>
+ public SceneManager(IMemory memory, IntPtr sceneManager)
+ {
+ _memory = memory;
+ _sceneManager = sceneManager;
+ }
+
+ /// <summary>
+ /// Gets the player list.
+ /// </summary>
+ public MapObjBaseList PlayerList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0xC));
+
+ /// <summary>
+ /// Gets the monster list.
+ /// </summary>
+ public MapObjBaseList MonsterList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0x10));
+
+ /// <summary>
+ /// Gets the npc list.
+ /// </summary>
+ public MapObjBaseList NpcList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0x14));
+
+ /// <summary>
+ /// Gets the item list.
+ /// </summary>
+ public MapObjBaseList ItemList => new MapObjBaseList(_memory, ReadPtr(_sceneManager + 0x18));
+
+ private IntPtr ReadPtr(IntPtr ptr)
+ {
+ _memory.Read(ptr, out IntPtr read);
+ return read;
+ }
+}<
\ No newline at end of file
M Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs => Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs +12 -0
@@ 36,4 36,16 @@ public static class ServiceCollectionExtensions
return serviceCollection;
}
+
+ /// <summary>
+ /// Adds packet interceptor.
+ /// </summary>
+ /// <param name="serviceCollection">The service collection.</param>
+ /// <typeparam name="TInterceptor">The type of the interceptor.</typeparam>
+ /// <returns>The collection.</returns>
+ public static IServiceCollection AddPacketInterceptor<TInterceptor>(this IServiceCollection serviceCollection)
+ where TInterceptor : class, IPacketInterceptor
+ {
+ return serviceCollection.AddSingleton<IPacketInterceptor, TInterceptor>();
+ }
}=
\ No newline at end of file