trainerbase 4.8.1__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.1 → trainerbase-4.8.2}/PKG-INFO +1 -1
  2. {trainerbase-4.8.1 → trainerbase-4.8.2}/pyproject.toml +20 -15
  3. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/codeinjection.py +8 -8
  4. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/common/helpers.py +4 -3
  5. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/common/rainbow.py +1 -2
  6. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/config.py +1 -1
  7. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gameobject.py +2 -2
  8. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/objects.py +6 -4
  9. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/logger.py +1 -1
  10. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/main.py +1 -2
  11. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/memory.py +17 -12
  12. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/process.py +2 -2
  13. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/scriptengine.py +3 -3
  14. {trainerbase-4.8.1 → trainerbase-4.8.2}/README.md +0 -0
  15. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/__init__.py +0 -0
  16. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/abc.py +0 -0
  17. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/common/__init__.py +0 -0
  18. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/common/abc.py +0 -0
  19. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/common/keyboard.py +0 -0
  20. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/common/tapper.py +0 -0
  21. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/common/teleport.py +0 -0
  22. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/__init__.py +0 -0
  23. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/helpers.py +0 -0
  24. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/injections.py +0 -0
  25. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/misc.py +0 -0
  26. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/rainbow.py +0 -0
  27. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/scripts.py +0 -0
  28. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/speedhack.py +0 -0
  29. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/tapper.py +0 -0
  30. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/teleport.py +0 -0
  31. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/gui/types.py +0 -0
  32. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/py.typed +0 -0
  33. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/speedhack.py +0 -0
  34. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/tts.py +0 -0
  35. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/vendor/FASM.EXE +0 -0
  36. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/vendor/big_logo.png +0 -0
  37. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/vendor/speedhack32.dll +0 -0
  38. {trainerbase-4.8.1 → trainerbase-4.8.2}/trainerbase/vendor/speedhack64.dll +0 -0
  39. {trainerbase-4.8.1 → 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.1
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.1"
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]
@@ -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)]
@@ -174,12 +177,12 @@ def ensure_address(obj: ConvertibleToAddress) -> AbstractAddress:
174
177
  case bytes() | str():
175
178
  return AOBScan(obj)
176
179
  case _:
177
- raise TypeError(f"Cannot create AbstractAddress from {type(obj)}")
180
+ assert_never(obj)
178
181
 
179
182
 
180
183
  def allocate(size: int = POINTER_SIZE, count: int = 1) -> int:
181
184
  total = size * count
182
- logger.debug(f"Allocating {total} bytes.")
185
+ logger.debug("Allocating %d bytes.", total)
183
186
 
184
187
  return pm.allocate(total)
185
188
 
@@ -239,19 +242,19 @@ def get_module_address(module_name: str, *, delay: float = 2, retry_limit: int |
239
242
  Set retry_limit to 0 if you want to disable retries.
240
243
  """
241
244
 
242
- logger.info(f"Looking for {module_name}...")
245
+ logger.info("Looking for %s...", module_name)
243
246
 
244
247
  for try_counter in count():
245
248
  module_info = module_from_name(pm.process_handle, module_name)
246
249
 
247
250
  if module_info is not None:
248
- 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)
249
252
  return module_info.lpBaseOfDll
250
253
 
251
254
  if try_counter == retry_limit:
252
255
  raise RuntimeError(f"Failed to find module: {module_name}, tried {try_counter + 1} time(s).")
253
256
 
254
- 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)
255
258
 
256
259
  sleep(delay)
257
260
 
@@ -273,7 +276,7 @@ def ensure_resolved(
273
276
  Set retry_limit to 0 if you want to disable retries.
274
277
  """
275
278
 
276
- logger.info(f"Looking for {verbose_name}...")
279
+ logger.info("Looking for %s...", verbose_name)
277
280
 
278
281
  for try_counter in count():
279
282
  try:
@@ -282,12 +285,14 @@ def ensure_resolved(
282
285
  if try_counter == retry_limit:
283
286
  raise RuntimeError(f"Failed to resolve {verbose_name}, tried {try_counter + 1} time(s).") from e
284
287
 
285
- 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
+ )
286
291
 
287
292
  sleep(delay)
288
293
  continue
289
294
 
290
- logger.info(f"Successfully found {verbose_name} at 0x{resolved:X}")
295
+ logger.info("Successfully found %s at 0x%X", verbose_name, resolved)
291
296
  return resolved
292
297
 
293
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