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.
Files changed (29) hide show
  1. minecraft_tweaks-0.1.0/LICENSE +21 -0
  2. minecraft_tweaks-0.1.0/PKG-INFO +185 -0
  3. minecraft_tweaks-0.1.0/README.md +172 -0
  4. minecraft_tweaks-0.1.0/minecraft_tweaks/__init__.py +121 -0
  5. minecraft_tweaks-0.1.0/minecraft_tweaks/__main__.py +6 -0
  6. minecraft_tweaks-0.1.0/minecraft_tweaks/_win.py +190 -0
  7. minecraft_tweaks-0.1.0/minecraft_tweaks/agent.py +93 -0
  8. minecraft_tweaks-0.1.0/minecraft_tweaks/anim.py +169 -0
  9. minecraft_tweaks-0.1.0/minecraft_tweaks/app.py +153 -0
  10. minecraft_tweaks-0.1.0/minecraft_tweaks/draw.py +252 -0
  11. minecraft_tweaks-0.1.0/minecraft_tweaks/esp.py +210 -0
  12. minecraft_tweaks-0.1.0/minecraft_tweaks/extensions.py +299 -0
  13. minecraft_tweaks-0.1.0/minecraft_tweaks/gui.py +154 -0
  14. minecraft_tweaks-0.1.0/minecraft_tweaks/hooks.py +114 -0
  15. minecraft_tweaks-0.1.0/minecraft_tweaks/keys.py +152 -0
  16. minecraft_tweaks-0.1.0/minecraft_tweaks/overlay.py +295 -0
  17. minecraft_tweaks-0.1.0/minecraft_tweaks/paint.py +617 -0
  18. minecraft_tweaks-0.1.0/minecraft_tweaks/png.py +116 -0
  19. minecraft_tweaks-0.1.0/minecraft_tweaks/process.py +136 -0
  20. minecraft_tweaks-0.1.0/minecraft_tweaks/py.typed +0 -0
  21. minecraft_tweaks-0.1.0/minecraft_tweaks/watchdog.py +96 -0
  22. minecraft_tweaks-0.1.0/minecraft_tweaks.egg-info/PKG-INFO +185 -0
  23. minecraft_tweaks-0.1.0/minecraft_tweaks.egg-info/SOURCES.txt +27 -0
  24. minecraft_tweaks-0.1.0/minecraft_tweaks.egg-info/dependency_links.txt +1 -0
  25. minecraft_tweaks-0.1.0/minecraft_tweaks.egg-info/top_level.txt +1 -0
  26. minecraft_tweaks-0.1.0/pyproject.toml +22 -0
  27. minecraft_tweaks-0.1.0/setup.cfg +4 -0
  28. minecraft_tweaks-0.1.0/setup.py +3 -0
  29. 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,6 @@
1
+ """Точка входа: python -m minecraft_tweaks [--version|--fps N|...]."""
2
+
3
+ from .watchdog import main
4
+
5
+ if __name__ == "__main__":
6
+ raise SystemExit(main())
@@ -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()