trainerbase 4.0.8__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.
- {trainerbase-4.0.8 → trainerbase-4.1.0}/PKG-INFO +3 -2
- {trainerbase-4.0.8 → trainerbase-4.1.0}/pyproject.toml +3 -3
- trainerbase-4.1.0/trainerbase/common/tapper.py +78 -0
- trainerbase-4.1.0/trainerbase/gui/tapper.py +120 -0
- trainerbase-4.1.0/trainerbase/process.py +87 -0
- trainerbase-4.0.8/trainerbase/common/tapper.py +0 -58
- trainerbase-4.0.8/trainerbase/gui/tapper.py +0 -77
- trainerbase-4.0.8/trainerbase/process.py +0 -51
- {trainerbase-4.0.8 → trainerbase-4.1.0}/LICENSE +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/README.md +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/__init__.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/abc.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/codeinjection.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/common/__init__.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/common/abc.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/common/helpers.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/common/keyboard.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/common/rainbow.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/common/teleport.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/config.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gameobject.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gui/__init__.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gui/helpers.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gui/injections.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gui/misc.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gui/objects.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gui/rainbow.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gui/scripts.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gui/speedhack.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gui/teleport.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/gui/types.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/logger.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/main.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/memory.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/scriptengine.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/speedhack.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase/tts.py +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase_vendor/FASM.EXE +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase_vendor/speedhack32.dll +0 -0
- {trainerbase-4.0.8 → trainerbase-4.1.0}/trainerbase_vendor/speedhack64.dll +0 -0
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: trainerbase
|
|
3
|
-
Version: 4.0
|
|
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
|
|
7
7
|
Author: v01d
|
|
8
8
|
Author-email: v01d@v01d.ru
|
|
9
|
-
Requires-Python: >=3.12,<
|
|
9
|
+
Requires-Python: >=3.12,<4.0
|
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
14
|
Requires-Dist: dearpygui (>=2.0.0,<3.0.0)
|
|
14
15
|
Requires-Dist: keyboard (>=0.13.5,<0.14.0)
|
|
15
16
|
Requires-Dist: mouse (>=0.7.1,<0.8.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "trainerbase"
|
|
3
|
-
version = "4.0
|
|
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.
|
|
20
|
+
ruff = "^0.7.1"
|
|
21
21
|
|
|
22
22
|
[tool.ruff]
|
|
23
23
|
src = ["trainerbase"]
|
|
@@ -47,7 +47,7 @@ lines-after-imports = 2
|
|
|
47
47
|
line-ending = "lf"
|
|
48
48
|
|
|
49
49
|
[tool.poetry.dependencies]
|
|
50
|
-
python = "
|
|
50
|
+
python = "^3.12"
|
|
51
51
|
pymem = "^1.13.3"
|
|
52
52
|
keyboard = "^0.13.5"
|
|
53
53
|
pyttsx3 = "^2.98"
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|