const { app, BrowserWindow, ipcMain, dialog, shell } = require('electron') const path = require('node:path') const fs = require('fs') const fsp = require('fs').promises const Downloader = require("nodejs-file-downloader"); const { spawn, exec, execSync } = require('child_process'); let got; (async () => { got = (await import('got')).default; })(); let open; (async () => { open = (await import('open')).default; })(); const crypto = require('crypto'); var kill = require('tree-kill'); const { error } = require('node:console'); const Vdownloadurl = 'https://cdn.metamsk.ru'; const BaseMosAuthUrl = 'https://back.metamsk.ru'; const UI = path.join(__dirname, '/UI/'); const userdata_file = path.join(app.getPath('userData'), '/auth_data.json'); const crypto_key = '892jsb5aspoeirut'; const Vfldername = 'VDNH'; const ConfigFile = path.join(app.getPath('userData'), '/config.cfg'); const Vdownloadlist = '/md5test.txt'; const defaultprogramfilespath = 'C:/Program Files (x86)'; let LaunchedProcesses = []; let gamedownloaded = false; let authtoken = ''; let VDNHFiles = app.getPath('userData'); // Проверяем, существует ли директория if (fs.existsSync(defaultprogramfilespath)) { // Если директория существует, используем ее VDNHFiles = defaultprogramfilespath; } else { // Если директория не существует, используем стандартный путь userData VDNHFiles = app.getPath('userData'); } let VDNHUserSelectFiles = VDNHFiles; let gamequality = 'Preset'; let totalloadingpercent = 0; let currentDownloadStream = null; let currentWriteStream = null; // Создает главное окно приложения const createWindow = () => { win = new BrowserWindow({ title: 'ВДНХ Лаунчер', icon: UI + '/icons/Logo.ico', width: 930, height: 600, minWidth: 930, minHeight: 600, maxWidth: 930, maxHeight: 600, resizable: false, frame: false, webPreferences: { webSecurity: true, contextIsolation: true, nodeIntegration: false, webviewTag: true, preload: path.join(__dirname, 'preload.js') } }) win.loadFile(UI + "index.html") win.on('close', (e) => { // Предотвращаем немедленное закрытие e.preventDefault(); //closeProcessForFile(path.join(VDNHUserSelectFiles, Vfldername, 'VDNHClient.exe')) //closeProcessForFile(path.join(VDNHUserSelectFiles, Vfldername, 'SignallingWebServer/platform_scripts/cmd/run_local.bat')) stopAllProcesses(); // Задержка перед закрытием setTimeout(() => { kill(process.pid, 'SIGKILL'); process.exit(); mainWindow = null; app.quit(); }, 1000); }); } app.whenReady().then(async () => { // Укажите путь к директории, где находится ваше приложение и обновляющее приложение const launcherLocation = path.join(__dirname).replace('\\resources\\app', ''); const launcherExe = "ВДНХ Лаунчер.exe"; // имя вашего основного приложения const launcherupdater = path.join(__dirname, 'updater', 'Проверка обновлений.exe'); // Запуск .exe приложения с правами администратора и с указанными аргументами console.log(launcherLocation); console.log(launcherExe); console.log(launcherupdater); // Формирование команды для запуска const command = `powershell -Command "Start-Process -FilePath '${launcherupdater}' -ArgumentList '-launcherlocation', '\\"${launcherLocation}\\"', '-launch', '\\"${launcherExe}\\"' -Verb runAs"`; console.log(command) exec(command, (error, stdout, stderr) => { if (error) { console.error(`Error launching application: ${error.message}`); return; } if (stderr) { console.error(`Application stderr: ${stderr}`); return; } console.log(`Application stdout: ${stdout}`); }); createWindow() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow() } }) }) // Выбор директории для загрузки файлов async function selectDirectory(defaultPath) { let options = { properties: ['openDirectory'], modal: true } let normalizedDefaultPath = path.normalize(defaultPath); if (normalizedDefaultPath && fs.existsSync(normalizedDefaultPath)) { options.defaultPath = normalizedDefaultPath; console.log("YES", normalizedDefaultPath); } let parentWindow = new BrowserWindow({ title: 'ВДНХ Лаунчер', icon: UI + '/icons/Logo.ico', width: 1, height: 1, show: false, alwaysOnTop: true, center: true }) try { const result = await dialog.showOpenDialog(parentWindow, options) // Обработка результатов... if (!result.canceled && result.filePaths.length > 0) { return result.filePaths[0] } else { return false } } catch (error) { console.error("Error opening dialog:", error); } } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Чтение или запись строки в файл async function readOrWriteLine(writeMode, lineNum, filePath, content = '') { const dir = path.dirname(filePath) if (writeMode) { await fsp.mkdir(dir, { recursive: true }).catch(err => { console.error('Error creating directories:', err) return false }) } try { if (writeMode) { let data = await fsp.readFile(filePath, 'utf8').catch(() => '') let lines = data.split('\n') while (lines.length < lineNum) { lines.push('') } lines[lineNum - 1] = content await fsp.writeFile(filePath, lines.join('\n')) return true } else { if (!fs.existsSync(filePath)) { return false } let data = await fsp.readFile(filePath, 'utf8') let lines = data.split('\n') if (lineNum > lines.length || !lines[lineNum - 1].trim()) { return false } return lines[lineNum - 1] } } catch (err) { console.error('Error:', err) return false } } // Проверка и установка директории для загрузки async function checkAndSetDirectory(configFile) { let setUpDirectory = await readOrWriteLine(false, 2, configFile) if (setUpDirectory !== false) { try { await fsp.access(setUpDirectory, fs.constants.F_OK) VDNHUserSelectFiles = `${setUpDirectory}` console.log('Setup directory exists:', VDNHUserSelectFiles) } catch (err) { console.error('Setup directory does not exist:', setUpDirectory) } } else { console.log('No setup directory specified in the config file.') } } checkAndSetDirectory(ConfigFile) app.on('window-all-closed', () => { if (currentDownloadStream) { currentDownloadStream.destroy(new Error('Stop Download Sreams Exit App')); currentWriteStream.destroy(new Error('Stop Download Sreams Exit App')); currentDownloadStream = null; currentWriteStream = null; } stopAllProcesses(); if (process.platform !== 'darwin') { app.quit(); } }) // Открытие ссылки во внешнем браузере ipcMain.on('open-link', async (event, url) => { open(url); }); // Минимизация окна ipcMain.on('minimize-window', () => { win.minimize(); }); // Закрытие окна ipcMain.on('exit-window', async () => { if (currentDownloadStream) { currentDownloadStream.destroy(new Error('Stop Download Sreams Exit App')); currentWriteStream.destroy(new Error('Stop Download Sreams Exit App')); currentDownloadStream = null; // Сбросить ссылку на поток, так как он уже не активен currentWriteStream = null; } stopAllProcesses(); if (process.platform !== 'darwin') { app.quit() } }); ipcMain.on('SetGameQuality', async (event, quality) => { if (quality == 'Preset') { gamequality = quality; await readOrWriteLine(true, 4, ConfigFile, gamequality) } else { gamequality = `"${quality}"`; await readOrWriteLine(true, 4, ConfigFile, gamequality) } event.reply('GetGameQuality', gamequality) }); // Сохранение учетных данных пользователя ipcMain.on('save-account', async (event, login, password) => { const algorithm = 'aes-256-cbc'; const key = crypto.createHash('sha256').update(crypto_key).digest('base64').substr(0, 32); function encrypt(text) { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), iv); let encrypted = cipher.update(text); encrypted = Buffer.concat([encrypted, cipher.final()]); return { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex') }; } const encryptedData = encrypt(login + '[SEP]' + password); fs.writeFileSync(userdata_file, JSON.stringify(encryptedData)); }); // Загрузка учетных данных пользователя ipcMain.on('load-account', async (event) => { const algorithm = 'aes-256-cbc'; const key = crypto.createHash('sha256').update(crypto_key).digest('base64').substr(0, 32); function decrypt(encryptedData) { const iv = Buffer.from(encryptedData.iv, 'hex'); const encryptedText = Buffer.from(encryptedData.encryptedData, 'hex'); const decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), iv); let decrypted = decipher.update(encryptedText); decrypted = Buffer.concat([decrypted, decipher.final()]); return decrypted.toString(); } if (fs.existsSync(userdata_file)) { const data = fs.readFileSync(userdata_file, 'utf8'); const encryptedData = JSON.parse(data); const decryptedData = decrypt(encryptedData); const [login, password] = decryptedData.split('[SEP]'); event.reply('account-data', { login, password }); } }); // Сохранение токена авторизации ipcMain.on('save-auth-token', (event, token) => { authtoken = token; }); // Выход из учетной записи ipcMain.on('exit-account', () => { authtoken = ''; if (fs.existsSync(userdata_file)) { fs.unlinkSync(userdata_file); } }); let authwin; ipcMain.on('MosAuth', () => { if (authwin) return authwin.focus(); authwin = new BrowserWindow({ title: 'Авторизация', icon: UI + '/icons/Logo.ico', width: 400, height: 600, minWidth: 400, minHeight: 600, maxWidth: 400, maxHeight: 600, resizable: false, autoHideMenuBar: true, webPreferences: { webSecurity: true, contextIsolation: true, nodeIntegration: false, webviewTag: true, preload: path.join(__dirname, 'preload.js') } }) //authwin.removeMenu() fetch(BaseMosAuthUrl + '/client/oauth') .then((response) => { if (!response.ok) { throw new Error('Network response was not ok ' + response.statusText); } return response.json(); }) .then((data) => { if (data.url && typeof data.url === 'string') { authwin.loadURL(data.url); } else { throw new Error('Invalid URL in response'); } }) .catch((error) => { console.error('There has been a problem with your fetch operation:', error); }); // Событие для вывода URL при навигации на новую страницу authwin.webContents.on('did-navigate', (event, url) => { console.log('Navigated to:', url); if (url.startsWith("https://metamsk.ru")) { authwin.close(); fetch(url.replace('https://metamsk.ru/authorization?code=', BaseMosAuthUrl + '/client/authorization?code=')) .then((response) => response.json()) .then(async (data) => { if (data.token) { win.webContents.send('OnMosAuth'); } }) .catch((error) => { console.error('There has been a problem with your fetch operation:', error); }) } }); authwin.on('close', (e) => { authwin = null; }); }); // Получение данных лаунчера ipcMain.on('GetLauncherData', async (event) => { let gamedownloaded = await readOrWriteLine(false, 3, ConfigFile) let getgamequality = await readOrWriteLine(false, 4, ConfigFile) if (getgamequality !== false) { gamequality = getgamequality } event.reply('LauncherData', [gamedownloaded, VDNHUserSelectFiles, gamequality]); }); // Установка директории установки ipcMain.on('SetInstallDirectory', async (event) => { const selectedDirectory = await selectDirectory(VDNHUserSelectFiles); VDNHUserSelectFiles = selectedDirectory || VDNHFiles; await readOrWriteLine(true, 2, ConfigFile, VDNHUserSelectFiles); event.reply('GetInstallDirectory', VDNHUserSelectFiles); }); // Директория установки ipcMain.on('OpenInstallDirectory', async (event) => { await shell.openPath(path.join(VDNHUserSelectFiles, Vfldername)); }); // Проверить все файлы ipcMain.on('CheckFiles', async (event) => { if (win) { win.webContents.send('GetDownloadStatus', 'StartInstall'); } totalloadingpercent = 0; if (win) { win.webContents.send('finishcheck', {text:'Ожидайте начала проверки...', updatell: true}); } try { console.log(`${Vdownloadurl}${Vdownloadlist}`); const downloader = new Downloader({ url: `${Vdownloadurl}${Vdownloadlist}`, directory: path.join(VDNHUserSelectFiles, Vfldername), cloneFiles: false, }); const {filePath, downloadStatus} = await downloader.download(); console.log("Installed InstallReq"); async function getFilesInDirectory(dir) { const dirents = await fsp.readdir(dir, { withFileTypes: true }); const files = await Promise.all(dirents.map((dirent) => { const res = path.resolve(dir, dirent.name); return dirent.isDirectory() ? getFilesInDirectory(res) : res; })); return Array.prototype.concat(...files); } // Функция для удаления файлов, не входящих в список разрешенных async function cleanDirectory(allowedFiles, directory) { try { const filesInDirectory = await getFilesInDirectory(directory); for (const file of filesInDirectory) { if (!allowedFiles.includes(file)) { await fsp.unlink(file); console.log(`Deleted: ${file}`); } } } catch (err) { console.error('Error cleaning directory:', err); if (win) { win.webContents.send('GetDownloadStatus', 'EndInstall'); } throw err; } } try { let files = await parseFile(filePath); const allowedFiles = files.map(file => file.localpath); await cleanDirectory(allowedFiles, path.join(VDNHUserSelectFiles, Vfldername)); downloadFiles(files).then(() => { console.log('FINISH'); if (win) { win.webContents.send('finishcheck', {text:'Все файлы успешно установлены!'}); } if (win) { win.webContents.send('GetDownloadStatus', 'EndInstall'); } }).catch(error => { console.error('An error occurred:', error); }); } catch (error) { console.error("Error during file cleaning or downloading", error); } } catch (error) { console.error("Download failed", error); } }); //ОПЕРАЦИИ ПО УСТАНОВКИ ФАЙЛОВ ОПЕРАЦИИ ПО УСТАНОВКИ ФАЙЛОВ ОПЕРАЦИИ ПО УСТАНОВКИ ФАЙЛОВ ОПЕРАЦИИ ПО УСТАНОВКИ ФАЙЛОВ async function calculateMD5(filePath) { return new Promise((resolve, reject) => { const hash = crypto.createHash('md5'); const stream = fs.createReadStream(filePath); stream.on('data', (data) => { hash.update(data); }); stream.on('end', () => { const md5sum = hash.digest('hex'); resolve(md5sum); }); stream.on('error', (err) => { console.error(`Ошибка при вычислении MD5 для файла ${filePath}:`, err); reject(err); }); }); } // Функция для разбора файла async function parseFile(filePath) { try { const fileContent = await fsp.readFile(filePath, 'utf8'); const lines = fileContent.trim().split('\n'); const entries = lines.map((line, index) => { const parts = line.split('|'); if (parts.length !== 3) { throw new Error(`Invalid format in line ${index + 1}: ${line}`); } const sizeStr = parts[0]; const url = `${Vdownloadurl}${parts[2]}`; const localpath = path.join(VDNHUserSelectFiles, Vfldername, parts[2]); const hash = parts[1] const size = parseInt(sizeStr, 10); if (isNaN(size)) { throw new Error(`Invalid size in line ${index + 1}: ${sizeStr}`); } return { size, url, localpath, hash }; }); return entries; } catch (err) { console.error('Error processing file:', err); throw err; } } // Флаги для управления процессом загрузки let canceled = false; let paused = false; let attempt = 0; const maxAttempts = 10; let totalDownloadedBytes = 0; let lastdownloadBites = 0; let downloadspeed = 0; // Функция для загрузки файлов async function downloadFiles(files) { if (win) { win.webContents.send('GetDownloadStatus', 'Ожидайте окончания загрузки'); } totalDownloadedBytes = 0; const totalSizeBytes = files.reduce((total, file) => total + file.size, 0); for (const entry of files) { attempt = 0; if (canceled) { console.log('Download canceled'); canceled = false; throw new Error('Download canceled by user'); } if (paused) { throw new Error('Download paused by user'); } const fullPath = entry.localpath; const directory = path.dirname(fullPath); // Проверка существования файла и его размера if (fs.existsSync(fullPath)) { const fileStats = fs.statSync(fullPath); const fileSize = fileStats.size; win.webContents.send('GetDownload', { totalloadingpercent, FileName: `Проверка ${entry.url.split("/").pop()}`, FilePercent: '...', downloadspeed }); const filehash = await calculateMD5(fullPath) if (filehash == entry.hash && fileSize === entry.size && (fullPath !== (await readOrWriteLine(false, 1, ConfigFile)))) { console.log(`File ${fullPath} already exists with the correct size, skipping download.`); totalDownloadedBytes += entry.size; const totalPercentage = (totalDownloadedBytes / totalSizeBytes) * 100; totalloadingpercent = totalPercentage.toFixed(1); const filesep = entry.url.split("/").pop(); win.webContents.send('GetDownload', { totalloadingpercent, FileName: filesep, FilePercent: '100', downloadspeed }); continue; } else { console.log(`File ${fullPath} exists but size does not match or config mismatch, deleting and starting download.`); fs.unlinkSync(fullPath); } } else { console.log(`File ${fullPath} not found, starting download.`); } if (!fs.existsSync(directory)) { fs.mkdirSync(directory, { recursive: true }); } await readOrWriteLine(true, 1, ConfigFile, fullPath); console.log(`Starting download of ${entry.url} to ${fullPath}`); await DownloadHandler(entry, fullPath, totalSizeBytes); } console.log('All files processed.'); } // Обработчик загрузки файла async function DownloadHandler(entry, fullPath, totalSizeBytes) { try { await downloadfile(entry, fullPath, totalSizeBytes); console.log(`File ${entry.url} downloaded successfully.`); } catch (error) { console.error(`Failed to download file ${entry.url}:`, error); if (canceled) { console.error('Download canceled.'); return; } if (attempt < maxAttempts) { attempt++; console.error(`Attempt ${attempt} failed, retrying after 3 seconds...`); if (win) { win.webContents.send('GetDownloadStatus', `Установка на паузе`); } if (paused) return; if (win) { win.webContents.send('GetDownloadStatus', `Попытка восстановить соединение: ${attempt}/${maxAttempts}`); } if (canceled) { console.error('Download canceled.'); } else if (paused) { console.error('Download paused.'); } else { await delay(3000); // Убедитесь, что у вас есть функция задержки await DownloadHandler(entry, fullPath, totalSizeBytes); } } else { if (win) { win.webContents.send('GetDownloadStatus', 'DError'); } console.error('Max attempts reached, download failed'); throw error; } } } // Функция для загрузки файла async function downloadfile(entry, fullPath, totalSizeBytes) { await new Promise((resolve, reject) => { if (currentDownloadStream) { currentDownloadStream.destroy(new Error('Create New Download Stream')); } if (currentWriteStream) { currentWriteStream.destroy(new Error('Create New Write Stream')); } const downloadStream = got.stream(entry.url); currentDownloadStream = downloadStream; const fileWriterStream = fs.createWriteStream(fullPath); currentWriteStream = fileWriterStream; downloadStream .on('downloadProgress', ({ transferred }) => { const totalPercentage = ((totalDownloadedBytes + transferred) / totalSizeBytes) * 100; totalloadingpercent = totalPercentage.toFixed(1); console.log(`Downloading ${entry.url}: ${transferred}/${entry.size} (${totalloadingpercent}%)`); if (win && downloadStream) { const filesep = entry.url.split("/").pop(); const percent = ((transferred / entry.size) * 100).toFixed(1); win.webContents.send('GetDownload', { totalloadingpercent, FileName: filesep, FilePercent: percent, downloadspeed }); win.webContents.send('GetDownloadStatus', 'Ожидайте окончания загрузки'); } }) .on('error', (error) => { console.error(`Download failed for ${entry.url}: ${error.message}`); reject(error); }); fileWriterStream .on('error', (error) => { console.error(`Could not write file to system: ${error.message}`); reject(error); }) .on('finish', () => { totalDownloadedBytes += entry.size; console.log(`File downloaded to ${fullPath}`); resolve(); }); downloadStream.pipe(fileWriterStream); }); console.log(`File ${entry.url} downloaded successfully.`); } ipcMain.on('FastCheckFiles', async (event) => { if (win) { win.webContents.send('GetDownloadStatus', 'StartInstall'); } const gamedownloaded = await readOrWriteLine(false, 3, ConfigFile); if (gamedownloaded) { totalloadingpercent = 0; if (win) { win.webContents.send('finishcheck', { text: 'Ожидайте начала проверки...', updatell: true }); } try { console.log(`${Vdownloadurl}${Vdownloadlist}`); const downloader = new Downloader({ url: `${Vdownloadurl}${Vdownloadlist}`, directory: path.join(VDNHUserSelectFiles, Vfldername), cloneFiles: false, }); const { filePath } = await downloader.download(); console.log("Installed InstallReq"); async function getFilesInDirectory(dir) { const dirents = await fsp.readdir(dir, { withFileTypes: true }); const files = await Promise.all(dirents.map((dirent) => { const res = path.resolve(dir, dirent.name); return dirent.isDirectory() ? getFilesInDirectory(res) : res; })); return Array.prototype.concat(...files); } // Функция для удаления файлов, не входящих в список разрешенных async function cleanDirectory(allowedFiles, directory) { try { const filesInDirectory = await getFilesInDirectory(directory); for (const file of filesInDirectory) { if (!allowedFiles.includes(file)) { await fsp.unlink(file); console.log(`Deleted: ${file}`); } } } catch (err) { console.error('Error cleaning directory:', err); if (win) { win.webContents.send('GetDownloadStatus', 'EndInstall'); } throw err; } } try { const files = await parseFile(filePath); const allowedFiles = files.map(file => file.localpath); await cleanDirectory(allowedFiles, path.join(VDNHUserSelectFiles, Vfldername)); await downloadFiles(files); console.log('FINISH'); if (win) { win.webContents.send('finishcheck', { text: 'Все файлы успешно установлены!' }); } if (win) { win.webContents.send('GetDownloadStatus', 'EndInstall'); } } catch (error) { console.error("Error during file cleaning or downloading", error); } } catch (error) { console.error("Download failed", error); } } }); //ОПЕРАЦИИ ПО УСТАНОВКИ ФАЙЛОВ ОПЕРАЦИИ ПО УСТАНОВКИ ФАЙЛОВ ОПЕРАЦИИ ПО УСТАНОВКИ ФАЙЛОВ ОПЕРАЦИИ ПО УСТАНОВКИ ФАЙЛОВ // Обработчик события для запуска загрузки ipcMain.on('StartDownload', async (event) => { if (win) { win.webContents.send('GetDownloadStatus', 'StartInstall'); } attempt = 0; totalloadingpercent = 0; paused = false; canceled = false; try { console.log(`${Vdownloadurl}${Vdownloadlist}`); const downloader = new Downloader({ url: `${Vdownloadurl}${Vdownloadlist}`, directory: path.join(VDNHUserSelectFiles, Vfldername), cloneFiles: false, }); const {filePath, downloadStatus} = await downloader.download(); console.log("Installed InstallReq"); async function getFilesInDirectory(dir) { const dirents = await fsp.readdir(dir, { withFileTypes: true }); const files = await Promise.all(dirents.map((dirent) => { const res = path.resolve(dir, dirent.name); return dirent.isDirectory() ? getFilesInDirectory(res) : res; })); return Array.prototype.concat(...files); } // Функция для удаления файлов, не входящих в список разрешенных async function cleanDirectory(allowedFiles, directory) { try { const filesInDirectory = await getFilesInDirectory(directory); for (const file of filesInDirectory) { if (!allowedFiles.includes(file)) { await fsp.unlink(file); console.log(`Deleted: ${file}`); } } } catch (err) { console.error('Error cleaning directory:', err); throw err; } } try { let files = await parseFile(filePath); const allowedFiles = files.map(file => file.localpath); await cleanDirectory(allowedFiles, path.join(VDNHUserSelectFiles, Vfldername)); downloadFiles(files).then(() => { console.log('FINISH'); if (win) { win.webContents.send('finishinstall'); if (win) { win.webContents.send('GetDownloadStatus', 'Загрузка прошла успешно!'); } if (win) { win.webContents.send('GetDownloadStatus', 'EndInstall'); } } readOrWriteLine(true, 3, ConfigFile, "true"); }).catch(error => { if (win) { if (!error.message.includes("Download paused by user")) { if (error.message !== 'Download canceled by user') { win.webContents.send('GetDownloadStatus', 'DError'); } } } console.error('An error occurred:', error); }); } catch (error) { console.error("Error during file cleaning or downloading", error); if (win) { win.webContents.send('GetDownloadStatus', 'EndInstall'); } } } catch (error) { if (win) { if (!error.message.includes("Download paused by user")) { if (error.message !== 'Download canceled by user') { win.webContents.send('GetDownloadStatus', 'DError'); } } } console.error("Download failed", error); } }); ipcMain.on('CancelDownload', async (event) => { canceled = true; if (currentDownloadStream) { currentDownloadStream.destroy(new Error('Download canceled by user')); currentWriteStream.destroy(new Error('Download canceled by user')); currentDownloadStream = null; currentWriteStream = null; } await readOrWriteLine(true, 1, ConfigFile, ''); const dirPath = path.join(VDNHUserSelectFiles, Vfldername); try { await fsp.rm(dirPath, { recursive: true, force: true }); console.log(`Directory ${dirPath} was deleted successfully.`); } catch (error) { console.error(`Error deleting the directory ${dirPath}:`, error); } }); ipcMain.on('PauseDownload', async (event) => { paused = true; if (currentDownloadStream) { currentDownloadStream.destroy(new Error('Download canceled by user')); currentWriteStream.destroy(new Error('Download canceled by user')); currentDownloadStream = null; currentWriteStream = null; } }); ipcMain.on('StartGame', async (event) => { console.log("STARTGAME"); event.reply('gameprocess', 'Start'); open('https://metamsk.ru/'); const clientArgs = [ `-Quality=${gamequality}`, '-DisableAllscreenMessages', '-PixelStreamingIP=127.0.0.1', '-PixelStreamingPort=8888', '-ResX=1920', '-ResY=1080', '-ForceRes', '-RenderOffscreen', '-AudioMixer' ]; const appsToRun = [ { path: path.join(VDNHUserSelectFiles, Vfldername, 'VDNHClient.exe'), args: clientArgs }, { path: path.join(VDNHUserSelectFiles, Vfldername, 'SignallingWebServer/platform_scripts/cmd/run_local.bat'), args: [] // Нет дополнительных параметров для run_local.bat } ]; appsToRun.forEach(app => { // Объединяем аргументы в одну строку, правильно обрабатывая пробелы в путях const command = `"${path.normalize(app.path)}" ${app.args.join(' ')}`; console.log(command) const child = spawn(command, { detached: false, stdio: 'ignore', shell: true }); child.unref(); // Добавляем объект процесса в массив LaunchedProcesses.push({ pid: child.pid, process: child, exe: path.normalize(app.path).split("\\").pop() // Используем "\\" в качестве разделителя }); // Обработчик для удаления процесса из массива при его завершении child.on('exit', (code) => { console.log(`Process with PID=${child.pid} exited with code ${code}`); LaunchedProcesses = LaunchedProcesses.filter(p => p.pid !== child.pid); console.log(`process remove from massive PID=${child.pid}`); // Проверяем, стал ли массив пустым после удаления процесса if (LaunchedProcesses.length === 0) { console.log('all processes was finished'); // Отправляем ответ обратно в рендерер-процесс event.reply('gameprocess', 'End'); } }); child.on('error', (err) => { console.error(`Ошибка в дочернем процессе с PID=${child.pid}:`, err); }); }); }); ipcMain.on('StopAllProcesses', async (event) => { stopAllProcesses(); }); // Функция для завершения всех запущенных процессов function stopAllProcesses() { //closeProcessForFile(path.join(VDNHUserSelectFiles, Vfldername, 'VDNHClient.exe')) //closeProcessForFile(path.join(VDNHUserSelectFiles, Vfldername, 'SignallingWebServer/platform_scripts/cmd/run_local.bat')) LaunchedProcesses.forEach(childProcess => { console.log(childProcess.exe) kill(childProcess.pid, 'SIGKILL'); kill(childProcess.process.pid, 'SIGKILL'); exec(`taskkill /IM ${childProcess.exe} /F`, (error, stdout, stderr) => { if (error) { console.error(`exec error: ${error}`); return; } console.log(`stdout: ${stdout}`); console.error(`stderr: ${stderr}`); }); }); LaunchedProcesses.length = 0; // Очищаем массив после остановки всех процессов } process.on('exit', () => { console.log('Process exit event triggered'); stopAllProcesses(); }); process.on('SIGINT', () => { console.log('Process SIGINT event triggered'); stopAllProcesses(); }); process.on('SIGTERM', () => { console.log('Process SIGTERM event triggered'); stopAllProcesses(); }); process.on('SIGHUP', () => { console.log('Process SIGHUP event triggered'); stopAllProcesses(); }); // Быстрая проверка при скаченной игре (запуск) ipcMain.on('FastCheckFiles', async (event) => { // Проверка наличия окна приложения if (win) { win.webContents.send('GetDownloadStatus', 'StartInstall'); } // Чтение статуса загрузки игры let gamedownloaded = await readOrWriteLine(false, 3, ConfigFile); if (gamedownloaded) { totalloadingpercent = 0; if (win) { win.webContents.send('finishcheck', { text: 'Ожидайте начала проверки...', updatell: true }); } try { console.log(`${Vdownloadurl}${Vdownloadlist}`); // Создание загрузчика const downloader = new Downloader({ url: `${Vdownloadurl}${Vdownloadlist}`, directory: path.join(VDNHUserSelectFiles, Vfldername), cloneFiles: false, }); // Загрузка списка файлов const { filePath, downloadStatus } = await downloader.download(); console.log("Installed InstallReq"); // Функция для получения всех файлов в директории async function getFilesInDirectory(dir) { const dirents = await fsp.readdir(dir, { withFileTypes: true }); const files = await Promise.all(dirents.map((dirent) => { const res = path.resolve(dir, dirent.name); return dirent.isDirectory() ? getFilesInDirectory(res) : res; })); return Array.prototype.concat(...files); } // Функция для удаления файлов, не входящих в список разрешенных async function cleanDirectory(allowedFiles, directory) { try { const filesInDirectory = await getFilesInDirectory(directory); for (const file of filesInDirectory) { if (!allowedFiles.includes(file)) { await fsp.unlink(file); console.log(`Deleted: ${file}`); } } } catch (err) { console.error('Error cleaning directory:', err); if (win) { win.webContents.send('GetDownloadStatus', 'EndInstall'); } throw err; } } try { // Парсинг файла и получение списка разрешенных файлов let files = await parseFile(filePath); const allowedFiles = files.map(file => file.localpath); // Очистка директории от неразрешенных файлов await cleanDirectory(allowedFiles, path.join(VDNHUserSelectFiles, Vfldername)); // Загрузка разрешенных файлов downloadFiles(files).then(() => { console.log('FINISH'); if (win) { win.webContents.send('finishcheck', { text: 'Все файлы успешно установлены!' }); } if (win) { win.webContents.send('GetDownloadStatus', 'EndInstall'); } }).catch(error => { console.error('An error occurred:', error); }); } catch (error) { console.error("Error during file cleaning or downloading", error); } } catch (error) { console.error("Download failed", error); } } }); ipcMain.on('deletegame', async (event) => { canceled = true; if (currentDownloadStream) { currentDownloadStream.destroy(new Error('Download canceled by user')); currentWriteStream.destroy(new Error('Download canceled by user')); currentDownloadStream = null; currentWriteStream = null; } const dirPath = path.join(VDNHUserSelectFiles, Vfldername); try { await fsp.rm(dirPath, { recursive: true, force: true }); console.log(`Directory ${dirPath} was deleted successfully.`); await readOrWriteLine(true, 3, ConfigFile, ''); event.reply('deletegamecallback', 'Succes'); } catch (error) { event.reply('deletegamecallback', 'Error'); console.error(`Error deleting the directory ${dirPath}:`, error); } if (win) { win.webContents.send('GetDownloadStatus', 'EndInstall'); } }); // Функция для получения аргумента запуска const getTestArg = () => { const args = process.argv; const TestIndex = args.indexOf('-Test'); if (TestIndex !== -1 && args[TestIndex + 1]) { return args[TestIndex + 1]; } return null; }; ipcMain.on('OnTestSend', async (event) => { const launcherLocation = getTestArg(); if (launcherLocation) { console.log('Is Test'); event.reply('OnTest', true); } else { console.log('Not Test'); } }); function closeProcessForFile(file) { const fileExtension = path.extname(file).toLowerCase(); // Проверяем, является ли файл исполняемым (.exe, .bat, .cmd и т.п.) if (['.exe', '.bat', '.cmd'].includes(fileExtension)) { const fileName = `"${path.basename(file)}"`; // Оборачиваем имя файла в кавычки try { execSync(`taskkill /F /IM ${fileName}`); console.log(`Closed process for ${fileName}`); } catch (error) { if (error.message.includes("Не удается найти процесс") || error.message.includes("The process")) { console.log(`Process for ${fileName} was not found, continuing...`); } else { console.error(`Could not close process for ${fileName}: ${error.message}`); } } } else { console.log(`File ${file} is not an executable.`); } } const si = require('systeminformation'); // Переменные для хранения предыдущих значений let previousRx = 0; let previousTx = 0; let previousTime = Date.now(); // Функция для получения текущего трафика async function getNetworkTraffic() { try { const stats = await si.networkStats(); const currentTime = Date.now(); const currentRx = stats[0].rx_bytes; const currentTx = stats[0].tx_bytes; const rxDiff = currentRx - previousRx; const txDiff = currentTx - previousTx; const timeDiff = (currentTime - previousTime) / 1000; // в секундах const rxSpeedMBps = (rxDiff / (1024 * 1024)) / timeDiff; // в МБ/с const txSpeedMBps = (txDiff / (1024 * 1024)) / timeDiff; // в МБ/с console.log(`Download Speed: ${rxSpeedMBps.toFixed(2)} MB/s`); downloadspeed = rxSpeedMBps.toFixed(2); console.log(`Upload Speed: ${txSpeedMBps.toFixed(2)} MB/s`); // Обновление предыдущих значений для следующего вызова previousRx = currentRx; previousTx = currentTx; previousTime = currentTime; } catch (err) { console.error(err); } } // Запуск таймера для проверки каждые секунду setInterval(getNetworkTraffic, 1000);