trainerbase 4.0.9__tar.gz → 4.1.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 (40) hide show
  1. {trainerbase-4.0.9 → trainerbase-4.1.0}/PKG-INFO +1 -1
  2. {trainerbase-4.0.9 → trainerbase-4.1.0}/pyproject.toml +2 -2
  3. trainerbase-4.1.0/trainerbase/common/tapper.py +78 -0
  4. trainerbase-4.1.0/trainerbase/gui/tapper.py +120 -0
  5. trainerbase-4.1.0/trainerbase/process.py +87 -0
  6. trainerbase-4.0.9/trainerbase/common/tapper.py +0 -58
  7. trainerbase-4.0.9/trainerbase/gui/tapper.py +0 -77
  8. trainerbase-4.0.9/trainerbase/process.py +0 -51
  9. {trainerbase-4.0.9 → trainerbase-4.1.0}/LICENSE +0 -0
  10. {trainerbase-4.0.9 → trainerbase-4.1.0}/README.md +0 -0
  11. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/__init__.py +0 -0
  12. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/abc.py +0 -0
  13. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/codeinjection.py +0 -0
  14. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/common/__init__.py +0 -0
  15. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/common/abc.py +0 -0
  16. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/common/helpers.py +0 -0
  17. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/common/keyboard.py +0 -0
  18. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/common/rainbow.py +0 -0
  19. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/common/teleport.py +0 -0
  20. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/config.py +0 -0
  21. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gameobject.py +0 -0
  22. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gui/__init__.py +0 -0
  23. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gui/helpers.py +0 -0
  24. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gui/injections.py +0 -0
  25. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gui/misc.py +0 -0
  26. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gui/objects.py +0 -0
  27. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gui/rainbow.py +0 -0
  28. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gui/scripts.py +0 -0
  29. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gui/speedhack.py +0 -0
  30. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gui/teleport.py +0 -0
  31. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/gui/types.py +0 -0
  32. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/logger.py +0 -0
  33. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/main.py +0 -0
  34. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/memory.py +0 -0
  35. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/scriptengine.py +0 -0
  36. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/speedhack.py +0 -0
  37. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase/tts.py +0 -0
  38. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase_vendor/FASM.EXE +0 -0
  39. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase_vendor/speedhack32.dll +0 -0
  40. {trainerbase-4.0.9 → trainerbase-4.1.0}/trainerbase_vendor/speedhack64.dll +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trainerbase
3
- Version: 4.0.9
3
+ Version: 4.1.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
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "trainerbase"
3
- version = "4.0.9"
3
+ version = "4.1.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.7.0"
20
+ ruff = "^0.7.1"
21
21
 
22
22
  [tool.ruff]
23
23
  src = ["trainerbase"]
