Skip to content
MPGHThe Dark Arts
/
RegisterLog in
Forum
Community
What's NewLatest posts across the boardTrendingHottest threads right nowSubscribedThreads you follow
Discussion
GeneralIntroductionsEntertainmentDebate FortFlaming & Rage
Board
News & AnnouncementsMPGH TimesSuggestions & HelpGiveaways
More Sections
Art & Graphic DesignProgrammingHackingCryptocurrency
Hacks & Cheats
Games
ValorantCS2 / CS:GOCall of Duty / WarzoneFortniteApex LegendsEscape From Tarkov
+14 moreLeague of LegendsGTA VMinecraftRustROTMGBattlefieldTroveBattleOnCombat ArmsCrossFireBlackshotRuneScapeDayZDead by Daylight
Resources
Game Hacking TutorialsReverse EngineeringGeneral Game HackingAnti-CheatConsole Game Hacking
Tools
Game Hacking ToolsTrainers & CheatsHack/Release NewsNew
Submit a release →Share your cheat, tool, or config with the community.
AINEW
AI Tools
General & DiscussionPrompt EngineeringLLM JailbreaksHotAI Agents & AutomationLocal / Open Models
AI × Gaming
AI Aimbots & VisionML Anti-CheatGame Bots & Automation
Create
AI Coding / Vibe CodingAI Art & MediaAI Voice & TTS
The AI frontier →Where game hacking meets modern machine learning. Jump in.
Marketplace
Buy & Sell
SellingBuyingTradingUser Services
Trust & Safety
Middleman LoungeMarketplace TalkVouch Copy Profiles
Money
Cryptocurrency TalkCurrency ExchangeWork & Job Offers
Start selling →List accounts, services, and goods. Use the middleman to trade safe.
MPGH The Dark Arts

A community for offensive security research, reverse engineering, and AI.

Community

ForumMarketplaceSearch

Account

RegisterLog in

Legal

Privacy PolicyForum RulesHelp & FAQ
© 2026 MPGH · All rights reserved.Built by the community, for the community. For educational purposes onlyContent is shared for security research and education — we don't condone illegal use. You're responsible for complying with applicable laws. Use at your own risk.
Home › Forum › MultiPlayer Game Hacks & Cheats › Other MMORPG Hacks › Trove Hacks & Cheats › Silent Aim

ArrowSilent Aim

Posts 1–4 of 4 · Page 1 of 1
FAISAL32
FAISAL32
Silent Aim
Hello everyone

First of all before I start saying anything, after I explain everything I’ll attach the source code I built in Python.

If you don’t care about the explanation, you can just install any Python version from the official website, as long as it’s Python 3 or higher.

After installing Python, use this command:

Code:
pip install pymem keystone-engine
And that’s it, congratulations, you can use it directly.

Also keep in mind this may break after updates, so you might need to update the signatures.

Code:
hook_sig = 48 8B C4 48 89 58 10 44 89 48 20 55 56 57 48 81 EC D0 00 00 00

Entitylist_sig = 48 8B 0D ?? ?? ?? ?? 48 85 C9 74 16 48 8B 05 + 0x3

localplayer_sig = 48 8B 0D ?? ?? ?? ?? 48 8D 50 98 + 0x3
Those are only needed if a future update breaks the addresses.

Alright now let’s get into the explanation.

# Explanation

The short version is that the new silent aim method now works at a later stage of the projectile path.

Instead of modifying functions close to the raycast or early aim calculations, the modification now happens at the function that receives the projectile origin and direction already prepared.

The important function is:

Code:
Trove_x64.exe+77E9E0
The parameters here are roughly:

Code:
rcx = projectile / object
rdx = projectile origin
r8  = projectile direction
r9  = mode / type
So when execution reaches this point, the game already prepared the projectile spawn position and projectile direction.

`origin` = projectile spawn position
`direction` = projectile direction vector

The silent aim idea here is extremely simple:

Take the target position, calculate a new direction from the projectile origin to the target, then overwrite the original direction stored in `r8`.

Something like this:

Code:
direction = normalize(target_pos - projectile_origin)
Then write the result into:

Code:
[r8]
The direction vector must be normalized, meaning its length must equal 1, because it’s a direction vector and not a world position.

Quick example:

Code:
dx = target_x - origin_x
dy = target_y - origin_y
dz = target_z - origin_z

