1
+ import logging
1
2
import requests
2
3
import pymem
3
4
import pyMeow as overlay
4
- from typing import Iterator , Optional , Dict , Any
5
+ from typing import Iterator , Optional , Dict
5
6
from classes .utils import read_vec3 , read_string , read_floats , transliterate
6
7
from classes .config import Offsets , Colors , overlay_settings
7
8
from PyQt6 .QtCore import QObject , pyqtSignal , pyqtSlot
8
- import logging
9
9
10
10
logging .basicConfig (level = logging .INFO , format = '[%(levelname)s] %(message)s' )
11
11
12
+ # Constants for entity iteration
13
+ ENTITY_COUNT = 64
14
+ ENTITY_ENTRY_SIZE = 120
12
15
13
16
class Entity :
14
17
"""
@@ -28,34 +31,53 @@ def name(self) -> str:
28
31
29
32
@property
30
33
def health (self ) -> int :
31
- return self .mem .read_int (self .pawn_ptr + Offsets .m_iHealth )
34
+ try :
35
+ return self .mem .read_int (self .pawn_ptr + Offsets .m_iHealth )
36
+ except Exception as e :
37
+ logging .error ("Failed to read health: %s" , e )
38
+ return 0
32
39
33
40
@property
34
41
def team (self ) -> int :
35
- return self .mem .read_int (self .pawn_ptr + Offsets .m_iTeamNum )
42
+ try :
43
+ return self .mem .read_int (self .pawn_ptr + Offsets .m_iTeamNum )
44
+ except Exception as e :
45
+ logging .error ("Failed to read team: %s" , e )
46
+ return - 1
36
47
37
48
@property
38
49
def pos (self ) -> Dict [str , float ]:
39
- return read_vec3 (self .mem , self .pawn_ptr + Offsets .m_vOldOrigin )
50
+ try :
51
+ return read_vec3 (self .mem , self .pawn_ptr + Offsets .m_vOldOrigin )
52
+ except Exception as e :
53
+ logging .error ("Failed to read position: %s" , e )
54
+ return {"x" : 0.0 , "y" : 0.0 , "z" : 0.0 }
40
55
41
56
@property
42
57
def dormant (self ) -> bool :
43
- return bool (self .mem .read_int (self .pawn_ptr + Offsets .m_bDormant ))
58
+ try :
59
+ return bool (self .mem .read_int (self .pawn_ptr + Offsets .m_bDormant ))
60
+ except Exception as e :
61
+ logging .error ("Failed to read dormant flag: %s" , e )
62
+ return True
44
63
45
64
def bone_pos (self , bone : int ) -> Dict [str , float ]:
46
- game_scene = self .mem .read_longlong (self .pawn_ptr + Offsets .m_pGameSceneNode )
47
- bone_array_ptr = self .mem .read_longlong (game_scene + Offsets .m_pBoneArray )
48
- return read_vec3 (self .mem , bone_array_ptr + bone * 32 )
65
+ try :
66
+ game_scene = self .mem .read_longlong (self .pawn_ptr + Offsets .m_pGameSceneNode )
67
+ bone_array_ptr = self .mem .read_longlong (game_scene + Offsets .m_pBoneArray )
68
+ return read_vec3 (self .mem , bone_array_ptr + bone * 32 )
69
+ except Exception as e :
70
+ logging .error ("Failed to get bone position for bone %d: %s" , bone , e )
71
+ return {"x" : 0.0 , "y" : 0.0 , "z" : 0.0 }
49
72
50
73
def world_to_screen (self , view_matrix : list ) -> bool :
51
74
try :
52
75
self .pos2d = overlay .world_to_screen (view_matrix , self .pos , 1 )
53
76
self .head_pos2d = overlay .world_to_screen (view_matrix , self .bone_pos (6 ), 1 )
77
+ return True
54
78
except Exception as e :
55
79
logging .error ("world_to_screen conversion failed: %s" , e )
56
80
return False
57
- return True
58
-
59
81
60
82
class CS2Esp :
61
83
"""
@@ -80,32 +102,36 @@ def load_offsets(self) -> None:
80
102
Loads game offsets and field mappings from remote JSON resources.
81
103
"""
82
104
try :
83
- offsets_url = "https://raw.githubusercontent.com/a2x/cs2-dumper/main/output/offsets.json"
84
- with requests .get (offsets_url ) as offsets_response :
105
+ # Use a session for improved performance and timeout handling
106
+ with requests .Session () as session :
107
+ offsets_url = "https://raw.githubusercontent.com/a2x/cs2-dumper/main/output/offsets.json"
108
+ offsets_response = session .get (offsets_url , timeout = 10 )
85
109
offsets_response .raise_for_status ()
86
110
offsets_data = offsets_response .json ()
87
- keys = ["dwViewMatrix" , "dwEntityList" , "dwLocalPlayerController" , "dwLocalPlayerPawn" ]
88
- for key in keys :
89
- setattr (Offsets , key , offsets_data ["client.dll" ][key ])
90
111
91
- client_dll_url = "https://raw.githubusercontent.com/a2x/cs2-dumper/main/output/client_dll.json"
92
- with requests .get (client_dll_url ) as client_dll_response :
112
+ # Load required offset keys
113
+ keys = ["dwViewMatrix" , "dwEntityList" , "dwLocalPlayerController" , "dwLocalPlayerPawn" ]
114
+ for key in keys :
115
+ setattr (Offsets , key , offsets_data ["client.dll" ].get (key ))
116
+
117
+ client_dll_url = "https://raw.githubusercontent.com/a2x/cs2-dumper/main/output/client_dll.json"
118
+ client_dll_response = session .get (client_dll_url , timeout = 10 )
93
119
client_dll_response .raise_for_status ()
94
120
client_dll_data = client_dll_response .json ()
95
121
96
- mapping = {
97
- "m_iIDEntIndex" : "C_CSPlayerPawnBase" ,
98
- "m_hPlayerPawn" : "CCSPlayerController" ,
99
- "m_fFlags" : "C_BaseEntity" ,
100
- "m_iszPlayerName" : "CBasePlayerController" ,
101
- "m_iHealth" : "C_BaseEntity" ,
102
- "m_iTeamNum" : "C_BaseEntity" ,
103
- "m_vOldOrigin" : "C_BasePlayerPawn" ,
104
- "m_pGameSceneNode" : "C_BaseEntity" ,
105
- "m_bDormant" : "CGameSceneNode" ,
106
- }
107
- for field , cls in mapping .items ():
108
- setattr (Offsets , field , client_dll_data ["client.dll" ]["classes" ][cls ]["fields" ][ field ] )
122
+ mapping = {
123
+ "m_iIDEntIndex" : "C_CSPlayerPawnBase" ,
124
+ "m_hPlayerPawn" : "CCSPlayerController" ,
125
+ "m_fFlags" : "C_BaseEntity" ,
126
+ "m_iszPlayerName" : "CBasePlayerController" ,
127
+ "m_iHealth" : "C_BaseEntity" ,
128
+ "m_iTeamNum" : "C_BaseEntity" ,
129
+ "m_vOldOrigin" : "C_BasePlayerPawn" ,
130
+ "m_pGameSceneNode" : "C_BaseEntity" ,
131
+ "m_bDormant" : "CGameSceneNode" ,
132
+ }
133
+ for field , cls in mapping .items ():
134
+ setattr (Offsets , field , client_dll_data ["client.dll" ]["classes" ][cls ]["fields" ]. get ( field ) )
109
135
except Exception as e :
110
136
raise Exception (f"Error loading offsets: { e } " )
111
137
@@ -120,10 +146,6 @@ def iterate_entities(self) -> Iterator[Entity]:
120
146
logging .error ("Error reading entity list or local controller pointer: %s" , e )
121
147
return iter ([])
122
148
123
- # Constants used for entity list indexing
124
- ENTITY_COUNT = 64
125
- ENTITY_ENTRY_SIZE = 120
126
-
127
149
for i in range (1 , ENTITY_COUNT + 1 ):
128
150
try :
129
151
list_index = (i & 0x7FFF ) >> 9
@@ -164,7 +186,7 @@ def draw_entity(self, entity: Entity, view_matrix: list, is_teammate: bool = Fal
164
186
overlay_settings .teammate_color_hex if is_teammate else overlay_settings .box_color_hex
165
187
)
166
188
text_color = overlay .get_color (overlay_settings .text_color_hex )
167
-
189
+ # Optionally draw snaplines
168
190
if overlay_settings .draw_snaplines :
169
191
screen_width = overlay .get_screen_width ()
170
192
screen_height = overlay .get_screen_height ()
@@ -235,6 +257,16 @@ def draw_entity(self, entity: Entity, view_matrix: list, is_teammate: bool = Fal
235
257
fill_height ,
236
258
fill_color
237
259
)
260
+ # Optionally draw health numbers
261
+ if getattr (overlay_settings , 'draw_health_numbers' , False ):
262
+ health_text = f"{ entity .health } "
263
+ overlay .draw_text (
264
+ health_text ,
265
+ int (bar_x - 25 ),
266
+ int (bar_y + bar_height / 2 - 5 ),
267
+ 10 ,
268
+ text_color
269
+ )
238
270
except Exception as e :
239
271
logging .error ("Error drawing entity: %s" , e )
240
272
@@ -290,7 +322,6 @@ def stop(self) -> None:
290
322
"""Stops the overlay loop."""
291
323
self .running = False
292
324
293
-
294
325
class OverlayWorker (QObject ):
295
326
"""
296
327
A Qt worker class for running the CS2 ESP overlay in a separate thread.
0 commit comments