minecraft-tweaks 0.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.
- minecraft_tweaks-0.1.0/LICENSE +21 -0
- minecraft_tweaks-0.1.0/PKG-INFO +185 -0
- minecraft_tweaks-0.1.0/README.md +172 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/__init__.py +121 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/__main__.py +6 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/_win.py +190 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/agent.py +93 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/anim.py +169 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/app.py +153 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/draw.py +252 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/esp.py +210 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/extensions.py +299 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/gui.py +154 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/hooks.py +114 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/keys.py +152 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/overlay.py +295 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/paint.py +617 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/png.py +116 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/process.py +136 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/py.typed +0 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks/watchdog.py +96 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks.egg-info/PKG-INFO +185 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks.egg-info/SOURCES.txt +27 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks.egg-info/dependency_links.txt +1 -0
- minecraft_tweaks-0.1.0/minecraft_tweaks.egg-info/top_level.txt +1 -0
- minecraft_tweaks-0.1.0/pyproject.toml +22 -0
- minecraft_tweaks-0.1.0/setup.cfg +4 -0
- minecraft_tweaks-0.1.0/setup.py +3 -0
- minecraft_tweaks-0.1.0/tests/test_smoke.py +122 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 newlevel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: minecraft-tweaks
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: simple minecraft injector
|
|
5
|
+
Author: newlevel
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://pypi.org/project/minecraft-tweaks/
|
|
8
|
+
Keywords: minecraft,overlay,esp,gui,win32,cheat
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# minecraft-tweaks
|
|
15
|
+
|
|
16
|
+
simple minecraft injector
|
|
17
|
+
|
|
18
|
+
Оверлей, ESP и ClickGUI для Minecraft на чистом Python. Зависимостей нет, игра не
|
|
19
|
+
перезапускается: библиотека создаёт слоёное окно с попиксельной прозрачностью
|
|
20
|
+
(`UpdateLayeredWindow` и 32-битный DIB) и держит его над клиентской областью игры.
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
import minecraft_tweaks as mt
|
|
24
|
+
|
|
25
|
+
@mt.on_start # Minecraft запустился
|
|
26
|
+
def started(mc):
|
|
27
|
+
print("нашли:", mc.title, "pid", mc.pid)
|
|
28
|
+
|
|
29
|
+
@mt.on_world # вошли в мир
|
|
30
|
+
def world(mc):
|
|
31
|
+
print("мир:", mc.world)
|
|
32
|
+
|
|
33
|
+
mt.run_forever(fps=144) # держит оверлей и вызывает хуки
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Установка
|
|
37
|
+
|
|
38
|
+
```powershell
|
|
39
|
+
cd minecraft-tweaks
|
|
40
|
+
python -m pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Нужен Python 3.10 или новее и Windows. Win32 вызывается через `ctypes`, дополнительных
|
|
44
|
+
пакетов не требуется.
|
|
45
|
+
|
|
46
|
+
## Что внутри
|
|
47
|
+
|
|
48
|
+
| Модуль | Что делает |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `mt.find()` / `mt.is_running()` / `mt.wait()` | ищет окно и процесс Minecraft по заголовку, без psutil |
|
|
51
|
+
| `@mt.on_start` / `@mt.on_world` / `@mt.on_exit` | хуки жизненного цикла игры |
|
|
52
|
+
| `mt.Overlay` | прозрачное окно поверх игры: per-pixel alpha, от 144 fps, фокус не забирает, клики пропускает |
|
|
53
|
+
| `mt.Canvas` | рисование: `fill_rect`, `frame`, `corners`, `line`, `gradient_line`, `rounded`, `hgradient`, `bar`, `text`, `pixel_art` |
|
|
54
|
+
| `mt.ESPRenderer` | рамки, уголки, свечение, иконки, дистанция, полоса HP, трассеры, интерполяция позиций |
|
|
55
|
+
| `mt.ClickGui` | колонки категорий, тумблеры, поиск, подсветка под курсором, тёмное стекло |
|
|
56
|
+
| `mt.Keys`, `mt.Combo`, `mt.Hotkey` | клавиши и мышь через `GetAsyncKeyState`, хуки не ставятся |
|
|
57
|
+
| `mt.agent` | по желанию: внедрение Java-агента из `../mc-inject` и чтение состояния |
|
|
58
|
+
|
|
59
|
+
## Оверлей
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
ov = mt.Overlay(fps=144)
|
|
63
|
+
|
|
64
|
+
def render(canvas, overlay, mc):
|
|
65
|
+
canvas.text(f"{overlay.stats.fps:.0f} FPS", 10, 10, 0xFFF2F2F5)
|
|
66
|
+
esp.draw(canvas, camera, entities, partial_tick=partial)
|
|
67
|
+
|
|
68
|
+
mt.run_forever(render, overlay=ov)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Почему картинка не дёргается:
|
|
72
|
+
|
|
73
|
+
1. Попиксельная альфа. DWM композитит окно каждый кадр монитора, цветовой ключ не нужен,
|
|
74
|
+
поэтому края не мигают.
|
|
75
|
+
2. Интерполяция. `Entity.position(partial_tick)` считает `pos - delta * (1 - partialTick)`,
|
|
76
|
+
как это делает ваниль. Сущности обновляются 20 раз в секунду, а кадры рисуются 144.
|
|
77
|
+
3. Один оверлей. Окна старых запусков гасятся при старте.
|
|
78
|
+
|
|
79
|
+
## ESP
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
esp = mt.ESPRenderer(mt.EspSettings(tracers=True, names=True))
|
|
83
|
+
cam = mt.Camera(x=0, y=1.62, z=0, yaw=90, pitch=0, fov=70)
|
|
84
|
+
ents = [
|
|
85
|
+
mt.Entity(5, 0, 12, kind="player", health=0.8, name="bot"),
|
|
86
|
+
mt.Entity(-3, 0, 9, kind="mob"),
|
|
87
|
+
mt.Entity(2, 0, 6, kind="storage"),
|
|
88
|
+
]
|
|
89
|
+
esp.draw(canvas, cam, ents, partial_tick=0.5)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`kind` задаёт цвет и иконку: `player` (фиолетовый, человечек), `mob` (красный, ромб),
|
|
93
|
+
`storage` (жёлтый, сундук). Дистанция и HP рисуются сами.
|
|
94
|
+
|
|
95
|
+
## ClickGUI
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
gui = mt.ClickGui({
|
|
99
|
+
"Combat": [mt.Module("KillAura"), mt.Module("Velocity")],
|
|
100
|
+
"Render": [mt.Module("PlayerESP"), mt.Module("Tracers")],
|
|
101
|
+
})
|
|
102
|
+
gui.draw(canvas, mt.Keys.cursor(), focused=True)
|
|
103
|
+
hit = gui.click(mt.Keys.cursor(), lmb_pressed=True) # переключит тумблер под курсором
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Клики окно не перехватывает: оно прозрачно для мыши, а состояние кнопок читается глобально.
|
|
107
|
+
|
|
108
|
+
## Свои методы (расширения)
|
|
109
|
+
|
|
110
|
+
Расширения пишутся декораторами, наследование и ручная регистрация не нужны:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import minecraft_tweaks as mt
|
|
114
|
+
|
|
115
|
+
# 1. ESP для любого блока: имя, цвет, подпись, дальность
|
|
116
|
+
@mt.esp_block("diamond_ore", color=0xFF00E5FF, label=True, max_distance=96)
|
|
117
|
+
def diamonds():
|
|
118
|
+
return [(12, 5, -3), (18, 4, 2)] # координаты углов блоков
|
|
119
|
+
|
|
120
|
+
# 2. Свои сущности (вызывается каждый кадр)
|
|
121
|
+
@mt.esp_entity
|
|
122
|
+
def my_ents():
|
|
123
|
+
return [mt.Entity(0, 0, 8, kind="mob")]
|
|
124
|
+
|
|
125
|
+
# 3. Своя отрисовка поверх кадра
|
|
126
|
+
@mt.esp_draw
|
|
127
|
+
def watermark(canvas, camera, app):
|
|
128
|
+
canvas.text("my client", 10, 10, 0xFFFFFFFF)
|
|
129
|
+
|
|
130
|
+
# 4. Своя логика каждый кадр
|
|
131
|
+
@mt.on_frame
|
|
132
|
+
def logic(dt, app):
|
|
133
|
+
app.camera.yaw += 10 * dt
|
|
134
|
+
|
|
135
|
+
# 5. Свой модуль: попадает в ClickGUI, получает колбэки
|
|
136
|
+
@mt.module("MyMod", category="Render", enabled=True)
|
|
137
|
+
class MyMod:
|
|
138
|
+
def on_tick(self, dt): ...
|
|
139
|
+
def on_draw(self, canvas, app): ...
|
|
140
|
+
def on_toggle(self, enabled): ...
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Такой же файл можно положить в папку и не трогать код библиотеки:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
mt.load_scripts("scripts") # импортирует все scripts/*.py
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Внутри скрипта обычный Python с теми же декораторами. Исключение в чужом колбэке не
|
|
150
|
+
ломает кадр: оно попадает в `mt.registry.errors`.
|
|
151
|
+
|
|
152
|
+
## Клавиши и взвод макроса
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
keys = mt.Keys()
|
|
156
|
+
combo = mt.Combo(["S", "R", "R"], modifier="ALT")
|
|
157
|
+
|
|
158
|
+
if combo.update(): # держим Alt, жмём S, R, R
|
|
159
|
+
armed = True
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Опционально: Java-агент
|
|
163
|
+
|
|
164
|
+
Если нужны данные прямо из игры (позиции сущностей, состояние мира), рядом лежит
|
|
165
|
+
`mc-inject` с Java-агентом:
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
mt.agent.attach() # процесс найдёт сам
|
|
169
|
+
state = mt.agent.wait_state() # прочитает ss-state.json, если агент его пишет
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Примеры
|
|
173
|
+
|
|
174
|
+
```powershell
|
|
175
|
+
python examples/esp_demo.py # плавный ESP на демо-мишенях
|
|
176
|
+
python examples/gui_demo.py # ClickGUI + HUD
|
|
177
|
+
python tests/test_smoke.py # смоук-тест без игры
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Ограничения
|
|
181
|
+
|
|
182
|
+
- Только Windows: используется Win32 API.
|
|
183
|
+
- Оверлей виден поверх окна и borderless-фуллскрина. Эксклюзивный fullscreen, когда игра
|
|
184
|
+
забирает дисплей целиком, перекрыть нельзя. Переключите игру в оконный режим.
|
|
185
|
+
- Данные о сущностях библиотека не добывает. Их нужно передать в `Entity` из своего источника.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# minecraft-tweaks
|
|
2
|
+
|
|
3
|
+
simple minecraft injector
|
|
4
|
+
|
|
5
|
+
Оверлей, ESP и ClickGUI для Minecraft на чистом Python. Зависимостей нет, игра не
|
|
6
|
+
перезапускается: библиотека создаёт слоёное окно с попиксельной прозрачностью
|
|
7
|
+
(`UpdateLayeredWindow` и 32-битный DIB) и держит его над клиентской областью игры.
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
import minecraft_tweaks as mt
|
|
11
|
+
|
|
12
|
+
@mt.on_start # Minecraft запустился
|
|
13
|
+
def started(mc):
|
|
14
|
+
print("нашли:", mc.title, "pid", mc.pid)
|
|
15
|
+
|
|
16
|
+
@mt.on_world # вошли в мир
|
|
17
|
+
def world(mc):
|
|
18
|
+
print("мир:", mc.world)
|
|
19
|
+
|
|
20
|
+
mt.run_forever(fps=144) # держит оверлей и вызывает хуки
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Установка
|
|
24
|
+
|
|
25
|
+
```powershell
|
|
26
|
+
cd minecraft-tweaks
|
|
27
|
+
python -m pip install -e .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Нужен Python 3.10 или новее и Windows. Win32 вызывается через `ctypes`, дополнительных
|
|
31
|
+
пакетов не требуется.
|
|
32
|
+
|
|
33
|
+
## Что внутри
|
|
34
|
+
|
|
35
|
+
| Модуль | Что делает |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `mt.find()` / `mt.is_running()` / `mt.wait()` | ищет окно и процесс Minecraft по заголовку, без psutil |
|
|
38
|
+
| `@mt.on_start` / `@mt.on_world` / `@mt.on_exit` | хуки жизненного цикла игры |
|
|
39
|
+
| `mt.Overlay` | прозрачное окно поверх игры: per-pixel alpha, от 144 fps, фокус не забирает, клики пропускает |
|
|
40
|
+
| `mt.Canvas` | рисование: `fill_rect`, `frame`, `corners`, `line`, `gradient_line`, `rounded`, `hgradient`, `bar`, `text`, `pixel_art` |
|
|
41
|
+
| `mt.ESPRenderer` | рамки, уголки, свечение, иконки, дистанция, полоса HP, трассеры, интерполяция позиций |
|
|
42
|
+
| `mt.ClickGui` | колонки категорий, тумблеры, поиск, подсветка под курсором, тёмное стекло |
|
|
43
|
+
| `mt.Keys`, `mt.Combo`, `mt.Hotkey` | клавиши и мышь через `GetAsyncKeyState`, хуки не ставятся |
|
|
44
|
+
| `mt.agent` | по желанию: внедрение Java-агента из `../mc-inject` и чтение состояния |
|
|
45
|
+
|
|
46
|
+
## Оверлей
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
ov = mt.Overlay(fps=144)
|
|
50
|
+
|
|
51
|
+
def render(canvas, overlay, mc):
|
|
52
|
+
canvas.text(f"{overlay.stats.fps:.0f} FPS", 10, 10, 0xFFF2F2F5)
|
|
53
|
+
esp.draw(canvas, camera, entities, partial_tick=partial)
|
|
54
|
+
|
|
55
|
+
mt.run_forever(render, overlay=ov)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Почему картинка не дёргается:
|
|
59
|
+
|
|
60
|
+
1. Попиксельная альфа. DWM композитит окно каждый кадр монитора, цветовой ключ не нужен,
|
|
61
|
+
поэтому края не мигают.
|
|
62
|
+
2. Интерполяция. `Entity.position(partial_tick)` считает `pos - delta * (1 - partialTick)`,
|
|
63
|
+
как это делает ваниль. Сущности обновляются 20 раз в секунду, а кадры рисуются 144.
|
|
64
|
+
3. Один оверлей. Окна старых запусков гасятся при старте.
|
|
65
|
+
|
|
66
|
+
## ESP
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
esp = mt.ESPRenderer(mt.EspSettings(tracers=True, names=True))
|
|
70
|
+
cam = mt.Camera(x=0, y=1.62, z=0, yaw=90, pitch=0, fov=70)
|
|
71
|
+
ents = [
|
|
72
|
+
mt.Entity(5, 0, 12, kind="player", health=0.8, name="bot"),
|
|
73
|
+
mt.Entity(-3, 0, 9, kind="mob"),
|
|
74
|
+
mt.Entity(2, 0, 6, kind="storage"),
|
|
75
|
+
]
|
|
76
|
+
esp.draw(canvas, cam, ents, partial_tick=0.5)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`kind` задаёт цвет и иконку: `player` (фиолетовый, человечек), `mob` (красный, ромб),
|
|
80
|
+
`storage` (жёлтый, сундук). Дистанция и HP рисуются сами.
|
|
81
|
+
|
|
82
|
+
## ClickGUI
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
gui = mt.ClickGui({
|
|
86
|
+
"Combat": [mt.Module("KillAura"), mt.Module("Velocity")],
|
|
87
|
+
"Render": [mt.Module("PlayerESP"), mt.Module("Tracers")],
|
|
88
|
+
})
|
|
89
|
+
gui.draw(canvas, mt.Keys.cursor(), focused=True)
|
|
90
|
+
hit = gui.click(mt.Keys.cursor(), lmb_pressed=True) # переключит тумблер под курсором
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Клики окно не перехватывает: оно прозрачно для мыши, а состояние кнопок читается глобально.
|
|
94
|
+
|
|
95
|
+
## Свои методы (расширения)
|
|
96
|
+
|
|
97
|
+
Расширения пишутся декораторами, наследование и ручная регистрация не нужны:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import minecraft_tweaks as mt
|
|
101
|
+
|
|
102
|
+
# 1. ESP для любого блока: имя, цвет, подпись, дальность
|
|
103
|
+
@mt.esp_block("diamond_ore", color=0xFF00E5FF, label=True, max_distance=96)
|
|
104
|
+
def diamonds():
|
|
105
|
+
return [(12, 5, -3), (18, 4, 2)] # координаты углов блоков
|
|
106
|
+
|
|
107
|
+
# 2. Свои сущности (вызывается каждый кадр)
|
|
108
|
+
@mt.esp_entity
|
|
109
|
+
def my_ents():
|
|
110
|
+
return [mt.Entity(0, 0, 8, kind="mob")]
|
|
111
|
+
|
|
112
|
+
# 3. Своя отрисовка поверх кадра
|
|
113
|
+
@mt.esp_draw
|
|
114
|
+
def watermark(canvas, camera, app):
|
|
115
|
+
canvas.text("my client", 10, 10, 0xFFFFFFFF)
|
|
116
|
+
|
|
117
|
+
# 4. Своя логика каждый кадр
|
|
118
|
+
@mt.on_frame
|
|
119
|
+
def logic(dt, app):
|
|
120
|
+
app.camera.yaw += 10 * dt
|
|
121
|
+
|
|
122
|
+
# 5. Свой модуль: попадает в ClickGUI, получает колбэки
|
|
123
|
+
@mt.module("MyMod", category="Render", enabled=True)
|
|
124
|
+
class MyMod:
|
|
125
|
+
def on_tick(self, dt): ...
|
|
126
|
+
def on_draw(self, canvas, app): ...
|
|
127
|
+
def on_toggle(self, enabled): ...
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Такой же файл можно положить в папку и не трогать код библиотеки:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
mt.load_scripts("scripts") # импортирует все scripts/*.py
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Внутри скрипта обычный Python с теми же декораторами. Исключение в чужом колбэке не
|
|
137
|
+
ломает кадр: оно попадает в `mt.registry.errors`.
|
|
138
|
+
|
|
139
|
+
## Клавиши и взвод макроса
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
keys = mt.Keys()
|
|
143
|
+
combo = mt.Combo(["S", "R", "R"], modifier="ALT")
|
|
144
|
+
|
|
145
|
+
if combo.update(): # держим Alt, жмём S, R, R
|
|
146
|
+
armed = True
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Опционально: Java-агент
|
|
150
|
+
|
|
151
|
+
Если нужны данные прямо из игры (позиции сущностей, состояние мира), рядом лежит
|
|
152
|
+
`mc-inject` с Java-агентом:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
mt.agent.attach() # процесс найдёт сам
|
|
156
|
+
state = mt.agent.wait_state() # прочитает ss-state.json, если агент его пишет
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Примеры
|
|
160
|
+
|
|
161
|
+
```powershell
|
|
162
|
+
python examples/esp_demo.py # плавный ESP на демо-мишенях
|
|
163
|
+
python examples/gui_demo.py # ClickGUI + HUD
|
|
164
|
+
python tests/test_smoke.py # смоук-тест без игры
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Ограничения
|
|
168
|
+
|
|
169
|
+
- Только Windows: используется Win32 API.
|
|
170
|
+
- Оверлей виден поверх окна и borderless-фуллскрина. Эксклюзивный fullscreen, когда игра
|
|
171
|
+
забирает дисплей целиком, перекрыть нельзя. Переключите игру в оконный режим.
|
|
172
|
+
- Данные о сущностях библиотека не добывает. Их нужно передать в `Entity` из своего источника.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""minecraft-tweaks: оверлей, ESP и GUI для Minecraft на чистом Python.
|
|
2
|
+
|
|
3
|
+
Быстрый старт::
|
|
4
|
+
|
|
5
|
+
import minecraft_tweaks as mt
|
|
6
|
+
|
|
7
|
+
@mt.on_start
|
|
8
|
+
def started(mc):
|
|
9
|
+
print("Minecraft:", mc.title)
|
|
10
|
+
|
|
11
|
+
ov = mt.Overlay(fps=144)
|
|
12
|
+
esp = mt.ESPRenderer()
|
|
13
|
+
|
|
14
|
+
def render(canvas, overlay, mc):
|
|
15
|
+
cam = mt.Camera(0, 0, 0, 0, 0, fov=70)
|
|
16
|
+
ents = [mt.Entity(5, 0, 10, kind="player", health=0.8)]
|
|
17
|
+
esp.draw(canvas, cam, ents)
|
|
18
|
+
canvas.text(f"FPS {overlay.stats.fps:.0f}", 10, 10, 0xFFFFFFFF)
|
|
19
|
+
|
|
20
|
+
mt.run_forever(render, overlay=ov)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from . import agent, process
|
|
26
|
+
from .app import App
|
|
27
|
+
from .draw import Canvas
|
|
28
|
+
from .esp import KIND_COLOR, Camera, Entity, ESPRenderer, EspSettings
|
|
29
|
+
from .extensions import (
|
|
30
|
+
BlockEsp,
|
|
31
|
+
Registry,
|
|
32
|
+
esp_block,
|
|
33
|
+
esp_draw,
|
|
34
|
+
esp_entity,
|
|
35
|
+
load_scripts,
|
|
36
|
+
module,
|
|
37
|
+
on_frame,
|
|
38
|
+
registry,
|
|
39
|
+
)
|
|
40
|
+
from .gui import ClickGui, Module
|
|
41
|
+
from .hooks import on_exit, on_start, on_world, run_forever
|
|
42
|
+
from .keys import (
|
|
43
|
+
Combo,
|
|
44
|
+
Hotkey,
|
|
45
|
+
Keys,
|
|
46
|
+
VK_ALT,
|
|
47
|
+
VK_CONTROL,
|
|
48
|
+
VK_ESCAPE,
|
|
49
|
+
VK_LBUTTON,
|
|
50
|
+
VK_RBUTTON,
|
|
51
|
+
VK_RSHIFT,
|
|
52
|
+
VK_SHIFT,
|
|
53
|
+
VK_SPACE,
|
|
54
|
+
vk,
|
|
55
|
+
)
|
|
56
|
+
from .overlay import Overlay, OverlayStats
|
|
57
|
+
from .process import MinecraftWindow
|
|
58
|
+
|
|
59
|
+
__version__ = "0.1.0"
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
"agent",
|
|
63
|
+
"process",
|
|
64
|
+
"App",
|
|
65
|
+
"Canvas",
|
|
66
|
+
"Overlay",
|
|
67
|
+
"OverlayStats",
|
|
68
|
+
"MinecraftWindow",
|
|
69
|
+
"Camera",
|
|
70
|
+
"Entity",
|
|
71
|
+
"ESPRenderer",
|
|
72
|
+
"EspSettings",
|
|
73
|
+
"KIND_COLOR",
|
|
74
|
+
"ClickGui",
|
|
75
|
+
"Module",
|
|
76
|
+
"Registry",
|
|
77
|
+
"BlockEsp",
|
|
78
|
+
"esp_entity",
|
|
79
|
+
"esp_block",
|
|
80
|
+
"esp_draw",
|
|
81
|
+
"on_frame",
|
|
82
|
+
"module",
|
|
83
|
+
"load_scripts",
|
|
84
|
+
"registry",
|
|
85
|
+
"Keys",
|
|
86
|
+
"Combo",
|
|
87
|
+
"Hotkey",
|
|
88
|
+
"vk",
|
|
89
|
+
"on_start",
|
|
90
|
+
"on_world",
|
|
91
|
+
"on_exit",
|
|
92
|
+
"run_forever",
|
|
93
|
+
"VK_ALT",
|
|
94
|
+
"VK_CONTROL",
|
|
95
|
+
"VK_ESCAPE",
|
|
96
|
+
"VK_LBUTTON",
|
|
97
|
+
"VK_RBUTTON",
|
|
98
|
+
"VK_RSHIFT",
|
|
99
|
+
"VK_SHIFT",
|
|
100
|
+
"VK_SPACE",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def find() -> MinecraftWindow | None:
|
|
105
|
+
"""Найти окно Minecraft прямо сейчас."""
|
|
106
|
+
return process.find()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def is_running() -> bool:
|
|
110
|
+
"""Запущен ли Minecraft (проверка по окну, без перебора процессов)."""
|
|
111
|
+
return process.is_running()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def wait(timeout: float | None = None) -> MinecraftWindow | None:
|
|
115
|
+
"""Дождаться запуска Minecraft."""
|
|
116
|
+
return process.wait(timeout)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def wait_world(timeout: float | None = None) -> MinecraftWindow | None:
|
|
120
|
+
"""Дождаться входа в мир."""
|
|
121
|
+
return process.wait_world(timeout)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""Тонкая обёртка над Win32 (ctypes): только то, что нужно библиотеке.
|
|
2
|
+
|
|
3
|
+
Здесь нет бизнес-логики: структуры, константы и объявления функций.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import ctypes
|
|
9
|
+
from ctypes import wintypes as wt
|
|
10
|
+
|
|
11
|
+
user32 = ctypes.WinDLL("user32", use_last_error=True)
|
|
12
|
+
gdi32 = ctypes.WinDLL("gdi32", use_last_error=True)
|
|
13
|
+
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
|
|
14
|
+
|
|
15
|
+
LRESULT = ctypes.c_ssize_t
|
|
16
|
+
WNDPROC = ctypes.WINFUNCTYPE(LRESULT, wt.HWND, ctypes.c_uint, wt.WPARAM, wt.LPARAM)
|
|
17
|
+
WNDENUMPROC = ctypes.WINFUNCTYPE(wt.BOOL, wt.HWND, wt.LPARAM)
|
|
18
|
+
|
|
19
|
+
# --- константы -------------------------------------------------------------
|
|
20
|
+
WS_POPUP = 0x80000000
|
|
21
|
+
WS_EX_LAYERED = 0x00080000
|
|
22
|
+
WS_EX_TRANSPARENT = 0x00000020
|
|
23
|
+
WS_EX_TOPMOST = 0x00000008
|
|
24
|
+
WS_EX_TOOLWINDOW = 0x00000080
|
|
25
|
+
WS_EX_NOACTIVATE = 0x08000000
|
|
26
|
+
|
|
27
|
+
SW_HIDE = 0
|
|
28
|
+
SW_SHOWNOACTIVATE = 4
|
|
29
|
+
SW_RESTORE = 9
|
|
30
|
+
|
|
31
|
+
SWP_NOZORDER = 0x0004
|
|
32
|
+
SWP_NOACTIVATE = 0x0010
|
|
33
|
+
SWP_SHOWWINDOW = 0x0040
|
|
34
|
+
|
|
35
|
+
ULW_ALPHA = 0x00000002
|
|
36
|
+
AC_SRC_OVER = 0x00
|
|
37
|
+
AC_SRC_ALPHA = 0x01
|
|
38
|
+
|
|
39
|
+
BI_RGB = 0
|
|
40
|
+
DIB_RGB_COLORS = 0
|
|
41
|
+
|
|
42
|
+
HWND_TOPMOST = -1
|
|
43
|
+
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class WNDCLASSEXW(ctypes.Structure):
|
|
47
|
+
_fields_ = [
|
|
48
|
+
("cbSize", ctypes.c_uint),
|
|
49
|
+
("style", ctypes.c_uint),
|
|
50
|
+
("lpfnWndProc", WNDPROC),
|
|
51
|
+
("cbClsExtra", ctypes.c_int),
|
|
52
|
+
("cbWndExtra", ctypes.c_int),
|
|
53
|
+
("hInstance", wt.HINSTANCE),
|
|
54
|
+
("hIcon", wt.HICON),
|
|
55
|
+
("hCursor", wt.HANDLE),
|
|
56
|
+
("hbrBackground", wt.HBRUSH),
|
|
57
|
+
("lpszMenuName", wt.LPCWSTR),
|
|
58
|
+
("lpszClassName", wt.LPCWSTR),
|
|
59
|
+
("hIconSm", wt.HICON),
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class BITMAPINFOHEADER(ctypes.Structure):
|
|
64
|
+
_fields_ = [
|
|
65
|
+
("biSize", ctypes.c_uint32),
|
|
66
|
+
("biWidth", ctypes.c_int32),
|
|
67
|
+
("biHeight", ctypes.c_int32),
|
|
68
|
+
("biPlanes", ctypes.c_uint16),
|
|
69
|
+
("biBitCount", ctypes.c_uint16),
|
|
70
|
+
("biCompression", ctypes.c_uint32),
|
|
71
|
+
("biSizeImage", ctypes.c_uint32),
|
|
72
|
+
("biXPelsPerMeter", ctypes.c_int32),
|
|
73
|
+
("biYPelsPerMeter", ctypes.c_int32),
|
|
74
|
+
("biClrUsed", ctypes.c_uint32),
|
|
75
|
+
("biClrImportant", ctypes.c_uint32),
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class BITMAPINFO(ctypes.Structure):
|
|
80
|
+
_fields_ = [("bmiHeader", BITMAPINFOHEADER), ("bmiColors", ctypes.c_uint32 * 3)]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class BLENDFUNCTION(ctypes.Structure):
|
|
84
|
+
_fields_ = [
|
|
85
|
+
("BlendOp", ctypes.c_ubyte),
|
|
86
|
+
("BlendFlags", ctypes.c_ubyte),
|
|
87
|
+
("SourceConstantAlpha", ctypes.c_ubyte),
|
|
88
|
+
("AlphaFormat", ctypes.c_ubyte),
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class SIZE(ctypes.Structure):
|
|
93
|
+
_fields_ = [("cx", ctypes.c_long), ("cy", ctypes.c_long)]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# --- прототипы -------------------------------------------------------------
|
|
97
|
+
user32.EnumWindows.argtypes = [WNDENUMPROC, wt.LPARAM]
|
|
98
|
+
user32.EnumWindows.restype = wt.BOOL
|
|
99
|
+
user32.IsWindowVisible.argtypes = [wt.HWND]
|
|
100
|
+
user32.IsWindowVisible.restype = wt.BOOL
|
|
101
|
+
user32.GetWindowTextW.argtypes = [wt.HWND, wt.LPWSTR, ctypes.c_int]
|
|
102
|
+
user32.GetWindowTextW.restype = ctypes.c_int
|
|
103
|
+
user32.GetClassNameW.argtypes = [wt.HWND, wt.LPWSTR, ctypes.c_int]
|
|
104
|
+
user32.GetClassNameW.restype = ctypes.c_int
|
|
105
|
+
user32.GetForegroundWindow.restype = wt.HWND
|
|
106
|
+
user32.GetClientRect.argtypes = [wt.HWND, ctypes.POINTER(wt.RECT)]
|
|
107
|
+
user32.GetClientRect.restype = wt.BOOL
|
|
108
|
+
user32.ClientToScreen.argtypes = [wt.HWND, ctypes.POINTER(wt.POINT)]
|
|
109
|
+
user32.ClientToScreen.restype = wt.BOOL
|
|
110
|
+
user32.GetWindowThreadProcessId.argtypes = [wt.HWND, ctypes.POINTER(wt.DWORD)]
|
|
111
|
+
user32.GetWindowThreadProcessId.restype = wt.DWORD
|
|
112
|
+
user32.CreateWindowExW.argtypes = [
|
|
113
|
+
wt.DWORD, wt.LPCWSTR, wt.LPCWSTR, wt.DWORD,
|
|
114
|
+
ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int,
|
|
115
|
+
wt.HWND, wt.HMENU, wt.HINSTANCE, wt.LPVOID,
|
|
116
|
+
]
|
|
117
|
+
user32.CreateWindowExW.restype = wt.HWND
|
|
118
|
+
user32.RegisterClassExW.argtypes = [ctypes.POINTER(WNDCLASSEXW)]
|
|
119
|
+
user32.RegisterClassExW.restype = wt.ATOM
|
|
120
|
+
user32.DefWindowProcW.argtypes = [wt.HWND, ctypes.c_uint, wt.WPARAM, wt.LPARAM]
|
|
121
|
+
user32.DefWindowProcW.restype = LRESULT
|
|
122
|
+
user32.ShowWindow.argtypes = [wt.HWND, ctypes.c_int]
|
|
123
|
+
user32.ShowWindow.restype = wt.BOOL
|
|
124
|
+
user32.DestroyWindow.argtypes = [wt.HWND]
|
|
125
|
+
user32.DestroyWindow.restype = wt.BOOL
|
|
126
|
+
user32.SetWindowPos.argtypes = [wt.HWND, wt.HWND, ctypes.c_int, ctypes.c_int,
|
|
127
|
+
ctypes.c_int, ctypes.c_int, ctypes.c_uint]
|
|
128
|
+
user32.SetWindowPos.restype = wt.BOOL
|
|
129
|
+
user32.GetAsyncKeyState.argtypes = [ctypes.c_int]
|
|
130
|
+
user32.GetAsyncKeyState.restype = ctypes.c_short
|
|
131
|
+
user32.GetCursorPos.argtypes = [ctypes.POINTER(wt.POINT)]
|
|
132
|
+
user32.GetCursorPos.restype = wt.BOOL
|
|
133
|
+
user32.GetDC.argtypes = [wt.HWND]
|
|
134
|
+
user32.GetDC.restype = wt.HDC
|
|
135
|
+
user32.ReleaseDC.argtypes = [wt.HWND, wt.HDC]
|
|
136
|
+
user32.ReleaseDC.restype = ctypes.c_int
|
|
137
|
+
|
|
138
|
+
user32.UpdateLayeredWindow.argtypes = [
|
|
139
|
+
wt.HWND, wt.HDC, ctypes.POINTER(wt.POINT), ctypes.POINTER(SIZE),
|
|
140
|
+
wt.HDC, ctypes.POINTER(wt.POINT), wt.COLORREF,
|
|
141
|
+
ctypes.POINTER(BLENDFUNCTION), wt.DWORD,
|
|
142
|
+
]
|
|
143
|
+
user32.UpdateLayeredWindow.restype = wt.BOOL
|
|
144
|
+
|
|
145
|
+
gdi32.CreateCompatibleDC.argtypes = [wt.HDC]
|
|
146
|
+
gdi32.CreateCompatibleDC.restype = wt.HDC
|
|
147
|
+
gdi32.CreateDIBSection.argtypes = [wt.HDC, ctypes.POINTER(BITMAPINFO), ctypes.c_uint,
|
|
148
|
+
ctypes.POINTER(ctypes.c_void_p), wt.HANDLE, wt.DWORD]
|
|
149
|
+
gdi32.CreateDIBSection.restype = wt.HBITMAP
|
|
150
|
+
gdi32.SelectObject.argtypes = [wt.HDC, wt.HGDIOBJ]
|
|
151
|
+
gdi32.SelectObject.restype = wt.HGDIOBJ
|
|
152
|
+
gdi32.DeleteObject.argtypes = [wt.HGDIOBJ]
|
|
153
|
+
gdi32.DeleteObject.restype = wt.BOOL
|
|
154
|
+
gdi32.DeleteDC.argtypes = [wt.HDC]
|
|
155
|
+
gdi32.DeleteDC.restype = wt.BOOL
|
|
156
|
+
|
|
157
|
+
kernel32.GetModuleHandleW.argtypes = [wt.LPCWSTR]
|
|
158
|
+
kernel32.GetModuleHandleW.restype = wt.HMODULE
|
|
159
|
+
kernel32.OpenProcess.argtypes = [wt.DWORD, wt.BOOL, wt.DWORD]
|
|
160
|
+
kernel32.OpenProcess.restype = wt.HANDLE
|
|
161
|
+
kernel32.CloseHandle.argtypes = [wt.HANDLE]
|
|
162
|
+
kernel32.CloseHandle.restype = wt.BOOL
|
|
163
|
+
kernel32.QueryFullProcessImageNameW.argtypes = [wt.HANDLE, wt.DWORD, wt.LPWSTR,
|
|
164
|
+
ctypes.POINTER(wt.DWORD)]
|
|
165
|
+
kernel32.QueryFullProcessImageNameW.restype = wt.BOOL
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
DPI_AWARE = False
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def set_dpi_aware() -> bool:
|
|
172
|
+
"""Включает per-monitor DPI. Без этого оверлей смещается при масштабе 125-150 %."""
|
|
173
|
+
global DPI_AWARE
|
|
174
|
+
try:
|
|
175
|
+
# DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4
|
|
176
|
+
user32.SetProcessDpiAwarenessContext(ctypes.c_void_p(-4))
|
|
177
|
+
DPI_AWARE = True
|
|
178
|
+
return True
|
|
179
|
+
except Exception:
|
|
180
|
+
try:
|
|
181
|
+
user32.SetProcessDPIAware()
|
|
182
|
+
DPI_AWARE = True
|
|
183
|
+
return True
|
|
184
|
+
except Exception:
|
|
185
|
+
# Старая Windows без DPI API: остаётся системный масштаб.
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def last_error() -> int:
|
|
190
|
+
return ctypes.get_last_error()
|