trainerbase 4.8.0__tar.gz → 4.8.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. {trainerbase-4.8.0 → trainerbase-4.8.2}/PKG-INFO +1 -1
  2. {trainerbase-4.8.0 → trainerbase-4.8.2}/pyproject.toml +22 -17
  3. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/codeinjection.py +8 -8
  4. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/common/helpers.py +4 -3
  5. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/common/rainbow.py +1 -2
  6. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/config.py +1 -1
  7. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gameobject.py +2 -2
  8. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/objects.py +6 -4
  9. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/logger.py +1 -1
  10. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/main.py +1 -2
  11. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/memory.py +19 -12
  12. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/process.py +2 -2
  13. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/scriptengine.py +3 -3
  14. {trainerbase-4.8.0 → trainerbase-4.8.2}/README.md +0 -0
  15. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/__init__.py +0 -0
  16. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/abc.py +0 -0
  17. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/common/__init__.py +0 -0
  18. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/common/abc.py +0 -0
  19. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/common/keyboard.py +0 -0
  20. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/common/tapper.py +0 -0
  21. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/common/teleport.py +0 -0
  22. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/__init__.py +0 -0
  23. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/helpers.py +0 -0
  24. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/injections.py +0 -0
  25. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/misc.py +0 -0
  26. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/rainbow.py +0 -0
  27. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/scripts.py +0 -0
  28. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/speedhack.py +0 -0
  29. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/tapper.py +0 -0
  30. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/teleport.py +0 -0
  31. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/gui/types.py +0 -0
  32. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/py.typed +0 -0
  33. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/speedhack.py +0 -0
  34. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/tts.py +0 -0
  35. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/vendor/FASM.EXE +0 -0
  36. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/vendor/big_logo.png +0 -0
  37. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/vendor/speedhack32.dll +0 -0
  38. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/vendor/speedhack64.dll +0 -0
  39. {trainerbase-4.8.0 → trainerbase-4.8.2}/trainerbase/vendor/trainerbase.ico +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trainerbase
3
- Version: 4.8.0
3
+ Version: 4.8.2
4
4
  Summary: Framework for creating game trainers/mods
5
5
  Author: v01d, doomayka
6
6
  Author-email: v01d <v01d@v01d.ru>, doomayka <doomaykaka@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "trainerbase"
3
- version = "4.8.0"
3
+ version = "4.8.2"
4
4
  description = "Framework for creating game trainers/mods"
5
5
  license = "MIT"
