Skip to content

Commit a2f79ae

Browse files
committed
feat: 增强模拟器关闭逻辑,添加状态检查以避免重复操作
- 新增 _emulator_down 标志追踪模拟器关闭状态 - 各等待策略在模拟器关闭时跳过设备操作直接等待 - _wait_close_emulator_or 关闭模拟器后标记状态,跳过 nemu_ipc 释放 - 主循环在模拟器关闭后重新创建 Device 实例以恢复连接 - nemu_ipc.py 添加 io 模块导入和安全的 fileno 检查 - 在 stop 模拟器前先检查实例是否仍在运行,避免已关闭时重复发送 shutdown_player - 修复 remove_duplicated_path 缺失导入
1 parent cd60736 commit a2f79ae

3 files changed

Lines changed: 57 additions & 3 deletions

File tree

‎module/device/method/nemu_ipc.py‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import ctypes
3+
import io
34
import os
45
import sys
56
from functools import partial, wraps
@@ -42,6 +43,19 @@ class CaptureStd:
4243
def __init__(self):
4344
self.stdout = b''
4445
self.stderr = b''
46+
self.enabled = False
47+
48+
@staticmethod
49+
def _safe_fileno(stream):
50+
if stream is None:
51+
return None
52+
fileno = getattr(stream, 'fileno', None)
53+
if not callable(fileno):
54+
return None
55+
try:
56+
return fileno()
57+
except (AttributeError, ValueError, OSError, io.UnsupportedOperation):
58+
return None
4559

4660
def _redirect_stdout(self, to):
4761
sys.stdout.close()
@@ -54,8 +68,14 @@ def _redirect_stderr(self, to):
5468
sys.stderr = os.fdopen(self.fderr, 'w')
5569

5670
def __enter__(self):
57-
self.fdout = sys.stdout.fileno()
58-
self.fderr = sys.stderr.fileno()
71+
self.fdout = self._safe_fileno(sys.stdout)
72+
self.fderr = self._safe_fileno(sys.stderr)
73+
if self.fdout is None or self.fderr is None:
74+
self.enabled = False
75+
logger.warning('CaptureStd disabled because stdout/stderr has no valid fileno')
76+
return self
77+
78+
self.enabled = True
5979
self.reader_out, self.writer_out = os.pipe()
6080
self.reader_err, self.writer_err = os.pipe()
6181
self.old_stdout = os.dup(self.fdout)
@@ -68,6 +88,9 @@ def __enter__(self):
6888
return self
6989

7090
def __exit__(self, exc_type, exc_val, exc_tb):
91+
if not self.enabled:
92+
return
93+
7194
self._redirect_stdout(to=self.old_stdout)
7295
self._redirect_stderr(to=self.old_stderr)
7396
os.close(self.old_stdout)

‎module/device/platform2/platform_windows.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import ctypes
2+
import os
23
import re
34
import subprocess
45
import psutil
@@ -10,6 +11,7 @@
1011
from module.device.handle import Handle
1112
from module.device.platform2.platform_base import PlatformBase
1213
from module.device.platform2.emulator_windows import Emulator, EmulatorInstance, EmulatorManager
14+
from module.device.platform2.emulator_base import remove_duplicated_path
1315
from module.logger import logger
1416

1517
import ctypes
@@ -114,6 +116,17 @@ def __bool__(self):
114116
return True
115117

116118
class PlatformWindows(PlatformBase, EmulatorManager):
119+
@staticmethod
120+
def _normalize_path(path: str) -> str:
121+
return os.path.abspath(path).replace('\\', '/').lower()
122+
123+
def _is_instance_running(self, instance: EmulatorInstance) -> bool:
124+
target = self._normalize_path(instance.path)
125+
for running_exe in remove_duplicated_path(list(self.iter_running_emulator())):
126+
if self._normalize_path(running_exe) == target:
127+
return True
128+
return False
129+
117130
@classmethod
118131
def execute(cls, command, show_window=True):
119132
"""
@@ -204,6 +217,9 @@ def _emulator_stop(self, instance: EmulatorInstance):
204217
Stop a emulator without error handling
205218
"""
206219
exe: str = instance.emulator.path
220+
if not self._is_instance_running(instance):
221+
logger.info(f'Emulator already closed, skip stop: {instance.name}')
222+
return
207223
if instance == Emulator.MuMuPlayer:
208224
# MuMu6 does not have multi instance, kill one means kill all
209225
# Has 4 processes

‎script.py‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,10 @@ def _handle_wait_during_idle(self, next_run: datetime) -> bool:
347347
return func(next_run)
348348

349349
def _wait_close_game(self, next_run: datetime, close_game_limit_time=None) -> bool:
350+
if self._emulator_down:
351+
logger.info("Emulator is down, skip close_game/goto_main action and wait directly")
352+
return self.wait_until(next_run)
353+
350354
if close_game_limit_time and next_run > datetime.now() + timedelta(
351355
hours=close_game_limit_time.hour, minutes=close_game_limit_time.minute, seconds=close_game_limit_time.second):
352356
logger.info("Close game during wait")
@@ -363,6 +367,10 @@ def _wait_close_game(self, next_run: datetime, close_game_limit_time=None) -> bo
363367
return True
364368

365369
def _wait_goto_main(self, next_run: datetime) -> bool:
370+
if self._emulator_down:
371+
logger.info("Emulator is down, skip goto_main and wait directly")
372+
return self.wait_until(next_run)
373+
366374
logger.info("Goto main page during wait")
367375
self.run("GotoMain")
368376
self.device.release_during_wait()
@@ -372,7 +380,10 @@ def _wait_close_emulator_or(self, next_run: datetime, close_game_limit_time=None
372380
if close_emulator_limit_time and next_run > datetime.now() + timedelta(
373381
hours=close_emulator_limit_time.hour, minutes=close_emulator_limit_time.minute, seconds=close_emulator_limit_time.second):
374382
logger.info("Close emulator during wait")
375-
self.device.emulator_stop()
383+
if not self._emulator_down:
384+
self.device.emulator_stop()
385+
else:
386+
logger.info("Emulator already closed")
376387
self._emulator_down = True
377388
# 模拟器已关闭,不需要释放nemu_ipc连接
378389
if not self.wait_until(next_run):
@@ -385,6 +396,10 @@ def _wait_close_emulator_or(self, next_run: datetime, close_game_limit_time=None
385396
return True
386397

387398
def _wait_stay_there(self, next_run: datetime) -> bool:
399+
if self._emulator_down:
400+
logger.info("Stay_there during wait (emulator is down)")
401+
return self.wait_until(next_run)
402+
388403
logger.info("Stay_there (no action) during wait")
389404
self.device.release_during_wait()
390405
return self.wait_until(next_run)

0 commit comments

Comments
 (0)