147 lines
5.0 KiB
JavaScript
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);
|
|
});
|
|
}
|
|
};
|