From 4d8c5ad7bfcbe934420495f7942b0694b21d9579 Mon Sep 17 00:00:00 2001 From: Davide Ceschia Date: Thu, 25 Apr 2019 14:02:04 +0200 Subject: [PATCH] v0.11.10-beta --- app/actions/downloadManager.js | 202 ++++++++++++++++++-------- app/components/DInstance/DInstance.js | 7 +- app/utils/MCLaunchCommand.js | 50 ++++--- app/utils/cursemeta.js | 9 ++ package.json | 2 +- 5 files changed, 184 insertions(+), 86 deletions(-) diff --git a/app/actions/downloadManager.js b/app/actions/downloadManager.js index fe771c5c4..a40e940c0 100644 --- a/app/actions/downloadManager.js +++ b/app/actions/downloadManager.js @@ -5,8 +5,7 @@ import axios from 'axios'; import makeDir from 'make-dir'; import fse, { outputFile } from 'fs-extra'; import { cpus } from 'os'; -import dirTree from 'directory-tree'; -import { spawn, exec } from 'child_process'; +import { exec } from 'child_process'; import jarAnalyzer from 'jarfile'; import log from 'electron-log'; import Promise from 'bluebird'; @@ -27,12 +26,21 @@ import { extractNatives, computeVanillaAndForgeLibraries } from '../utils/getMCFilesList'; -import { downloadMod, getModsList, createDoNotTouchFile } from '../utils/mods'; +import { downloadMod, createDoNotTouchFile } from '../utils/mods'; import { findJavaHome } from '../utils/javaHelpers'; import { arraify } from '../utils/strings'; import { copyAssetsToLegacy, copyAssetsToResources } from '../utils/assets'; -import { getAddonFile, getAddon } from '../utils/cursemeta'; -import { getForgeVersionJSON, checkForgeMeta, checkForgeDownloaded } from '../utils/forgeHelpers'; +import { + getAddonFile, + getAddon, + getAddonFileIDFromVersion +} from '../utils/cursemeta'; +import { + getForgeVersionJSON, + checkForgeMeta, + checkForgeDownloaded +} from '../utils/forgeHelpers'; +import { readConfig } from '../utils/instances'; export const START_DOWNLOAD = 'START_DOWNLOAD'; export const CLEAR_QUEUE = 'CLEAR_QUEUE'; @@ -41,7 +49,47 @@ export const DOWNLOAD_COMPLETED = 'DOWNLOAD_COMPLETED'; export const UPDATE_TOTAL_FILES_TO_DOWNLOAD = 'UPDATE_TOTAL_FILES_TO_DOWNLOAD'; export const UPDATE_PROGRESS = 'UPDATE_PROGRESS'; -export function addToQueue(pack, version, forgeVersion = null) { +export function repairInstance(pack) { + return async (dispatch, getState) => { + const { packCreator } = getState(); + const config = await readConfig(pack); + if (config.projectID) { + const fileID = await getAddonFileIDFromVersion( + config.projectID, + config.modpackVersion + ); + if (fileID) + dispatch(addCursePackToQueue(pack, config.projectID, fileID, true)); + else { + message.error('Could not repair'); + log.error( + 'Could not find fileID for', + pack, + config.projectID, + config.modpackVersion + ); + } + } else { + dispatch( + addToQueue( + pack, + config.version, + config.forgeVersion + ? config.forgeVersion.replace('forge-', '') + : null, + true + ) + ); + } + }; +} + +export function addToQueue( + pack, + version, + forgeVersion = null, + isRepair = false +) { return (dispatch, getState) => { const { downloadManager } = getState(); dispatch({ @@ -55,7 +103,7 @@ export function addToQueue(pack, version, forgeVersion = null) { type: START_DOWNLOAD, payload: pack }); - dispatch(downloadPack(pack)); + dispatch(downloadPack(pack, isRepair)); } }; } @@ -96,7 +144,7 @@ export function importTwitchProfile(pack, filePath) { }; } -export function addCursePackToQueue(pack, addonID, fileID) { +export function addCursePackToQueue(pack, addonID, fileID, isRepair = false) { return async (dispatch, getState) => { const { downloadManager } = getState(); const packURL = (await getAddonFile(addonID, fileID)).downloadUrl; @@ -105,7 +153,7 @@ export function addCursePackToQueue(pack, addonID, fileID) { 'temp', path.basename(packURL) ); - await downloadFile(tempPackPath, packURL, () => { }); + await downloadFile(tempPackPath, packURL, () => {}); await compressing.zip.uncompress( tempPackPath, path.join(INSTANCES_PATH, 'temp', pack) @@ -134,7 +182,7 @@ export function addCursePackToQueue(pack, addonID, fileID) { type: START_DOWNLOAD, payload: pack }); - dispatch(downloadPack(pack)); + dispatch(downloadPack(pack, isRepair)); } }; } @@ -156,12 +204,14 @@ export function clearQueue() { }; } -export function downloadPack(pack) { +export function downloadPack(pack, isRepair = false) { return async (dispatch, getState) => { const { downloadManager, packCreator } = getState(); const currPack = downloadManager.downloadQueue[pack]; let vnlJSON = null; try { + // If is repair, skip this and download it again + if (isRepair) throw new Error(); vnlJSON = JSON.parse( await promisify(fs.readFile)( path.join( @@ -194,8 +244,10 @@ export function downloadPack(pack) { const assets = await extractAssets(vnlJSON, pack); const mainJar = await extractMainJar(vnlJSON); - if (currPack.forgeVersion !== null) { + if (currPack.forgeVersion) { try { + // If is repair, skip this and download it again + if (isRepair) throw new Error(); forgeJSON = await checkForgeMeta(currPack.forgeVersion); await checkForgeDownloaded(forgeJSON.mavenVersionString); } catch (err) { @@ -206,27 +258,30 @@ export function downloadPack(pack) { ...arraify(forgeJSON.mavenVersionString) ); - await downloadFile( - forgeBinPath, - forgeJSON.downloadUrl, - p => { - dispatch({ - type: UPDATE_PROGRESS, - payload: { pack, percentage: ((p * 18) / 100).toFixed(0) } - }); - } - ); + await downloadFile(forgeBinPath, forgeJSON.downloadUrl, p => { + dispatch({ + type: UPDATE_PROGRESS, + payload: { pack, percentage: ((p * 18) / 100).toFixed(0) } + }); + }); - await outputFile(path.join( - META_PATH, - 'net.minecraftforge', - `forge-${currPack.forgeVersion}`, - `forge-${currPack.forgeVersion}.json` - ), JSON.stringify(forgeJSON)) + await outputFile( + path.join( + META_PATH, + 'net.minecraftforge', + `forge-${currPack.forgeVersion}`, + `forge-${currPack.forgeVersion}.json` + ), + JSON.stringify(forgeJSON) + ); } } - const libraries = await computeVanillaAndForgeLibraries(vnlJSON, forgeJSON, false); + const libraries = await computeVanillaAndForgeLibraries( + vnlJSON, + forgeJSON, + false + ); // This is the main config file for the instance await makeDir(path.join(PACKS_PATH, pack)); @@ -242,9 +297,9 @@ export function downloadPack(pack) { const legacyJavaFixer = vCompare(currPack.forgeVersion, '10.13.1.1217') === -1 ? { - url: GDL_LEGACYJAVAFIXER_MOD_URL, - path: path.join(PACKS_PATH, pack, 'mods', 'LJF.jar') - } + url: GDL_LEGACYJAVAFIXER_MOD_URL, + path: path.join(PACKS_PATH, pack, 'mods', 'LJF.jar') + } : null; // Here we work on the mods @@ -334,7 +389,7 @@ export function downloadPack(pack) { await downloadFile( path.join(PACKS_PATH, pack, 'thumbnail.png'), thumbnailURL, - () => { } + () => {} ); // Copy the thumbnail as icon @@ -344,22 +399,25 @@ export function downloadPack(pack) { ); } - await promisify(fs.writeFile)( - path.join(PACKS_PATH, pack, 'config.json'), - JSON.stringify({ - version: currPack.version, - forgeVersion: - currPack.forgeVersion === null - ? null - : `forge-${currPack.forgeVersion}`, - ...(currPack.addonID && { projectID: currPack.addonID }), - ...(modpackVersion && { modpackVersion }), - ...(thumbnailURL && { icon: 'icon.png' }), - timePlayed: 0, - mods: modsManifest, - overrideFiles: overrideFilesList - }) - ); + // Don't write a new config if it's repairing + if (!isRepair) { + await promisify(fs.writeFile)( + path.join(PACKS_PATH, pack, 'config.json'), + JSON.stringify({ + version: currPack.version, + forgeVersion: + currPack.forgeVersion === null + ? null + : `forge-${currPack.forgeVersion}`, + ...(currPack.addonID && { projectID: currPack.addonID }), + ...(modpackVersion && { modpackVersion }), + ...(thumbnailURL && { icon: 'icon.png' }), + timePlayed: 0, + mods: modsManifest, + overrideFiles: overrideFilesList + }) + ); + } const totalFiles = libraries.length + assets.length + mainJar.length; @@ -398,7 +456,8 @@ export function downloadPack(pack) { await extractNatives(libraries.filter(lib => 'natives' in lib), pack); // Finish forge patches >= 1.13 - const installProfileJson = JSON.parse(forgeJSON.installProfileJson); + const installProfileJson = + forgeJSON && JSON.parse(forgeJSON.installProfileJson); if (installProfileJson) { const { processors } = installProfileJson; const replaceIfPossible = arg => { @@ -406,41 +465,56 @@ export function downloadPack(pack) { if (installProfileJson.data[finalArg]) { // Handle special case if (finalArg === 'BINPATCH') { - return path.join( - INSTANCES_PATH, - 'libraries', - ...arraify(installProfileJson.path) - ).replace('.jar', "-clientdata.lzma") + return path + .join( + INSTANCES_PATH, + 'libraries', + ...arraify(installProfileJson.path) + ) + .replace('.jar', '-clientdata.lzma'); } // Return replaced string return installProfileJson.data[finalArg].client; } // Return original string (checking for MINECRAFT_JAR) return arg.replace('{MINECRAFT_JAR}', mainJar[0].path); - } + }; const computePathIfPossible = arg => { if (arg[0] === '[') { - return path.join(INSTANCES_PATH, 'libraries', arraify(arg.replace('[', '').replace(']', '')).join('/')); + return path.join( + INSTANCES_PATH, + 'libraries', + arraify(arg.replace('[', '').replace(']', '')).join('/') + ); } return arg; - } + }; const javaPath = await findJavaHome(); for (const p in processors) { - const filePath = path.join(INSTANCES_PATH, 'libraries', ...arraify(processors[p].jar)); + const filePath = path.join( + INSTANCES_PATH, + 'libraries', + ...arraify(processors[p].jar) + ); const args = processors[p].args .map(arg => replaceIfPossible(arg)) .map(arg => computePathIfPossible(arg)); - const classPaths = processors[p].classpath.map(cp => path.join(INSTANCES_PATH, 'libraries', arraify(cp).join('/'))); + const classPaths = processors[p].classpath.map(cp => + path.join(INSTANCES_PATH, 'libraries', arraify(cp).join('/')) + ); const jarFile = await promisify(jarAnalyzer.fetchJarAtPath)(filePath); - const mainClass = jarFile.valueForManifestEntry("Main-Class"); + const mainClass = jarFile.valueForManifestEntry('Main-Class'); const { stderr, stdout } = await promisify(exec)( - `"${javaPath}" -classpath "${filePath}${CLASSPATH_DIVIDER_CHAR}${classPaths.join(CLASSPATH_DIVIDER_CHAR)}" ${mainClass} ${args.join(' ')}` - , { maxBuffer: 10000000000 }) + `"${javaPath}" -classpath "${filePath}${CLASSPATH_DIVIDER_CHAR}${classPaths.join( + CLASSPATH_DIVIDER_CHAR + )}" ${mainClass} ${args.join(' ')}`, + { maxBuffer: 10000000000 } + ); - console.log(stderr, stdout) + console.log(stderr, stdout); } } diff --git a/app/components/DInstance/DInstance.js b/app/components/DInstance/DInstance.js index 62fea279b..f368cb16f 100644 --- a/app/components/DInstance/DInstance.js +++ b/app/components/DInstance/DInstance.js @@ -34,6 +34,7 @@ type Props = { selectedInstance: ?string, startInstance: () => void, selectInstance: () => void, + repairInstance: () => void, playing: array }; @@ -352,19 +353,19 @@ export default class DInstance extends Component { {' '} Export - {/* el.name === name) } - onClick={() => this.props.addToQueue(name, version, forgeVersion)} + onClick={() => this.props.repairInstance(name)} > {' '} Repair - */} + el.name === name) diff --git a/app/utils/MCLaunchCommand.js b/app/utils/MCLaunchCommand.js index e0c227213..2b039b131 100644 --- a/app/utils/MCLaunchCommand.js +++ b/app/utils/MCLaunchCommand.js @@ -5,7 +5,13 @@ import { promisify } from 'util'; import log from 'electron-log'; import { connect } from 'react-redux'; import { findJavaHome } from './javaHelpers'; -import { PACKS_PATH, INSTANCES_PATH, WINDOWS, META_PATH, CLASSPATH_DIVIDER_CHAR } from '../constants'; +import { + PACKS_PATH, + INSTANCES_PATH, + WINDOWS, + META_PATH, + CLASSPATH_DIVIDER_CHAR +} from '../constants'; import { computeVanillaAndForgeLibraries } from './getMCFilesList'; const getStartCommand = async (packName, userData, settings, javaArguments) => { @@ -53,9 +59,15 @@ const getStartCommand = async (packName, userData, settings, javaArguments) => { : ''; // It concatenates vanilla and forge libraries. If the instance does not contain forge, it concatenates an empty array const libs = await computeVanillaAndForgeLibraries(vanillaJSON, forgeJSON); - const Arguments = getMCArguments(vanillaJSON, JSON.parse(forgeJSON.versionJson), packName, userData); - const mainClass = - forge === null ? vanillaJSON.mainClass : JSON.parse(forgeJSON.versionJson).mainClass; + const Arguments = getMCArguments( + vanillaJSON, + forgeJSON && JSON.parse(forgeJSON.versionJson), + packName, + userData + ); + const mainClass = forge + ? JSON.parse(forgeJSON.versionJson).mainClass + : vanillaJSON.mainClass; const config = JSON.parse( await promisify(fs.readFile)(path.join(PACKS_PATH, packName, 'config.json')) @@ -63,7 +75,9 @@ const getStartCommand = async (packName, userData, settings, javaArguments) => { const completeCMD = ` -"${javaPath}" ${config.overrideArgs || javaArguments} -Xmx${instanceConfigJSON.overrideMemory || settings.java.memory}m ${dosName} -Djava.library.path="${path.join( +"${javaPath}" ${config.overrideArgs || + javaArguments} -Xmx${instanceConfigJSON.overrideMemory || + settings.java.memory}m ${dosName} -Djava.library.path="${path.join( PACKS_PATH, packName, 'natives' @@ -75,14 +89,14 @@ const getStartCommand = async (packName, userData, settings, javaArguments) => { `${vanillaJSON.id}.jar` )}" -cp ${libs - .filter(lib => !lib.natives) - .map(lib => `"${lib.path}"`) - .join(CLASSPATH_DIVIDER_CHAR)}${CLASSPATH_DIVIDER_CHAR}${`"${path.join( - INSTANCES_PATH, - 'versions', - vanillaJSON.id, - `${vanillaJSON.id}.jar` - )}"`} + .filter(lib => !lib.natives) + .map(lib => `"${lib.path}"`) + .join(CLASSPATH_DIVIDER_CHAR)}${CLASSPATH_DIVIDER_CHAR}${`"${path.join( + INSTANCES_PATH, + 'versions', + vanillaJSON.id, + `${vanillaJSON.id}.jar` + )}"`} ${mainClass} ${Arguments} `; // We need to hide the access token before printing it to the logs @@ -96,7 +110,7 @@ const getStartCommand = async (packName, userData, settings, javaArguments) => { const getMCArguments = (vanilla, forge, packName, userData) => { let Arguments = ''; - if (forge !== null && forge.minecraftArguments) { + if (forge && forge.minecraftArguments) { Arguments = forge.minecraftArguments; } else if (vanilla.minecraftArguments) { // Up to 1.13 @@ -107,9 +121,9 @@ const getMCArguments = (vanilla, forge, packName, userData) => { .filter(arg => typeof arg === 'string') .join(' '); - const forgeArguments = forge.arguments.game.filter( - arg => typeof arg === 'string' - ).join(' ') + const forgeArguments = forge + ? forge.arguments.game.filter(arg => typeof arg === 'string').join(' ') + : ''; Arguments = `${vanillaArguments} ${forgeArguments}`; } @@ -125,7 +139,7 @@ const getMCArguments = (vanilla, forge, packName, userData) => { vanilla.assets === 'legacy' ? '/virtual/legacy' : '' ) ) // Another check for really old versions - .replace('${version_name}', forge !== null ? forge.id : vanilla.id) + .replace('${version_name}', forge ? forge.id : vanilla.id) .replace( '${assets_root}', path.join( diff --git a/app/utils/cursemeta.js b/app/utils/cursemeta.js index d5ad949a4..fbc3c1462 100644 --- a/app/utils/cursemeta.js +++ b/app/utils/cursemeta.js @@ -39,6 +39,15 @@ export const getAddonFile = async ( return makeRequest(url); }; +export const getAddonFileIDFromVersion = async ( + addonID: number | string, + modpackVersion: string +) => { + const files = await getAddonFiles(addonID); + const foundID = files.find(a => a.fileNameOnDisk.includes(modpackVersion)); + return foundID ? foundID.id : null; +}; + export const getSearch = ( type: 'mods' | 'modpacks', searchFilter: string, diff --git a/package.json b/package.json index fe0535b9e..af83adc64 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gdlauncher", - "version": "0.11.10", + "version": "0.11.10-beta", "description": "GDLauncher is simple, yet powerful Minecraft custom launcher with a strong focus on the user experience", "keywords": [ "minecraft",