218 lines
10 KiB
Python
218 lines
10 KiB
Python
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 |