i tried this for debugging, but screen cords are always 0, 0... is ViewMatrix Problem or my w2s problem?
Code:
import pymem
import pymem.process
import struct
import win32gui
import math
import time
# Offsets
SAMP_INFO_OFFSET = 0x21A0F8
PPOOL_OFFSET = 0x3CD
PLAYER_POOL_OFFSET = 0x18
MAX_PLAYER_ID_OFFSET = 0x0
REMOTEPLAYER_OFFSET = 0x2E
REMOTEPLAYERDATA_OFFSET = 0x0
ACTOR_OFFSET = 0x0
GTA_ENTITY_OFFSET = 0x40
CAMERA_MATRIX_OFFSET = 0xB6FA2C
def get_game_window_rect(window_title_substring="GTA:SA"):
def enum_windows_callback(hwnd, results):
if win32gui.IsWindowVisible(hwnd):
title = win32gui.GetWindowText(hwnd)
if window_title_substring.lower() in title.lower():
results.append(hwnd)
results = []
win32gui.EnumWindows(enum_windows_callback, results)
if not results:
return None
hwnd = results[0]
rect = win32gui.GetClientRect(hwnd)
width = rect[2] - rect[0]
height = rect[3] - rect[1]
return hwnd, width, height
def read_matrix(pm: pymem.Pymem, address: int):
try:
data = pm.read_bytes(address, 64) # 64 bytes
return struct.unpack('<16f', data)
except pymem.exception.MemoryReadError:
return None
def world_to_screen(pos, matrix, width, height):
clip_x = pos[0]*matrix[0] + pos[1]*matrix[1] + pos[2]*matrix[2] + matrix[3]
clip_y = pos[0]*matrix[4] + pos[1]*matrix[5] + pos[2]*matrix[6] + matrix[7]
clip_w = pos[0]*matrix[12] + pos[1]*matrix[13] + pos[2]*matrix[14] + matrix[15]
# You can try lowering this threshold temporarily
if clip_w < 0.01:
return None
ndc_x = clip_x / clip_w
ndc_y = clip_y / clip_w
print(f"NDC coords: x={ndc_x}, y={ndc_y}")
if any(map(lambda v: math.isnan(v) or math.isinf(v), [ndc_x, ndc_y])):
print("NDC has NaN or Inf")
return None
screen_x = int((ndc_x + 1) * 0.5 * width)
screen_y = int((1 - ndc_y) * 0.5 * height)
print(f"Screen coords: x={screen_x}, y={screen_y}")
# Optionally filter out coords outside screen bounds:
if screen_x < 0 or screen_x > width or screen_y < 0 or screen_y > height:
print("Screen coords out of bounds")
# return None # comment out to see all outputs
return (screen_x, screen_y)
def read_dword(pm: pymem.Pymem, address: int) -> int:
try:
return pm.read_int(address)
except pymem.exception.MemoryReadError:
return 0
def read_float(pm: pymem.Pymem, address: int) -> float:
try:
data = pm.read_bytes(address, 4)
return struct.unpack('<f', data)[0]
except pymem.exception.MemoryReadError:
return float('nan')
def get_max_player_id(pm: pymem.Pymem, samp_base: int) -> int:
samp_info = read_dword(pm, samp_base + SAMP_INFO_OFFSET)
if not samp_info:
return 0
samp_pools = read_dword(pm, samp_info + PPOOL_OFFSET)
if not samp_pools:
return 0
player_pool = read_dword(pm, samp_pools + PLAYER_POOL_OFFSET)
if not player_pool:
return 0
max_player_id = read_dword(pm, player_pool + MAX_PLAYER_ID_OFFSET)
return max_player_id
def get_gta_entity(pm: pymem.Pymem, samp_base: int, playerID: int) -> int | None:
samp_info = read_dword(pm, samp_base + SAMP_INFO_OFFSET)
if not samp_info:
return None
samp_pools = read_dword(pm, samp_info + PPOOL_OFFSET)
if not samp_pools:
return None
player_pool = read_dword(pm, samp_pools + PLAYER_POOL_OFFSET)
if not player_pool:
return None
max_player_id = read_dword(pm, player_pool + MAX_PLAYER_ID_OFFSET)
if playerID < 0 or playerID > max_player_id:
return None
remote_player_ptr_addr = player_pool + REMOTEPLAYER_OFFSET + (playerID * 4)
remote_player = read_dword(pm, remote_player_ptr_addr)
if not remote_player:
return None
player_data = read_dword(pm, remote_player + REMOTEPLAYERDATA_OFFSET)
if not player_data:
return None
samp_actor = read_dword(pm, player_data + ACTOR_OFFSET)
if not samp_actor:
return None
gta_entity = read_dword(pm, samp_actor + GTA_ENTITY_OFFSET)
return gta_entity if gta_entity != 0 else None
def get_player_coords(pm: pymem.Pymem, gta_entity: int):
if gta_entity == 0:
return None
dwAddress = read_dword(pm, gta_entity + 0x14)
if not dwAddress:
return None
x = read_float(pm, dwAddress + 0x30)
y = read_float(pm, dwAddress + 0x34)
z = read_float(pm, dwAddress + 0x38)
if any(coord != coord for coord in (x, y, z)):
return None
return (x, y, z)
if __name__ == "__main__":
pm = pymem.Pymem("gta_sa.exe")
samp_module = pymem.process.module_from_name(pm.process_handle, "samp.dll")
samp_base = samp_module.lpBaseOfDll
window_data = get_game_window_rect()
if window_data is None:
print("Game window not found")
exit()
hwnd, screen_width, screen_height = window_data
print(f"Game window found: hwnd={hwnd}, size={screen_width}x{screen_height}")
print(f"SAMP base address: {hex(samp_base)}")
while True:
viewmatrix = read_matrix(pm, CAMERA_MATRIX_OFFSET)
if not viewmatrix:
print("Failed to read view matrix")
time.sleep(0.05)
continue
max_id = get_max_player_id(pm, samp_base)
valid_players = []
for player_id in range(max_id + 1):
entity = get_gta_entity(pm, samp_base, player_id)
if entity is None:
# print(f"Player {player_id} entity is None")
continue
coords = get_player_coords(pm, entity)
if coords is None:
# print(f"Player {player_id} coords are None")
continue
x, y, z = coords
#print(f"Player {player_id} coords: X={x:.2f}, Y={y:.2f}, Z={z:.2f}")
valid_players.append((player_id, coords))
for player_id, coords in valid_players:
screen_pos = world_to_screen(coords, viewmatrix, screen_width, screen_height)
if screen_pos:
sx, sy = screen_pos
print(f"PlayerID {player_id} at screen coords: {sx}, {sy}")
time.sleep(0.05)