trainerbase 3.16.2__tar.gz → 4.0.0__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 (36) hide show
  1. {trainerbase-3.16.2 → trainerbase-4.0.0}/PKG-INFO +3 -2
  2. {trainerbase-3.16.2 → trainerbase-4.0.0}/pyproject.toml +4 -3
  3. trainerbase-4.0.0/trainerbase/common/abc.py +18 -0
  4. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/common/keyboard.py +1 -15
  5. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/common/rainbow.py +5 -1
  6. trainerbase-4.0.0/trainerbase/common/tapper.py +56 -0
  7. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/common/teleport.py +2 -2
  8. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gameobject.py +1 -1
  9. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gui/scripts.py +3 -3
  10. trainerbase-4.0.0/trainerbase/gui/tapper.py +71 -0
  11. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/main.py +6 -6
  12. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/memory.py +18 -6
  13. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/process.py +6 -13
  14. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/scriptengine.py +35 -13
  15. {trainerbase-3.16.2 → trainerbase-4.0.0}/LICENSE +0 -0
  16. {trainerbase-3.16.2 → trainerbase-4.0.0}/README.md +0 -0
  17. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/__init__.py +0 -0
  18. {trainerbase-3.16.2/trainerbase/common → trainerbase-4.0.0/trainerbase}/abc.py +0 -0
  19. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/codeinjection.py +0 -0
  20. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/common/__init__.py +0 -0
  21. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/common/helpers.py +0 -0
  22. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/config.py +0 -0
  23. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gui/__init__.py +0 -0
  24. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gui/helpers.py +0 -0
  25. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gui/injections.py +0 -0
  26. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gui/misc.py +0 -0
  27. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gui/objects.py +0 -0
  28. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gui/rainbow.py +0 -0
  29. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gui/speedhack.py +0 -0
  30. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gui/teleport.py +0 -0
  31. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/gui/types.py +0 -0
  32. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/speedhack.py +0 -0
  33. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase/tts.py +0 -0
  34. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase_vendor/FASM.EXE +0 -0
  35. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase_vendor/speedhack32.dll +0 -0
  36. {trainerbase-3.16.2 → trainerbase-4.0.0}/trainerbase_vendor/speedhack64.dll +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trainerbase
3
- Version: 3.16.2
3
+ Version: 4.0.0
4
4
  Summary: Framework for creating game trainers/mods
5
5
  Home-page: https://gitlab.com/python-trainers/trainer-base
6
6
  License: MIT
@@ -12,10 +12,11 @@ Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Requires-Dist: dearpygui (>=1.11.1,<2.0.0)
14
14
  Requires-Dist: keyboard (>=0.13.5,<0.14.0)
15
+ Requires-Dist: mouse (>=0.7.1,<0.8.0)
15
16
  Requires-Dist: psutil (>=6.0.0,<7.0.0)
16
17
  Requires-Dist: pyinjector (>=1.3.0,<2.0.0)
17
18
  Requires-Dist: pymem (>=1.13.1,<2.0.0)
18
- Requires-Dist: pyttsx3 (>=2.90,<3.0)
19
+ Requires-Dist: pyttsx3 (>=2.98,<3.0)
19
20
  Project-URL: Repository, https://gitlab.com/python-trainers/trainer-base
20
21
  Description-Content-Type: text/markdown
21
22
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "trainerbase"
3
- version = "3.16.2"
3
+ version = "4.0.0"
4
4
  description = "Framework for creating game trainers/mods"
5
5
  authors = ["v01d <v01d@v01d.ru>", "doomayka <zd@doomayka.ru>"]
6
6
  license = "MIT"
@@ -17,7 +17,7 @@ include = [
17
17
  ]
18
18
 
19
19
  [tool.poetry.group.dev.dependencies]
20
- ruff = "^0.5.7"
20
+ ruff = "^0.6.8"
21
21
 
22
22
  [tool.ruff]
23
23
  src = ["trainerbase"]
@@ -51,9 +51,10 @@ python = "^3.12"
51
51
  pymem = "^1.13.1"
52
52
  dearpygui = "^1.11.1"
53
53
  keyboard = "^0.13.5"
54
- pyttsx3 = "^2.90"
54
+ pyttsx3 = "^2.98"
55
55
  pyinjector = "^1.3.0"
56
56
  psutil = "^6.0.0"
57
+ mouse = "^0.7.1"
57
58
 
58
59
  [build-system]
59
60
  requires = ["poetry-core"]
