ForgeWrapper support, ESLint added

This commit is contained in:
Pierce 2020-05-28 22:39:13 -04:00
parent 9e21e8189c
commit 99b7ff2367
9 changed files with 2446 additions and 767 deletions

21
.eslintrc.json Normal file
View file

@ -0,0 +1,21 @@
{
"env": {
"browser": true,
"commonjs": true,
"es6": true
},
"extends": [
"standard"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"camelcase": "off",
"no-template-curly-in-string": "off"
}
}

View file

@ -72,7 +72,7 @@ launcher.on('data', (e) => console.log(e));
| `options.version.custom` | String | The name of the folder, jar file, and version json in the version folder. | False | | `options.version.custom` | String | The name of the folder, jar file, and version json in the version folder. | False |
| `options.memory.max` | String | Max amount of memory being used by Minecraft. | True | | `options.memory.max` | String | Max amount of memory being used by Minecraft. | True |
| `options.memory.min` | String | Min amount of memory being used by Minecraft. | True | | `options.memory.min` | String | Min amount of memory being used by Minecraft. | True |
| `options.forge` | String | Path to Universal Forge Jar. (Only for versions below 1.13+ | False | | `options.forge` | String | Path to Forge Jar. (Versions below 1.13 should be the "universal" jar while versions above 1.13+ should be the "installer" jar)| False |
| `options.javaPath` | String | Path to the JRE executable file, will default to `java` if not entered. | False | | `options.javaPath` | String | Path to the JRE executable file, will default to `java` if not entered. | False |
| `options.server.host` | String | Host url to the server, don't include the port. | 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.server.port` | String | Port of the host url, will default to `25565` if not entered. | False |
@ -118,16 +118,8 @@ let opts = {
##### Custom ##### Custom
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. If no version jar is specified, MCLC will default back to the normal MC jar so mods like Fabric work. 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. If no version jar is specified, MCLC will default back to the normal MC jar so mods like Fabric work.
##### Installer ##### Installer
You'll need to provide the folder created in the versions if you're running the new forge like so This runs an executable with specified launch arguments. Was used to support Forge 1.13 before ForgeWrapper.
```json
{
"version": {
"number": "1.14.2",
"type": "release",
"custom": "1.14.2-forge-26.0.63"
},
"installer": "forge-1.14.2-26.0.63-installer.jar"
}
``` ```
#### Authenticator Functions #### Authenticator Functions

View file

@ -1,143 +1,143 @@
const request = require('request'); const request = require('request')
const uuid = require('uuid/v1'); const uuid = require('uuid/v1')
let api_url = "https://authserver.mojang.com"; let api_url = 'https://authserver.mojang.com'
module.exports.getAuth = function (username, password) { module.exports.getAuth = function (username, password) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!password) { if (!password) {
const user = { const user = {
access_token: uuid(), access_token: uuid(),
client_token: uuid(), client_token: uuid(),
uuid: uuid(), uuid: uuid(),
name: username, name: username,
user_properties: JSON.stringify({}) user_properties: JSON.stringify({})
}; }
return resolve(user); return resolve(user)
} }
const requestObject = { const requestObject = {
url: api_url + "/authenticate", url: api_url + '/authenticate',
json: { json: {
agent: { agent: {
name: "Minecraft", name: 'Minecraft',
version: 1 version: 1
}, },
username: username, username: username,
password: password, password: password,
clientToken: uuid(), clientToken: uuid(),
requestUser: true requestUser: true
} }
}; }
request.post(requestObject, function (error, response, body) { request.post(requestObject, function (error, response, body) {
if (error) return reject(error); if (error) return reject(error)
if (!body || !body.selectedProfile) { if (!body || !body.selectedProfile) {
return reject("Validation error: " + response.statusMessage); return reject(new Error('Validation error: ' + response.statusMessage))
} }
const userProfile = { const userProfile = {
access_token: body.accessToken, access_token: body.accessToken,
client_token: body.clientToken, client_token: body.clientToken,
uuid: body.selectedProfile.id, uuid: body.selectedProfile.id,
name: body.selectedProfile.name, name: body.selectedProfile.name,
selected_profile: body.selectedProfile, selected_profile: body.selectedProfile,
user_properties: JSON.stringify(body.user.properties || {}) user_properties: JSON.stringify(body.user.properties || {})
}; }
resolve(userProfile); resolve(userProfile)
}); })
}); })
}; }
module.exports.validate = function (access_token, client_token) { module.exports.validate = function (access_token, client_token) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const requestObject = { const requestObject = {
url: api_url + "/validate", url: api_url + '/validate',
json: { json: {
"accessToken": access_token, accessToken: access_token,
"clientToken": client_token clientToken: client_token
} }
}; }
request.post(requestObject, async function (error, response, body) { request.post(requestObject, async function (error, response, body) {
if (error) return reject(error); if (error) return reject(error)
if (!body) resolve(true); if (!body) resolve(true)
else reject(body); else reject(body)
}); })
}); })
}; }
module.exports.refreshAuth = function (accessToken, clientToken, selectedProfile) { module.exports.refreshAuth = function (accessToken, clientToken, selectedProfile) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const requestObject = { const requestObject = {
url: api_url + "/refresh", url: api_url + '/refresh',
json: { json: {
"accessToken": accessToken, accessToken: accessToken,
"clientToken": clientToken, clientToken: clientToken,
"selectedProfile": selectedProfile, selectedProfile: selectedProfile,
"requestUser": true requestUser: true
} }
}; }
request.post(requestObject, function (error, response, body) { request.post(requestObject, function (error, response, body) {
if (error) return reject(error); if (error) return reject(error)
if (!body || !body.selectedProfile) { if (!body || !body.selectedProfile) {
return reject("Validation error: " + response.statusMessage); return reject(new Error('Validation error: ' + response.statusMessage))
} }
const userProfile = { const userProfile = {
access_token: body.accessToken, access_token: body.accessToken,
client_token: uuid(), client_token: uuid(),
uuid: body.selectedProfile.id, uuid: body.selectedProfile.id,
name: body.selectedProfile.name, name: body.selectedProfile.name,
user_properties: JSON.stringify(body.user.properties || {}) user_properties: JSON.stringify(body.user.properties || {})
}; }
resolve(userProfile); resolve(userProfile)
}); })
}); })
}; }
module.exports.invalidate = function (accessToken, clientToken) { module.exports.invalidate = function (accessToken, clientToken) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const requestObject = { const requestObject = {
url: api_url + "/invalidate", url: api_url + '/invalidate',
json: { json: {
"accessToken": accessToken, accessToken: accessToken,
"clientToken": clientToken clientToken: clientToken
} }
}; }
request.post(requestObject, function (error, response, body) { request.post(requestObject, function (error, response, body) {
if (error) return reject(error); if (error) return reject(error)
if (!body) resolve(true); if (!body) resolve(true)
else reject(body); else reject(body)
}); })
}); })
}; }
module.exports.signOut = function (username, password) { module.exports.signOut = function (username, password) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const requestObject = { const requestObject = {
url: api_url + "/signout", url: api_url + '/signout',
json: { json: {
"username": username, username: username,
"password": password password: password
} }
}; }
request.post(requestObject, function (error, response, body) { request.post(requestObject, function (error, response, body) {
if (error) return reject(error); if (error) return reject(error)
if (!body) resolve(true); if (!body) resolve(true)
else reject(body); else reject(body)
}); })
}); })
}; }
module.exports.changeApiUrl = function(url) { module.exports.changeApiUrl = function (url) {
api_url = url; api_url = url
} }

BIN
components/fw.jar Normal file

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -1,140 +1,146 @@
const child = require('child_process'); const child = require('child_process')
const path = require('path'); const path = require('path')
const handler = require('./handler'); const Handler = require('./handler')
const fs = require('fs'); const fs = require('fs')
const EventEmitter = require('events').EventEmitter; const EventEmitter = require('events').EventEmitter
class MCLCore extends EventEmitter { class MCLCore extends EventEmitter {
constructor() { async launch (options = this.options) {
super(); this.options = options
this.options.root = path.resolve(this.options.root)
this.options.overrides = {
...this.options.overrides,
url: {
meta: 'https://launchermeta.mojang.com',
resource: 'https://resources.download.minecraft.net',
mavenForge: 'http://files.minecraftforge.net/maven/',
defaultRepoForge: 'https://libraries.minecraft.net/',
fallbackMaven: 'https://search.maven.org/remotecontent?filepath=',
...this.options.overrides
? this.options.overrides.url
: undefined
}
}
// ForgeWrapper fork that is maintained on a side repo (https://github.com/Pierce01/ForgeWrapper)
this.options.forgeWrapper = {
jar: path.join(__dirname, 'fw.jar'),
version: '1.4.1-mclc'
} }
async launch(options) { this.handler = new Handler(this)
this.options = options;
this.options.root = path.resolve(this.options.root);
this.options.overrides = {
...this.options.overrides,
url: {
meta: "https://launchermeta.mojang.com",
resource: "https://resources.download.minecraft.net",
mavenForge: "http://files.minecraftforge.net/maven/",
defaultRepoForge: "https://libraries.minecraft.net/",
fallbackMaven: "https://search.maven.org/remotecontent?filepath=",
...this.options.overrides ? this.options.overrides.url : undefined
}
};
this.handler = new handler(this); if (fs.existsSync(path.join(__dirname, '..', 'package.json'))) {
// Lets the events register. our magic switch! this.emit('debug', `[MCLC]: MCLC version ${JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), { encoding: 'utf8' })).version}`)
await void(0); } else { this.emit('debug', '[MCLC]: Package JSON not found, skipping MCLC version check.') }
const java = await this.handler.checkJava(this.options.javaPath || 'java')
if (!java.run) {
this.emit('debug', `[MCLC]: Couldn't start Minecraft due to: ${java.message}`)
this.emit('close', 1)
return null
}
if(fs.existsSync(path.join(__dirname,'..', 'package.json'))) { if (!fs.existsSync(this.options.root)) {
this.emit('debug', `[MCLC]: MCLC version ${JSON.parse(fs.readFileSync(path.join(__dirname,'..', 'package.json'), { encoding: 'utf8' })).version}`); this.emit('debug', '[MCLC]: Attempting to create root folder')
} else { this.emit('debug', `[MCLC]: Package JSON not found, skipping MCLC version check.`); } fs.mkdirSync(this.options.root)
const java = await this.handler.checkJava(this.options.javaPath || 'java'); }
if(!java.run) {
this.emit('debug', `[MCLC]: Couldn't start Minecraft due to: ${java.message}`);
this.emit('close', 1);
return null;
}
if(!fs.existsSync(this.options.root)) { if (this.options.clientPackage) {
this.emit('debug', '[MCLC]: Attempting to create root folder'); this.emit('debug', `[MCLC]: Extracting client package to ${this.options.root}`)
fs.mkdirSync(this.options.root); await this.handler.extractPackage()
} }
if(this.options.clientPackage) { if (this.options.installer) {
this.emit('debug', `[MCLC]: Extracting client package to ${this.options.root}`); // So the forge installer can run without breaking :)
await this.handler.extractPackage(); const profilePath = path.join(this.options.root, 'launcher_profiles.json')
} if (!fs.existsSync(profilePath)) { fs.writeFileSync(profilePath, JSON.stringify({}, null, 4)) }
await this.handler.runInstaller(this.options.installer)
}
if(this.options.installer) { const directory = this.options.overrides.directory || path.join(this.options.root, 'versions', this.options.version.number)
// So the forge installer can run without breaking :) this.options.directory = directory
const profilePath = path.join(this.options.root, 'launcher_profiles.json');
if(!fs.existsSync(profilePath))
fs.writeFileSync(profilePath, JSON.stringify({}, null, 4));
await this.handler.runInstaller(this.options.installer)
}
const directory = this.options.overrides.directory || path.join(this.options.root, 'versions', this.options.version.number); // Version JSON for the main launcher folder
this.options.directory = directory; const versionFile = await this.handler.getVersion()
const mcPath = this.options.overrides.minecraftJar || (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()
// Version JSON for the main launcher folder if (!fs.existsSync(mcPath)) {
const versionFile = await this.handler.getVersion(); this.emit('debug', '[MCLC]: Attempting to download Minecraft version jar')
const mcPath = this.options.overrides.minecraftJar || (this.options.version.custom ? path.join(this.options.root, 'versions', this.options.version.custom , `${this.options.version.custom}.jar`): await this.handler.getJar()
path.join(directory, `${this.options.version.number}.jar`)); }
const nativePath = await this.handler.getNatives();
if (!fs.existsSync(mcPath)) { let forge = null
this.emit('debug', '[MCLC]: Attempting to download Minecraft version jar'); let custom = null
await this.handler.getJar(); if (this.options.forge) {
} this.options.forge = path.resolve(this.options.forge)
let forge = null; this.emit('debug', '[MCLC]: Detected Forge in options, getting dependencies')
let custom = null; forge = await this.handler.getForgeDependenciesLegacy()
if(this.options.forge) { if (forge === false) custom = await this.handler.getForgedWrapped()
this.emit('debug', '[MCLC]: Detected Forge in options, getting dependencies'); }
forge = await this.handler.getForgeDependenciesLegacy(); if (this.options.version.custom || custom) {
} this.emit('debug', '[MCLC]: Detected custom in options, setting custom version file')
if(this.options.version.custom) { custom = custom || JSON.parse(fs.readFileSync(path.join(this.options.root, 'versions', this.options.version.custom, `${this.options.version.custom}.json`), { encoding: 'utf8' }))
this.emit('debug', '[MCLC]: Detected custom in options, setting custom version file'); }
custom = JSON.parse(fs.readFileSync(path.join(this.options.root, 'versions', this.options.version.custom, `${this.options.version.custom}.json`), { encoding: 'utf8' }));
}
const args = []; const args = []
// Jvm // Jvm
let jvm = [ let jvm = [
'-XX:-UseAdaptiveSizePolicy', '-XX:-UseAdaptiveSizePolicy',
'-XX:-OmitStackTraceInFastThrow', '-XX:-OmitStackTraceInFastThrow',
'-Dfml.ignorePatchDiscrepancies=true', '-Dfml.ignorePatchDiscrepancies=true',
'-Dfml.ignoreInvalidMinecraftCertificates=true', '-Dfml.ignoreInvalidMinecraftCertificates=true',
`-Djava.library.path=${nativePath}`, `-Djava.library.path=${nativePath}`,
`-Xmx${this.options.memory.max}M`, `-Xmx${this.options.memory.max}M`,
`-Xms${this.options.memory.min}M` `-Xms${this.options.memory.min}M`
]; ]
if(this.handler.getOS() === 'osx') { if (this.handler.getOS() === 'osx') {
if(parseInt(versionFile.id.split('.')[1]) > 12) jvm.push(await this.handler.getJVM()); if (parseInt(versionFile.id.split('.')[1]) > 12) jvm.push(await this.handler.getJVM())
} else jvm.push(await this.handler.getJVM()); } else jvm.push(await this.handler.getJVM())
if(this.options.customArgs) jvm = jvm.concat(this.options.customArgs); if (this.options.customArgs) jvm = jvm.concat(this.options.customArgs)
const classes = this.options.overrides.classes || await handler.cleanUp(await this.handler.getClasses()); const classes = this.options.overrides.classes || await Handler.cleanUp(await this.handler.getClasses(custom))
let classPaths = ['-cp']; const classPaths = ['-cp']
const separator = this.handler.getOS() === "windows" ? ";" : ":"; const separator = this.handler.getOS() === 'windows' ? ';' : ':'
this.emit('debug', `[MCLC]: Using ${separator} to separate class paths`); this.emit('debug', `[MCLC]: Using ${separator} to separate class paths`)
if(forge) { if (forge) {
this.emit('debug', '[MCLC]: Setting Forge class paths'); this.emit('debug', '[MCLC]: Setting Forge class paths')
classPaths.push(`${path.resolve(this.options.forge)}${separator}${forge.paths.join(separator)}${separator}${classes.join(separator)}${separator}${mcPath}`); classPaths.push(`${path.resolve(this.options.forge)}${separator}${forge.paths.join(separator)}${separator}${classes.join(separator)}${separator}${mcPath}`)
classPaths.push(forge.forge.mainClass) classPaths.push(forge.forge.mainClass)
} else { } else {
const file = custom || versionFile; const file = custom || versionFile
// So mods like fabric work. // So mods like fabric work.
const jar = fs.existsSync(mcPath) ? `${separator}${mcPath}` : `${separator}${path.join(directory, `${this.options.version.number}.jar`)}`; const jar = fs.existsSync(mcPath)
classPaths.push(`${classes.join(separator)}${jar}`); ? `${separator}${mcPath}`
classPaths.push(file.mainClass); : `${separator}${path.join(directory, `${this.options.version.number}.jar`)}`
} classPaths.push(`${classes.join(separator)}${jar}`)
classPaths.push(file.mainClass)
// 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);
this.emit('arguments', launchArguments);
this.emit('debug', `[MCLC]: Launching with arguments ${launchArguments.join(' ')}`);
const minecraft = child.spawn(this.options.javaPath ? this.options.javaPath : 'java', launchArguments,
{cwd: this.options.overrides.cwd || this.options.root});
minecraft.stdout.on('data', (data) => this.emit('data', data.toString('utf-8')));
minecraft.stderr.on('data', (data) => this.emit('data', data.toString('utf-8')));
minecraft.on('close', (code) => this.emit('close', code));
return minecraft;
} }
// 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)
this.emit('arguments', launchArguments)
this.emit('debug', `[MCLC]: Launching with arguments ${launchArguments.join(' ')}`)
const minecraft = child.spawn(this.options.javaPath ? this.options.javaPath : 'java', launchArguments,
{ cwd: this.options.overrides.cwd || this.options.root })
minecraft.stdout.on('data', (data) => this.emit('data', data.toString('utf-8')))
minecraft.stderr.on('data', (data) => this.emit('data', data.toString('utf-8')))
minecraft.on('close', (code) => this.emit('close', code))
return minecraft
}
} }
module.exports = MCLCore; module.exports = MCLCore

View file

@ -1,4 +1,4 @@
module.exports = { module.exports = {
Client: require('./components/launcher'), Client: require('./components/launcher'),
Authenticator: require('./components/authenticator'), Authenticator: require('./components/authenticator')
}; }

1647
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "minecraft-launcher-core", "name": "minecraft-launcher-core",
"version": "3.13.3", "version": "3.14.0",
"description": "Lightweight module that downloads and runs Minecraft using javascript / NodeJS", "description": "Lightweight module that downloads and runs Minecraft using javascript / NodeJS",
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
@ -10,9 +10,16 @@
"shelljs": "^0.8.2", "shelljs": "^0.8.2",
"uuid": "^3.3.2" "uuid": "^3.3.2"
}, },
"devDependencies": {}, "devDependencies": {
"eslint": "^6.8.0",
"eslint-config-standard": "^14.1.1",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1"
},
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "eslint ."
}, },
"repository": { "repository": {
"type": "git", "type": "git",