length = sqrt(dx*dx + dy*dy + dz*dz)

dir_x = dx / length
dir_y = dy / length
dir_z = dz / length
After that, write `dir_x`, `dir_y`, and `dir_z` into the direction pointer inside `r8`.

The important x64 flow now roughly looks like this:

Code:
77D970 -> 77E010 -> 755910 -> 77E9E0
Short explanation:

Code:
755910 = closest function to aim / ray direction calculations
77E9E0 = function receiving the final direction
Function:

Code:
Trove_x64.exe+755910
is the closest thing to the actual raycast / aim calculation logic, and contains calculations related to direction and projectile pathing.

But as a practical silent aim hook, it’s much better to hook:

Code:
Trove_x64.exe+77E9E0
because it’s simpler and cleaner.

At this point the direction is already fully prepared and directly accessible in `r8`, so there’s no need to mess with internal calculation logic.

Instead of modifying how the game calculates the direction, you simply modify the final result before the projectile uses it.

That’s the important difference.

The old method was closer to the direction calculation logic itself.

The new method grabs the direction after everything is already calculated.

That’s why the new method is easier to understand and easier to update, because the location is very clear:

Code:
rdx = origin
r8  = direction
The hook logic becomes basically:

Code:
if silent aim enabled
and target is valid
calculate new direction
write direction into [r8]
continue original function
So in very simple terms:

The game prepares the projectile and prepares the direction, and right before the game uses that direction, we replace it with our own target direction.

If a future update breaks the address, you’ll most likely only need to update the hook signature:

Code:
48 8B C4 48 89 58 10 44 89 48 20 55 56 57 48 81 EC D0 00 00 00
And if you want the closest function related to ray / aim calculations, it’s:

Code:
Trove_x64.exe+755910
But for practical usage, hooking:

