Skip to content

Commit 1cf8623

Browse files
committed
v3.18.0
- Removed options.installer - Bumped default timeout to 50000ms - Changed hard coded maven forge URLs to use proper override url - Added options.overrides.versionName - Fixed width and height flags getting ignored - Updated ForgeWrapper to 1.5.7 (1.20+ forge support) - Changed mavenForge to use https - Minor Refactoring
1 parent 0955015 commit 1cf8623

File tree

6 files changed

+2525
-104
lines changed

6 files changed

+2525
-104
lines changed

README.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
![logo](https://owo.whats-th.is/8mT5kxc.png)
2-
##### This project is complete for now.
2+
##### Project rewrite coming soon™
33
[![Build Status](https://travis-ci.com/Pierce01/MinecraftLauncher-core.svg?branch=master)](https://travis-ci.com/Pierce01/MinecraftLauncher-core)
44
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
55
![version](https://img.shields.io/badge/stable_version-3.17.2-blue)
6-
![badge](https://img.shields.io/badge/ncurses-not_supported-purple)
76

87
MCLC (Minecraft Launcher Core) is a NodeJS solution for launching modded and vanilla Minecraft without having to download and format everything yourself.
98
Basically a core for your Electron or script based launchers.
@@ -61,7 +60,6 @@ launcher.on('data', (e) => console.log(e));
6160
|--------------------------|----------|-------------------------------------------------------------------------------------------|----------|
6261
| `options.clientPackage` | String | Path or URL to a zip file, which will be extracted to the root directory. (Not recommended for production use)| False |
6362
| `options.removePackage` | Boolean | Option to remove the client package zip file after its finished extracting. | False |
64-
| `options.installer` | String | Path to installer being executed. | False |
6563
| `options.root` | String | Path where you want the launcher to work in. `C:/Users/user/AppData/Roaming/.mc` | True |
6664
| `options.cache` | String | Path where launcher files will be cached in. `C:/Users/user/AppData/Roaming/.mc/cache` | False |
6765
| `options.os` | String | windows, osx or linux. MCLC will auto determine the OS if this field isn't provided. | False |
@@ -73,7 +71,7 @@ launcher.on('data', (e) => console.log(e));
7371
| `options.version.custom` | String | The name of the folder, jar file, and version json in the version folder. | False |
7472
| `options.memory.max` | String | Max amount of memory being used by Minecraft. | True |
7573
| `options.memory.min` | String | Min amount of memory being used by Minecraft. | True |
76-
| `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 |
74+
| `options.forge` | String | Path to Forge Jar. (Versions below 1.12.2 should be the "universal" jar while versions above 1.13 should be the "installer" jar) | False |
7775
| `options.javaPath` | String | Path to the JRE executable file, will default to `java` if not entered. | False |
7876
| `options.quickPlay.type` | String | The type of the quickPlay session. `singleplayer`, `multiplayer`, `realms`, `legacy` | False |
7977
| `options.quickPlay.identifier` | String | The folder name, server address, or realm ID, relating to the specified type. `legacy` follows `multiplayer` format. | False |
@@ -94,6 +92,7 @@ let opts = {
9492
overrides: {
9593
gameDirectory: '', // where the game process generates folders like saves and resource packs.
9694
minecraftJar: '',
95+
versionName: '', // replaces the value after the version flag.
9796
versionJson: '',
9897
directory: '', // where the Minecraft jar and version json are located.
9998
natives: '', // native directory path.
@@ -130,9 +129,8 @@ let opts = {
130129
#### Notes
131130
##### Custom
132131
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.
133-
##### Installer
134-
This runs an executable with specified launch arguments. Was used to support Forge 1.13 before ForgeWrapper.
135-
##### Authentication
132+
133+
#### Authentication (Deprecated)
136134
MCLC's authenticator module does not support Microsoft authentication. You will need to use a library like [MSMC](https://github.com/Hanro50/MSMC). If you want to create your own solution, the following is the authorization JSON object format.
137135
```js
138136
{

components/handler.js

Lines changed: 48 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Handler {
1212
this.options = client.options
1313
this.baseRequest = request.defaults({
1414
pool: { maxSockets: this.options.overrides.maxSockets || 2 },
15-
timeout: this.options.timeout || 10000
15+
timeout: this.options.timeout || 50000
1616
})
1717
}
1818

@@ -95,10 +95,9 @@ class Handler {
9595
checksum.file(file, (err, sum) => {
9696
if (err) {
9797
this.client.emit('debug', `[MCLC]: Failed to check file hash due to ${err}`)
98-
resolve(false)
99-
} else {
100-
resolve(hash === sum)
98+
return resolve(false)
10199
}
100+
return resolve(hash === sum)
102101
})
103102
})
104103
}
@@ -125,34 +124,28 @@ class Handler {
125124
this.client.emit('debug', '[MCLC]: Cached version_manifest.json')
126125
})
127126
}
127+
const parsed = (error && error.code === 'ENOTFOUND')
128+
? JSON.parse(fs.readFileSync(`${cache}/version_manifest.json`))
129+
: JSON.parse(body)
130+
const desiredVersion = Object.values(parsed.versions).find(version => version.id === this.options.version.number)
131+
if (desiredVersion) {
132+
request.get(desiredVersion.url, (error, response, body) => {
133+
if (error && error.code !== 'ENOTFOUND') throw Error(error)
134+
if (!error) {
135+
fs.writeFile(path.join(`${cache}/${this.options.version.number}.json`), body, (err) => {
136+
if (err) throw Error(err)
137+
this.client.emit('debug', `[MCLC]: Cached ${this.options.version.number}.json`)
138+
})
139+
}
128140

129-
let parsed
130-
if (error && (error.code === 'ENOTFOUND')) {
131-
parsed = JSON.parse(fs.readFileSync(`${cache}/version_manifest.json`))
141+
this.client.emit('debug', '[MCLC]: Parsed version from version manifest')
142+
this.version = error && error.code === 'ENOTFOUND'
143+
? JSON.parse(fs.readFileSync(`${cache}/${this.options.version.number}.json`))
144+
: JSON.parse(body)
145+
return resolve(this.version)
146+
})
132147
} else {
133-
parsed = JSON.parse(body)
134-
}
135-
136-
for (const desiredVersion in parsed.versions) {
137-
if (parsed.versions[desiredVersion].id === this.options.version.number) {
138-
request.get(parsed.versions[desiredVersion].url, (error, response, body) => {
139-
if (error && error.code !== 'ENOTFOUND') return resolve(error)
140-
if (!error) {
141-
fs.writeFile(path.join(`${cache}/${this.options.version.number}.json`), body, (err) => {
142-
if (err) return resolve(err)
143-
this.client.emit('debug', `[MCLC]: Cached ${this.options.version.number}.json`)
144-
})
145-
}
146-
147-
this.client.emit('debug', '[MCLC]: Parsed version from version manifest')
148-
if (error && (error.code === 'ENOTFOUND')) {
149-
this.version = JSON.parse(fs.readFileSync(`${cache}/${this.options.version.number}.json`))
150-
} else {
151-
this.version = JSON.parse(body)
152-
}
153-
return resolve(this.version)
154-
})
155-
}
148+
throw Error(`Failed to find version ${this.options.version.number} in version_manifest.json`)
156149
}
157150
})
158151
})
@@ -168,8 +161,7 @@ class Handler {
168161
const assetDirectory = path.resolve(this.options.overrides.assetRoot || path.join(this.options.root, 'assets'))
169162
const assetId = this.options.version.custom || this.options.version.number
170163
if (!fs.existsSync(path.join(assetDirectory, 'indexes', `${assetId}.json`))) {
171-
await this.downloadAsync(this.version.assetIndex.url, path.join(assetDirectory, 'indexes'),
172-
`${assetId}.json`, true, 'asset-json')
164+
await this.downloadAsync(this.version.assetIndex.url, path.join(assetDirectory, 'indexes'), `${assetId}.json`, true, 'asset-json')
173165
}
174166

175167
const index = JSON.parse(fs.readFileSync(path.join(assetDirectory, 'indexes', `${assetId}.json`), { encoding: 'utf8' }))
@@ -186,8 +178,7 @@ class Handler {
186178
const subAsset = path.join(assetDirectory, 'objects', subhash)
187179

188180
if (!fs.existsSync(path.join(subAsset, hash)) || !await this.checkSum(hash, path.join(subAsset, hash))) {
189-
await this.downloadAsync(`${this.options.overrides.url.resource}/${subhash}/${hash}`, subAsset, hash,
190-
true, 'assets')
181+
await this.downloadAsync(`${this.options.overrides.url.resource}/${subhash}/${hash}`, subAsset, hash, true, 'assets')
191182
}
192183
counter++
193184
this.client.emit('progress', {
@@ -246,13 +237,10 @@ class Handler {
246237
parseRule (lib) {
247238
if (lib.rules) {
248239
if (lib.rules.length > 1) {
249-
if (lib.rules[0].action === 'allow' &&
250-
lib.rules[1].action === 'disallow' &&
251-
lib.rules[1].os.name === 'osx') {
240+
if (lib.rules[0].action === 'allow' && lib.rules[1].action === 'disallow' && lib.rules[1].os.name === 'osx') {
252241
return this.getOS() === 'osx'
253-
} else {
254-
return true
255242
}
243+
return true
256244
} else {
257245
if (lib.rules[0].action === 'allow' && lib.rules[0].os) return lib.rules[0].os.name !== this.getOS()
258246
}
@@ -363,7 +351,7 @@ class Handler {
363351
}
364352
}
365353

366-
this.client.emit('debug', '[MCLC]: Generating a proper version json, this might take a bit')
354+
this.client.emit('debug', '[MCLC]: Generating Forge version json, this might take a bit')
367355
const zipFile = new Zip(this.options.forge)
368356
json = zipFile.readAsText('version.json')
369357
if (zipFile.getEntry('install_profile.json')) installerJson = zipFile.readAsText('install_profile.json')
@@ -409,7 +397,7 @@ class Handler {
409397
for (const library of json.mavenFiles) {
410398
const lib = library.name.split(':')
411399
if (lib[0] === 'net.minecraftforge' && lib[1].includes('forge')) {
412-
library.downloads.artifact.url = 'https://files.minecraftforge.net/maven/' + library.downloads.artifact.path
400+
library.downloads.artifact.url = this.options.overrides.url.mavenForge + library.downloads.artifact.path
413401
break
414402
}
415403
}
@@ -455,10 +443,11 @@ class Handler {
455443
// If a downloads property exists, we modify the inital forge entry to include ${jarEnding} so ForgeWrapper can work properly.
456444
// If it doesn't, we simply remove it since we're already providing the universal jar.
457445
if (json.libraries[0].downloads) {
458-
if (json.libraries[0].name.includes('minecraftforge')) {
459-
json.libraries[0].name = json.libraries[0].name + `:${jarEnding}`
446+
const name = json.libraries[0].name
447+
if (name.includes('minecraftforge:forge') && !name.includes('universal')) {
448+
json.libraries[0].name = name + `:${jarEnding}`
460449
json.libraries[0].downloads.artifact.path = json.libraries[0].downloads.artifact.path.replace('.jar', `-${jarEnding}.jar`)
461-
json.libraries[0].downloads.artifact.url = 'https://files.minecraftforge.net/maven/' + json.libraries[0].downloads.artifact.path
450+
json.libraries[0].downloads.artifact.url = this.options.overrides.url.mavenForge + json.libraries[0].downloads.artifact.path
462451
}
463452
} else {
464453
delete json.libraries[0]
@@ -482,13 +471,6 @@ class Handler {
482471
return json
483472
}
484473

485-
runInstaller (path) {
486-
return new Promise(resolve => {
487-
const installer = child.exec(path)
488-
installer.on('close', (code) => resolve(code))
489-
})
490-
}
491-
492474
async downloadToDirectory (directory, libraries, eventName) {
493475
const libs = []
494476

@@ -511,14 +493,15 @@ class Handler {
511493
if (library.url) {
512494
const url = `${library.url}${lib[0].replace(/\./g, '/')}/${lib[1]}/${lib[2]}/${name}`
513495
await this.downloadAsync(url, jarPath, name, true, eventName)
514-
} else if (library.downloads && library.downloads.artifact) {
496+
} else if (library.downloads && library.downloads.artifact && library.downloads.artifact.url) {
497+
// Only download if there's a URL provided. If not, we're assuming it's going a generated dependency.
515498
await this.downloadAsync(library.downloads.artifact.url, jarPath, name, true, eventName)
516499
}
517500
}
518501

519-
if (!fs.existsSync(path.join(jarPath, name))) downloadLibrary(library)
520-
else if (library.downloads && library.downloads.artifact) {
521-
if (!this.checkSum(library.downloads.artifact.sha1, path.join(jarPath, name))) downloadLibrary(library)
502+
if (!fs.existsSync(path.join(jarPath, name))) await downloadLibrary(library)
503+
if (library.downloads && library.downloads.artifact) {
504+
if (!this.checkSum(library.downloads.artifact.sha1, path.join(jarPath, name))) await downloadLibrary(library)
522505
}
523506

524507
counter++
@@ -543,7 +526,7 @@ class Handler {
543526
if (classJson.mavenFiles) {
544527
await this.downloadToDirectory(libraryDirectory, classJson.mavenFiles, 'classes-maven-custom')
545528
}
546-
libs = (await this.downloadToDirectory(libraryDirectory, classJson.libraries, 'classes-custom'))
529+
libs = await this.downloadToDirectory(libraryDirectory, classJson.libraries, 'classes-custom')
547530
}
548531

549532
const parsed = this.version.libraries.map(lib => {
@@ -561,18 +544,11 @@ class Handler {
561544
}
562545

563546
popString (path) {
564-
const tempArray = path.split('/')
565-
tempArray.pop()
566-
return tempArray.join('/')
547+
return path.split('/').slice(0, -1).join('/')
567548
}
568549

569550
cleanUp (array) {
570-
const newArray = []
571-
for (const classPath in array) {
572-
if (newArray.includes(array[classPath]) || array[classPath] === null) continue
573-
newArray.push(array[classPath])
574-
}
575-
return newArray
551+
return [...new Set(Object.values(array).filter(value => value !== null))]
576552
}
577553

578554
formatQuickPlay () {
@@ -620,7 +596,7 @@ class Handler {
620596
'${auth_xuid}': this.options.authorization.meta.xuid || this.options.authorization.access_token,
621597
'${user_properties}': this.options.authorization.user_properties,
622598
'${user_type}': this.options.authorization.meta.type,
623-
'${version_name}': this.options.version.number,
599+
'${version_name}': this.options.version.number || this.options.overrides.versionName,
624600
'${assets_index_name}': this.options.overrides.assetIndex || this.options.version.custom || this.options.version.number,
625601
'${game_directory}': this.options.overrides.gameDirectory || this.options.root,
626602
'${assets_root}': assetPath,
@@ -671,14 +647,12 @@ class Handler {
671647
}
672648
}
673649
if (this.options.window) {
674-
// eslint-disable-next-line no-unused-expressions
675-
this.options.window.fullscreen
676-
? args.push('--fullscreen')
677-
: () => {
678-
if (this.options.features ? !this.options.features.includes('has_custom_resolution') : true) {
679-
args.push('--width', this.options.window.width, '--height', this.options.window.height)
680-
}
681-
}
650+
if (this.options.window.fullscreen) {
651+
args.push('--fullscreen')
652+
} else {
653+
if (this.options.window.width) args.push('--width', this.options.window.width)
654+
if (this.options.window.height) args.push('--height', this.options.window.height)
655+
}
682656
}
683657
if (this.options.server) this.client.emit('debug', '[MCLC]: server and port are deprecated launch flags. Use the quickPlay field.')
684658
if (this.options.quickPlay) args = args.concat(this.formatQuickPlay())

components/launcher.js

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class MCLCore extends EventEmitter {
1515
url: {
1616
meta: 'https://launchermeta.mojang.com',
1717
resource: 'https://resources.download.minecraft.net',
18-
mavenForge: 'http://files.minecraftforge.net/maven/',
18+
mavenForge: 'https://files.minecraftforge.net/maven/',
1919
defaultRepoForge: 'https://libraries.minecraft.net/',
2020
fallbackMaven: 'https://search.maven.org/remotecontent?filepath=',
2121
...this.options.overrides
@@ -24,9 +24,9 @@ class MCLCore extends EventEmitter {
2424
},
2525
fw: {
2626
baseUrl: 'https://github.com/ZekerZhayard/ForgeWrapper/releases/download/',
27-
version: '1.5.6',
28-
sh1: 'b38d28e8b7fde13b1bc0db946a2da6760fecf98d',
29-
size: 34715,
27+
version: '1.5.7',
28+
sh1: '155ac9f4e5f65288eaacae19025ac4d9da1f0ef2',
29+
size: 34910,
3030
...this.options.overrides
3131
? this.options.overrides.fw
3232
: undefined
@@ -49,19 +49,6 @@ class MCLCore extends EventEmitter {
4949

5050
await this.extractPackage()
5151

52-
if (this.options.installer) {
53-
// So installers that create a profile in launcher_profiles.json can run without breaking.
54-
const profilePath = path.join(this.options.root, 'launcher_profiles.json')
55-
if (!fs.existsSync(profilePath) || !JSON.parse(fs.readFileSync(profilePath)).profiles) {
56-
fs.writeFileSync(profilePath, JSON.stringify({ profiles: {} }, null, 4))
57-
}
58-
const code = await this.handler.runInstaller(this.options.installer)
59-
if (!this.options.version.custom && code === 0) {
60-
this.emit('debug', '[MCLC]: Installer successfully ran, but no custom version was provided')
61-
}
62-
this.emit('debug', `[MCLC]: Installer closed with code ${code}`)
63-
}
64-
6552
const directory = this.options.overrides.directory || path.join(this.options.root, 'versions', this.options.version.custom ? this.options.version.custom : this.options.version.number)
6653
this.options.directory = directory
6754

index.d.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ declare module "minecraft-launcher-core" {
1010
minArgs?: number;
1111
minecraftJar?: string;
1212
versionJson?: string;
13+
versionName?: string;
1314
/**
1415
* Folder, where the game process generates folders like saves and resource packs.
1516
*/
@@ -88,10 +89,6 @@ declare module "minecraft-launcher-core" {
8889
* if true MCLC will remove the client package zip file after its finished extracting.
8990
*/
9091
removePackage?: boolean;
91-
/**
92-
* Path to installer being executed.
93-
*/
94-
installer?: string;
9592
/**
9693
* Path where you want the launcher to work in.
9794
* This will usually be your .minecraft folder

0 commit comments

Comments
 (0)