launcher/src/utils/game_launcher.py
2025-01-15 00:00:28 +07:00

218 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
from subprocess import Popen, PIPE
from pathlib import Path
import logging
import platform
import shutil
import json
class GameLauncher:
def __init__(self, settings: dict):
self.settings = settings
self.logger = logging.getLogger('GameLauncher')
self.platform = platform.system().lower()
self.account_username = None
self.account_id = None
# Кэшируем пути к файлам
self.game_path = Path(settings.get('game', {}).get('path', ''))
self.config_path = self.game_path / 'WTF' / 'Config.wtf'
self.realmlist_paths = [
self.game_path / 'Data' / 'ruRU' / 'realmlist.wtf',
self.game_path / 'Data' / 'realmlist.wtf'
]
def validate_game_path(self, path: str) -> bool:
"""Проверяет корректность пути к игре"""
if not path:
return False
game_path = Path(path)
required_files = [
game_path / 'Wow.exe',
game_path / 'Data/common.MPQ',
game_path / 'Data/common-2.MPQ'
]
try:
# Проверяем каждый файл
for required_file in required_files:
if not required_file.exists():
self.logger.error(f"Missing required file: {required_file}")
return False
return True
except Exception as e:
self.logger.error(f"Error validating game path: {e}")
return False
def update_realmlist(self, path: str, realmlist: str) -> bool:
"""Обновляет файл realmlist.wtf"""
try:
# Проверяем оба возможных пути
data_paths = [
Path(path) / 'Data' / 'ruRU' / 'realmlist.wtf', # Путь для русской локализации
Path(path) / 'Data' / 'realmlist.wtf' # Стандартный путь
]
# Обновляем существующие файлы или создаем новые
updated = False
for data_path in data_paths:
try:
data_path.parent.mkdir(parents=True, exist_ok=True)
with open(data_path, 'w', encoding='utf-8') as f:
f.write(f'set realmlist {realmlist}\n')
updated = True
except Exception as e:
self.logger.warning(f"Could not update {data_path}: {e}")
return updated
except Exception as e:
self.logger.error(f"Error updating realmlist: {e}")
return False
def update_config_wtf(self, path: str) -> bool:
"""Обновляет файл Config.wtf для автологина"""
try:
# Получаем realmlist из настроек
realmlist = self.settings.get('game', {}).get('realmlist', 'logon.server.com')
config_path = Path(path) / 'WTF' / 'Config.wtf'
# Создаем директорию WTF если её нет
config_path.parent.mkdir(parents=True, exist_ok=True)
# Читаем существующие настройки
config = {}
if config_path.exists():
with open(config_path, 'r', encoding='utf-8') as f:
for line in f:
if line.startswith('SET '):
key, value = line[4:].strip().split(' ', 1)
config[key] = value.strip('"')
# Обновляем настройки
if self.account_username:
# Настройки для автологина
config['accountName'] = self.account_username.upper() # Имя должно быть в верхнем регистре
config['accountList'] = self.account_username.upper() # Список аккаунтов
config['lastAccountName'] = self.account_username.upper() # Последний использованный аккаунт
# Дополнительные настройки для списка аккаунтов
config['portal'] = self.account_username.upper() # Текущий аккаунт в портале
config['lastCharacterIndex'] = "0" # Индекс последнего персонажа
config['realmName'] = "WoW Server" # Имя реалма
config['savedAccountList'] = f"{self.account_username.upper()}|{realmlist}" # Сохраненный список
config['lastSelectedAccount'] = self.account_username.upper() # Последний выбранный аккаунт
# Добавляем другие важные настройки если их нет
defaults = {
'locale': 'ruRU',
'readTOS': '1',
'readEULA': '1',
'readTerminationWithoutNotice': '1',
'accounttype': 'LK',
'lastSelectedRealm': '1', # Индекс последнего выбранного реалма
'realmList': realmlist, # Адрес сервера
'patchlist': f"'{realmlist}'", # Адрес сервера для патчей
'accountListType': "1", # Тип списка аккаунтов
'autoSelect': "1", # Автовыбор аккаунта
'autoConnect': "1" # Автоподключение
}
for key, value in defaults.items():
if key not in config:
config[key] = value
# Записываем обновленный конфиг
with open(config_path, 'w', encoding='utf-8') as f:
for key, value in config.items():
# Если значение похоже на число, записываем без кавычек
if value.replace('.', '').isdigit():
f.write(f'SET {key} {value}\n')
else:
f.write(f'SET {key} "{value}"\n')
return True
except Exception as e:
self.logger.error(f"Error updating Config.wtf: {e}")
return False
def set_account_info(self, username: str, account_id: int):
"""Устанавливает данные аккаунта для автологина"""
self.account_username = username
self.account_id = account_id
def launch_game(self) -> bool:
"""Запускает игру с заданными параметрами"""
try:
game_path = self.settings.get('game', {}).get('path', '')
if not game_path or not self.validate_game_path(game_path):
self.logger.error("Invalid game path")
return False
# Обновляем realmlist
realmlist = self.settings.get('game', {}).get('realmlist', 'logon.server.com')
if not self.update_realmlist(game_path, realmlist):
return False
# Обновляем Config.wtf для автологина
if self.account_username and not self.update_config_wtf(game_path):
return False
# Формируем путь к исполняемому файлу
exe_path = str(Path(game_path) / 'Wow.exe')
# Формируем параметры запуска
launch_options = self.settings.get('game', {}).get('launch_options', '').split()
# Добавляем параметры графики
graphics = self.settings.get('graphics', {})
if graphics.get('windowed', False):
launch_options.append('-windowed')
resolution = graphics.get('resolution', '1920x1080')
if resolution:
width, height = resolution.split('x')
launch_options.extend(['-width', width, '-height', height])
# Запускаем процесс
if self.platform == 'linux':
try:
runner = self.settings.get('game', {}).get('runner', 'wine')
if runner == 'portproton':
cmd = ['portproton', exe_path] + launch_options
elif runner == 'wine':
cmd = ['wine', exe_path] + launch_options
elif runner == 'lutris':
cmd = ['lutris', 'rungame', exe_path] + launch_options
elif runner == 'proton':
cmd = ['proton', 'run', exe_path] + launch_options
elif runner == 'crossover':
cmd = ['crossover', exe_path] + launch_options
else:
raise RuntimeError(f"Неизвестный эмулятор: {runner}")
# Добавляем переменные окружения для Wine
env = os.environ.copy()
if self.settings.get('game', {}).get('wineprefix'):
env['WINEPREFIX'] = self.settings['game']['wineprefix']
env['WINEARCH'] = 'win32'
# Запускаем процесс
Popen(cmd, env=env)
except Exception as e:
self.logger.error(f"Error launching with Wine: {e}")
return False
elif self.platform == 'darwin':
Popen(['open', exe_path, '--args'] + launch_options)
else:
Popen([exe_path] + launch_options)
return True
except Exception as e:
self.logger.error(f"Error launching game: {e}")
return False