Code:
```cpp
Trove_x64.exe+77E9E0

is much better because that’s where we directly receive and modify the final direction vector.

And that’s basically the new silent aim method.

Hope everyone enjoys it.

 
Source Code

import ctypes
import math
import struct
import sys
import threading
import time
from dataclasses import dataclass

import pymem
import pymem.process

try:
from keystone import Ks, KS_ARCH_X86, KS_MODE_64
except ImportError:
print("[-] Install keystone-engine: pip install keystone-engine")
sys.exit(1)


# ================================================== ===========================
# Config
# ================================================== ===========================

PROCESS_NAME = "Trove_x64.exe"

ENTITY_REFRESH_DELAY = 0.03
TARGET_UPDATE_MIN_DELTA = 0.01

# Max silent-aim target range.
# This does NOT affect entity extraction.
# It only decides whether the closest extracted entity is allowed as a target.
MAX_RANGE = 50.0

# Entity base position is usually low. This lifts the aim point into the body.
AIM_Y_OFFSET = 0.45

# Full camera/projectile parallax compensation.
PARALLAX = 1.0
MIN_LEN_SQ = 0.000001

# Contains-based blacklist:
# If the entity name contains any keyword anywhere, it is ignored.
blacklist_keywords = [
"pet",
"portal",
"abilities",
"cornerstone",
"services",
"client",
#"mana",
"karma",
"adventure",
"placeable",
]


# ================================================== ===========================
# RVAs / offsets
# ================================================== ===========================

EntityList = 0x1396BC0
LOCAL_BASE_PTR = 0x0139F870

CAM_ROOT_OFFSET = 0x40
CAM_POS_OFFSET = 0x140

# Confirmed active SetProjectileOrigin variant.
# Entry args:
# rdx = projectile origin pointer
# r8 = projectile direction pointer
HOOK_RVA = 0x77E9E0
PATCH_LEN = 14
RETURN_RVA = HOOK_RVA + PATCH_LEN

EXPECTED_ORIGINAL = bytes.fromhex(
"48 8B C4 48 89 58 10 44 89 48 20 55 56 57"
)

OFFSETS = {
"hash_base": 0xE0,
"hash_stride": 0xE8,
"hash_count": 0xF0,

"node_next": 0x00,
"node_object": 0x10,

"name": [0x98, 0x0],
"position": [0x130, 0x8, 0xD0],
"scale": [0x130, 0x8, 0x178],
"level": [0x130, 0xA8, 0x208],
"current_health": [0x130, 0x108, 0xD8],
}

NODE_MARK_MASK = 0xFFFFFFFFFFFFFFFE
MAX_BUCKETS = 8192
MAX_CHAIN_DEPTH = 256
MAX_TOTAL_NODES = 30000
NAME_MAX_LEN = 160


# ================================================== ===========================
# Shared data block
# ================================================== ===========================

# Python writes target data here. The codecave reads it on each projectile call.
DATA_ENABLED = 0x00 # u32
DATA_VALID = 0x04 # u32
DATA_X = 0x08 # float
DATA_Y = 0x0C # float
DATA_Z = 0x10 # float
DATA_PARALLAX = 0x14 # float
DATA_MIN_LEN_SQ = 0x18 # float
DATA_LOCAL_BASE_PTR = 0x20 # u64


# ================================================== ===========================
# WinAPI
# ================================================== ===========================

MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
MEM_RELEASE = 0x8000
PAGE_EXECUTE_READWRITE = 0x40

kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)

VirtualAllocEx = kernel32.VirtualAllocEx
VirtualAllocEx.argtypes = [
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_size_t,
ctypes.c_uint32,
ctypes.c_uint32,
]
VirtualAllocEx.restype = ctypes.c_void_p

VirtualFreeEx = kernel32.VirtualFreeEx
VirtualFreeEx.argtypes = [
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_size_t,
ctypes.c_uint32,
]
VirtualFreeEx.restype = ctypes.c_bool

VirtualProtectEx = kernel32.VirtualProtectEx
VirtualProtectEx.argtypes = [
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_size_t,
ctypes.c_uint32,
ctypes.POINTER(ctypes.c_uint32),
]
VirtualProtectEx.restype = ctypes.c_bool

FlushInstructionCache = kernel32.FlushInstructionCache
FlushInstructionCache.argtypes = [
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_size_t,
]
FlushInstructionCache.restype = ctypes.c_bool


@dataclass(slots=True)
class Entity:
address: int
name: str
hp: float
x: float
y: float
z: float
dist: float = 0.0


class RemoteAlloc:
"""Simple wrapper around VirtualAllocEx / VirtualFreeEx."""

def __init__(self, pm: pymem.Pymem, size: int):
self.pm = pm
self.size = size

self.address = int(VirtualAllocEx(
pm.process_handle,
None,
size,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE,
))

if not self.address:
raise ctypes.WinError(ctypes.get_last_error())

def free(self) -> None:
if self.address:
VirtualFreeEx(
self.pm.process_handle,
ctypes.c_void_p(self.address),
0,
MEM_RELEASE,
)
self.address = 0


# ================================================== ===========================
# Memory helpers
# ================================================== ===========================

def valid_ptr(value: int) -> bool:
return 0x10000 <= value <= 0x00007FFFFFFFFFFF


def module_base(pm: pymem.Pymem, name: str) -> int:
mod = pymem.process.module_from_name(pm.process_handle, name)

if mod is None:
raise RuntimeError(f"Module not found: {name}")

return mod.lpBaseOfDll


def rp(pm: pymem.Pymem, addr: int) -> int:
try:
value = pm.read_ulonglong(addr)
return value if valid_ptr(value) else 0
except Exception:
return 0


def ru64(pm: pymem.Pymem, addr: int) -> int | None:
try:
return pm.read_ulonglong(addr)
except Exception:
return None


def rf32(pm: pymem.Pymem, addr: int) -> float | None:
try:
value = pm.read_float(addr)
return value if math.isfinite(value) else None
except Exception:
return None


def rf64(pm: pymem.Pymem, addr: int) -> float | None:
try:
value = pm.read_double(addr)
return value if math.isfinite(value) else None
except Exception:
return None


def wb(pm: pymem.Pymem, addr: int, data: bytes) -> None:
pm.write_bytes(addr, data, len(data))


def wu32(pm: pymem.Pymem, addr: int, value: int) -> None:
wb(pm, addr, struct.pack("<I", value & 0xFFFFFFFF))


def wu64(pm: pymem.Pymem, addr: int, value: int) -> None:
wb(pm, addr, struct.pack("<Q", value & 0xFFFFFFFFFFFFFFFF))


def wf32(pm: pymem.Pymem, addr: int, value: float) -> None:
wb(pm, addr, struct.pack("<f", float(value)))


def protect(pm: pymem.Pymem, addr: int, size: int, prot: int) -> int:
old = ctypes.c_uint32(0)

ok = VirtualProtectEx(
pm.process_handle,
ctypes.c_void_p(addr),
size,
prot,
ctypes.byref(old),
)

if not ok:
raise ctypes.WinError(ctypes.get_last_error())

return old.value


def flush(pm: pymem.Pymem, addr: int, size: int) -> None:
FlushInstructionCache(
pm.process_handle,
ctypes.c_void_p(addr),
size,
)


# ================================================== ===========================
# Entity reading
# ================================================== ===========================

def chain_addr(pm: pymem.Pymem, base: int, chain: list[int]) -> int:
"""Resolve pointer chain and return final value address."""
cur = base

for off in chain[:-1]:
cur = rp(pm, cur + off)

if not valid_ptr(cur):
return 0

addr = cur + chain[-1]
return addr if valid_ptr(addr) else 0


def read_str(pm: pymem.Pymem, addr: int, max_len: int = NAME_MAX_LEN) -> str:
if not valid_ptr(addr):
return ""

try:
raw = pm.read_bytes(addr, max_len).split(b"\0", 1)[0]
return raw.decode("latin-1", errors="replace").strip() if raw else ""
except Exception:
return ""


def read_name(pm: pymem.Pymem, ent: int) -> str:
"""Name may be direct or behind one extra pointer."""
addr = chain_addr(pm, ent, OFFSETS["name"])

direct = read_str(pm, addr)
if direct:
return direct

return read_str(pm, rp(pm, addr))


def read_vec3(pm: pymem.Pymem, addr: int) -> tuple[float, float, float] | None:
x = rf32(pm, addr)
y = rf32(pm, addr + 4)
z = rf32(pm, addr + 8)

if x is None or y is None or z is None:
return None

if abs(x) > 1_000_000 or abs(y) > 1_000_000 or abs(z) > 1_000_000:
return None

if abs(x) < 0.0001 and abs(y) < 0.0001 and abs(z) < 0.0001:
return None

return x, y, z


def read_vec3_chain(pm: pymem.Pymem, ent: int, chain: list[int]) -> tuple[float, float, float] | None:
return read_vec3(pm, chain_addr(pm, ent, chain))


def blacklisted(name: str) -> bool:
"""
Contains-based blacklist.

This does not require the entity name to start with the keyword.
If the keyword appears anywhere in the name, the entity is ignored.
"""
low = name.lower()
return any(keyword.lower() in low for keyword in blacklist_keywords)


def read_entity(pm: pymem.Pymem, ent: int) -> Entity | None:
name = read_name(pm, ent)

if not name or blacklisted(name):
return None

pos = read_vec3_chain(pm, ent, OFFSETS["position"])
if pos is None:
return None

hp_addr = chain_addr(pm, ent, OFFSETS["current_health"])
hp = rf64(pm, hp_addr)

x, y, z = pos
return Entity(ent, name, hp, x, y, z)


def collect_entities(pm: pymem.Pymem, owner: int) -> list[Entity]:
"""
Walk the entity hash table and return valid targets only.

Important:
MAX_RANGE is not applied here.
This function only extracts valid entities.
Range filtering happens later in closest_entity().
"""
base = rp(pm, owner + OFFSETS["hash_base"])
stride = ru64(pm, owner + OFFSETS["hash_stride"])
count = ru64(pm, owner + OFFSETS["hash_count"])

if not valid_ptr(base) or stride is None or count is None:
return []

if stride <= 0 or stride > 0x400 or count <= 0 or count > MAX_BUCKETS:
return []

out: list[Entity] = []
seen_nodes: set[int] = set()
seen_entities: set[int] = set()
nodes = 0

for bucket in range(int(count)):
node = base + bucket * int(stride)
depth = 0

while valid_ptr(node) and depth < MAX_CHAIN_DEPTH and nodes < MAX_TOTAL_NODES:
if node in seen_nodes:
break

seen_nodes.add(node)
nodes += 1

raw_next = rp(pm, node + OFFSETS["node_next"])
next_node = raw_next & NODE_MARK_MASK if raw_next else 0

ent_ptr = rp(pm, node + OFFSETS["node_object"])

if valid_ptr(ent_ptr) and ent_ptr not in seen_entities:
seen_entities.add(ent_ptr)

ent = read_entity(pm, ent_ptr)
if ent is not None:
out.append(ent)

if raw_next == 1 or not valid_ptr(next_node) or next_node == node:
break

node = next_node
depth += 1

return out


# ================================================== ===========================
# Target selection
# ================================================== ===========================

def cam_pos(pm: pymem.Pymem, base: int) -> tuple[float, float, float] | None:
"""Read current camera position."""
local = rp(pm, base + LOCAL_BASE_PTR)
if not valid_ptr(local):
return None

root = rp(pm, local + CAM_ROOT_OFFSET)
if not valid_ptr(root):
return None

return read_vec3(pm, root + CAM_POS_OFFSET)


def dist3(a: tuple[float, float, float], b: tuple[float, float, float]) -> float:
dx = a[0] - b[0]
dy = a[1] - b[1]
dz = a[2] - b[2]

return math.sqrt(dx * dx + dy * dy + dz * dz)


def aim_pos(ent: Entity) -> tuple[float, float, float]:
"""Convert entity base position to aim point."""
return ent.x, ent.y + AIM_Y_OFFSET, ent.z


def closest_entity(
entities: list[Entity],
camera: tuple[float, float, float],
) -> Entity | None:
"""
Select the closest entity inside MAX_RANGE only.

This does not affect extraction.
If all extracted entities are outside MAX_RANGE, return None.
Returning None disables silent aim until a valid target enters range.
"""
best = None
best_dist = float("inf")

for ent in entities:
d = dist3(camera, aim_pos(ent))

if d > MAX_RANGE:
continue

if d < best_dist:
best_dist = d
ent.dist = d
best = ent

return best


# ================================================== ===========================
# Codecave
# ================================================== ===========================

def asm_bytes(asm: str) -> bytes:
"""Assemble code with Keystone after removing comments/empty lines."""
clean = "\n".join(
line.split(";", 1)[0].strip()
for line in asm.splitlines()
if line.split(";", 1)[0].strip()
)

ks = Ks(KS_ARCH_X86, KS_MODE_64)
code, _ = ks.asm(clean)

if not code:
raise RuntimeError("Keystone returned empty code")

return bytes(code)


def jmp_abs(addr: int) -> bytes:
"""14-byte absolute jump, works even when cave is far away."""
return b"\xFF\x25\x00\x00\x00\x00" + struct.pack("<Q", addr)


def init_data(pm: pymem.Pymem, data: int, local_base_ptr: int) -> None:
"""Initialize shared block read by the injected cave."""
wu32(pm, data + DATA_ENABLED, 1)
wu32(pm, data + DATA_VALID, 0)

wf32(pm, data + DATA_X, 0.0)
wf32(pm, data + DATA_Y, 0.0)
wf32(pm, data + DATA_Z, 0.0)

wf32(pm, data + DATA_PARALLAX, PARALLAX)
wf32(pm, data + DATA_MIN_LEN_SQ, MIN_LEN_SQ)

wu64(pm, data + DATA_LOCAL_BASE_PTR, local_base_ptr)


def build_cave(data: int, ret: int) -> bytes:
"""
Entry hook for SetProjectileOrigin.

It writes a corrected normalized direction directly into [r8],
because r8 is the incoming projectile direction pointer.
"""
return asm_bytes(f"""
pushfq
push rax
push rdx
push r10
push r11

sub rsp, 0x60
movdqu xmmword ptr [rsp + 0x00], xmm0
movdqu xmmword ptr [rsp + 0x10], xmm1
movdqu xmmword ptr [rsp + 0x20], xmm2
movdqu xmmword ptr [rsp + 0x30], xmm3
movdqu xmmword ptr [rsp + 0x40], xmm4
movdqu xmmword ptr [rsp + 0x50], xmm5

mov r10, 0x{data:X}

cmp dword ptr [r10 + 0x{DATA_ENABLED:X}], 1
jne skip

cmp dword ptr [r10 + 0x{DATA_VALID:X}], 1
jne skip

test rdx, rdx
je skip

test r8, r8
je skip

mov rax, qword ptr [r10 + 0x{DATA_LOCAL_BASE_PTR:X}]
test rax, rax
je skip

mov rax, qword ptr [rax]
test rax, rax
je skip

mov r11, qword ptr [rax + 0x{CAM_ROOT_OFFSET:X}]
test r11, r11
je skip

movss xmm0, dword ptr [r10 + 0x{DATA_X:X}]
movss xmm3, dword ptr [r11 + 0x{CAM_POS_OFFSET + 0:X}]
movss xmm4, xmm3
subss xmm4, dword ptr [rdx + 0x00]
mulss xmm4, dword ptr [r10 + 0x{DATA_PARALLAX:X}]
addss xmm0, xmm4
subss xmm0, xmm3

movss xmm1, dword ptr [r10 + 0x{DATA_Y:X}]
movss xmm3, dword ptr [r11 + 0x{CAM_POS_OFFSET + 4:X}]
movss xmm4, xmm3
subss xmm4, dword ptr [rdx + 0x04]
mulss xmm4, dword ptr [r10 + 0x{DATA_PARALLAX:X}]
addss xmm1, xmm4
subss xmm1, xmm3

movss xmm2, dword ptr [r10 + 0x{DATA_Z:X}]
movss xmm3, dword ptr [r11 + 0x{CAM_POS_OFFSET + 8:X}]
movss xmm4, xmm3
subss xmm4, dword ptr [rdx + 0x08]
mulss xmm4, dword ptr [r10 + 0x{DATA_PARALLAX:X}]
addss xmm2, xmm4
subss xmm2, xmm3

movss xmm3, xmm0
mulss xmm3, xmm3

movss xmm4, xmm1
mulss xmm4, xmm4
addss xmm3, xmm4

movss xmm5, xmm2
mulss xmm5, xmm5
addss xmm3, xmm5

comiss xmm3, dword ptr [r10 + 0x{DATA_MIN_LEN_SQ:X}]
jbe skip

sqrtss xmm3, xmm3

divss xmm0, xmm3
divss xmm1, xmm3
divss xmm2, xmm3

movss dword ptr [r8 + 0x00], xmm0
movss dword ptr [r8 + 0x04], xmm1
movss dword ptr [r8 + 0x08], xmm2

skip:
movdqu xmm0, xmmword ptr [rsp + 0x00]
movdqu xmm1, xmmword ptr [rsp + 0x10]
movdqu xmm2, xmmword ptr [rsp + 0x20]
movdqu xmm3, xmmword ptr [rsp + 0x30]
movdqu xmm4, xmmword ptr [rsp + 0x40]
movdqu xmm5, xmmword ptr [rsp + 0x50]

add rsp, 0x60

pop r11
pop r10
pop rdx
pop rax
popfq

mov rax, rsp
mov qword ptr [rax + 0x10], rbx
mov dword ptr [rax + 0x20], r9d
push rbp
push rsi
push rdi

mov r11, 0x{ret:X}
jmp r11
""")


def install_hook(pm: pymem.Pymem, hook: int, cave: int) -> bytes:
"""Patch the function entry with an absolute jump."""
original = pm.read_bytes(hook, PATCH_LEN)

if original != EXPECTED_ORIGINAL:
raise RuntimeError(
"Hook bytes mismatch\n"
f"expected: {EXPECTED_ORIGINAL.hex(' ').upper()}\n"
f"actual : {original.hex(' ').upper()}"
)

patch = jmp_abs(cave)
old = protect(pm, hook, PATCH_LEN, PAGE_EXECUTE_READWRITE)

try:
wb(pm, hook, patch)
flush(pm, hook, PATCH_LEN)
finally:
protect(pm, hook, PATCH_LEN, old)

return original


def uninstall_hook(pm: pymem.Pymem, hook: int, original: bytes) -> None:
"""Restore original function bytes."""
old = protect(pm, hook, len(original), PAGE_EXECUTE_READWRITE)

try:
wb(pm, hook, original)
flush(pm, hook, len(original))
finally:
protect(pm, hook, len(original), old)


# ================================================== ===========================
# Target updater
# ================================================== ===========================

def set_valid(pm: pymem.Pymem, data: int, valid: bool) -> None:
wu32(pm, data + DATA_VALID, 1 if valid else 0)


def write_target(pm: pymem.Pymem, data: int, pos: tuple[float, float, float]) -> None:
"""Write selected target position to shared block."""
wf32(pm, data + DATA_X, pos[0])
wf32(pm, data + DATA_Y, pos[1])
wf32(pm, data + DATA_Z, pos[2])
wu32(pm, data + DATA_VALID, 1)


def target_loop(pm: pymem.Pymem, base: int, data: int, stop: threading.Event) -> None:
"""
Background thread.

It extracts entities normally, then applies MAX_RANGE only during final
target selection. This keeps extraction independent from range filtering.
"""
owner_ptr = base + EntityList
last_addr = 0
last_pos = None
last_clear = 0.0

while not stop.is_set():
try:
camera = cam_pos(pm, base)
owner = rp(pm, owner_ptr)

if camera is None or not valid_ptr(owner):
if time.time() - last_clear >= 0.25:
set_valid(pm, data, False)
last_clear = time.time()

time.sleep(ENTITY_REFRESH_DELAY)
continue

entities = collect_entities(pm, owner)
target = closest_entity(entities, camera)

if target is None:
last_addr = 0
last_pos = None

if time.time() - last_clear >= 0.25:
set_valid(pm, data, False)
last_clear = time.time()

time.sleep(ENTITY_REFRESH_DELAY)
continue

pos = aim_pos(target)
moved = 999999.0 if last_pos is None else dist3(last_pos, pos)

if target.address != last_addr or last_pos is None or moved >= TARGET_UPDATE_MIN_DELTA:
write_target(pm, data, pos)
last_addr = target.address
last_pos = pos

time.sleep(ENTITY_REFRESH_DELAY)

except Exception:
set_valid(pm, data, False)
time.sleep(ENTITY_REFRESH_DELAY)


# ================================================== ===========================
# Main
# ================================================== ===========================

def main() -> None:
print(f"[*] Attaching to {PROCESS_NAME}...")

pm = pymem.Pymem(PROCESS_NAME)
base = module_base(pm, PROCESS_NAME)

hook = base + HOOK_RVA
ret = base + RETURN_RVA
local_base_ptr = base + LOCAL_BASE_PTR

data = RemoteAlloc(pm, 0x1000)
cave = RemoteAlloc(pm, 0x4000)

original = b""
installed = False
stop = threading.Event()
worker = None

try:
init_data(pm, data.address, local_base_ptr)

cave_code = build_cave(data.address, ret)
wb(pm, cave.address, cave_code)
flush(pm, cave.address, len(cave_code))

original = install_hook(pm, hook, cave.address)
installed = True

worker = threading.Thread(
target=target_loop,
args=(pm, base, data.address, stop),
daemon=True,
)
worker.start()

print("[+] Silent aim running")
print(f"[+] max range = {MAX_RANGE:.1f}")
print("[*] Press Ctrl+C to stop")

while True:
time.sleep(1)

except KeyboardInterrupt:
print("\n[*] Stopping...")

finally:
stop.set()

if worker is not None:
worker.join(timeout=1.0)

# Disable the cave logic before restoring/freeing memory.
# This makes shutdown safer if the game hits the hook during cleanup.
if data is not None and data.address:
try:
wu32(pm, data.address + DATA_ENABLED, 0)
wu32(pm, data.address + DATA_VALID, 0)
except Exception:
pass

if installed:
try:
uninstall_hook(pm, hook, original)
print("[+] Hook restored")
except Exception as exc:
print(f"[-] Failed to restore hook: {exc}")

if cave is not None:
try:
cave.free()
except Exception:
pass

if data is not None:
try:
data.free()
except Exception:
pass

print("[+] Done")

if __name__ == "__main__":
main()
#1 · 29d ago
IO
iohan2
HOW TO USE IT
any tutorials? pls
#2 · 29d ago
FR
fraten
will you bel able to make a trainer similar to your previous creation where we could prioritize or ignore certain mob groups?
#3 · 27d ago
AL
alekx1235
hey do you have the new bypass offsets struggling to find them would appreciate it if you could share if you have them
#4 · 27d ago
Posts 1–4 of 4 · Page 1 of 1

Post a Reply

Similar Threads

  • Silent AimBy FunTrat0r in Trove Hacks & Cheats
    21Last post 7mo ago
  • Silent aimBy adryan3s in CrossFire Discussions
    10Last post 16y ago
  • CA SILENTBy GameTrainerMaker in Combat Arms Hack Coding / Programming / Source Code
    18Last post 16y ago
  • Have they done a silent patch?By Allan990 in Combat Arms Help
    3Last post 15y ago
  • Silent PatchBy TheFallenOwns in Combat Arms Hack Coding / Programming / Source Code
    5Last post 16y ago

Tags for this Thread

None