OpenModLauncher-Base/lib/downloader.js

147 lines
5.0 KiB
JavaScript

const path = require("node:path");
const fs = require("node:fs");
const fsPromises = require("node:fs/promises");
const httpsWrapper = require("./util/https.js");
const utilities = require("./util/functions.js");
let maxConcurrentDownloads = 16;
let downloadQueue = [];
let totalDownloads = 0;
let isDownloading = false;
let ongoingDownloads = 0;
function waitUntilDownloadsFinish() {
return new Promise(async function (resolve, reject) {
var downloadCountChecker = setInterval(function () {
if (ongoingDownloads < 1 && downloadQueue.length < 1 && !isDownloading) {
resolve(clearInterval(downloadCountChecker));
}
}, 256);
});
}
module.exports = {
"download_file": function (url, filePath) {
return new Promise(async function (resolve, reject) {
var directory = path.dirname(filePath);
if (!(await fs.existsSync(directory))) await fsPromises.mkdir(directory, {"recursive": true});
var writeStream = fs.createWriteStream(filePath);
writeStream.on("finish", resolve);
httpsWrapper.getStream(url).then(function (response) {
response.pipe(writeStream);
}).catch(reject);
});
},
"process_queue": function (progressCB) {
return new Promise(async function (resolve, reject) {
totalDownloads = downloadQueue.length;
if (downloadQueue.length > 0) isDownloading = true;
if (progressCB) {
progressCB(0.0, 0, downloadQueue.length, totalDownloads);
}
while (downloadQueue.length > 0) {
if (ongoingDownloads < maxConcurrentDownloads) {
setImmediate(function (downloadData) {
module.exports.download_file(downloadData.url, downloadData.path).then(function () {
ongoingDownloads--;
if (downloadQueue.length < 1) {
isDownloading = false;
}
}).catch(function (error) {
ongoingDownloads--;
downloadQueue.push(downloadData);
});
}, downloadQueue.shift());
ongoingDownloads++;
let progress = (totalDownloads - (downloadQueue.length + ongoingDownloads)) / totalDownloads;
if (progressCB) {
progressCB(progress, ongoingDownloads, downloadQueue.length, totalDownloads);
}
} else {
await utilities.sleep(256);
}
}
waitUntilDownloadsFinish().then(function () {
if (progressCB) {
progressCB(1.0, 0, 0, totalDownloads);
}
resolve();
}).catch(reject);
});
},
"download_minecraft": function (directory, meta, manifestSource, progressCB) {
return new Promise(async function (resolve, reject) {
let gameManifest = JSON.parse(await httpsWrapper.get(manifestSource["versions_manifest"]));
let versionInManifest = gameManifest.versions.find(ver => ver.id == meta["version"]);
if (!versionInManifest) {
return reject(new Error("Minecraft version \"" + meta["version"] + "\" could not be found in given source. "));
}
if (!fs.existsSync(directory)) await fsPromises.mkdir(directory, {"recursive": true});
let versionDirectory = path.join(directory, "versions", meta["version"]);
if (!fs.existsSync(versionDirectory)) await fsPromises.mkdir(versionDirectory, {"recursive": true});
let versionManifest = JSON.parse(await httpsWrapper.get(versionInManifest.url));
fs.writeFileSync(path.join(versionDirectory, meta["version"] + ".json"), JSON.stringify(versionManifest, null, "\t"));
let librariesDirectory = path.join(directory, "libraries");
for (let l = 0; l < versionManifest.libraries.length; l++) {
let library = versionManifest.libraries[l];
if ("classifiers" in library.downloads) continue; // Attempt at skipping platform-specific code, hoping the game still starts.
let libraryFilePath = path.join(librariesDirectory, ...(library.downloads.artifact.path.split("/")));
downloadQueue.push({
"url": library.downloads.artifact.url,
"path": libraryFilePath
});
}
let assetsDirectory = path.join(directory, "assets");
let assetIndexesDirectory = path.join(assetsDirectory, "indexes");
if (!fs.existsSync(assetIndexesDirectory)) await fsPromises.mkdir(assetIndexesDirectory, {"recursive": true});
let assetIndexPath = path.join(assetIndexesDirectory, versionManifest.assetIndex.id + ".json");
let assetIndex = JSON.parse(await httpsWrapper.get(versionManifest.assetIndex.url));
fs.writeFileSync(assetIndexPath, JSON.stringify(assetIndex, null, "\t"));
let assetObjectsDirectory = path.join(assetsDirectory, "objects");
for (const assetLocation in assetIndex.objects) {
if (assetIndex.objects.hasOwnProperty(assetLocation)) {
let asset = assetIndex.objects[assetLocation];
let assetPath = [
asset.hash.substring(0, 2), asset.hash
];
downloadQueue.push({
"url": manifestSource["asset_base"] + assetPath.join("/"),
"path": path.join(assetObjectsDirectory, ...assetPath)
});
}
}
downloadQueue.push({
"url": versionManifest.downloads.client.url,
"path": path.join(versionDirectory, meta["version"] + ".jar")
});
if (!isDownloading) await module.exports.process_queue(progressCB);
resolve(true);
});
}
};