This commit is contained in:
Pierce 2019-05-22 18:28:48 -04:00
parent 736c554c69
commit 0d4bcfd6f6
7 changed files with 503 additions and 513 deletions

View file

@ -2,7 +2,6 @@ const request = require('request');
const uuid = require('uuid/v1');
const api_url = "https://authserver.mojang.com";
module.exports.getAuth = function (username, password) {
return new Promise(resolve => {
if(!password) {
@ -98,4 +97,40 @@ module.exports.refreshAuth = function (accessToken, clientToken, selectedProfile
resolve(userProfile);
});
});
};
module.exports.invalidate = function(accessToken, clientToken) {
return new Promise(resolve => {
const requestObject = {
url: api_url + "/invalidate",
json: {
"accessToken": accessToken,
"clientToken": clientToken
}
};
request.post(requestObject, function(error, response, body) {
if (error) resolve(error);
if(!body) resolve(true); else resolve(false);
});
});
};
module.exports.signOut = function(username, password) {
return new Promise(resolve => {
const requestObject = {
url: api_url + "/invalidate",
json: {
"username": username,
"password": password
}
};
request.post(requestObject, function(error, response, body) {
if (error) resolve(error);
if(!body) resolve(true); else resolve(false);
});
});
};

View file

@ -4,389 +4,366 @@ const path = require('path');
const request = require('request');
const checksum = require('checksum');
const zip = require('adm-zip');
const event = require('./events');
class Handler {
constructor(client) {
this.client = client;
this.options = client.options;
this.version = undefined;
}
function downloadAsync (url, directory, name) {
return new Promise(resolve => {
shelljs.mkdir('-p', directory);
downloadAsync(url, directory, name) {
return new Promise(resolve => {
shelljs.mkdir('-p', directory);
const _request = request(url, {timeout: 10000});
const _request = request(url, {timeout: this.options.timeout || 10000});
_request.on('error', function(error) {
resolve({
failed: true,
asset: {
url: url,
directory: directory,
name: name
_request.on('error', function(error) {
resolve({
failed: true,
asset: {
url: url,
directory: directory,
name: name
}
});
});
_request.on('data', (data) => {
let size = 0;
if(fs.existsSync(path.join(directory, name))) size = fs.statSync(path.join(directory, name))["size"];
this.client.emit('download-status', {
"name": name,
"current": Math.round(size / 10000),
"total": data.length
})
});
const file = fs.createWriteStream(path.join(directory, name));
_request.pipe(file);
file.once('finish', () => {
this.client.emit('download', name);
resolve({failed: false, asset: null});
});
file.on('error', (e) => {
this.client.emit('debug', `[MCLC]: Failed to download asset to ${path.join(directory, name)} due to\n${e}`);
if(fs.existsSync(path.join(directory, name))) shelljs.rm(path.join(directory, name));
resolve({
failed: true,
asset: {
url: url,
directory: directory,
name: name
}
});
});
});
}
checkSum(hash, file) {
return new Promise(resolve => {
checksum.file(file, (err, sum) => resolve(hash === sum));
});
}
getVersion() {
return new Promise(resolve => {
if(fs.existsSync(path.join(this.options.directory, `${this.options.version}.json`))) {
this.version = require(path.join(this.options.directory, `${this.options.version}.json`));
resolve(this.version);
return;
}
const manifest = "https://launchermeta.mojang.com/mc/game/version_manifest.json";
request.get(manifest, (error, response, body) => {
if (error) resolve(error);
const parsed = JSON.parse(body);
for (const desiredVersion in parsed.versions) {
if(parsed.versions[desiredVersion].id === this.options.version.number) {
request.get(parsed.versions[desiredVersion].url, (error, response, body) => {
if (error) resolve(error);
this.client.emit('debug', `[MCLC]: Parsed version from version manifest`);
this.version = JSON.parse(body);
resolve(this.version);
});
}
}
});
});
}
_request.on('data', (data) => {
let size = 0;
if(fs.existsSync(path.join(directory, name))) size = fs.statSync(path.join(directory, name))["size"];
event.emit('download-status', {
"name": name,
"current": Math.round(size / 10000),
"total": data.length
})
getJar() {
return new Promise(async (resolve)=> {
await this.downloadAsync(this.version.downloads.client.url, directory, `${this.options.version.number}.jar`);
fs.writeFileSync(path.join(directory, `${this.options.version.number}.json`), JSON.stringify(this.options.version, null, 4));
this.client.emit('debug', '[MCLC]: Downloaded version jar and wrote version json');
resolve();
});
}
const file = fs.createWriteStream(path.join(directory, name));
_request.pipe(file);
getAssets() {
return new Promise(async(resolve) => {
const assetsUrl = 'https://resources.download.minecraft.net';
const failed = [];
file.once('finish', function() {
event.emit('download', name);
resolve({failed: false, asset: null});
});
file.on('error', (e) => {
event.emit('debug', `[MCLC]: Failed to download asset to ${path.join(directory, name)} due to\n${e}`);
if(fs.existsSync(path.join(directory, name))) shelljs.rm(path.join(directory, name));
resolve({
failed: true,
asset: {
url: url,
directory: directory,
name: name
}
});
});
});
}
function checkSum(hash, file, size) {
return new Promise(resolve => {
checksum.file(file, (err, sum) => resolve(hash === sum));
});
}
module.exports.getVersion = function (version, directory) {
return new Promise(resolve => {
if(fs.existsSync(path.join(directory, `${version}.json`))) {
resolve(require(path.join(directory, `${version}.json`)));
return;
}
const manifest = "https://launchermeta.mojang.com/mc/game/version_manifest.json";
request.get(manifest, function(error, response, body) {
if (error) resolve(error);
const parsed = JSON.parse(body);
for (const desiredVersion in parsed.versions) {
if(parsed.versions[desiredVersion].id === version) {
request.get(parsed.versions[desiredVersion].url, function(error, response, body) {
if (error) resolve(error);
event.emit('debug', `[MCLC]: Parsed version from version manifest`);
resolve(JSON.parse(body));
});
}
if(!fs.existsSync(path.join(this.options.directory, 'assets', 'indexes', `${this.version.assetIndex.id}.json`))) {
await this.downloadAsync(this.version.assetIndex.url, path.join(this.options.directory, 'assets', 'indexes'), `${this.version.assetIndex.id}.json`);
}
});
});
};
module.exports.getJar = function (version, number, directory) {
return new Promise(async (resolve)=> {
await downloadAsync(version.downloads.client.url, directory, `${number}.jar`);
const index = require(path.join(this.options.directory, 'assets', 'indexes',`${this.version.assetIndex.id}.json`));
fs.writeFileSync(path.join(directory, `${number}.json`), JSON.stringify(version, null, 4));
event.emit('debug', '[MCLC]: Downloaded version jar and wrote version json');
resolve();
});
};
module.exports.getAssets = function (directory, version) {
return new Promise(async(resolve) => {
const assetsUrl = 'https://resources.download.minecraft.net';
const failed = [];
if(!fs.existsSync(path.join(directory, 'assets', 'indexes', `${version.assetIndex.id}.json`))) {
await downloadAsync(version.assetIndex.url, path.join(directory, 'assets', 'indexes'), `${version.assetIndex.id}.json`);
}
const index = require(path.join(directory, 'assets', 'indexes',`${version.assetIndex.id}.json`));
await Promise.all(Object.keys(index.objects).map(async asset => {
const hash = index.objects[asset].hash;
const subhash = hash.substring(0,2);
const assetDirectory = path.join(directory, 'assets', 'objects', subhash);
if(!fs.existsSync(path.join(assetDirectory, hash)) || !await checkSum(hash, path.join(assetDirectory, hash))) {
const download = await downloadAsync(`${assetsUrl}/${subhash}/${hash}`, assetDirectory, hash);
if(download.failed) failed.push(download.asset);
}
}));
// why do we have this? B/c sometimes Minecraft's resource site times out!
if(failed) {
await Promise.all(failed.map(async asset => await downloadAsync(asset.url, asset.directory, asset.name)))
}
// Copy assets to legacy if it's an older Minecarft version.
if(version.assets === "legacy" || version.assets === "pre-1.6") {
await Promise.all(Object.keys(index.objects).map(async asset => {
const hash = index.objects[asset].hash;
const subhash = hash.substring(0,2);
const assetDirectory = path.join(directory, 'assets', 'objects', subhash);
const assetDirectory = path.join(this.options.directory, 'assets', 'objects', subhash);
let legacyAsset = asset.split('/');
legacyAsset.pop();
if(!fs.existsSync(path.join(assetDirectory, hash)) || !await this.checkSum(hash, path.join(assetDirectory, hash))) {
const download = await this.downloadAsync(`${assetsUrl}/${subhash}/${hash}`, assetDirectory, hash);
if(!fs.existsSync(path.join(directory, 'assets', 'legacy', legacyAsset.join('/')))) {
shelljs.mkdir('-p', path.join(directory, 'assets', 'legacy', legacyAsset.join('/')));
}
if (!fs.existsSync(path.join(directory, 'assets', 'legacy', asset))) {
fs.copyFileSync(path.join(assetDirectory, hash), path.join(directory, 'assets', 'legacy', asset))
if(download.failed) failed.push(download.asset);
}
}));
}
event.emit('debug', '[MCLC]: Downloaded assets');
resolve();
});
};
// why do we have this? B/c sometimes Minecraft's resource site times out!
if(failed) {
await Promise.all(failed.map(async asset => await this.downloadAsync(asset.url, asset.directory, asset.name)))
}
module.exports.getNatives = function (root, version, os) {
return new Promise(async(resolve) => {
let nativeDirectory;
// Copy assets to legacy if it's an older Minecarft version.
if(this.version.assets === "legacy" || this.version.assets === "pre-1.6") {
await Promise.all(Object.keys(index.objects).map(async asset => {
const hash = index.objects[asset].hash;
const subhash = hash.substring(0,2);
const assetDirectory = path.join(this.options.directory, 'assets', 'objects', subhash);
if(fs.existsSync(path.join(root, 'natives', version.id))) {
nativeDirectory = path.join(root, 'natives', version.id);
} else {
nativeDirectory = path.join(root, "natives", version.id);
let legacyAsset = asset.split('/');
legacyAsset.pop();
shelljs.mkdir('-p', nativeDirectory);
await Promise.all(version.libraries.map(async function (lib) {
if (!lib.downloads.classifiers) return;
const type = `natives-${os}`;
const native = lib.downloads.classifiers[type];
if (native) {
const name = native.path.split('/').pop();
await downloadAsync(native.url, nativeDirectory, name);
if(!await checkSum(native.sha1, path.join(nativeDirectory, name))) {
await downloadAsync(native.url, nativeDirectory, name);
if(!fs.existsSync(path.join(this.options.directory, 'assets', 'legacy', legacyAsset.join('/')))) {
shelljs.mkdir('-p', path.join(this.options.directory, 'assets', 'legacy', legacyAsset.join('/')));
}
try {new zip(path.join(nativeDirectory, name)).extractAllTo(nativeDirectory, true);} catch(e) {
// Only doing a console.warn since a stupid error happens. You can basically ignore this.
// if it says Invalid file name, just means two files were downloaded and both were deleted.
// All is well.
console.warn(e);
if (!fs.existsSync(path.join(this.options.directory, 'assets', 'legacy', asset))) {
fs.copyFileSync(path.join(assetDirectory, hash), path.join(this.options.directory, 'assets', 'legacy', asset))
}
shelljs.rm(path.join(nativeDirectory, name));
}
}));
event.emit('debug', '[MCLC]: Downloaded and extracted natives');
}
}));
}
event.emit('debug', `[MCLC]: Set native path to ${nativeDirectory}`);
resolve(nativeDirectory);
});
};
module.exports.getForgeDependencies = async function(root, version, forgeJarPath) {
if(!fs.existsSync(path.join(root, 'forge'))) {
shelljs.mkdir('-p', path.join(root, 'forge'));
this.client.emit('debug', '[MCLC]: Downloaded assets');
resolve();
});
}
await new zip(forgeJarPath).extractEntryTo('version.json', path.join(root, 'forge', `${version.id}`), false, true);
const forge = require(path.join(root, 'forge', `${version.id}`, 'version.json'));
const mavenUrl = 'http://files.minecraftforge.net/maven/';
const defaultRepo = 'https://libraries.minecraft.net/';
const paths = [];
getNatives() {
return new Promise(async(resolve) => {
let nativeDirectory;
await Promise.all(forge.libraries.map(async library => {
const lib = library.name.split(':');
if(lib[0] === 'net.minecraftforge' && lib[1].includes('forge')) return;
let url = mavenUrl;
const jarPath = path.join(root, 'libraries', `${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}`);
const name = `${lib[1]}-${lib[2]}.jar`;
if(!library.url) {
if(library.serverreq || library.clientreq) {
url = defaultRepo;
if(fs.existsSync(path.join(this.options.root, 'natives', this.version.id))) {
nativeDirectory = path.join(this.options.root, 'natives', this.version.id);
} else {
return
}
}
nativeDirectory = path.join(this.options.root, "natives", this.version.id);
const downloadLink = `${url}${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}/${lib[1]}-${lib[2]}.jar`;
shelljs.mkdir('-p', nativeDirectory);
if(fs.existsSync(path.join(jarPath, name))) {
paths.push(`${jarPath}${path.sep}${name}`);
return;
}
if(!fs.existsSync(jarPath)) shelljs.mkdir('-p', jarPath);
await Promise.all(version.libraries.map(async (lib) => {
if (!lib.downloads.classifiers) return;
const type = `natives-${this.options.os}`;
const native = lib.downloads.classifiers[type];
await downloadAsync(downloadLink, jarPath, name);
paths.push(`${jarPath}${path.sep}${name}`);
}));
event.emit('debug', '[MCLC]: Downloaded Forge dependencies');
return {paths, forge};
};
module.exports.getClasses = function (options, version) {
return new Promise(async (resolve) => {
const libs = [];
if(options.version.custom) {
const customJarJson = require(path.join(options.root, 'versions', options.version.custom, `${options.version.custom}.json`));
await Promise.all(customJarJson.libraries.map(async library => {
const lib = library.name.split(':');
const jarPath = path.join(options.root, 'libraries', `${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}`);
const name = `${lib[1]}-${lib[2]}.jar`;
if(!fs.existsSync(path.join(jarPath, name))) {
if(library.url) {
const url = `${library.url}${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}/${lib[1]}-${lib[2]}.jar`;
await downloadAsync(url, jarPath, name);
if (native) {
const name = native.path.split('/').pop();
await this.downloadAsync(native.url, nativeDirectory, name);
if(!await this.checkSum(native.sha1, path.join(nativeDirectory, name))) {
await this.downloadAsync(native.url, nativeDirectory, name);
}
try {new zip(path.join(nativeDirectory, name)).extractAllTo(nativeDirectory, true);} catch(e) {
// Only doing a console.warn since a stupid error happens. You can basically ignore this.
// if it says Invalid file name, just means two files were downloaded and both were deleted.
// All is well.
console.warn(e);
}
shelljs.rm(path.join(nativeDirectory, name));
}
}
libs.push(`${jarPath}/${name}`);
}));
}
await Promise.all(version.libraries.map(async (_lib) => {
if(!_lib.downloads.artifact) return;
const libraryPath = _lib.downloads.artifact.path;
const libraryUrl = _lib.downloads.artifact.url;
const libraryHash = _lib.downloads.artifact.sha1;
const libraryDirectory = path.join(options.root, 'libraries', libraryPath);
if(!fs.existsSync(libraryDirectory) || !await checkSum(libraryHash, libraryDirectory)) {
let directory = libraryDirectory.split(path.sep);
const name = directory.pop();
directory = directory.join(path.sep);
await downloadAsync(libraryUrl, directory, name);
}));
this.client.emit('debug', '[MCLC]: Downloaded and extracted natives');
}
libs.push(libraryDirectory);
this.client.emit('debug', `[MCLC]: Set native path to ${nativeDirectory}`);
resolve(nativeDirectory);
});
}
async getForgeDependencies() {
if(!fs.existsSync(path.join(root, 'forge'))) {
shelljs.mkdir('-p', path.join(root, 'forge'));
}
await new zip(this.options.forge).extractEntryTo('version.json', path.join(this.options.root, 'forge', `${this.version.id}`), false, true);
const forge = require(path.join(this.options.root, 'forge', `${this.version.id}`, 'version.json'));
const mavenUrl = 'http://files.minecraftforge.net/maven/';
const defaultRepo = 'https://libraries.minecraft.net/';
const paths = [];
await Promise.all(forge.libraries.map(async library => {
const lib = library.name.split(':');
if(lib[0] === 'net.minecraftforge' && lib[1].includes('forge')) return;
let url = mavenUrl;
const jarPath = path.join(root, 'libraries', `${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}`);
const name = `${lib[1]}-${lib[2]}.jar`;
if(!library.url) {
if(library.serverreq || library.clientreq) {
url = defaultRepo;
} else {
return
}
}
const downloadLink = `${url}${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}/${lib[1]}-${lib[2]}.jar`;
if(fs.existsSync(path.join(jarPath, name))) {
paths.push(`${jarPath}${path.sep}${name}`);
return;
}
if(!fs.existsSync(jarPath)) shelljs.mkdir('-p', jarPath);
await this.downloadAsync(downloadLink, jarPath, name);
paths.push(`${jarPath}${path.sep}${name}`);
}));
event.emit('debug', '[MCLC]: Collected class paths');
resolve(libs)
});
};
this.client.emit('debug', '[MCLC]: Downloaded Forge dependencies');
module.exports.cleanUp = async function(array) {
const newArray = [];
for(let argument in array) {
if(newArray.includes(array[argument])) continue;
newArray.push(array[argument]);
return {paths, forge};
}
return newArray;
};
getClasses() {
return new Promise(async (resolve) => {
const libs = [];
module.exports.getLaunchOptions = function (version, modification, options) {
return new Promise(resolve => {
let type = modification || version;
if(this.options.version.custom) {
const customJarJson = require(path.join(this.options.root, 'versions', this.options.version.custom, `${this.options.version.custom}.json`));
await Promise.all(customJarJson.libraries.map(async library => {
const lib = library.name.split(':');
let arguments = type.minecraftArguments ? type.minecraftArguments.split(' ') : type.arguments.game;
const assetPath = version.assets === "legacy" || version.assets === "pre-1.6" ? path.join(options.root, 'assets', 'legacy') : path.join(options.root, 'assets');
const jarPath = path.join(this.options.root, 'libraries', `${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}`);
const name = `${lib[1]}-${lib[2]}.jar`;
if(arguments.length < 5) arguments = arguments.concat(version.minecraftArguments ? version.minecraftArguments.split(' ') : version.arguments.game);
const fields = {
'${auth_access_token}': options.authorization.access_token,
'${auth_session}': options.authorization.access_token,
'${auth_player_name}': options.authorization.name,
'${auth_uuid}': options.authorization.uuid,
'${user_properties}': options.authorization.user_properties,
'${user_type}': 'mojang',
'${version_name}': options.version.number,
'${assets_index_name}': version.assetIndex.id,
'${game_directory}': path.join(options.root),
'${assets_root}': assetPath,
'${game_assets}': assetPath,
'${version_type}': options.version.type
};
for (let index = 0; index < arguments.length; index++) {
if (Object.keys(fields).includes(arguments[index])) {
arguments[index] = fields[arguments[index]];
if(!fs.existsSync(path.join(jarPath, name))) {
if(library.url) {
const url = `${library.url}${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}/${lib[1]}-${lib[2]}.jar`;
await this.downloadAsync(url, jarPath, name);
}
}
libs.push(`${jarPath}/${name}`);
}));
}
await Promise.all(this.version.libraries.map(async (_lib) => {
if(!_lib.downloads.artifact) return;
const libraryPath = _lib.downloads.artifact.path;
const libraryUrl = _lib.downloads.artifact.url;
const libraryHash = _lib.downloads.artifact.sha1;
const libraryDirectory = path.join(this.options.root, 'libraries', libraryPath);
if(!fs.existsSync(libraryDirectory) || !await this.checkSum(libraryHash, libraryDirectory)) {
let directory = libraryDirectory.split(path.sep);
const name = directory.pop();
directory = directory.join(path.sep);
await this.downloadAsync(libraryUrl, directory, name);
}
libs.push(libraryDirectory);
}));
this.client.emit('debug', '[MCLC]: Collected class paths');
resolve(libs)
});
}
static cleanUp(array) {
const newArray = [];
for(let argument in array) {
if(newArray.includes(array[argument])) continue;
newArray.push(array[argument]);
}
if(options.server) arguments.push('--server', options.server.host, '--port', options.server.port || "25565");
if(options.proxy) arguments.push(
'--proxyHost',
options.proxy.host,
'--proxyPort',
options.proxy.port || "8080",
'--proxyUser',
options.proxy.username,
'--proxyPass',
options.proxy.password
);
return newArray;
}
event.emit('debug', '[MCLC]: Set launch options');
resolve(arguments);
});
};
getLaunchOptions(modification) {
return new Promise(resolve => {
let type = modification || this.version;
module.exports.getJVM = function (version, options) {
return new Promise(resolve => {
switch(options.os) {
let args = type.minecraftArguments ? type.minecraftArguments.split(' ') : type.arguments.game;
const assetPath = this.version.assets === "legacy" || this.version.assets === "pre-1.6" ? path.join(this.options.root, 'assets', 'legacy') : path.join(this.options.root, 'assets');
if(args.length < 5) args = args.concat(this.version.minecraftArguments ? this.version.minecraftArguments.split(' ') : this.version.arguments.game);
const fields = {
'${auth_access_token}': this.options.authorization.access_token,
'${auth_session}': this.options.authorization.access_token,
'${auth_player_name}': this.options.authorization.name,
'${auth_uuid}': this.options.authorization.uuid,
'${user_properties}': this.options.authorization.user_properties,
'${user_type}': 'mojang',
'${version_name}': this.options.version.number,
'${assets_index_name}': this.version.assetIndex.id,
'${game_directory}': path.join(this.options.root),
'${assets_root}': assetPath,
'${game_assets}': assetPath,
'${version_type}': this.options.version.type
};
for (let index = 0; index < args.length; index++) {
if (Object.keys(fields).includes(args[index])) {
args[index] = fields[args[index]];
}
}
if(this.options.server) args.push('--server', this.options.server.host, '--port', this.options.server.port || "25565");
if(this.options.proxy) args.push(
'--proxyHost',
this.options.proxy.host,
'--proxyPort',
this.options.proxy.port || "8080",
'--proxyUser',
this.options.proxy.username,
'--proxyPass',
this.options.proxy.password
);
this.client.emit('debug', '[MCLC]: Set launch options');
resolve(args);
});
}
async getJVM() {
switch(this.options.os) {
case "windows": {
resolve("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump");
break;
return "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump"
}
case "osx": {
resolve("-XstartOnFirstThread");
break;
return "-XstartOnFirstThread"
}
case "linux": {
resolve("-Xss1M");
break;
return "-Xss1M"
}
}
});
};
module.exports.makePackage = async function(versions, os) {
const directory = path.join(process.cwd(), 'clientpackage');
for(const version in versions) {
const versionFile = await this.getVersion(versions[version], directory);
await this.getNatives(`${directory}/natives/${versions[version]}`, versionFile, os, true);
await this.getJar(versionFile, versions[version], `${directory}/versions/${versions[version]}`);
await this.getClasses(directory, versionFile);
await this.getAssets(directory, versionFile);
}
}
const archive = new zip();
archive.addLocalFolder(directory);
archive.writeZip(`${directory}.zip`);
};
module.exports.extractPackage = function(root, clientPackage) {
return new Promise(async resolve => {
if(clientPackage.startsWith('http')) {
await downloadAsync(clientPackage, root, "clientPackage.zip");
clientPackage = path.join(root, "clientPackage.zip")
}
new zip(clientPackage).extractAllTo(root, true);
event.emit('package-extract', true);
resolve();
});
};
module.exports = Handler;

View file

@ -2,89 +2,102 @@ const child = require('child_process');
const event = require('./events');
const path = require('path');
const handler = require('./handler');
const packager = require('./package');
const fs = require('fs');
const EventEmitter = require('events').EventEmitter;
class MCLCore extends EventEmitter {
constructor(options) {
super();
module.exports = async function (options) {
options.root = path.resolve(options.root);
if(!fs.existsSync(options.root)) {
event.emit('debug', '[MCLC]: Attempting to create root folder');
fs.mkdirSync(options.root);
this.options = options;
this.handler = new handler(this);
}
if(options.clientPackage) {
event.emit('debug', `[MCLC]: Extracting client package to ${options.root}`);
await handler.extractPackage(options.root, options.clientPackage);
async launch() {
this.options.root = path.resolve(this.options.root);
if(!fs.existsSync(this.options.root)) {
this.emit('debug', '[MCLC]: Attempting to create root folder');
fs.mkdirSync(this.options.root);
}
if(this.options.clientPackage) {
this.emit('debug', `[MCLC]: Extracting client package to ${this.options.root}`);
await packager.extractPackage(this.options.root, this.options.clientPackage);
}
const directory = path.join(this.options.root, 'versions', this.options.version.number);
this.options.directory = directory;
// Version JSON for the main launcher folder
const versionFile = await this.handler.getVersion();
const mcPath = this.options.version.custom ? path.join(this.options.root, 'versions', this.options.version.custom , `${this.options.version.custom}.jar`):
path.join(directory, `${this.options.version.number}.jar`);
const nativePath = await this.handler.getNatives();
if (!fs.existsSync(mcPath)) {
this.emit('debug', '[MCLC]: Attempting to download Minecraft version jar');
await this.handler.getJar();
}
let forge = null;
let custom = null;
if(this.options.forge) {
this.emit('debug', '[MCLC]: Detected Forge in options, getting dependencies');
forge = await this.handler.getForgeDependencies();
}
if(this.options.version.custom) {
this.emit('debug', '[MCLC]: Detected custom in options, setting custom version file');
custom = require(path.join(this.options.root, 'versions', this.options.version.custom, `${this.options.version.custom}.json`));
}
const args = [];
// Jvm
let jvm = [
'-XX:-UseAdaptiveSizePolicy',
'-XX:-OmitStackTraceInFastThrow',
'-Dfml.ignorePatchDiscrepancies=true',
'-Dfml.ignoreInvalidMinecraftCertificates=true',
`-Djava.library.path=${nativePath}`,
`-Xmx${this.options.memory.max}M`,
`-Xms${this.options.memory.min}M`
];
jvm.push(await this.handler.getJVM());
if(this.options.customArgs) jvm = jvm.concat(this.options.customArgs);
const classes = await this.handler.getClasses();
let classPaths = ['-cp'];
const separator = this.options.os === "windows" ? ";" : ":";
this.emit('debug', `[MCLC]: Using ${separator} to separate class paths`);
if(forge) {
this.emit('debug', '[MCLC]: Setting Forge class paths');
classPaths.push(`${this.options.forge.path || this.options.forge}${separator}${forge.paths.join(separator)}${separator}${classes.join(separator)}${separator}${mcPath}`);
classPaths.push(forge.forge.mainClass)
} else {
const file = custom || versionFile;
classPaths.push(`${mcPath}${separator}${classes.join(separator)}`);
classPaths.push(file.mainClass);
}
classPaths = await handler.cleanUp(classPaths);
// Download version's assets
this.emit('debug', '[MCLC]: Attempting to download assets');
await this.handler.getAssets();
// Launch options. Thank you Lyrus for the reformat <3
const modification = forge ? forge.forge : null || custom ? custom : null;
const launchOptions = await this.handler.getLaunchOptions(modification);
const launchArguments = args.concat(jvm, classPaths, launchOptions);
event.emit('arguments', launchArguments);
event.emit('debug', launchArguments.join(' '));
const minecraft = child.spawn(this.options.javaPath ? this.options.javaPath : 'java', launchArguments);
minecraft.stdout.on('data', (data) => this.emit('data', data));
minecraft.stderr.on('data', (data) => this.emit('error', data));
minecraft.on('close', (code) => this.emit('close', code));
}
}
const directory = path.join(options.root, 'versions', options.version.number);
options.directory = directory;
const versionFile = await handler.getVersion(options.version.number, options.directory);
const mcPath = options.version.custom ? path.join(options.root, 'versions', options.version.custom , `${options.version.custom}.jar`):
path.join(directory, `${options.version.number}.jar`);
const nativePath = await handler.getNatives(options.root, versionFile, options.os);
if (!fs.existsSync(mcPath)) {
event.emit('debug', '[MCLC]: Attempting to download Minecraft version jar');
await handler.getJar(versionFile, options.version.number, directory);
}
let forge = null;
let custom = null;
if(options.forge) {
if(options.forge.path) process.emitWarning('\'options.forge.path\' is deprecated and will be removed. Use \'options.forge\' instead');
event.emit('debug', '[MCLC]: Detected Forge in options, getting dependencies');
forge = await handler.getForgeDependencies(options.root, versionFile, options.forge.path || options.forge);
}
if(options.version.custom) {
event.emit('debug', '[MCLC]: Detected custom in options, setting custom version file');
custom = require(path.join(options.root, 'versions', options.version.custom, `${options.version.custom}.json`));
}
const args = [];
// Jvm
let jvm = [
'-XX:-UseAdaptiveSizePolicy',
'-XX:-OmitStackTraceInFastThrow',
'-Dfml.ignorePatchDiscrepancies=true',
'-Dfml.ignoreInvalidMinecraftCertificates=true',
`-Djava.library.path=${nativePath}`,
`-Xmx${options.memory.max}M`,
`-Xms${options.memory.min}M`
];
jvm.push(await handler.getJVM(versionFile, options));
if(options.customArgs) jvm = jvm.concat(options.customArgs);
const classes = await handler.getClasses(options, versionFile);
let classPaths = ['-cp'];
const separator = options.os === "windows" ? ";" : ":";
event.emit('debug', `[MCLC]: Using ${separator} to separate class paths`);
if(forge) {
event.emit('debug', '[MCLC]: Setting Forge class paths');
classPaths.push(`${options.forge.path || options.forge}${separator}${forge.paths.join(separator)}${separator}${classes.join(separator)}${separator}${mcPath}`);
classPaths.push(forge.forge.mainClass)
} else {
const file = custom || versionFile;
classPaths.push(`${mcPath}${separator}${classes.join(separator)}`);
classPaths.push(file.mainClass);
}
classPaths = await handler.cleanUp(classPaths);
// Download version's assets
event.emit('debug', '[MCLC]: Attempting to download assets');
await handler.getAssets(options.root, versionFile);
// Launch options. Thank you Lyrus for the reformat <3
const modification = forge ? forge.forge : null || custom ? custom : null;
const launchOptions = await handler.getLaunchOptions(versionFile, modification, options);
const launchArguments = args.concat(jvm, classPaths, launchOptions);
event.emit('arguments', launchArguments);
event.emit('debug', launchArguments.join(' '));
const minecraft = child.spawn(options.javaPath ? options.javaPath : 'java', launchArguments);
minecraft.stdout.on('data', (data) => event.emit('data', data));
minecraft.stderr.on('data', (data) => event.emit('error', data));
minecraft.on('close', (code) => event.emit('close', code));
};
module.exports = MCLCore;

14
components/package.js Normal file
View file

@ -0,0 +1,14 @@
const path = require('path');
const zip = require('adm-zip');
module.exports.extractPackage = function(root, clientPackage) {
return new Promise(async resolve => {
if(clientPackage.startsWith('http')) {
await downloadAsync(clientPackage, root, "clientPackage.zip");
clientPackage = path.join(root, "clientPackage.zip")
}
new zip(clientPackage).extractAllTo(root, true);
this.client.emit('package-extract', true);
resolve();
});
};