@@ -0,0 +1,78 @@
1
+ from enum import StrEnum, auto
2
+ from time import sleep
3
+ from typing import override
4
+
5
+ from keyboard import press as press_keyboard_key
6
+ from keyboard import release as release_keyboard_key
7
+ from mouse import press as press_mouse_button
8
+ from mouse import release as release_mouse_button
9
+
10
+ from trainerbase.abc import Switchable
11
+ from trainerbase.scriptengine import ScriptEngine
12
+
13
+
14
+ class TappingDevice(StrEnum):
15
+ MOUSE = auto()
16
+ KEYBOARD = auto()
17
+
18
+
19
+ class Tapper(Switchable):
20
+ def __init__(
21
+ self,
22
+ default_delay: float,
23
+ tap_button: str = "left",
24
+ device: TappingDevice = TappingDevice.MOUSE,
25
+ duration: float = 0.15,
26
+ ):
27
+ self._delay = default_delay
28
+ self._duration = duration
29
+ self.button = tap_button
30
+ self.device = device
31
+ self.tapper_script_engine: ScriptEngine | None = None
32
+ self.tapper_function = (
33
+ self._send_mouse_click_with_duration
34
+ if device is TappingDevice.MOUSE
35
+ else self._send_keyboard_press_and_release_with_duration
36
+ )
37
+
38
+ @override
39
+ def enable(self):
40
+ self.disable()
41
+
42
+ self.tapper_script_engine = ScriptEngine(self._delay)
43
+ self.tapper_script_engine.simple_script(self.tapper_function, enabled=True)
44
+ self.tapper_script_engine.start()
45
+
46
+ @override
47
+ def disable(self):
48
+ if self.tapper_script_engine is not None:
49
+ self.tapper_script_engine.stop()
50
+
51
+ def _send_mouse_click_with_duration(self):
52
+ press_mouse_button(self.button)
53
+ sleep(self._duration)
54
+ release_mouse_button(self.button)
55
+
56
+ def _send_keyboard_press_and_release_with_duration(self):
57
+ press_keyboard_key(self.button)
58
+ sleep(self._duration)
59
+ release_keyboard_key(self.button)
60
+
61
+ @property
62
+ def delay(self):
63
+ return self._delay
64
+
65
+ @delay.setter
66
+ def delay(self, new_delay: float):
67
+ if self.tapper_script_engine is not None:
68
+ self.tapper_script_engine.delay = new_delay
69
+
70
+ self._delay = new_delay
71
+
72
+ @property
73
+ def duration(self):
74
+ return self._duration
75
+
76
+ @duration.setter
77
+ def duration(self, new_duration: float):
78
+ self._duration = new_duration
@@ -0,0 +1,120 @@
1
+ from collections.abc import Sequence
2
+ from typing import override
3
+ from uuid import uuid4
4
+
5
+ from dearpygui import dearpygui as dpg
6
+
7
+ from trainerbase.common.keyboard import ReleaseHotkeySwitch
8
+ from trainerbase.common.tapper import Tapper
9
+ from trainerbase.gui.helpers import add_components
10
+ from trainerbase.gui.misc import HotkeyHandlerUI, TextUI
11
+ from trainerbase.gui.types import AbstractUIComponent
12
+
13
+
14
+ class TapperUI(AbstractUIComponent):
15
+ DPG_TAG_TAPPER_DELAY_INPUT_PREFIX = "tag_tapper_delay_input"
16
+ DPG_TAG_TAPPER_DELAY_PRESET_INPUT_PREFIX = "tag_tapper_delay_preset_input"
17
+ DPG_TAG_TAPPER_DURATION_INPUT_PREFIX = "tag_tapper_duration_input"
18
+ DPG_TAG_TAPPER_DURATION_PRESET_INPUT_PREFIX = "tag_tapper_duration_preset_input"
19
+
20
+ DELAY_PRESETS: tuple[float, ...] = (0.05, 0.15, 0.25, 0.3, 0.5, 0.7, 1, 2, 5, 10)
21
+ DURATION_PRESETS: tuple[float, ...] = (0.0, 0.15, 0.3, 0.5, 0.75, 1, 1.5, 5.0)
22
+
23
+ def __init__(
24
+ self,
25
+ tapper: Tapper | None = None,
26
+ key: str = "PageUp",
27
+ default_delay: float = 0.15,
28
+ default_duration: float = 0.15,
29
+ ):
30
+ if tapper is None:
31
+ self.tapper = Tapper(default_delay, duration=default_duration)
32
+ self.default_delay = default_delay
33
+ self.default_duration = default_duration
34
+ else:
35
+ self.tapper = tapper
36
+ self.default_delay = tapper.delay
37
+ self.default_duration = tapper.duration
38
+
39
+ self.key = key
40
+ self.dpg_tag_tapper_delay_input = f"{self.DPG_TAG_TAPPER_DELAY_INPUT_PREFIX}_{uuid4()}"
41
+ self.dpg_tag_tapper_delay_preset_input = f"{self.DPG_TAG_TAPPER_DELAY_PRESET_INPUT_PREFIX}_{uuid4()}"
42
+ self.dpg_tag_tapper_duration_input = f"{self.DPG_TAG_TAPPER_DELAY_INPUT_PREFIX}_{uuid4()}"
43
+ self.dpg_tag_tapper_duration_preset_input = f"{self.DPG_TAG_TAPPER_DELAY_PRESET_INPUT_PREFIX}_{uuid4()}"
44
+
45
+ @override
46
+ def add_to_ui(self) -> None:
47
+ add_components(
48
+ TextUI(f"Tapper for {self.tapper.device} {self.tapper.button}"),
49
+ HotkeyHandlerUI(ReleaseHotkeySwitch(self.tapper, self.key), "Tapper"),
50
+ )
51
+
52
+ dpg.add_input_double(
53
+ tag=self.dpg_tag_tapper_delay_input,
54
+ label="Delay",
55
+ min_value=0.0,
56
+ max_value=60.0,
57
+ default_value=self.default_delay,
58
+ min_clamped=True,
59
+ max_clamped=True,
60
+ callback=self.on_delay_change,
61
+ )
62
+
63
+ dpg.add_slider_int(
64
+ tag=self.dpg_tag_tapper_delay_preset_input,
65
+ label="Delay Preset",
66
+ min_value=0,
67
+ max_value=len(self.DELAY_PRESETS) - 1,
68
+ clamped=True,
69
+ default_value=self.get_closest_preset_index(self.DELAY_PRESETS, self.default_delay),
70
+ callback=self.on_delay_preset_change,
71
+ )
72
+
73
+ dpg.add_separator()
74
+
75
+ dpg.add_input_double(
76
+ tag=self.dpg_tag_tapper_duration_input,
77
+ label="Duration",
78
+ min_value=0.0,
79
+ max_value=5.0,
80
+ default_value=self.default_duration,
81
+ min_clamped=True,
82
+ max_clamped=True,
83
+ callback=self.on_duration_change,
84
+ )
85
+
86
+ dpg.add_slider_int(
87
+ tag=self.dpg_tag_tapper_duration_preset_input,
88
+ label="Duration Preset",
89
+ min_value=0,
90
+ max_value=len(self.DURATION_PRESETS) - 1,
91
+ clamped=True,
92
+ default_value=self.get_closest_preset_index(self.DURATION_PRESETS, self.default_delay),
93
+ callback=self.on_duration_preset_change,
94
+ )
95
+
96
+ def on_delay_preset_change(self):
97
+ new_delay = self.DELAY_PRESETS[dpg.get_value(self.dpg_tag_tapper_delay_preset_input)]
98
+ self.tapper.delay = new_delay
99
+ dpg.set_value(self.dpg_tag_tapper_delay_input, new_delay)
100
+
101
+ def on_delay_change(self):
102
+ new_delay = dpg.get_value(self.dpg_tag_tapper_delay_input)
103
+ closest_preset_index = self.get_closest_preset_index(self.DELAY_PRESETS, new_delay)
104
+ self.tapper.delay = new_delay
105
+ dpg.set_value(self.dpg_tag_tapper_delay_preset_input, closest_preset_index)
106
+
107
+ def on_duration_preset_change(self):
108
+ new_duration = self.DURATION_PRESETS[dpg.get_value(self.dpg_tag_tapper_duration_preset_input)]
109
+ self.tapper.duration = new_duration
110
+ dpg.set_value(self.dpg_tag_tapper_duration_input, new_duration)
111
+
112
+ def on_duration_change(self):
113
+ new_duration = dpg.get_value(self.dpg_tag_tapper_duration_input)
114
+ closest_preset_index = self.get_closest_preset_index(self.DELAY_PRESETS, new_duration)
115
+ self.tapper.duration = new_duration
116
+ dpg.set_value(self.dpg_tag_tapper_duration_preset_input, closest_preset_index)
117
+
118
+ def get_closest_preset_index(self, presests: Sequence[float], value: float) -> int:
119
+ closest_preset = min(presests, key=lambda preset: abs(preset - value))
120
+ return presests.index(closest_preset)
@@ -0,0 +1,87 @@
1
+ from collections.abc import Iterable
2
+ from contextlib import suppress
3
+ from itertools import cycle
4
+ from os import _exit as force_exit
5
+ from typing import cast
6
+
7
+ from psutil import Process, pid_exists, process_iter
8
+ from pymem import Pymem
9
+
10
+ from trainerbase.config import pymem_config
11
+ from trainerbase.logger import logger
12
+
13
+
14
+ def find_processes(process_name: str, *, exact_match: bool, ignore_case: bool) -> list[Process]:
15
+ if ignore_case:
16
+ process_name = process_name.lower()
17
+
18
+ result_process_list = []
19
+
20
+ for process in process_iter():
21
+ name = process.name()
22
+
23
+ if ignore_case:
24
+ name = name.lower()
25
+
26
+ condition = process_name == name if exact_match else process_name in name
27
+
28
+ if condition:
29
+ result_process_list.append(process)
30
+
31
+ return result_process_list
32
+
33
+
34
+ def select_one_process(processes: Iterable[Process]) -> Process:
35
+ processes = sorted(processes, key=lambda process: process.memory_info().rss, reverse=True)
36
+
37
+ if not processes:
38
+ raise ValueError("Cannot select from empty sequence!")
39
+
40
+ if len(processes) > 1:
41
+ logger.debug("Multiple processes found! Selecting one with highest Resident Set Size.")
42
+
43
+ return next(iter(processes))
44
+
45
+
46
+ def get_target_pid(process_names: Iterable[str], *, exact_match: bool, ignore_case: bool) -> int:
47
+ logger.info(f"Waiting for process in: {process_names}")
48
+
49
+ for process_name in cycle(process_names):
50
+ found_processes = find_processes(process_name, exact_match=exact_match, ignore_case=ignore_case)
51
+
52
+ if found_processes:
53
+ pid = select_one_process(found_processes).pid
54
+ logger.info(f"Found {process_name}, pid: {pid}")
55
+
56
+ return pid
57
+
58
+ raise RuntimeError("This place must be unreachable! `process_names` is probably empty.")
59
+
60
+
61
+ def attach_to_process() -> Pymem:
62
+ process_names = pymem_config["process_names"]
63
+ exact_match = pymem_config["exact_match"]
64
+ ignore_case = pymem_config["ignore_case"]
65
+
66
+ if not process_names:
67
+ raise ValueError("Empty process name list")
68
+
69
+ with suppress(KeyboardInterrupt):
70
+ pid = get_target_pid(process_names, exact_match=exact_match, ignore_case=ignore_case)
71
+ return Pymem(pid)
72
+
73
+ force_exit(0)
74
+
75
+
76
+ def shutdown_if_process_exited():
77
+ if not pid_exists(cast(int, _pm.process_id)):
78
+ logger.info("Game process exited. Shutting down.")
79
+ force_exit(0)
80
+
81
+
82
+ def kill_game_process():
83
+ logger.info("Killing game process.")
84
+ Process(_pm.process_id).kill()
85
+
86
+
87
+ _pm = attach_to_process()
@@ -1,58 +0,0 @@
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.button = tap_button
32
- self.device = device
33
- self.tapper_script_engine: ScriptEngine | None = None
34
- self.tapper_function = partial(device.function, tap_button)
35
-
36
- @override
37
- def enable(self):
38
- self.disable()
39
-
40
- self.tapper_script_engine = ScriptEngine(self._delay)
41
- self.tapper_script_engine.simple_script(self.tapper_function, enabled=True)
42
- self.tapper_script_engine.start()
43
-
44
- @override
45
- def disable(self):
46
- if self.tapper_script_engine is not None:
47
- self.tapper_script_engine.stop()
48
-
49
- @property
50
- def delay(self):
51
- return self._delay
52
-
53
- @delay.setter
54
- def delay(self, new_delay: float):
55
- if self.tapper_script_engine is not None:
56
- self.tapper_script_engine.delay = new_delay
57
-
58
- self._delay = new_delay
@@ -1,77 +0,0 @@
1
- from typing import override
2
- from uuid import uuid4
3
-
4
- from dearpygui import dearpygui as dpg
5
-
6
- from trainerbase.common.keyboard import ReleaseHotkeySwitch
7
- from trainerbase.common.tapper import Tapper
8
- from trainerbase.gui.helpers import add_components
9
- from trainerbase.gui.misc import HotkeyHandlerUI, TextUI
10
- from trainerbase.gui.types import AbstractUIComponent
11
-
12
-
13
- class TapperUI(AbstractUIComponent):
14
- DPG_TAG_TAPPER_DELAY_INPUT_PREFIX = "tag_tapper_delay_input"
15
- DPG_TAG_TAPPER_PRESET_INPUT_PREFIX = "tag_tapper_preset_input"
16
-
17
- PRESETS: tuple[float, ...] = (0.05, 0.15, 0.25, 0.3, 0.5, 0.7, 1, 2, 5, 10)
18
-
19
- def __init__(
20
- self,
21
- tapper: Tapper | None = None,
22
- key: str = "PageUp",
23
- default_delay: float = 0.15,
24
- ):
25
- if tapper is None:
26
- self.tapper = Tapper(default_delay)
27
- self.default_delay = default_delay
28
- else:
29
- self.tapper = tapper
30
- self.default_delay = tapper.delay
31
-
32
- self.key = key
33
- self.dpg_tag_tapper_delay_input = f"{self.DPG_TAG_TAPPER_DELAY_INPUT_PREFIX}_{uuid4()}"
34
- self.dpg_tag_tapper_preset_input = f"{self.DPG_TAG_TAPPER_PRESET_INPUT_PREFIX}_{uuid4()}"
35
-
36
- @override
37
- def add_to_ui(self) -> None:
38
- add_components(
39
- TextUI(f"Tapper for {self.tapper.device} {self.tapper.button}"),
40
- HotkeyHandlerUI(ReleaseHotkeySwitch(self.tapper, self.key), "Tapper"),
41
- )
42
-
43
- dpg.add_input_double(
44
- tag=self.dpg_tag_tapper_delay_input,
45
- label="Tapper Delay",
46
- min_value=0.0,
47
- max_value=60.0,
48
- default_value=self.default_delay,
49
- min_clamped=True,
50
- max_clamped=True,
51
- callback=self.on_delay_change,
52
- )
53
-
54
- dpg.add_slider_int(
55
- tag=self.dpg_tag_tapper_preset_input,
56
- label="Preset",
57
- min_value=0,
58
- max_value=len(self.PRESETS) - 1,
59
- clamped=True,
60
- default_value=self.get_closest_preset_index(self.default_delay),
61
- callback=self.on_preset_change,
62
- )
63
-
64
- def on_preset_change(self):
65
- new_delay = self.PRESETS[dpg.get_value(self.dpg_tag_tapper_preset_input)]
66
- self.tapper.delay = new_delay
67
- dpg.set_value(self.dpg_tag_tapper_delay_input, new_delay)
68
-
69
- def on_delay_change(self):
70
- new_delay = dpg.get_value(self.dpg_tag_tapper_delay_input)
71
- closest_preset_index = self.get_closest_preset_index(new_delay)
72
- self.tapper.delay = new_delay
73
- dpg.set_value(self.dpg_tag_tapper_preset_input, closest_preset_index)
74
-
75
- def get_closest_preset_index(self, delay: float) -> int:
76
- closest_preset = min(self.PRESETS, key=lambda preset: abs(preset - delay))
77
- return self.PRESETS.index(closest_preset)
@@ -1,51 +0,0 @@
1
- from contextlib import suppress
2
- from itertools import cycle
3
- from os import _exit as force_exit
4
- from typing import cast
5
-
6
- from psutil import Process, pid_exists
7
- from pymem import Pymem
8
- from pymem.exception import ProcessNotFound
9
-
10
- from trainerbase.config import pymem_config
11
- from trainerbase.logger import logger
12
-
13
-
14
- def attach_to_process() -> Pymem:
15
- process_names = pymem_config["process_names"]
16
-
17
- if not process_names:
18
- raise ValueError("Empty process name list")
19
-
20
- logger.info(f"Waiting for process in: {process_names}")
21
-
22
- with suppress(KeyboardInterrupt):
23
- for process_name in cycle(process_names):
24
- try:
25
- pm = Pymem(
26
- process_name,
27
- exact_match=pymem_config["exact_match"],
28
- ignore_case=pymem_config["ignore_case"],
29
- )
30
- except ProcessNotFound:
31
- continue
32
-
33
- logger.info(f"Found {process_name}, pid: {pm.process_id}")
34
-
35
- return pm
36
-
37
- force_exit(0)
38
-
39
-
40
- def shutdown_if_process_exited():
41
- if not pid_exists(cast(int, _pm.process_id)):
42
- logger.info("Game process exited. Shutting down.")
43
- force_exit(0)
44
-
45
-
46
- def kill_game_process():
47
- logger.info("Killing game process.")
48
- Process(_pm.process_id).kill()
49
-
50
-
51
- _pm = attach_to_process()
File without changes
File without changes