diff --git a/src/main/minecraft-launcher.ts b/src/main/minecraft-launcher.ts index ce8d517..f3e45d1 100644 --- a/src/main/minecraft-launcher.ts +++ b/src/main/minecraft-launcher.ts @@ -481,40 +481,66 @@ export function initMinecraftHandlers() { memory = 4096, baseVersion = '1.21.4', fabricVersion = '0.16.14', - packName = 'Comfort', // Название основной сборки - versionToLaunchOverride = '', // Возможность переопределить версию для запуска + packName = 'Comfort', // имя сборки (папка с модами) + versionToLaunchOverride = '', // переопределение версии для запуска (например, 1.21.10 для ванили) serverIp = 'popa-popa.ru', - serverPort, // опциональный порт + serverPort, + isVanillaVersion = false, } = gameConfig || {}; const appPath = path.dirname(app.getPath('exe')); const minecraftDir = path.join(appPath, '.minecraft'); const versionsDir = path.join(minecraftDir, 'versions'); + fs.mkdirSync(versionsDir, { recursive: true }); + + // gamePath: + // - ваниль → .minecraft + // - модпак → .minecraft/versions/Comfort (или другое packName) + const packDir = isVanillaVersion + ? minecraftDir + : path.join(versionsDir, packName); + if (!fs.existsSync(packDir)) { + fs.mkdirSync(packDir, { recursive: true }); + } - // Определяем версию для запуска const versionsContents = fs.existsSync(versionsDir) ? fs.readdirSync(versionsDir) : []; console.log('Доступные версии:', versionsContents); - // Найти версию пакета, Fabric или базовую версию - let versionToLaunch = versionToLaunchOverride; + // --- Определяем базовую / фактическую версию --- + let effectiveBaseVersion = baseVersion; - console.log('fabric:', `${baseVersion}-fabric${fabricVersion}`); + // Для ванили считаем базовой именно ту, которую хотим запустить + if (isVanillaVersion && versionToLaunchOverride) { + effectiveBaseVersion = versionToLaunchOverride; + } + + let versionToLaunch: string | undefined = + versionToLaunchOverride || undefined; if (!versionToLaunch) { - if ( - versionsContents.includes(`${baseVersion}-fabric${fabricVersion}`) - ) { - versionToLaunch = `${baseVersion}-fabric${fabricVersion}`; - } else if (versionsContents.includes(packName)) { - versionToLaunch = packName; + if (isVanillaVersion) { + // Ваниль — запускаем baseVersion (или override) + versionToLaunch = effectiveBaseVersion; } else { - versionToLaunch = baseVersion; + // Модпак — запускаем именно fabric-версию + const fabricId = `${effectiveBaseVersion}-fabric${fabricVersion}`; + if (versionsContents.includes(fabricId)) { + versionToLaunch = fabricId; + } else { + // даже если папки нет — installFabric её создаст + versionToLaunch = fabricId; + } } } - console.log('Запускаем версию:', versionToLaunch); + console.log('isVanillaVersion:', isVanillaVersion); + console.log('baseVersion:', baseVersion); + console.log('effectiveBaseVersion:', effectiveBaseVersion); + console.log('fabricVersion:', fabricVersion); + console.log('versionToLaunch:', versionToLaunch); + console.log('packDir:', packDir); // --- Поиск Java --- event.sender.send('installation-status', { @@ -524,23 +550,24 @@ export function initMinecraftHandlers() { console.log('Поиск Java...'); - let javaPath: string; + let javaPath = 'java'; try { javaPath = await findJava(); } catch (error) { console.warn('Ошибка при поиске Java:', error); event.sender.send('installation-status', { step: 'java-error', - message: 'Не удалось найти Java. Используем системную Java.', + message: + 'Не удалось найти Java. Попробуем запустить с системной Java.', }); - javaPath = 'java'; } - // --- Установка Minecraft / Fabric / зависимостей --- + console.log('Используем Java:', javaPath); + + // --- 1. Установка ванильного Minecraft (effectiveBaseVersion) --- let resolvedVersion: any; try { - // 1. Получаем список версий и устанавливаем ванильный Minecraft event.sender.send('installation-status', { step: 'minecraft-list', message: 'Получение списка версий Minecraft...', @@ -550,22 +577,13 @@ export function initMinecraftHandlers() { const versionList = await getVersionList(); const minecraftVersion = versionList.versions.find( - (v) => v.id === baseVersion, + (v) => v.id === effectiveBaseVersion, ); console.log('minecraftVersion:', minecraftVersion); if (minecraftVersion) { - // Устанавливаем базовую версию Minecraft - event.sender.send('installation-status', { - step: 'minecraft-install', - message: `Установка Minecraft ${baseVersion}...`, - }); - - console.log('Установка Minecraft...'); - const installMcTask = installTask(minecraftVersion, minecraftDir, { - // немного уменьшаем агрессию загрузчика skipRevalidate: true, assetsDownloadConcurrency: 2, librariesDownloadConcurrency: 2, @@ -573,6 +591,11 @@ export function initMinecraftHandlers() { console.log('installMcTask started for', minecraftVersion.id); + event.sender.send('installation-status', { + step: 'minecraft-install', + message: `Установка Minecraft ${minecraftVersion.id}...`, + }); + await installMcTask.startAndWait({ onFailed(task, error) { const stepName = (task as any).path || task.name || 'unknown'; @@ -588,113 +611,9 @@ export function initMinecraftHandlers() { }); }, }); - - // 2. Устанавливаем Fabric - console.log('Попытка установки Fabric:', { - minecraftVersion: baseVersion, - fabricVersion: fabricVersion, - minecraftDir: minecraftDir, - }); - - try { - event.sender.send('installation-status', { - step: 'fabric-list', - message: 'Получение списка версий Fabric...', - }); - - if (fabricVersion) { - event.sender.send('installation-status', { - step: 'fabric-install', - message: `Установка Fabric ${fabricVersion}...`, - }); - - console.log('installFabric:', { - minecraftVersion: baseVersion, - fabricVersion: fabricVersion, - minecraftDir: minecraftDir, - }); - - await installFabric({ - minecraftVersion: baseVersion, - version: fabricVersion, - minecraft: minecraftDir, - }); - } - } catch (error) { - console.log('Ошибка при установке Fabric, продолжаем:', error); - } - - // 3. Подготовка версии и установка зависимостей - try { - const fabricVersionId = `${baseVersion}-fabric${fabricVersion}`; - - console.log('version-parse:', fabricVersionId); - - event.sender.send('installation-status', { - step: 'version-parse', - message: 'Подготовка версии...', - }); - - console.log('version-parse:', { - minecraftDir: minecraftDir, - fabricVersionId: fabricVersionId, - }); - - resolvedVersion = await Version.parse( - minecraftDir, - fabricVersionId, - ); - - event.sender.send('installation-status', { - step: 'dependencies', - message: 'Установка библиотек и ресурсов...', - }); - - const depsTask = installDependenciesTask(resolvedVersion, { - // максимально душим параллельность - assetsDownloadConcurrency: 2, - librariesDownloadConcurrency: 2, - // общие оптимизации - skipRevalidate: true, - prevalidSizeOnly: true, - checksumValidatorResolver: () => ({ - validate: async () => { - // отключаем проверку хэшей, чтобы не падать от мелких расхождений - }, - }), - }); - - try { - await depsTask.startAndWait({ - onFailed(task, error) { - const stepName = (task as any).path || task.name || 'unknown'; - console.warn( - `[deps] step "${stepName}" failed: ${ - (error as any).code ?? '' - } ${(error as any).message}`, - ); - - event.sender.send('installation-status', { - step: `dependencies.${stepName}`, - message: `Ошибка: ${(error as any).message}`, - }); - }, - }); - } catch (error: any) { - console.log( - 'Ошибка при загрузке ресурсов, продолжаем запуск:', - error.message || error, - ); - } - } catch (error: any) { - console.log( - 'Ошибка при подготовке версии, продолжаем:', - error.message || error, - ); - } } else { console.warn( - `Версия ${baseVersion} не найдена в списке версий Minecraft`, + `Версия ${effectiveBaseVersion} не найдена в списке версий Minecraft. Предполагаем, что она уже установлена.`, ); } } catch (error: any) { @@ -702,12 +621,93 @@ export function initMinecraftHandlers() { const innerCount = Array.isArray(agg?.errors) ? agg.errors.length : 0; console.log( - 'Ошибка при установке Minecraft/Fabric/зависимостей, продолжаем:', + 'Ошибка при установке ванильного Minecraft, продолжаем:', agg?.message || String(agg), innerCount ? `(внутренних ошибок: ${innerCount})` : '', ); } + // --- 2. Установка Fabric (только для модпаков) --- + if (!isVanillaVersion && fabricVersion) { + try { + event.sender.send('installation-status', { + step: 'fabric-install', + message: `Установка Fabric ${fabricVersion}...`, + }); + + console.log('installFabric:', { + minecraftVersion: effectiveBaseVersion, + fabricVersion, + minecraftDir, + }); + + await installFabric({ + minecraftVersion: effectiveBaseVersion, + version: fabricVersion, + minecraft: minecraftDir, + }); + } catch (error) { + console.log('Ошибка при установке Fabric, продолжаем:', error); + } + } + + // --- 3. Установка зависимостей для versionToLaunch --- + try { + if (!versionToLaunch) { + throw new Error('versionToLaunch не определён'); + } + + console.log('version-parse:', { + minecraftDir, + versionToLaunch, + }); + + event.sender.send('installation-status', { + step: 'version-parse', + message: 'Подготовка версии...', + }); + + resolvedVersion = await Version.parse(minecraftDir, versionToLaunch); + + event.sender.send('installation-status', { + step: 'dependencies', + message: 'Установка библиотек и ресурсов...', + }); + + const depsTask = installDependenciesTask(resolvedVersion, { + skipRevalidate: true, + prevalidSizeOnly: true, + assetsDownloadConcurrency: 2, + librariesDownloadConcurrency: 2, + checksumValidatorResolver: () => ({ + async validate() { + // Игнорируем sha1, чтобы не падать из-за несоответствий + }, + }), + }); + + await depsTask.startAndWait({ + onFailed(task, error) { + const stepName = (task as any).path || task.name || 'unknown'; + console.warn( + `[deps] step "${stepName}" failed: ${ + (error as any).code ?? '' + } ${(error as any).message}`, + ); + + event.sender.send('installation-status', { + step: `dependencies.${stepName}`, + message: `Ошибка: ${(error as any).message}`, + }); + }, + }); + } catch (error: any) { + console.log( + 'Ошибка при подготовке версии/зависимостей, продолжаем запуск:', + error.message || error, + ); + } + // --- authlib-injector --- const authlibPath = await ensureAuthlibInjectorExists(appPath); console.log('authlibPath:', authlibPath); @@ -725,26 +725,17 @@ export function initMinecraftHandlers() { message: 'Запуск игры...', }); - const packDir = path.join(versionsDir, packName); - - const serverConfig: any = { ip: serverIp }; - if (serverPort) { - serverConfig.port = serverPort; - } - - console.log('packDir:', packDir); - const proc = await launch({ gamePath: packDir, resourcePath: minecraftDir, javaPath, - version: versionToLaunch, + version: versionToLaunch!, launcherName: 'popa-popa', extraJVMArgs: [ '-Dlog4j2.formatMsgNoLookups=true', `-javaagent:${authlibPath}=${API_BASE_URL}`, `-Xmx${memory}M`, - '-Dauthlibinjector.skinWhitelist=127.0.0.1,falrfg-213-87-196-173.ru.tuna.am', + '-Dauthlibinjector.skinWhitelist=https://minecraft.api.popa-popa.ru/', '-Dauthlibinjector.debug=verbose,authlib', '-Dauthlibinjector.legacySkinPolyfill=enabled', '-Dauthlibinjector.mojangAntiFeatures=disabled', @@ -842,28 +833,32 @@ export function initMinecraftHandlers() { for (const item of items) { const versionPath = path.join(versionsDir, item); - if (fs.statSync(versionPath).isDirectory()) { - // Проверяем, есть ли конфигурация для пакета - const versionJsonPath = path.join(versionPath, `${item}.json`); - let versionInfo = { - id: item, - name: item, - version: item, - }; + if (!fs.statSync(versionPath).isDirectory()) continue; - if (fs.existsSync(versionJsonPath)) { - try { - const versionData = JSON.parse( - fs.readFileSync(versionJsonPath, 'utf8'), - ); - versionInfo.version = versionData.id || item; - } catch (error) { - console.warn(`Ошибка при чтении файла версии ${item}:`, error); - } - } - - versions.push(versionInfo); + // ❗ Прячем технические fabric-версии типа 1.21.10-fabric0.18.1 + if (item.includes('-fabric')) { + continue; } + + const versionJsonPath = path.join(versionPath, `${item}.json`); + let versionInfo = { + id: item, + name: item, + version: item, + }; + + if (fs.existsSync(versionJsonPath)) { + try { + const versionData = JSON.parse( + fs.readFileSync(versionJsonPath, 'utf8'), + ); + versionInfo.version = versionData.id || item; + } catch (error) { + console.warn(`Ошибка при чтении файла версии ${item}:`, error); + } + } + + versions.push(versionInfo); } return { success: true, versions }; @@ -1117,9 +1112,9 @@ ipcMain.handle('get-version-config', async (event, { versionId }) => { versionFileName: `${versionId}_version.txt`, packName: versionId, memory: 4096, - baseVersion: '1.21.4', + baseVersion: '1.21.10', serverIp: 'popa-popa.ru', - fabricVersion: '0.16.14', + fabricVersion: '0.18.1', preserveFiles: ['popa-launcher-config.json'], }; @@ -1129,6 +1124,8 @@ ipcMain.handle('get-version-config', async (event, { versionId }) => { 'https://github.com/DIKER0K/Comfort/releases/latest/download/Comfort.zip'; config.apiReleaseUrl = 'https://api.github.com/repos/DIKER0K/Comfort/releases/latest'; + config.baseVersion = '1.21.10'; + config.fabricVersion = '0.18.1'; } // Если есть конфигурационный файл, загружаем из него diff --git a/src/renderer/pages/LaunchPage.tsx b/src/renderer/pages/LaunchPage.tsx index 9c86613..bfa867a 100644 --- a/src/renderer/pages/LaunchPage.tsx +++ b/src/renderer/pages/LaunchPage.tsx @@ -115,17 +115,21 @@ const LaunchPage = ({ const savedConfig = localStorage.getItem('selected_version_config'); if (savedConfig) { const parsedConfig = JSON.parse(savedConfig); - setVersionConfig(parsedConfig); - // Устанавливаем значения памяти и preserveFiles из конфигурации - setConfig({ - memory: parsedConfig.memory || 4096, - preserveFiles: parsedConfig.preserveFiles || [], - }); + // Если конфиг пустой — считаем, что он невалидный и идём по IPC-ветке + if (Object.keys(parsedConfig).length > 0) { + setVersionConfig(parsedConfig); - // Очищаем localStorage - localStorage.removeItem('selected_version_config'); - return; + setConfig({ + memory: parsedConfig.memory || 4096, + preserveFiles: parsedConfig.preserveFiles || [], + }); + + localStorage.removeItem('selected_version_config'); + return; + } else { + localStorage.removeItem('selected_version_config'); + } } // Если нет в localStorage, запрашиваем с сервера diff --git a/src/renderer/pages/VersionsExplorer.tsx b/src/renderer/pages/VersionsExplorer.tsx index 1b61960..899b45d 100644 --- a/src/renderer/pages/VersionsExplorer.tsx +++ b/src/renderer/pages/VersionsExplorer.tsx @@ -177,11 +177,17 @@ export const VersionsExplorer = () => { fetchVersions(); }, []); - const handleSelectVersion = (version: VersionInfo) => { - localStorage.setItem( - 'selected_version_config', - JSON.stringify(version.config || {}), - ); + const handleSelectVersion = (version: VersionInfo | AvailableVersionInfo) => { + const cfg: any = (version as any).config; + + if (cfg && (cfg.downloadUrl || cfg.apiReleaseUrl)) { + // Версия из Gist — у неё есть нормальный config + localStorage.setItem('selected_version_config', JSON.stringify(cfg)); + } else { + // Установленная версия без config — не засоряем localStorage пустым объектом + localStorage.removeItem('selected_version_config'); + } + navigate(`/launch/${version.id}`); }; @@ -240,7 +246,8 @@ export const VersionsExplorer = () => { justifyContent: 'center', alignItems: 'center', cursor: 'pointer', - filter: hoveredCardId && hoveredCardId !== 'add' ? 'blur(5px)' : 'blur(0)', + filter: + hoveredCardId && hoveredCardId !== 'add' ? 'blur(5px)' : 'blur(0)', transform: hoveredCardId === 'add' ? 'scale(1.03)' : 'scale(1)', zIndex: hoveredCardId === 'add' ? 10 : 1, '&:hover': {