fix: prevent premature game termination detection for EGS games

Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
2025-06-16 22:51:55 +05:00
parent 3f3e2f8d2c
commit 5a1ac2206b
2 changed files with 98 additions and 32 deletions

View File

@ -16,6 +16,29 @@ from PySide6.QtGui import QPixmap
logger = get_logger(__name__)
def get_egs_executable(app_name: str, legendary_config_path: str) -> str | None:
"""Получает путь к исполняемому файлу EGS-игры из installed.json с использованием orjson."""
installed_json_path = os.path.join(legendary_config_path, "installed.json")
try:
with open(installed_json_path, "rb") as f:
installed_data = orjson.loads(f.read())
if app_name in installed_data:
install_path = installed_data[app_name].get("install_path", "").decode('utf-8') if isinstance(installed_data[app_name].get("install_path"), bytes) else installed_data[app_name].get("install_path", "")
executable = installed_data[app_name].get("executable", "").decode('utf-8') if isinstance(installed_data[app_name].get("executable"), bytes) else installed_data[app_name].get("executable", "")
if install_path and executable:
return os.path.join(install_path, executable)
logger.warning(f"No executable found for EGS app_name: {app_name}")
return None
except FileNotFoundError:
logger.error(f"installed.json not found at {installed_json_path}")
return None
except orjson.JSONDecodeError:
logger.error(f"Invalid JSON in {installed_json_path}")
return None
except Exception as e:
logger.error(f"Error reading installed.json: {e}")
return None
def get_cache_dir() -> Path:
"""Returns the path to the cache directory, creating it if necessary."""
xdg_cache_home = os.getenv(

View File

@ -17,7 +17,7 @@ from portprotonqt.system_overlay import SystemOverlay
from portprotonqt.image_utils import load_pixmap_async, round_corners, ImageCarousel
from portprotonqt.steam_api import get_steam_game_info_async, get_full_steam_game_info_async, get_steam_installed_games
from portprotonqt.egs_api import load_egs_games_async
from portprotonqt.egs_api import load_egs_games_async, get_egs_executable
from portprotonqt.theme_manager import ThemeManager, load_theme_screenshots, load_logo
from portprotonqt.time_utils import save_last_launch, get_last_launch, parse_playtime_file, format_playtime, get_last_launch_timestamp, format_last_launch
from portprotonqt.config_utils import (
@ -1863,26 +1863,15 @@ class MainWindow(QMainWindow):
# Обработка EGS-игр
if exec_line.startswith("legendary:launch:"):
# Извлекаем app_name из exec_line
app_name = exec_line.split("legendary:launch:")[1]
legendary_path = self.legendary_path # Путь к legendary
# Формируем переменные окружения
env_vars = os.environ.copy()
env_vars['START_FROM_STEAM'] = '1'
env_vars['LEGENDARY_CONFIG_PATH'] = self.legendary_config_path
# Получаем путь к .exe из installed.json
game_exe = get_egs_executable(app_name, self.legendary_config_path)
if not game_exe or not os.path.exists(game_exe):
QMessageBox.warning(self, _("Error"), _("Executable not found for EGS game: {0}").format(app_name))
return
wrapper = "flatpak run ru.linux_gaming.PortProton"
if self.portproton_location is not None and ".var" not in self.portproton_location:
start_sh = os.path.join(self.portproton_location, "data", "scripts", "start.sh")
wrapper = start_sh
# Формируем команду
cmd = [
legendary_path, "launch", app_name, "--no-wine", "--wrapper", wrapper
]
current_exe = os.path.basename(legendary_path)
current_exe = os.path.basename(game_exe)
if self.game_processes and self.target_exe is not None and self.target_exe != current_exe:
QMessageBox.warning(self, _("Error"), _("Cannot launch game while another game is running"))
return
@ -1891,28 +1880,82 @@ class MainWindow(QMainWindow):
update_button = button if button is not None else self.current_play_button
self.current_running_button = update_button
self.target_exe = current_exe
exe_name = app_name # Используем app_name для EGS-игр
exe_name = os.path.splitext(current_exe)[0]
# Запускаем процесс
try:
process = subprocess.Popen(cmd, env=env_vars, shell=False, preexec_fn=os.setsid)
self.game_processes.append(process)
save_last_launch(exe_name, datetime.now())
# Проверяем, запущена ли игра
if self.game_processes and self.target_exe == current_exe:
# Останавливаем игру
if hasattr(self, 'input_manager'):
self.input_manager.enable_gamepad_handling()
for proc in self.game_processes:
try:
parent = psutil.Process(proc.pid)
children = parent.children(recursive=True)
for child in children:
try:
child.terminate()
except psutil.NoSuchProcess:
pass
psutil.wait_procs(children, timeout=5)
for child in children:
if child.is_running():
child.kill()
os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
except psutil.NoSuchProcess:
pass
self.game_processes = []
if update_button:
update_button.setText(_("Launching"))
icon = self.theme_manager.get_icon("stop")
update_button.setText(_("Play"))
icon = self.theme_manager.get_icon("play")
if isinstance(icon, str):
icon = QIcon(icon)
elif icon is None:
icon = QIcon()
update_button.setIcon(icon)
if hasattr(self, 'checkProcessTimer') and self.checkProcessTimer is not None:
self.checkProcessTimer.stop()
self.checkProcessTimer.deleteLater()
self.checkProcessTimer = None
self.current_running_button = None
self.target_exe = None
self._gameLaunched = False
else:
# Запускаем игру через PortProton
env_vars = os.environ.copy()
env_vars['START_FROM_STEAM'] = '1'
env_vars['LEGENDARY_CONFIG_PATH'] = self.legendary_config_path
self.checkProcessTimer = QTimer(self)
self.checkProcessTimer.timeout.connect(self.checkTargetExe)
self.checkProcessTimer.start(500)
except Exception as e:
logger.error(f"Failed to launch EGS game {app_name}: {e}")
QMessageBox.warning(self, _("Error"), _("Failed to launch game: {0}").format(str(e)))
wrapper = "flatpak run ru.linux_gaming.PortProton"
if self.portproton_location is not None and ".var" not in self.portproton_location:
start_sh = os.path.join(self.portproton_location, "data", "scripts", "start.sh")
wrapper = start_sh
cmd = [wrapper, game_exe]
try:
process = subprocess.Popen(cmd, env=env_vars, shell=False, preexec_fn=os.setsid)
self.game_processes.append(process)
save_last_launch(exe_name, datetime.now())
if update_button:
update_button.setText(_("Launching"))
icon = self.theme_manager.get_icon("stop")
if isinstance(icon, str):
icon = QIcon(icon)
elif icon is None:
icon = QIcon()
update_button.setIcon(icon)
# Delay disabling gamepad handling
if hasattr(self, 'input_manager'):
QTimer.singleShot(200, self.input_manager.disable_gamepad_handling)
self.checkProcessTimer = QTimer(self)
self.checkProcessTimer.timeout.connect(self.checkTargetExe)
self.checkProcessTimer.start(500)
except Exception as e:
logger.error(f"Failed to launch EGS game {app_name}: {e}")
QMessageBox.warning(self, _("Error"), _("Failed to launch game: {0}").format(str(e)))
return
# Обработка PortProton-игр