Write some info in README.md, design titlebar, make downloader and very basic instance checker
This commit is contained in:
		
							
								
								
									
										10
									
								
								lib/checker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								lib/checker.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
const path = require("node:path");
 | 
			
		||||
const fs = require("node:fs");
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
	"check_install": function (directory) {
 | 
			
		||||
		return new Promise(function (resolve, reject) {
 | 
			
		||||
			resolve(fs.existsSync(path.join(directory, "versions")));
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										146
									
								
								lib/downloader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								lib/downloader.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
			
		||||
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);
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										7
									
								
								lib/util/functions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								lib/util/functions.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
	"sleep": function (delay) {
 | 
			
		||||
		return new Promise(function (resolve, reject) {
 | 
			
		||||
			setTimeout(resolve, delay);
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										35
									
								
								lib/util/https.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/util/https.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
const https = require("https");
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
	"get": function (url) {
 | 
			
		||||
		return new Promise(function (resolve, reject) {
 | 
			
		||||
			var request = https.get(url, function (response) {
 | 
			
		||||
				if (response.statusCode != 200) return reject(new Error("Response status code wasn't 200. "));
 | 
			
		||||
 | 
			
		||||
				response.setEncoding("utf-8");
 | 
			
		||||
				let body = "";
 | 
			
		||||
 | 
			
		||||
				response.on("data", function (chunk) {
 | 
			
		||||
					body += chunk;
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
				response.on("end", function () {
 | 
			
		||||
					resolve(body);
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			request.on("error", reject);
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	"getStream": function (url) {
 | 
			
		||||
		return new Promise(function (resolve, reject) {
 | 
			
		||||
			var request = https.get(url, function (response) {
 | 
			
		||||
				if (response.statusCode != 200) return reject(new Error("Response status code wasn't 200"));
 | 
			
		||||
 | 
			
		||||
				resolve(response);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			request.on("error", reject);
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user