6
6
  authors = [
@@ -34,20 +34,25 @@ src = ["trainerbase"]
34
34
  line-length = 120
35
35
 
36
36
  [tool.ruff.lint]
37
- select = [
38
- "E", # pycodestyle
39
- "F", # Pyflakes
40
- "UP", # pyupgrade
41
- "B", # flake8-bugbear
42
- "SIM", # flake8-simplify
43
- "I", # isort
44
- "N", # pep8-naming
45
- "FIX",
46
- "FBT",
47
- "ARG",
48
- "PTH",
49
- "ERA",
50
- "RUF",
37
+ select = ["ALL"]
38
+ ignore = [
39
+ "D",
40
+ "ANN",
41
+ "TC",
42
+ "EM",
43
+ "A005",
44
+ "FIX002",
45
+ "COM812",
46
+ "TC002",
47
+ "S607",
48
+ "BLE001",
49
+ "TRY003",
50
+ "RET504",
51
+ "PLR2004",
52
+ "PLR0913",
53
+ "PLW2901",
54
+ "TD002",
55
+ "TD003",
51
56
  ]
52
57
 
53
58
  [tool.ruff.lint.isort]
@@ -70,12 +75,12 @@ module-name = "trainerbase"
70
75
  module-root = ""
71
76
 
72
77
  [build-system]
73
- requires = ["uv_build>=0.10.9,<0.11.0"]
78
+ requires = ["uv_build>=0.11.3,<1.0.0"]
74
79
  build-backend = "uv_build"
75
80
 
76
81
  [dependency-groups]
77
82
  dev = [
78
83
  "invoke>=2.2.1",
79
84
  "pyright>=1.1.407",
80
- "ruff>=0.15.5",
85
+ "ruff>=0.15.9",
81
86
  ]
@@ -59,7 +59,7 @@ class CodeInjection(AbstractCodeInjection):
59
59
  self.injection_point = self.address.resolve()
60
60
  self.original_code = pm.read_bytes(self.injection_point, len(self.code_to_inject))
61
61
 
62
- logger.debug(f"Injecting {len(self.code_to_inject)} byte(s) at 0x{self.injection_point:X}")
62
+ logger.debug("Injecting %d byte(s) at 0x%X", len(self.code_to_inject), self.injection_point)
63
63
 
64
64
  pm.write_bytes(self.injection_point, self.code_to_inject, len(self.code_to_inject))
65
65
  self._injected = True
@@ -67,7 +67,7 @@ class CodeInjection(AbstractCodeInjection):
67
67
  @override
68
68
  def eject(self):
69
69
  if self._injected:
70
- logger.debug(f"Ejecting {len(self.code_to_inject)} byte(s) from 0x{self.injection_point:X}")
70
+ logger.debug("Ejecting %d byte(s) from 0x%X", len(self.code_to_inject), self.injection_point)
71
71
 
72
72
  pm.write_bytes(self.injection_point, self.original_code, len(self.original_code))
73
73
  self._injected = False
@@ -140,19 +140,19 @@ class AllocatingCodeInjection(AbstractCodeInjection):
140
140
 
141
141
  code_to_inject = code_to_inject + jump_back
142
142
 
143
- logger.debug(f"Injecting {len(code_to_inject)} byte(s) (INJECTION) at 0x{self.new_memory_address:X}")
143
+ logger.debug("Injecting %d byte(s) (INJECTION) at 0x%X", len(code_to_inject), self.new_memory_address)
144
144
  pm.write_bytes(self.new_memory_address, code_to_inject, len(code_to_inject))
145
145
 
146
- logger.debug(f"Injecting {len(jump_to_new_mem)} byte(s) (JUMPER) at 0x{self.injection_point:X}")
146
+ logger.debug("Injecting %d byte(s) (JUMPER) at 0x%X", len(jump_to_new_mem), self.injection_point)
147
147
  pm.write_bytes(self.injection_point, jump_to_new_mem, len(jump_to_new_mem))
148
148
 
149
149
  @override
150
150
  def eject(self):
151
151
  if self.new_memory_address and self.injection_point:
152
- logger.debug(f"Ejecting {self.original_code_length} byte(s) (JUMPER) from 0x{self.injection_point:X}")
152
+ logger.debug("Ejecting %d byte(s) (JUMPER) from 0x%X", self.original_code_length, self.injection_point)
153
153
  pm.write_bytes(self.injection_point, self.original_code, self.original_code_length)
154
154
 
155
- logger.debug(f"Freeing memory at 0x{self.new_memory_address:X}")
155
+ logger.debug("Freeing memory at 0x%X", self.new_memory_address)
156
156
  pm.free(self.new_memory_address)
157
157
  self.new_memory_address = 0
158
158
  self.injection_point = 0
@@ -200,7 +200,7 @@ def preprocess_asm(code: str, *, original_code: bytes | None, base_address: int
200
200
  @lru_cache
201
201
  def compile_asm(code: Code) -> bytes:
202
202
  if isinstance(code, str):
203
- logger.debug(f"Assembling for x{ARCH}:\n{code}")
203
+ logger.debug("Assembling for x%d:\n%s", ARCH, code)
204
204
 
205
205
  fasm_mode = f"use{ARCH}"
206
206
  with TemporaryDirectory() as tmp_dir:
@@ -208,7 +208,7 @@ def compile_asm(code: Code) -> bytes:
208
208
 
209
209
  asm_file.write_text(f"{fasm_mode}\n{code}", encoding="utf-8")
210
210
 
211
- run_executable([FASM_EXECUTABLE_PATH, asm_file], stdout=DEVNULL, check=True)
211
+ run_executable([FASM_EXECUTABLE_PATH, asm_file], stdout=DEVNULL, check=True) # noqa: S603
212
212
  code = asm_file.with_suffix(".bin").read_bytes()
213
213
 
214
214
  if not isinstance(code, bytes):
@@ -22,8 +22,8 @@ class ASMArrayManager(Sequence):
22
22
  offsets: None | list[int] = None,
23
23
  add: int = 0,
24
24
  ) -> None:
25
- if offsets:
26
- assert element_size == POINTER_SIZE, "If `offsets` is not None, `element_size` must be `POINTER_SIZE`"
25
+ if offsets and element_size != POINTER_SIZE:
26
+ raise ValueError("If `offsets` is not None, `element_size` must be `POINTER_SIZE`")
27
27
 
28
28
  self.array_length = array_length
29
29
  self.element_size = element_size
@@ -72,7 +72,8 @@ class ASMArrayManager(Sequence):
72
72
  if from_register == array_index_register:
73
73
  array_index_register = reserve_register
74
74
 
75
- assert len({from_register, array_address_register, array_index_register}) == 3, "Registers must be unique"
75
+ if len({from_register, array_address_register, array_index_register}) != 3:
76
+ raise RuntimeError("Registers must be unique")
76
77
 
77
78
  label_continue = f"__label_continue_{uuid4().hex}"
78
79
 
@@ -61,8 +61,7 @@ class GradientUpdater(AbstractBaseScript):
61
61
  colors = []
62
62
 
63
63
  for _ in range(length // 2):
64
- for _ in range(self.color_length):
65
- colors.append((red, green, blue))
64
+ colors.extend((red, green, blue) for _ in range(self.color_length))
66
65
 
67
66
  red, delta_red = self.next_color_channel_value(red, delta_red)
68
67
  green, delta_green = self.next_color_channel_value(green, delta_green)
@@ -9,7 +9,7 @@ CONFIG_FILE: Final[Path] = Path("./trainerbase.toml")
9
9
 
10
10
  DEFAULT_VENDOR_FOLDER_PATHS: Final[list[Path]] = [
11
11
  Path(__file__).resolve().parent / "vendor",
12
- Path().resolve() / "trainerbase_vendor",
12
+ Path.cwd() / "trainerbase_vendor",
13
13
  ]
14
14
 
15
15
 
@@ -186,11 +186,11 @@ class GameBool(GameObject[bool, bool], Switchable):
186
186
 
187
187
  @override
188
188
  def enable(self):
189
- self.value = True # pylint: disable=attribute-defined-outside-init
189
+ self.value = True
190
190
 
191
191
  @override
192
192
  def disable(self):
193
- self.value = False # pylint: disable=attribute-defined-outside-init
193
+ self.value = False
194
194
 
195
195
 
196
196
  class ReadonlyGameObjectSumGetter(AbstractReadableObject[int | float]):
@@ -123,15 +123,17 @@ class GameObjectUI(AbstractUIComponent):
123
123
  | GameUnsignedLongLong()
124
124
  ):
125
125
  # There is no input for integers that are not simple `signed long int`.
126
- # TODO: Use better input component if it's already added to dpg. Remove this crutch. # noqa: FIX002
126
+ # TODO: Use better input component if it's already added to dpg. Remove this crutch.
127
+
128
+ if GameInt.value_range is None:
129
+ raise RuntimeError("GameInt must have value_range")
127
130
 
128
- assert GameInt.value_range is not None, "GameInt must have value_range"
129
131
  min_value = default_kwargs.pop("min_value", GameInt.value_range[0])
130
132
  max_value = default_kwargs.pop("max_value", GameInt.value_range[1])
131
133
 
132
134
  if self.gameobject.value_range is not None:
133
- min_value = max(min_value, GameInt.value_range[0]) # type: ignore
134
- max_value = min(max_value, GameInt.value_range[1]) # type: ignore
135
+ min_value = max(min_value, GameInt.value_range[0])
136
+ max_value = min(max_value, GameInt.value_range[1])
135
137
 
136
138
  dpg.add_input_int(min_value=min_value, max_value=max_value, **default_kwargs)
137
139
  case GameBool():
@@ -38,7 +38,7 @@ getLogger("comtypes").setLevel(ERROR)
38
38
 
39
39
  logger = getLogger("TrainerBase")
40
40
  if using_default_config:
41
- logger.debug(f"No logging config in {CONFIG_FILE}! Using default one.")
41
+ logger.debug("No logging config in %s! Using default one.", CONFIG_FILE)
42
42
 
43
43
 
44
44
  def _exception_hook(*args):
@@ -22,7 +22,7 @@ from trainerbase.tts import tts_manager
22
22
  type Callback = Callable[[], None]
23
23
 
24
24
 
25
- def run( # pylint: disable=too-complex
25
+ def run(
26
26
  run_gui: Callable[[Callback, Callback], None],
27
27
  on_gui_initialized_hook: Callback | None = None,
28
28
  on_shutdown_hook: Callback | None = None,
@@ -80,6 +80,5 @@ def run( # pylint: disable=too-complex
80
80
  print_exc()
81
81
  except KeyboardInterrupt:
82
82
  logger.info("KeyboardInterrupt (Ctrl+C)")
83
- pass
84
83
 
85
84
  on_shutdown()
@@ -15,7 +15,8 @@ from trainerbase.process import pm
15
15
 
16
16
  ARCH: Final[int] = 64 if pm.is_64_bit else 32
17
17
  POINTER_SIZE: Final[int] = 8 if pm.is_64_bit else 4
18
- DEFAULT_HARDCODED_ADDRESS_OFFSET_READER: Callable[[int], int] = cast(Callable[[int], int], pm.read_uint)
18
+ DEFAULT_HARDCODED_ADDRESS_OFFSET_READER: Final[Callable[[int], int]] = cast(Callable[[int], int], pm.read_uint)
19
+ ANY_BYTE_MARKER_HEX: Final[str] = "2E" # 2E is hex(46), 46 is ord(".")
19
20
 
20
21
 
21
22
  type ConvertibleToAddress = AbstractAddress | int | bytes | str
@@ -24,7 +25,7 @@ type ConvertibleToAddress = AbstractAddress | int | bytes | str
24
25
  read_pointer = cast(Callable[[int], int], pm.read_ulonglong if pm.is_64_bit else pm.read_uint)
25
26
 
26
27
 
27
- logger.debug(f"Process arch is {ARCH}")
28
+ logger.debug("Process arch is %d", ARCH)
28
29
 
29
30
 
30
31
  class AbstractAddress(ABC):
@@ -51,9 +52,10 @@ class AOBScan(AbstractAddress):
51
52
  ):
52
53
  self._should_cache = should_cache
53
54
  self._saved_scan_result: int | None = None
55
+ self._any_byte_marker = any_byte_marker
54
56
 
55
57
  if isinstance(aob, str):
56
- aob = bytes.fromhex(aob.replace(any_byte_marker, "2E")) # 2E is hex(46), 46 is ord(".")
58
+ aob = bytes.fromhex(aob.replace(any_byte_marker, ANY_BYTE_MARKER_HEX)) # 2E is hex(46), 46 is ord(".")
57
59
 
58
60
  if should_escape:
59
61
  aob = self._escape_aob(aob)
@@ -88,7 +90,8 @@ class AOBScan(AbstractAddress):
88
90
  scan_result: list[int] | int | None = find_aob(self.aob, check_memory_protection=self.check_memory_protection)
89
91
 
90
92
  if not scan_result:
91
- raise ValueError(f"AOB not found: {self.aob}")
93
+ aob_repr = self.aob.hex(" ").upper().replace(ANY_BYTE_MARKER_HEX, self._any_byte_marker)
94
+ raise ValueError(f"AOB not found: {aob_repr}")
92
95
 
93
96
  if isinstance(scan_result, list):
94
97
  result_address = scan_result[cast(int, self.multiple_result_index)]
@@ -109,6 +112,8 @@ class AOBScan(AbstractAddress):
109
112
  module_name=self.module_name,
110
113
  multiple_result_index=self.multiple_result_index,
111
114
  should_cache=self._should_cache,
115
+ check_memory_protection=self.check_memory_protection,
116
+ should_escape=False,
112
117
  )
113
118
 
114
119
  if new_add is not None:
@@ -172,12 +177,12 @@ def ensure_address(obj: ConvertibleToAddress) -> AbstractAddress:
172
177
  case bytes() | str():
173
178
  return AOBScan(obj)
174
179
  case _:
175
- raise TypeError(f"Cannot create AbstractAddress from {type(obj)}")
180
+ assert_never(obj)
176
181
 
177
182
 
178
183
  def allocate(size: int = POINTER_SIZE, count: int = 1) -> int:
179
184
  total = size * count
180
- logger.debug(f"Allocating {total} bytes.")
185
+ logger.debug("Allocating %d bytes.", total)
181
186
 
182
187
  return pm.allocate(total)
183
188
 
@@ -237,19 +242,19 @@ def get_module_address(module_name: str, *, delay: float = 2, retry_limit: int |
237
242
  Set retry_limit to 0 if you want to disable retries.
238
243
  """
239
244
 
240
- logger.info(f"Looking for {module_name}...")
245
+ logger.info("Looking for %s...", module_name)
241
246
 
242
247
  for try_counter in count():
243
248
  module_info = module_from_name(pm.process_handle, module_name)
244
249
 
245
250
  if module_info is not None:
246
- logger.info(f"Successfully found {module_name} at 0x{module_info.lpBaseOfDll:X}")
251
+ logger.info("Successfully found %s at 0x%X", module_name, module_info.lpBaseOfDll)
247
252
  return module_info.lpBaseOfDll
248
253
 
249
254
  if try_counter == retry_limit:
250
255
  raise RuntimeError(f"Failed to find module: {module_name}, tried {try_counter + 1} time(s).")
251
256
 
252
- logger.info(f"Failed to find {module_name}. I will retry in {delay} s. Retry limit is {retry_limit}")
257
+ logger.info("Failed to find %s. I will retry in %f s. Retry limit is %s", module_name, delay, retry_limit)
253
258
 
254
259
  sleep(delay)
255
260
 
@@ -271,7 +276,7 @@ def ensure_resolved(
271
276
  Set retry_limit to 0 if you want to disable retries.
272
277
  """
273
278
 
274
- logger.info(f"Looking for {verbose_name}...")
279
+ logger.info("Looking for %s...", verbose_name)
275
280
 
276
281
  for try_counter in count():
277
282
  try:
@@ -280,12 +285,14 @@ def ensure_resolved(
280
285
  if try_counter == retry_limit:
281
286
  raise RuntimeError(f"Failed to resolve {verbose_name}, tried {try_counter + 1} time(s).") from e
282
287
 
283
- logger.info(f"Failed to resolve {verbose_name}. I will retry in {delay} s. Retry limit is {retry_limit}")
288
+ logger.info(
289
+ "Failed to resolve %s. I will retry in %f s. Retry limit is %s", verbose_name, delay, retry_limit
290
+ )
284
291
 
285
292
  sleep(delay)
286
293
  continue
287
294
 
288
- logger.info(f"Successfully found {verbose_name} at 0x{resolved:X}")
295
+ logger.info("Successfully found %s at 0x%X", verbose_name, resolved)
289
296
  return resolved
290
297
 
291
298
  raise RuntimeError("This code must be unreachable!")
@@ -46,14 +46,14 @@ def select_one_process(processes: Iterable[Process]) -> Process:
46
46
 
47
47
 
48
48
  def get_target_pid(process_names: Iterable[str], *, exact_match: bool, ignore_case: bool) -> int:
49
- logger.info(f"Waiting for process in: {process_names}")
49
+ logger.info("Waiting for process in: %s", process_names)
50
50
 
51
51
  for process_name in cycle(process_names):
52
52
  found_processes = find_processes(process_name, exact_match=exact_match, ignore_case=ignore_case)
53
53
 
54
54
  if found_processes:
55
55
  pid = select_one_process(found_processes).pid
56
- logger.info(f"Found {process_name}, pid: {pid}")
56
+ logger.info("Found %s, pid: %d", process_name, pid)
57
57
 
58
58
  return pid
59
59
 
@@ -1,7 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from collections.abc import Callable
3
3
  from contextlib import suppress
4
- from sys import stderr
5
4
  from threading import Thread
6
5
  from time import sleep
7
6
  from typing import Any, override
@@ -9,6 +8,7 @@ from typing import Any, override
9
8
  from pymem.exception import MemoryReadError, MemoryWriteError
10
9
 
11
10
  from trainerbase.abc import Switchable
11
+ from trainerbase.logger import logger
12
12
 
13
13
 
14
14
  class AbstractBaseScript(Switchable, ABC):
@@ -73,8 +73,8 @@ class ScriptEngine:
73
73
  script()
74
74
  except MemoryReadError, MemoryWriteError:
75
75
  continue
76
- except Exception as e: # pylint: disable=broad-exception-caught
77
- print(e, file=stderr)
76
+ except Exception:
77
+ logger.exception("Error in script %r. Stopping Script Engine.", script)
78
78
  self.stop()
79
79
 
80
80
  sleep(self.delay)
File without changes