@@ -0,0 +1,18 @@
1
+ from collections.abc import Callable
2
+ from typing import override
3
+
4
+ from trainerbase.abc import Switchable
5
+
6
+
7
+ class SimpleSwitchable(Switchable):
8
+ def __init__(self, on_enabled: Callable[[], None], on_disabled: Callable[[], None]):
9
+ self._on_enabled = on_enabled
10
+ self._on_disabled = on_disabled
11
+
12
+ @override
13
+ def enable(self):
14
+ self._on_enabled()
15
+
16
+ @override
17
+ def disable(self):
18
+ self._on_disabled()
@@ -4,24 +4,10 @@ from typing import override
4
4
 
5
5
  from keyboard import add_hotkey, on_press_key, on_release_key
6
6
 
7
- from trainerbase.common.abc import AbstractKeyboardHandler, Switchable
7
+ from trainerbase.abc import AbstractKeyboardHandler, Switchable
8
8
  from trainerbase.gameobject import GameBool
9
9
 
10
10
 
11
- class SimpleSwitchable(Switchable):
12
- def __init__(self, on_enabled: Callable[[], None], on_disabled: Callable[[], None]):
13
- self._on_enabled = on_enabled
14
- self._on_disabled = on_disabled
15
-
16
- @override
17
- def enable(self):
18
- self._on_enabled()
19
-
20
- @override
21
- def disable(self):
22
- self._on_disabled()
23
-
24
-
25
11
  class SimpleHotkeyHandler(AbstractKeyboardHandler):
26
12
  def __init__(self, callback: Callable[[], None], hotkey: str):
27
13
  self.callback = callback
@@ -1,15 +1,18 @@
1
1
  from collections.abc import Iterable, Iterator
2
2
  from itertools import cycle
3
+ from typing import override
3
4
 
4
5
  from dearpygui import dearpygui as dpg
5
6
 
7
+ from trainerbase.scriptengine import AbstractBaseScript
8
+
6
9
 
7
10
  type TagList = list[int | str]
8
11
  type Color = tuple[int, int, int]
9
12
  type Colors = Iterable[Color]
10
13
 
11
14
 
12
- class GradientUpdater:
15
+ class GradientUpdater(AbstractBaseScript):
13
16
  def __init__(self):
14
17
  self.tags: TagList | None = None
15
18
  self.colors: list[Color] | None = None
@@ -18,6 +21,7 @@ class GradientUpdater:
18
21
  self.initial_delta: tuple[int, int, int] | None = None
19
22
  self.color_length: int | None = None
20
23
 
24
+ @override
21
25
  def __call__(self):
22
26
  if self.tags is None or self.colors is None:
23
27
  raise RuntimeError("Call setup(...)")
@@ -0,0 +1,56 @@
1
+ from collections.abc import Callable
2
+ from enum import StrEnum, auto
3
+ from functools import partial
4
+ from typing import assert_never, override
5
+
6
+ from keyboard import send
7
+ from mouse import click
8
+
9
+ from trainerbase.abc import Switchable
10
+ from trainerbase.scriptengine import ScriptEngine
11
+
12
+
13
+ class TappingDevice(StrEnum):
14
+ MOUSE = auto()
15
+ KEYBOARD = auto()
16
+
17
+ @property
18
+ def function(self) -> Callable[[str], None]:
19
+ match self:
20
+ case self.MOUSE:
21
+ return click
22
+ case self.KEYBOARD:
23
+ return send
24
+ case _:
25
+ assert_never(self)
26
+
27
+
28
+ class Tapper(Switchable):
29
+ def __init__(self, default_delay: float, tap_button: str = "left", device: TappingDevice = TappingDevice.MOUSE):
30
+ self._delay = default_delay
31
+ self.tapper_script_engine: ScriptEngine | None = None
32
+ self.tapper_function = partial(device.function, tap_button)
33
+
34
+ @override
35
+ def enable(self):
36
+ self.disable()
37
+
38
+ self.tapper_script_engine = ScriptEngine(self._delay)
39
+ self.tapper_script_engine.simple_script(self.tapper_function, enabled=True)
40
+ self.tapper_script_engine.start()
41
+
42
+ @override
43
+ def disable(self):
44
+ if self.tapper_script_engine is not None:
45
+ self.tapper_script_engine.stop()
46
+
47
+ @property
48
+ def delay(self):
49
+ return self._delay
50
+
51
+ @delay.setter
52
+ def delay(self, new_delay: float):
53
+ if self.tapper_script_engine is not None:
54
+ self.tapper_script_engine.delay = new_delay
55
+
56
+ self._delay = new_delay
@@ -8,7 +8,7 @@ from pymem.exception import MemoryReadError
8
8
 
