From d45a01445b7eeeb9fbbb8dc14c5e046612a765d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Boh=C3=A1=C4=8Dek?= Date: Fri, 21 Jan 2022 00:20:32 +0100 Subject: [PATCH] feat(localbinding): add follow and unfollow (attack) entity methods --- .../Objects/CharacterBinding.cs | 155 +++++++++++++++++- .../Options/CharacterBindingOptions.cs | 22 +++ 2 files changed, 174 insertions(+), 3 deletions(-) diff --git a/Local/NosSmooth.LocalBinding/Objects/CharacterBinding.cs b/Local/NosSmooth.LocalBinding/Objects/CharacterBinding.cs index 152afc53486d4b9f24c0e75174c142928353e1e0..09cca1018691a2aaaa275d6be304031856e8a8f3 100644 --- a/Local/NosSmooth.LocalBinding/Objects/CharacterBinding.cs +++ b/Local/NosSmooth.LocalBinding/Objects/CharacterBinding.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using NosSmooth.LocalBinding.Errors; using NosSmooth.LocalBinding.Options; +using NosSmooth.LocalBinding.Structs; using Reloaded.Hooks.Definitions; using Reloaded.Hooks.Definitions.X86; using Remora.Results; @@ -24,7 +25,29 @@ public class CharacterBinding FunctionAttribute.Register.eax, FunctionAttribute.StackCleanup.Callee )] - private delegate bool WalkDelegate(IntPtr characterObject, int position, short unknown0 = 0, int unknown1 = 1); + private delegate bool WalkDelegate(IntPtr 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 + )] + private delegate bool FollowEntityDelegate + ( + IntPtr playerManagerPtr, + IntPtr entityPtr, + char unknown1 = '\0', + char unknown2 = '' + ); + + [Function + ( + new[] { FunctionAttribute.Register.eax, FunctionAttribute.Register.edx }, + FunctionAttribute.Register.eax, + FunctionAttribute.StackCleanup.Callee + )] + private delegate void UnfollowEntityDelegate(IntPtr playerManagerPtr, int unknown = 0); /// /// Create the network binding with finding the network object and functions. @@ -47,15 +70,37 @@ public class CharacterBinding return new BindingNotFoundError(options.WalkFunctionPattern, "CharacterBinding.Walk"); } + var followEntityAddress = bindingManager.Scanner.CompiledFindPattern(options.FollowEntityPattern); + if (!followEntityAddress.Found) + { + return new BindingNotFoundError(options.FollowEntityPattern, "CharacterBinding.FollowEntity"); + } + + var unfollowEntityAddress = bindingManager.Scanner.CompiledFindPattern(options.UnfollowEntityPattern); + if (!unfollowEntityAddress.Found) + { + return new BindingNotFoundError(options.UnfollowEntityPattern, "CharacterBinding.UnfollowEntity"); + } + var walkFunction = bindingManager.Hooks.CreateFunction (walkFunctionAddress.Offset + (int)process.MainModule!.BaseAddress); var walkWrapper = walkFunction.GetWrapper(); + var followEntityFunction = bindingManager.Hooks.CreateFunction + (followEntityAddress.Offset + (int)process.MainModule!.BaseAddress); + var followEntityWrapper = followEntityFunction.GetWrapper(); + + var unfollowEntityFunction = bindingManager.Hooks.CreateFunction + (unfollowEntityAddress.Offset + (int)process.MainModule!.BaseAddress); + var unfollowEntityWrapper = unfollowEntityFunction.GetWrapper(); + var binding = new CharacterBinding ( bindingManager, (IntPtr)(characterObjectAddress.Offset + (int)process.MainModule!.BaseAddress + 0x06), - walkWrapper + walkWrapper, + followEntityWrapper, + unfollowEntityWrapper ); if (options.HookWalk) @@ -65,25 +110,49 @@ public class CharacterBinding binding._originalWalk = binding._walkHook.OriginalFunction; } + if (options.HookFollowEntity) + { + binding._followHook = followEntityFunction.Hook(binding.FollowEntityDetour); + binding._originalFollowEntity = binding._followHook.OriginalFunction; + } + + if (options.HookUnfollowEntity) + { + binding._unfollowHook = unfollowEntityFunction.Hook(binding.UnfollowEntityDetour); + binding._originalUnfollowEntity = binding._unfollowHook.OriginalFunction; + } + binding._walkHook?.Activate(); + binding._followHook?.Activate(); + binding._unfollowHook?.Activate(); return binding; } private readonly NosBindingManager _bindingManager; private readonly IntPtr _characterAddress; + private IHook? _walkHook; + private IHook? _followHook; + private IHook? _unfollowHook; + + private FollowEntityDelegate _originalFollowEntity; + private UnfollowEntityDelegate _originalUnfollowEntity; private WalkDelegate _originalWalk; private CharacterBinding ( NosBindingManager bindingManager, IntPtr characterAddress, - WalkDelegate originalWalk + WalkDelegate originalWalk, + FollowEntityDelegate originalFollowEntity, + UnfollowEntityDelegate originalUnfollowEntity ) { _bindingManager = bindingManager; _characterAddress = characterAddress; _originalWalk = originalWalk; + _originalFollowEntity = originalFollowEntity; + _originalUnfollowEntity = originalUnfollowEntity; } /// @@ -94,6 +163,14 @@ public class CharacterBinding /// public event Func? WalkCall; + /// + /// Event that is called when entity follow or unfollow was called. + /// + /// + /// The follow/unfollow entity must be hooked for this event to be called. + /// + public event Func? FollowEntityCall; + /// /// Disable all the hooks that are currently enabled. /// @@ -142,4 +219,76 @@ public class CharacterBinding return false; } + + /// + /// Follow the entity. + /// + /// The entity. + /// A result that may or may not have succeeded. + public Result FollowEntity(MapBaseObj? entity) + => FollowEntity(entity?.Address ?? IntPtr.Zero); + + /// + /// Follow the entity. + /// + /// The entity address. + /// A result that may or may not have succeeded. + public Result FollowEntity(IntPtr entityAddress) + { + try + { + _originalFollowEntity(GetCharacterAddress(), entityAddress); + } + catch (Exception e) + { + return e; + } + + return Result.FromSuccess(); + } + + /// + /// Stop following entity. + /// + /// A result that may or may not have succeeded. + public Result UnfollowEntity() + { + try + { + _originalUnfollowEntity(GetCharacterAddress()); + } + catch (Exception e) + { + return e; + } + + return Result.FromSuccess(); + } + + private bool FollowEntityDetour + ( + IntPtr sceneManagerPtr, + IntPtr entityPtr, + char unknown1, + char unknown2 + ) + { + var result = FollowEntityCall?.Invoke(new MapBaseObj(_bindingManager.Memory, entityPtr)); + if (result ?? true) + { + return _originalFollowEntity(sceneManagerPtr, entityPtr, unknown1, unknown2); + } + + return false; + } + + private void UnfollowEntityDetour(IntPtr entityPtr, int unknown) + { + var result = FollowEntityCall?.Invoke(null); + if (result ?? true) + { + Console.WriteLine("Called with unknown: " + unknown); + _originalUnfollowEntity(entityPtr, unknown); + } + } } \ No newline at end of file diff --git a/Local/NosSmooth.LocalBinding/Options/CharacterBindingOptions.cs b/Local/NosSmooth.LocalBinding/Options/CharacterBindingOptions.cs index e233aea171f4219f4df080c4ef2ae762cb5dd9eb..d45c984bfcf9419bcd47523bb982682e3c319d44 100644 --- a/Local/NosSmooth.LocalBinding/Options/CharacterBindingOptions.cs +++ b/Local/NosSmooth.LocalBinding/Options/CharacterBindingOptions.cs @@ -31,4 +31,26 @@ public class CharacterBindingOptions /// Gets or sets the pattern to find the walk function at. /// public string WalkFunctionPattern { get; set; } = "55 8B EC 83 C4 EC 53 56 57 66 89 4D FA"; + + /// + /// Gets or sets the pattern to find the follow entity method at. + /// + public string FollowEntityPattern { get; set; } + = "55 8B EC 51 53 56 57 88 4D FF 8B F2 8B F8"; + + /// + /// Gets or sets the pattern to find the unfollow entity method at. + /// + public string UnfollowEntityPattern { get; set; } + = "80 78 14 00 74 1A"; + + /// + /// Gets or sets whether to hook the follow entity function. + /// + public bool HookFollowEntity { get; set; } = true; + + /// + /// Gets or sets whether to hook the unfollow entity function. + /// + public bool HookUnfollowEntity { get; set; } = true; } \ No newline at end of file