Skip to content

Commit 3096dfa

Browse files
committed
Merge main and resolve conflicts
1 parent 7b01bd0 commit 3096dfa

3 files changed

Lines changed: 99 additions & 3 deletions

File tree

noita_env.py

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,14 @@
4646
# still works. Each env instance owns its own mss handle (mss is not thread-safe).
4747
import cv2
4848
import mss
49-
import win32gui
50-
import win32process
49+
try:
50+
import win32gui
51+
except ImportError:
52+
win32gui = None
53+
try:
54+
import win32process
55+
except ImportError:
56+
win32process = None
5157

5258

5359
def _capture_noita_frame() -> "Optional[Image.Image]":
@@ -88,6 +94,66 @@ def _cb(hwnd: int, _lparam) -> bool:
8894
return found[0] if found else None
8995

9096

97+
98+
def _dismiss_error_dialog(target_pid: Optional[int] = None) -> bool:
99+
"""
100+
Searches for Noita error/crash dialogs and clicks the 'Always Ignore'
101+
button if present to prevent the training from hanging.
102+
"""
103+
BM_CLICK = 0x00F5
104+
clicked = False
105+
106+
def _enum_child_cb(child_hwnd: int, _lparam) -> bool:
107+
nonlocal clicked
108+
if not win32gui.IsWindowVisible(child_hwnd):
109+
return True
110+
child_title = win32gui.GetWindowText(child_hwnd) or ""
111+
child_title_lower = child_title.lower()
112+
if "ignore" in child_title_lower and "always" in child_title_lower:
113+
try:
114+
# SendMessage is blocking, PostMessage is async
115+
import win32con
116+
import win32api
117+
win32api.PostMessage(child_hwnd, BM_CLICK, 0, 0)
118+
logger.info("Dismissed Noita crash dialog: clicked '{}' (hwnd: {})", child_title, child_hwnd)
119+
clicked = True
120+
except Exception as e:
121+
logger.debug("Failed to click dialog button: {}", e)
122+
return True
123+
124+
def _enum_windows_cb(hwnd: int, _lparam) -> bool:
125+
if not win32gui.IsWindowVisible(hwnd):
126+
return True
127+
128+
try:
129+
_, pid = win32process.GetWindowThreadProcessId(hwnd)
130+
except Exception:
131+
return True
132+
133+
if target_pid is not None and pid != target_pid:
134+
# If target PID is provided, only look at dialogs owned by it
135+
return True
136+
137+
title = win32gui.GetWindowText(hwnd) or ""
138+
title_lower = title.lower()
139+
140+
# Check if this might be an error dialog (usually they have 'Noita' or 'Error' in title,
141+
# but their class is often '#32770' for standard dialogs).
142+
class_name = win32gui.GetClassName(hwnd)
143+
if "noita" in title_lower or "error" in title_lower or class_name == "#32770":
144+
try:
145+
win32gui.EnumChildWindows(hwnd, _enum_child_cb, None)
146+
except Exception:
147+
pass
148+
return True
149+
150+
try:
151+
win32gui.EnumWindows(_enum_windows_cb, None)
152+
except Exception as exc:
153+
logger.debug("EnumWindows failed during dialog check: {}", exc)
154+
155+
return clicked
156+
91157
def _find_any_noita_hwnd() -> Optional[int]:
92158
"""Fallback: any visible window with 'Noita' in the title (single-instance mode)."""
93159
found: list[int] = []
@@ -547,7 +613,9 @@ def _wait_for_new_frame(self, prev_frame: int, timeout: float = 2.0) -> Optional
547613
if s is not None and s.get("frame", -1) != prev_frame:
548614
return s
549615
time.sleep(0.008) # poll every 8 ms (~2x per Noita frame at 60fps)
550-
# timeout — return whatever we have (Noita may be loading/paused)
616+
617+
# timeout — return whatever we have (Noita may be loading/paused/crashed)
618+
_dismiss_error_dialog(self.noita_pid)
551619
return self._get_state()
552620

553621
def step(self, action: int):

patch_req.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
with open("requirements.txt", "r") as f:
2+
content = f.read()
3+
4+
import re
5+
content = re.sub(r'<<<<<<< HEAD.*?=======\n', '', content, flags=re.DOTALL)
6+
content = content.replace('>>>>>>> origin/main\n', '')
7+
8+
# add missing test reqs
9+
content += """
10+
# Development
11+
black>=23.0.0
12+
flake8>=6.0.0
13+
mypy>=1.5.0
14+
pytest>=7.4.0
15+
pytest-cov>=4.1.0
16+
pygetwindow
17+
"""
18+
19+
with open("requirements.txt", "w") as f:
20+
f.write(content)

requirements.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,11 @@ websockets>=12.0
3030
# Screenshots & Image processing
3131
mss>=9.0.0
3232
pillow>=10.0.0
33+
34+
# Development
35+
black>=23.0.0
36+
flake8>=6.0.0
37+
mypy>=1.5.0
38+
pytest>=7.4.0
39+
pytest-cov>=4.1.0
40+
pygetwindow

0 commit comments

Comments
 (0)