mirror of
https://github.com/artegoser/pimi-launcher-core.git
synced 2024-11-22 20:26:22 +03:00
BREAKING CHANGES: v3
Version 3 merge
This commit is contained in:
commit
e5afd60ba3
8 changed files with 507 additions and 514 deletions
2
.travis.yml
Normal file
2
.travis.yml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
language: node_js
|
||||||
|
script: echo "npm test temporarily disabled"
|
159
README.md
159
README.md
|
@ -1,4 +1,6 @@
|
||||||
![logo](https://pierce.is-serious.business/44U1xXh.png)
|
![logo](https://pierce.is-serious.business/44U1xXh.png)
|
||||||
|
##### This project is near complete.
|
||||||
|
[![Build Status](https://travis-ci.com/Pierce01/MinecraftLauncher-core.svg?branch=master)](https://travis-ci.com/Pierce01/MinecraftLauncher-core)
|
||||||
|
|
||||||
MCLC is a NodeJS solution for launching modded and vanilla Minecraft without having to download and format everything yourself.
|
MCLC is a NodeJS solution for launching modded and vanilla Minecraft without having to download and format everything yourself.
|
||||||
Basically a core for your Electron or script based launchers.
|
Basically a core for your Electron or script based launchers.
|
||||||
|
@ -13,57 +15,56 @@ https://discord.gg/8uYVbXP
|
||||||
|
|
||||||
### Standard Example
|
### Standard Example
|
||||||
```javascript
|
```javascript
|
||||||
const launcher = require('minecraft-launcher-core');
|
const { Client, Authenticator } = require('minecraft-launcher-core');
|
||||||
|
|
||||||
launcher.authenticator.getAuth("email", "password").then(auth => {
|
let opts = {
|
||||||
// Save the auth to a file so it can be used later on!
|
authorization: async () => { return await Authenticator.getAuth(username, password) },
|
||||||
launcher.core({
|
clientPackage: null,
|
||||||
authorization: auth,
|
root: "./minecraft",
|
||||||
clientPackage: null,
|
os: "windows",
|
||||||
forge: null,
|
version: {
|
||||||
root: "C:/Users/user/AppData/Roaming/.mc",
|
number: "1.14",
|
||||||
os: "windows",
|
type: "release"
|
||||||
version: {
|
},
|
||||||
number: "1.13.2",
|
memory: {
|
||||||
type: "release"
|
max: "6000",
|
||||||
},
|
min: "4000"
|
||||||
memory: {
|
}
|
||||||
max: "3000",
|
}
|
||||||
min: "1000"
|
|
||||||
}
|
const launcher = new Client(opts);
|
||||||
});
|
launcher.launch();
|
||||||
});
|
|
||||||
|
launcher.on('debug', (e) => console.log(e));
|
||||||
|
launcher.on('data', (e) => console.log(e));
|
||||||
|
launcher.on('error', (e) => console.log(e));
|
||||||
```
|
```
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
##### launcher.core Options
|
##### Client Options
|
||||||
|
|
||||||
| Parameter | Type | Description | Required |
|
|
||||||
|--------------------------|--------|-------------------------------------------------------------------------------------------|----------|
|
|
||||||
| `options.authorization` | Object | The result from `getAuth` function, allows the client to login in online or offline mode. | True |
|
|
||||||
| `options.clientPackage` | String | Path to the client package zip file. | False |
|
|
||||||
| `options.root` | String | Path where you want the launcher to work in. like `C:/Users/user/AppData/Roaming/.mc` | True |
|
|
||||||
| `options.os` | String | windows, osx or linux | True |
|
|
||||||
| `options.javaPath` | String | Path to the JRE executable file, will default to `java` if not entered. | False |
|
|
||||||
| `options.version.number` | String | Minecraft version that is going to be launched. | True |
|
|
||||||
| `options.version.type` | String | Any string. The actual Minecraft launcher uses `release` and `snapshot`. | True |
|
|
||||||
| `options.version.custom` | String | Name of the jar, json, and folder of the custom client you are launching with. (Optifine) | False |
|
|
||||||
| `options.memory.max` | String | Max amount of memory being used by Minectaft | True |
|
|
||||||
| `options.memory.min` | String | Min amount of memory being used by Minectaft | True |
|
|
||||||
| `options.forge` | String | Path to Universal Forge Jar | False |
|
|
||||||
| `options.customArgs` | String | Array of custom JVM options | False |
|
|
||||||
| `options.server.host` | String | Host url to the server, don't include the port | False |
|
|
||||||
| `options.server.port` | String | Port of the host url, will default to `25565` if not entered. | False |
|
|
||||||
| `options.proxy.host` | String | Host url to the proxy, don't include the port | False |
|
|
||||||
| `options.proxy.port` | String | Port of the host proxy, will default to `8080` if not entered. | False |
|
|
||||||
| `options.proxy.username` | String | Username for the proxy. | False |
|
|
||||||
| `options.proxy.password` | String | Password for the proxy. | False |
|
|
||||||
|
|
||||||
|
| Parameter | Type | Description | Required |
|
||||||
|
|--------------------------|----------|-------------------------------------------------------------------------------------------|----------|
|
||||||
|
| `options.authorization` | Object | The result from `getAuth` function, allows the client to login in online or offline mode. | True |
|
||||||
|
| `options.clientPackage` | String | Path to the client package zip file. | False |
|
||||||
|
| `options.root` | String | Path where you want the launcher to work in. like `C:/Users/user/AppData/Roaming/.mc`, | True |
|
||||||
|
| `options.os` | String | windows, osx or linux, | True |
|
||||||
|
| `options.version.number` | String | Minecraft version that is going to be launched. | True |
|
||||||
|
| `options.version.type` | String | Any string. The actual Minecraft launcher uses `release` and `snapshot`. | True |
|
||||||
|
| `options.memory.max` | String | Max amount of memory being used by Minectaft. | True |
|
||||||
|
| `options.forge.path` | String | Path to Universal Forge Jar. | False |
|
||||||
|
| `options.server.host` | String | Host url to the server, don't include the port. | False |
|
||||||
|
| `options.server.port` | String | Port of the host url, will default to `25565` if not entered. | False |
|
||||||
|
| `options.proxy.host` | String | Host url to the proxy, don't include the port. | False |
|
||||||
|
| `options.proxy.port` | String | Port of the host proxy, will default to `8080` if not entered. | False |
|
||||||
|
| `options.proxy.username` | String | Username for the proxy. | False |
|
||||||
|
| `options.proxy.password` | String | Password for the proxy. | False |
|
||||||
|
| `options.timeout` | Interger | Timeout on download requests. | False |
|
||||||
##### Note
|
##### Note
|
||||||
If you are loading up a client outside of vanilla Minecraft or Forge (Optifine and for an example), you'll need to download the needed files yourself
|
If you are loading up a client outside of vanilla Minecraft or Forge (Optifine and for an example), you'll need to download the needed files yourself
|
||||||
if you don't provide downloads url downloads like Forge and Fabric. Still need to provide the version jar.
|
if you don't provide downloads url downloads like Forge and Fabric. Still need to provide the version jar.
|
||||||
|
|
||||||
#### launcher.authenticator Functions
|
#### Authenticator Functions
|
||||||
|
|
||||||
##### getAuth
|
##### getAuth
|
||||||
|
|
||||||
|
@ -86,6 +87,20 @@ if you don't provide downloads url downloads like Forge and Fabric. Still need t
|
||||||
| `client_token` | String | Token being checked if it's the same client that the access_token was created from. | True |
|
| `client_token` | String | Token being checked if it's the same client that the access_token was created from. | True |
|
||||||
| `selected_profile` | Object | Json Object that was returned from Mojangs auth api. | True |
|
| `selected_profile` | Object | Json Object that was returned from Mojangs auth api. | True |
|
||||||
|
|
||||||
|
##### invalidate
|
||||||
|
|
||||||
|
| Parameter | Type | Description | Required |
|
||||||
|
|--------------|--------|-------------------------------------------------------------------|----------|
|
||||||
|
| `access_token` | String | Token being checked if it can be used to login with (online mode). | True |
|
||||||
|
| `client_token` | String | Token being checked if it's the same client that the access_token was created from. | True |
|
||||||
|
|
||||||
|
##### signOut
|
||||||
|
|
||||||
|
| Parameter | Type | Description | Required |
|
||||||
|
|--------------|--------|--------------------------------------|----------|
|
||||||
|
| `username` | String | Username used to login with | True |
|
||||||
|
| `password` | String | Password used to login with | True |
|
||||||
|
|
||||||
#### Events
|
#### Events
|
||||||
|
|
||||||
| Event Name | Type | Description |
|
| Event Name | Type | Description |
|
||||||
|
@ -98,66 +113,6 @@ if you don't provide downloads url downloads like Forge and Fabric. Still need t
|
||||||
| `download` | String | Emitted when a file successfully downloads |
|
| `download` | String | Emitted when a file successfully downloads |
|
||||||
| `download-status` | Object | Emitted when data is received while downloading |
|
| `download-status` | Object | Emitted when data is received while downloading |
|
||||||
| `debug` | String | Emitted when functions occur, made to help debug if errors occur |
|
| `debug` | String | Emitted when functions occur, made to help debug if errors occur |
|
||||||
#### Client Package Function
|
|
||||||
|
|
||||||
Client Packages allow the client to run offline on setup. This function should be used outside the actual launcher.
|
|
||||||
this function is in the `handler` component.
|
|
||||||
|
|
||||||
##### makePackage
|
|
||||||
|
|
||||||
| Parameter | Type | Description | Required |
|
|
||||||
|------------|--------|-----------------------------------------------------------------------|----------|
|
|
||||||
| `versions` | Array | Array of the versions being downloaded and being made into a package. | True |
|
|
||||||
| `os` | String | OS that the package will be loaded on. OS specific natives need this. | True |
|
|
||||||
|
|
||||||
### Other Examples
|
|
||||||
|
|
||||||
##### Using Validate and Refresh
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
let auth = require("pathToUserAuthJson.json");
|
|
||||||
|
|
||||||
const validateCheck = await launcher.authenticator.validate(auth.access_token);
|
|
||||||
if(!validateCheck) {
|
|
||||||
auth = await launcher.authenticator.refreshAuth(auth.access_token, auth.client_token, auth.selected_profile);
|
|
||||||
}
|
|
||||||
launcher.core({
|
|
||||||
authorization: auth,
|
|
||||||
clientPackage: null,
|
|
||||||
root: "directory",
|
|
||||||
os: "windows",
|
|
||||||
version: {
|
|
||||||
number: "1.13.2",
|
|
||||||
type: "MCC-Launcher"
|
|
||||||
},
|
|
||||||
memory: {
|
|
||||||
max: "500",
|
|
||||||
min: "100"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Using With Forge
|
|
||||||
|
|
||||||
```js
|
|
||||||
launcher.authenticator.getAuth("email", "password").then(auth => {
|
|
||||||
launcher.core({
|
|
||||||
authorization: auth,
|
|
||||||
clientPackage: null,
|
|
||||||
root: "C:/Users/user/AppData/Roaming/.mc",
|
|
||||||
forge: "C:/Users/user/Desktop/forge.jar",
|
|
||||||
os: "windows",
|
|
||||||
version: {
|
|
||||||
number: "1.12.2", // needs to be the same as the Forge version
|
|
||||||
type: "MCC-Launcher"
|
|
||||||
},
|
|
||||||
memory: {
|
|
||||||
max: "500",
|
|
||||||
min: "100"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### What should it look like running from console?
|
#### What should it look like running from console?
|
||||||
|
|
|
@ -2,7 +2,6 @@ const request = require('request');
|
||||||
const uuid = require('uuid/v1');
|
const uuid = require('uuid/v1');
|
||||||
const api_url = "https://authserver.mojang.com";
|
const api_url = "https://authserver.mojang.com";
|
||||||
|
|
||||||
|
|
||||||
module.exports.getAuth = function (username, password) {
|
module.exports.getAuth = function (username, password) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if(!password) {
|
if(!password) {
|
||||||
|
@ -82,7 +81,6 @@ module.exports.refreshAuth = function (accessToken, clientToken, selectedProfile
|
||||||
|
|
||||||
request.post(requestObject, function(error, response, body) {
|
request.post(requestObject, function(error, response, body) {
|
||||||
if (error) resolve(error);
|
if (error) resolve(error);
|
||||||
console.log(body);
|
|
||||||
if(!body.selectedProfile) {
|
if(!body.selectedProfile) {
|
||||||
throw new Error("Validation error: " + response.statusMessage);
|
throw new Error("Validation error: " + response.statusMessage);
|
||||||
}
|
}
|
||||||
|
@ -99,3 +97,39 @@ module.exports.refreshAuth = function (accessToken, clientToken, selectedProfile
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
|
@ -4,389 +4,366 @@ const path = require('path');
|
||||||
const request = require('request');
|
const request = require('request');
|
||||||
const checksum = require('checksum');
|
const checksum = require('checksum');
|
||||||
const zip = require('adm-zip');
|
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) {
|
downloadAsync(url, directory, name) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
shelljs.mkdir('-p', directory);
|
shelljs.mkdir('-p', directory);
|
||||||
|
|
||||||
const _request = request(url, {timeout: 10000});
|
const _request = request(url, {timeout: this.options.timeout || 10000});
|
||||||
|
|
||||||
_request.on('error', function(error) {
|
_request.on('error', function(error) {
|
||||||
resolve({
|
resolve({
|
||||||
failed: true,
|
failed: true,
|
||||||
asset: {
|
asset: {
|
||||||
url: url,
|
url: url,
|
||||||
directory: directory,
|
directory: directory,
|
||||||
name: name
|
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) => {
|
getJar() {
|
||||||
let size = 0;
|
return new Promise(async (resolve)=> {
|
||||||
if(fs.existsSync(path.join(directory, name))) size = fs.statSync(path.join(directory, name))["size"];
|
await this.downloadAsync(this.version.downloads.client.url, directory, `${this.options.version.number}.jar`);
|
||||||
event.emit('download-status', {
|
|
||||||
"name": name,
|
fs.writeFileSync(path.join(directory, `${this.options.version.number}.json`), JSON.stringify(this.options.version, null, 4));
|
||||||
"current": Math.round(size / 10000),
|
|
||||||
"total": data.length
|
this.client.emit('debug', '[MCLC]: Downloaded version jar and wrote version json');
|
||||||
})
|
|
||||||
|
resolve();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const file = fs.createWriteStream(path.join(directory, name));
|
getAssets() {
|
||||||
_request.pipe(file);
|
return new Promise(async(resolve) => {
|
||||||
|
const assetsUrl = 'https://resources.download.minecraft.net';
|
||||||
|
const failed = [];
|
||||||
|
|
||||||
file.once('finish', function() {
|
if(!fs.existsSync(path.join(this.options.directory, 'assets', 'indexes', `${this.version.assetIndex.id}.json`))) {
|
||||||
event.emit('download', name);
|
await this.downloadAsync(this.version.assetIndex.url, path.join(this.options.directory, 'assets', 'indexes'), `${this.version.assetIndex.id}.json`);
|
||||||
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));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.getJar = function (version, number, directory) {
|
const index = require(path.join(this.options.directory, 'assets', 'indexes',`${this.version.assetIndex.id}.json`));
|
||||||
return new Promise(async (resolve)=> {
|
|
||||||
await downloadAsync(version.downloads.client.url, directory, `${number}.jar`);
|
|
||||||
|
|
||||||
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 => {
|
await Promise.all(Object.keys(index.objects).map(async asset => {
|
||||||
const hash = index.objects[asset].hash;
|
const hash = index.objects[asset].hash;
|
||||||
const subhash = hash.substring(0,2);
|
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('/');
|
if(!fs.existsSync(path.join(assetDirectory, hash)) || !await this.checkSum(hash, path.join(assetDirectory, hash))) {
|
||||||
legacyAsset.pop();
|
const download = await this.downloadAsync(`${assetsUrl}/${subhash}/${hash}`, assetDirectory, hash);
|
||||||
|
|
||||||
if(!fs.existsSync(path.join(directory, 'assets', 'legacy', legacyAsset.join('/')))) {
|
if(download.failed) failed.push(download.asset);
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
|
|
||||||
event.emit('debug', '[MCLC]: Downloaded assets');
|
// why do we have this? B/c sometimes Minecraft's resource site times out!
|
||||||
resolve();
|
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) {
|
// Copy assets to legacy if it's an older Minecarft version.
|
||||||
return new Promise(async(resolve) => {
|
if(this.version.assets === "legacy" || this.version.assets === "pre-1.6") {
|
||||||
let nativeDirectory;
|
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))) {
|
let legacyAsset = asset.split('/');
|
||||||
nativeDirectory = path.join(root, 'natives', version.id);
|
legacyAsset.pop();
|
||||||
} else {
|
|
||||||
nativeDirectory = path.join(root, "natives", version.id);
|
|
||||||
|
|
||||||
shelljs.mkdir('-p', nativeDirectory);
|
if(!fs.existsSync(path.join(this.options.directory, 'assets', 'legacy', legacyAsset.join('/')))) {
|
||||||
|
shelljs.mkdir('-p', path.join(this.options.directory, 'assets', 'legacy', legacyAsset.join('/')));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
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 (!fs.existsSync(path.join(this.options.directory, 'assets', 'legacy', asset))) {
|
||||||
// if it says Invalid file name, just means two files were downloaded and both were deleted.
|
fs.copyFileSync(path.join(assetDirectory, hash), path.join(this.options.directory, 'assets', 'legacy', asset))
|
||||||
// All is well.
|
|
||||||
console.warn(e);
|
|
||||||
}
|
}
|
||||||
shelljs.rm(path.join(nativeDirectory, name));
|
}));
|
||||||
}
|
}
|
||||||
}));
|
|
||||||
event.emit('debug', '[MCLC]: Downloaded and extracted natives');
|
|
||||||
}
|
|
||||||
|
|
||||||
event.emit('debug', `[MCLC]: Set native path to ${nativeDirectory}`);
|
this.client.emit('debug', '[MCLC]: Downloaded assets');
|
||||||
resolve(nativeDirectory);
|
resolve();
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.getForgeDependencies = async function(root, version, forgeJarPath) {
|
|
||||||
if(!fs.existsSync(path.join(root, 'forge'))) {
|
|
||||||
shelljs.mkdir('-p', path.join(root, 'forge'));
|
|
||||||
}
|
}
|
||||||
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'));
|
getNatives() {
|
||||||
const mavenUrl = 'http://files.minecraftforge.net/maven/';
|
return new Promise(async(resolve) => {
|
||||||
const defaultRepo = 'https://libraries.minecraft.net/';
|
let nativeDirectory;
|
||||||
const paths = [];
|
|
||||||
|
|
||||||
await Promise.all(forge.libraries.map(async library => {
|
if(fs.existsSync(path.join(this.options.root, 'natives', this.version.id))) {
|
||||||
const lib = library.name.split(':');
|
nativeDirectory = path.join(this.options.root, 'natives', this.version.id);
|
||||||
|
|
||||||
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 {
|
} 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))) {
|
await Promise.all(version.libraries.map(async (lib) => {
|
||||||
paths.push(`${jarPath}${path.sep}${name}`);
|
if (!lib.downloads.classifiers) return;
|
||||||
return;
|
const type = `natives-${this.options.os}`;
|
||||||
}
|
const native = lib.downloads.classifiers[type];
|
||||||
if(!fs.existsSync(jarPath)) shelljs.mkdir('-p', jarPath);
|
|
||||||
|
|
||||||
await downloadAsync(downloadLink, jarPath, name);
|
if (native) {
|
||||||
|
const name = native.path.split('/').pop();
|
||||||
paths.push(`${jarPath}${path.sep}${name}`);
|
await this.downloadAsync(native.url, nativeDirectory, name);
|
||||||
}));
|
if(!await this.checkSum(native.sha1, path.join(nativeDirectory, name))) {
|
||||||
|
await this.downloadAsync(native.url, nativeDirectory, name);
|
||||||
event.emit('debug', '[MCLC]: Downloaded Forge dependencies');
|
}
|
||||||
|
try {new zip(path.join(nativeDirectory, name)).extractAllTo(nativeDirectory, true);} catch(e) {
|
||||||
return {paths, forge};
|
// 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.
|
||||||
module.exports.getClasses = function (options, version) {
|
console.warn(e);
|
||||||
return new Promise(async (resolve) => {
|
}
|
||||||
const libs = [];
|
shelljs.rm(path.join(nativeDirectory, name));
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}));
|
||||||
libs.push(`${jarPath}/${name}`);
|
this.client.emit('debug', '[MCLC]: Downloaded and extracted natives');
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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');
|
this.client.emit('debug', '[MCLC]: Downloaded Forge dependencies');
|
||||||
resolve(libs)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.cleanUp = async function(array) {
|
return {paths, forge};
|
||||||
const newArray = [];
|
|
||||||
|
|
||||||
for(let argument in array) {
|
|
||||||
if(newArray.includes(array[argument])) continue;
|
|
||||||
newArray.push(array[argument]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return newArray;
|
getClasses() {
|
||||||
};
|
return new Promise(async (resolve) => {
|
||||||
|
const libs = [];
|
||||||
|
|
||||||
module.exports.getLaunchOptions = function (version, modification, options) {
|
if(this.options.version.custom) {
|
||||||
return new Promise(resolve => {
|
const customJarJson = require(path.join(this.options.root, 'versions', this.options.version.custom, `${this.options.version.custom}.json`));
|
||||||
let type = modification || version;
|
await Promise.all(customJarJson.libraries.map(async library => {
|
||||||
|
const lib = library.name.split(':');
|
||||||
|
|
||||||
let arguments = type.minecraftArguments ? type.minecraftArguments.split(' ') : type.arguments.game;
|
const jarPath = path.join(this.options.root, 'libraries', `${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}`);
|
||||||
const assetPath = version.assets === "legacy" || version.assets === "pre-1.6" ? path.join(options.root, 'assets', 'legacy') : path.join(options.root, 'assets');
|
const name = `${lib[1]}-${lib[2]}.jar`;
|
||||||
|
|
||||||
if(arguments.length < 5) arguments = arguments.concat(version.minecraftArguments ? version.minecraftArguments.split(' ') : version.arguments.game);
|
if(!fs.existsSync(path.join(jarPath, name))) {
|
||||||
|
if(library.url) {
|
||||||
const fields = {
|
const url = `${library.url}${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}/${lib[1]}-${lib[2]}.jar`;
|
||||||
'${auth_access_token}': options.authorization.access_token,
|
await this.downloadAsync(url, jarPath, name);
|
||||||
'${auth_session}': options.authorization.access_token,
|
}
|
||||||
'${auth_player_name}': options.authorization.name,
|
}
|
||||||
'${auth_uuid}': options.authorization.uuid,
|
libs.push(`${jarPath}/${name}`);
|
||||||
'${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]];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
return newArray;
|
||||||
if(options.proxy) arguments.push(
|
}
|
||||||
'--proxyHost',
|
|
||||||
options.proxy.host,
|
|
||||||
'--proxyPort',
|
|
||||||
options.proxy.port || "8080",
|
|
||||||
'--proxyUser',
|
|
||||||
options.proxy.username,
|
|
||||||
'--proxyPass',
|
|
||||||
options.proxy.password
|
|
||||||
);
|
|
||||||
|
|
||||||
event.emit('debug', '[MCLC]: Set launch options');
|
getLaunchOptions(modification) {
|
||||||
resolve(arguments);
|
return new Promise(resolve => {
|
||||||
});
|
let type = modification || this.version;
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.getJVM = function (version, options) {
|
let args = type.minecraftArguments ? type.minecraftArguments.split(' ') : type.arguments.game;
|
||||||
return new Promise(resolve => {
|
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');
|
||||||
switch(options.os) {
|
|
||||||
|
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": {
|
case "windows": {
|
||||||
resolve("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump");
|
return "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump"
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case "osx": {
|
case "osx": {
|
||||||
resolve("-XstartOnFirstThread");
|
return "-XstartOnFirstThread"
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case "linux": {
|
case "linux": {
|
||||||
resolve("-Xss1M");
|
return "-Xss1M"
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
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();
|
module.exports = Handler;
|
||||||
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();
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -2,89 +2,102 @@ const child = require('child_process');
|
||||||
const event = require('./events');
|
const event = require('./events');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const handler = require('./handler');
|
const handler = require('./handler');
|
||||||
|
const packager = require('./package');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
|
class MCLCore extends EventEmitter {
|
||||||
|
constructor(options) {
|
||||||
|
super();
|
||||||
|
|
||||||
module.exports = async function (options) {
|
this.options = options;
|
||||||
options.root = path.resolve(options.root);
|
this.handler = new handler(this);
|
||||||
if(!fs.existsSync(options.root)) {
|
|
||||||
event.emit('debug', '[MCLC]: Attempting to create root folder');
|
|
||||||
fs.mkdirSync(options.root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(options.clientPackage) {
|
async launch() {
|
||||||
event.emit('debug', `[MCLC]: Extracting client package to ${options.root}`);
|
this.options.root = path.resolve(this.options.root);
|
||||||
await handler.extractPackage(options.root, options.clientPackage);
|
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);
|
module.exports = MCLCore;
|
||||||
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));
|
|
||||||
};
|
|
14
components/package.js
Normal file
14
components/package.js
Normal 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();
|
||||||
|
});
|
||||||
|
};
|
6
index.js
6
index.js
|
@ -1,6 +1,4 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
core: require('./components/launcher'),
|
Client: require('./components/launcher'),
|
||||||
event: require('./components/events'),
|
Authenticator: require('./components/authenticator'),
|
||||||
handler: require('./components/handler'),
|
|
||||||
authenticator: require('./components/authenticator'),
|
|
||||||
};
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "minecraft-launcher-core",
|
"name": "minecraft-launcher-core",
|
||||||
"version": "2.8.1",
|
"version": "3.0.0",
|
||||||
"description": "Module that downloads Minecraft assets and runs Minecraft. Also Supports Forge",
|
"description": "Module that downloads Minecraft assets and runs Minecraft. Also Supports Forge",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
Loading…
Add table
Reference in a new issue