Skip to content

Commit

Permalink
Merge pull request #13 from Proxwian/modrinth-support
Browse files Browse the repository at this point in the history
add modrinth support
  • Loading branch information
Proxwian authored May 2, 2024
2 parents 266b03a + e860835 commit 936f515
Show file tree
Hide file tree
Showing 29 changed files with 3,207 additions and 1,043 deletions.
3 changes: 3 additions & 0 deletions jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"include": ["src/**/*.js"]
}
21 changes: 18 additions & 3 deletions src/app/desktop/components/Instances/Instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -477,14 +477,29 @@ const Instance = ({ instanceName }) => {
disabled={Boolean(isInQueue) || Boolean(isPlaying)}
onClick={async () => {
let manifest = null;
try {
const isCursePack = await fs
.stat(path.join(instancesPath, instanceName, 'manifest.json'))
.then(() => true)
.catch(() => false);

if (isCursePack) {
// CurseForge
manifest = JSON.parse(
await fs.readFile(
path.join(instancesPath, instanceName, 'manifest.json')
)
);
} catch {
// NO-OP
} else {
// Modrinth
manifest = JSON.parse(
await fs.readFile(
path.join(
instancesPath,
instanceName,
'modrinth.index.json'
)
)
);
}

dispatch(
Expand Down
105 changes: 105 additions & 0 deletions src/app/desktop/utils/downloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,111 @@ const downloadFileInstance = async (fileName, url, sha1, legacyPath) => {
}
};

/**
* @param {{ path: string, hashes: { sha1: string, sha512: string }, downloads: string[] }[]} files
* @param {string} instancePath
* @param {number} updatePercentage
* @param {number} threads
*/
export const downloadInstanceFilesWithFallbacks = async (
files,
instancePath,
updatePercentage,
threads = 4
) => {
let downloaded = 0;
await pMap(
files,
async file => {
let counter = 0;
let res = false;
do {
counter += 1;
if (counter !== 1) {
await new Promise(resolve => setTimeout(resolve, 5000));
}

try {
res = await downloadFileInstanceWithFallbacks(file, instancePath);
} catch {
// Do nothing
}
} while (!res && counter < 3);
downloaded += 1;
if (
updatePercentage &&
(downloaded % 5 === 0 || downloaded === files.length)
) {
updatePercentage(downloaded);
}
},
{ concurrency: threads }
);
};

/**
* @param {{ path: string, hashes: { [algo: string]: string }, downloads: string[] }[]} file
* @param {string} instancePath
*/
const downloadFileInstanceWithFallbacks = async (file, instancePath) => {
const filePath = path.join(instancePath, file.path);
const dirPath = path.dirname(filePath);
try {
await fs.access(filePath);

let allChecksumsMatch = false;
for (const algo of Object.keys(file.hashes)) {
const checksum = await computeFileHash(filePath, algo);
if (file.hashes[algo] === checksum) {
allChecksumsMatch = true;
}
}
if (allChecksumsMatch) {
// the file already exists on disk, skip it
return true;
}
} catch {
await makeDir(dirPath);
}

// this loop exits as soon as a download has been successful
for (const url of file.downloads) {
const encodedUrl = getUri(url);
try {
const { data } = await axios.get(encodedUrl, {
responseType: 'stream',
responseEncoding: null,
adapter,
timeout: 60000 * 20
});

const wStream = fss.createWriteStream(filePath, {
encoding: null
});

data.pipe(wStream);

await new Promise((resolve, reject) => {
data.on('error', err => {
console.error(err);
reject(err);
});

data.on('end', () => {
wStream.end();
resolve();
});
});

return true;
} catch (e) {
console.error(
`Error while downloading <${url} | ${encodedUrl}> to <${file.path}> --> ${e.message}`
);
}
}
};

export const downloadFile = async (fileName, url, onProgress) => {
await makeDir(path.dirname(fileName));

Expand Down
78 changes: 47 additions & 31 deletions src/app/desktop/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { promises as fs } from 'fs';
import originalFs from 'original-fs';
import fse from 'fs-extra';
import { extractFull } from 'node-7z';
import * as Seven from 'node-7z';
import jimp from 'jimp';
import makeDir from 'make-dir';
import { promisify } from 'util';
Expand All @@ -20,7 +20,6 @@ import {
} from '../../../common/utils/constants';

import {
addQuotes,
removeDuplicates,
sortByForgeVersionDesc
} from '../../../common/utils';
Expand Down Expand Up @@ -450,14 +449,46 @@ export const get7zPath = async () => {

get7zPath();

export const extract = async (source, destination, args = {}, funcs = {}) => {
const sevenZipPath = await get7zPath();
const extraction = Seven.extract(source, destination, {
...args,
yes: true,
$bin: sevenZipPath,
$spawnOptions: { shell: true }
});
let extractedParentDir = null;
await new Promise((resolve, reject) => {
if (funcs.progress) {
extraction.on('progress', ({ percent }) => {
funcs.progress(percent);
});
}
extraction.on('data', data => {
if (!extractedParentDir) {
[extractedParentDir] = data.file.split('/');
}
});
extraction.on('end', () => {
funcs.end?.();
resolve(extractedParentDir);
});
extraction.on('error', err => {
funcs.error?.();
reject(err);
});
});
return { extraction, extractedParentDir };
};

export const extractAll = async (
source,
destination,
args = {},
funcs = {}
) => {
const sevenZipPath = await get7zPath();
const extraction = extractFull(source, destination, {
const extraction = Seven.extractFull(source, destination, {
...args,
yes: true,
$bin: sevenZipPath,
Expand Down Expand Up @@ -539,14 +570,13 @@ export const getJVMArguments112 = (
hideAccessToken,
jvmOptions = []
) => {
const needsQuote = process.platform !== 'win32';
const args = [];
args.push('-cp');

args.push(
[...libraries, mcjar]
.filter(l => !l.natives)
.map(l => `${addQuotes(needsQuote, l.path)}`)
.map(l => `"${l.path}"`)
.join(process.platform === 'win32' ? ';' : ':')
);

Expand All @@ -562,15 +592,8 @@ export const getJVMArguments112 = (
args.push('-javaagent:authlib-inj.jar=oxlauncher.com')
if (javaArgs.length > 0) args.push(...javaArgs);
args.push(...jvmOptions);
args.push(
`-Djava.library.path=${addQuotes(
needsQuote,
path.join(instancePath, 'natives')
)}`
);
args.push(
`-Dminecraft.applet.TargetDirectory=${addQuotes(needsQuote, instancePath)}`
);
args.push(`-Djava.library.path="${path.join(instancePath, 'natives')}"`);
args.push(`-Dminecraft.applet.TargetDirectory="${instancePath}"`);
if (mcJson.logging) {
args.push(mcJson?.logging?.client?.argument || '');
}
Expand All @@ -592,13 +615,13 @@ export const getJVMArguments112 = (
val = mcJson.id;
break;
case 'game_directory':
val = `${addQuotes(needsQuote, instancePath)}`;
val = `"${instancePath}"`;
break;
case 'assets_root':
val = `${addQuotes(needsQuote, assetsPath)}`;
val = `"${assetsPath}"`;
break;
case 'game_assets':
val = `${path.join(assetsPath, 'virtual', 'legacy')}`;
val = `"${path.join(assetsPath, 'virtual', 'legacy')}"`;
break;
case 'assets_index_name':
val = mcJson.assets;
Expand Down Expand Up @@ -627,9 +650,6 @@ export const getJVMArguments112 = (
if (val != null) {
mcArgs[i] = val;
}
if (typeof args[i] === 'string' && !needsQuote) {
args[i] = args[i].replaceAll('"', '');
}
}
}

Expand Down Expand Up @@ -657,7 +677,6 @@ export const getJVMArguments113 = (
) => {
const argDiscovery = /\${*(.*)}/;
let args = mcJson.arguments.jvm.filter(v => !skipLibrary(v));
const needsQuote = process.platform !== 'win32';

const javaArgs = jvmOptions.filter(Boolean);
// if (process.platform === "darwin") {
Expand All @@ -667,9 +686,7 @@ export const getJVMArguments113 = (

args.push(`-Xmx${memory}m`);
args.push(`-Xms${memory}m`);
args.push(
`-Dminecraft.applet.TargetDirectory=${addQuotes(needsQuote, instancePath)}`
);
args.push(`-Dminecraft.applet.TargetDirectory="${instancePath}"`);
if (mcJson.logging) {
args.push(mcJson?.logging?.client?.argument || '');
}
Expand Down Expand Up @@ -697,9 +714,9 @@ export const getJVMArguments113 = (
for (let i = 0; i < args.length; i += 1) {
if (typeof args[i] === 'object' && args[i].rules) {
if (typeof args[i].value === 'string') {
args[i] = `${addQuotes(needsQuote, args[i].value)}`;
args[i] = `"${args[i].value}"`;
} else if (typeof args[i].value === 'object') {
args.splice(i, 1, ...args[i].value.map(v => `${v}`));
args.splice(i, 1, ...args[i].value.map(v => `"${v}"`));
}
i -= 1;
} else if (typeof args[i] === 'string') {
Expand All @@ -714,10 +731,10 @@ export const getJVMArguments113 = (
val = mcJson.id;
break;
case 'game_directory':
val = `${addQuotes(needsQuote, instancePath)}`;
val = `"${instancePath}"`;
break;
case 'assets_root':
val = `${addQuotes(needsQuote, assetsPath)}`;
val = `"${assetsPath}"`;
break;
case 'assets_index_name':
val = mcJson.assets;
Expand All @@ -743,7 +760,7 @@ export const getJVMArguments113 = (
case 'natives_directory':
val = args[i].replace(
argDiscovery,
`${addQuotes(needsQuote, path.join(instancePath, 'natives'))}`
`"${path.join(instancePath, 'natives')}"`
);
break;
case 'launcher_name':
Expand All @@ -755,7 +772,7 @@ export const getJVMArguments113 = (
case 'classpath':
val = [...libraries, mcjar]
.filter(l => !l.natives)
.map(l => `${addQuotes(needsQuote, l.path)}`)
.map(l => `"${l.path}"`)
.join(process.platform === 'win32' ? ';' : ':');
break;
default:
Expand All @@ -765,7 +782,6 @@ export const getJVMArguments113 = (
args[i] = val;
}
}
if (!needsQuote && args[i] != undefined) args[i] = args[i].replaceAll('"', '');
}
}

Expand Down
Loading

0 comments on commit 936f515

Please sign in to comment.