9
9
  from trainerbase.common.helpers import Number
10
10
  from trainerbase.gameobject import GameObject
11
- from trainerbase.scriptengine import Script, system_script_engine
11
+ from trainerbase.scriptengine import system_script_engine
12
12
 
13
13
 
14
14
  type Coords[T: (int, float)] = tuple[T, T, T]
@@ -74,7 +74,7 @@ class Teleport:
74
74
  self.movement_vector = Vector3(0, 0, 0)
75
75
  self.minimal_movement_vector_length = minimal_movement_vector_length
76
76
 
77
- system_script_engine.register_script(Script(self.update_movement_vector, enabled=True))
77
+ system_script_engine.simple_script(self.update_movement_vector, enabled=True)
78
78
 
79
79
  def set_coords(self, x: Number, y: Number, z: Number = 100) -> None:
80
80
  self.player_x.value = x
@@ -6,7 +6,7 @@ from typing import ClassVar, Self, cast, override
6
6
 
7
7
  from pymem.exception import MemoryWriteError
8
8
 
9
- from trainerbase.common.abc import Switchable
9
+ from trainerbase.abc import Switchable
10
10
  from trainerbase.memory import ConvertibleToAddress, ensure_address, pm
11
11
 
12
12
 
@@ -5,16 +5,16 @@ from dearpygui import dearpygui as dpg
5
5
  from keyboard import add_hotkey
6
6
 
7
7
  from trainerbase.gui.types import AbstractUIComponent
8
- from trainerbase.scriptengine import Script
8
+ from trainerbase.scriptengine import AbstractBaseScript
9
9
  from trainerbase.tts import say
10
10
 
11
11
 
12
- class ScriptUI(AbstractUIComponent):
12
+ class ScriptUI[T: AbstractBaseScript](AbstractUIComponent):
13
13
  DPG_TAG_PREFIX = "script__"
14
14
 
15
15
  def __init__(
16
16
  self,
17
- script: Script,
17
+ script: T,
18
18
  label: str,
19
19
  hotkey: str | None = None,
20
20
  *,
@@ -0,0 +1,71 @@
1
+ from typing import override
2
+
3
+ from dearpygui import dearpygui as dpg
4
+
5
+ from trainerbase.common.keyboard import ReleaseHotkeySwitch
6
+ from trainerbase.common.tapper import Tapper
7
+ from trainerbase.gui.helpers import add_components
8
+ from trainerbase.gui.misc import HotkeyHandlerUI
9
+ from trainerbase.gui.types import AbstractUIComponent
10
+
11
+
12
+ class TapperUI(AbstractUIComponent):
13
+ DPG_TAG_TAPPER_DELAY_INPUT = "tag_tapper_delay_input"
14
+ DPG_TAG_TAPPER_PRESET_INPUT = "tag_tapper_preset_input"
15
+
16
+ PRESETS: tuple[float, ...] = (0.05, 0.15, 0.25, 0.3, 0.5, 0.7, 1, 2, 5, 10)
17
+
18
+ def __init__(
19
+ self,
20
+ tapper: Tapper | None = None,
21
+ key: str = "PageUp",
22
+ default_delay: float = 0.15,
23
+ ):
24
+ if tapper is None:
25
+ self.tapper = Tapper(default_delay)
26
+ self.default_delay = default_delay
27
+ else:
28
+ self.tapper = tapper
29
+ self.default_delay = tapper.delay
30
+
31
+ self.key = key
32
+
33
+ @override
34
+ def add_to_ui(self) -> None:
35
+ add_components(HotkeyHandlerUI(ReleaseHotkeySwitch(self.tapper, self.key), "Tapper"))
36
+
37
+ dpg.add_input_double(
38
+ tag=self.DPG_TAG_TAPPER_DELAY_INPUT,
39
+ label="Tapper Delay",
40
+ min_value=0.0,
41
+ max_value=60.0,
42
+ default_value=self.default_delay,
43
+ min_clamped=True,
44
+ max_clamped=True,
45
+ callback=self.on_delay_change,
46
+ )
47
+
48
+ dpg.add_slider_int(
49
+ tag=self.DPG_TAG_TAPPER_PRESET_INPUT,
50
+ label="Preset",
51
+ min_value=0,
52
+ max_value=len(self.PRESETS) - 1,
53
+ clamped=True,
54
+ default_value=self.get_closest_preset_index(self.default_delay),
55
+ callback=self.on_preset_change,
56
+ )
57
+
58
+ def on_preset_change(self):
59
+ new_delay = self.PRESETS[dpg.get_value(self.DPG_TAG_TAPPER_PRESET_INPUT)]
60
+ self.tapper.delay = new_delay
61
+ dpg.set_value(self.DPG_TAG_TAPPER_DELAY_INPUT, new_delay)
62
+
63
+ def on_delay_change(self):
64
+ new_delay = dpg.get_value(self.DPG_TAG_TAPPER_DELAY_INPUT)
65
+ closest_preset_index = self.get_closest_preset_index(new_delay)
66
+ self.tapper.delay = new_delay
67
+ dpg.set_value(self.DPG_TAG_TAPPER_PRESET_INPUT, closest_preset_index)
68
+
69
+ def get_closest_preset_index(self, delay: float) -> int:
70
+ closest_preset = min(self.PRESETS, key=lambda preset: abs(preset - delay))
71
+ return self.PRESETS.index(closest_preset)
@@ -7,9 +7,9 @@ from trainerbase.codeinjection import safely_eject_all_code_injections
7
7
  from trainerbase.common.rainbow import gradient_updater
8
8
  from trainerbase.gameobject import update_frozen_objects
9
9
  from trainerbase.gui.objects import update_displayed_objects
10
- from trainerbase.process import show_message_and_shutdown_if_process_exited
10
+ from trainerbase.process import shutdown_if_process_exited
11
11
  from trainerbase.scriptengine import (
12
- Script,
12
+ enabled_by_default,
13
13
  process_healthcheck_script_engine,
14
14
  rainbow_script_engine,
15
15
  system_script_engine,
@@ -50,10 +50,10 @@ def run( # pylint: disable=too-complex
50
50
  print_exc()
51
51
  on_shutdown()
52
52
 
53
- system_script_engine.register_script(Script(update_frozen_objects, enabled=True))
54
- system_script_engine.register_script(Script(update_displayed_objects, enabled=True))
55
- process_healthcheck_script_engine.register_script(Script(show_message_and_shutdown_if_process_exited, enabled=True))
56
- rainbow_script_engine.register_script(Script(gradient_updater, enabled=True))
53
+ system_script_engine.simple_script(update_frozen_objects, enabled=True)
54
+ system_script_engine.simple_script(update_displayed_objects, enabled=True)
55
+ process_healthcheck_script_engine.simple_script(shutdown_if_process_exited, enabled=True)
56
+ rainbow_script_engine.register_script(enabled_by_default(gradient_updater))
57
57
 
58
58
  try:
59
59
  run_gui(on_gui_initialized, on_shutdown)
@@ -1,6 +1,8 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from collections.abc import Callable
3
3
  from functools import partial
4
+ from itertools import count
5
+ from time import sleep
4
6
  from types import MappingProxyType
5
7
  from typing import Final, Self, assert_never, cast, override
6
8
 
@@ -170,14 +172,24 @@ def allocate(size: int = POINTER_SIZE, count: int = 1) -> int:
170
172
  return pm.allocate(size * count)
171
173
 
172
174
 
173
- def get_module_address(module_name: str) -> int:
175
+ def get_module_address(module_name: str, *, delay: float = 2, retry_limit: int | None = 4) -> int:
174
176
  """
175
- Returns 0 if module is not found else module address
177
+ Returns module address.
178
+ Raises RuntimeError if the address was not found and retry_limit is reached.
179
+
180
+ Set retry_limit to None or negative value if you need infinite retry cycle.
181
+ Set retry_limit to 0 if you want to disable retries.
176
182
  """
177
183
 
178
- module_info = module_from_name(pm.process_handle, module_name)
184
+ for try_counter in count():
185
+ module_info = module_from_name(pm.process_handle, module_name)
186
+
187
+ if module_info is not None:
188
+ return module_info.lpBaseOfDll
189
+
190
+ if try_counter == retry_limit:
191
+ raise RuntimeError(f"Failed to find module: {module_name}, tried {try_counter + 1} time(s).")
179
192
 
180
- if module_info is None:
181
- return 0
193
+ sleep(delay)
182
194
 
183
- return module_info.lpBaseOfDll
195
+ raise RuntimeError("This code must be unreachable!")
@@ -3,11 +3,9 @@ from itertools import cycle
3
3
  from os import _exit as force_exit
4
4
  from typing import cast
5
5
 
6
- from psutil import pid_exists
6
+ from psutil import Process, pid_exists
7
7
  from pymem import Pymem
8
8
  from pymem.exception import ProcessNotFound
9
- from win32api import MessageBox
10
- from win32con import MB_ICONERROR, MB_OK
11
9
 
12
10
  from trainerbase.config import pymem_config
13
11
 
@@ -38,18 +36,13 @@ def attach_to_process() -> Pymem:
38
36
  force_exit(0)
39
37
 
40
38
 
41
- def show_message_and_shutdown_if_process_exited():
42
- if pid_exists(cast(int, _pm.process_id)):
43
- return
39
+ def shutdown_if_process_exited():
40
+ if not pid_exists(cast(int, _pm.process_id)):
41
+ force_exit(0)
44
42
 
45
- MessageBox(
46
- None,
47
- "Shut down trainer?",
48
- "Process Exited!",
49
- MB_ICONERROR | MB_OK,
50
- )
51
43
 
52
- force_exit(0)
44
+ def kill_game_process():
45
+ Process(_pm.process_id).kill()
53
46
 
54
47
 
55
48
  _pm = attach_to_process()
@@ -1,22 +1,46 @@
1
+ from abc import ABC, abstractmethod
1
2
  from collections.abc import Callable
2
3
  from contextlib import suppress
3
4
  from sys import stderr
4
5
  from threading import Thread
5
6
  from time import sleep
7
+ from typing import Any, override
6
8
 
7
9
  from pymem.exception import MemoryReadError, MemoryWriteError
8
10
 
11
+ from trainerbase.abc import Switchable
9
12
 
10
- class Script:
11
- def __init__(self, callback: Callable, *, enabled: bool = False):
12
- self.callback = callback
13
+
14
+ class AbstractBaseScript(Switchable, ABC):
15
+ enabled: bool = False
16
+
17
+ @abstractmethod
18
+ def __call__(self) -> Any:
19
+ pass
20
+
21
+ def __repr__(self):
22
+ return f"<{self.__class__.__name__}: enabled={self.enabled}>"
23
+
24
+ @override
25
+ def enable(self):
26
+ self.enabled = True
27
+
28
+ @override
29
+ def disable(self):
30
+ self.enabled = False
31
+
32
+
33
+ class SimpleScript(AbstractBaseScript):
34
+ def __init__(self, callback: Callable[[], None], *, enabled: bool = False):
35
+ self._callback = callback
13
36
  self.enabled = enabled
14
37
 
15
38
  def __repr__(self):
16
- return f"<Script {getattr(self.callback, '__name__', 'Anon')}:" f" enabled={self.enabled}" f">"
39
+ return f"<Script {getattr(self._callback, "__name__", "Anon function-based script")}: enabled={self.enabled}>"
17
40
 
41
+ @override
18
42
  def __call__(self):
19
- return self.callback()
43
+ self._callback()
20
44
 
21
45
 
22
46
  class ScriptEngine:
@@ -24,7 +48,7 @@ class ScriptEngine:
24
48
  self.delay = delay
25
49
  self.should_run = False
26
50
  self.thread = Thread(target=self.script_loop)
27
- self.scripts: list[Script] = []
51
+ self.scripts: list[AbstractBaseScript] = []
28
52
 
29
53
  def __repr__(self):
30
54
  return f"<ScriptEngine delay={self.delay} should_run={self.should_run} scripts={len(self.scripts)}>"
@@ -55,18 +79,16 @@ class ScriptEngine:
55
79
 
56
80
  sleep(self.delay)
57
81
 
58
- def register_script(self, script: Script) -> Script:
82
+ def register_script[T: AbstractBaseScript](self, script: T) -> T:
59
83
  self.scripts.append(script)
60
84
  return script
61
85
 
62
- def simple_script(self, executor: Callable) -> Script:
63
- script = Script(executor)
64
- self.register_script(script)
65
- return script
86
+ def simple_script(self, executor: Callable[[], None], *, enabled: bool = False) -> AbstractBaseScript:
87
+ return self.register_script(SimpleScript(executor, enabled=enabled))
66
88
 
67
89
 
68
- def enabled_by_default(script: Script) -> Script:
69
- script.enabled = True
90
+ def enabled_by_default[T: AbstractBaseScript](script: T) -> T:
91
+ script.enable()
70
92
  return script
71
93
 
72
94
 
File without changes
File without changes