├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── config.yml │ └── bug_report.yml ├── webfiles ├── instances │ ├── aynor │ │ └── README.md │ ├── hypixel │ │ └── README.md │ └── selvania │ │ └── README.md ├── php │ ├── instances.php │ └── scandir.php └── index.php ├── .gitignore ├── assets ├── icons │ ├── microsoft.icns │ ├── microsoft.ico │ └── microsoft.png └── LWJGL │ ├── aarch │ ├── 2.9.4.json │ ├── 3.1.2.json │ ├── 3.1.6.json │ ├── 3.2.1.json │ ├── 3.2.2.json │ └── 3.3.1.json │ └── aarch64 │ ├── 2.9.4.json │ ├── 3.1.2.json │ ├── 3.1.6.json │ ├── 3.2.1.json │ └── 3.2.2.json ├── .vscode └── settings.json ├── tsconfig.json ├── src ├── Authenticator │ ├── GUI │ │ ├── Terminal.ts │ │ ├── NW.ts │ │ └── Electron.ts │ ├── Mojang.ts │ └── AZauth.ts ├── Index.ts ├── StatusServer │ ├── buffer.ts │ └── status.ts ├── Minecraft │ ├── Minecraft-Json.ts │ ├── Minecraft-Lwjgl-Native.ts │ ├── Minecraft-Assets.ts │ ├── Minecraft-Loader.ts │ ├── Minecraft-Bundle.ts │ └── Minecraft-Libraries.ts ├── Minecraft-Loader │ ├── patcher.ts │ └── loader │ │ ├── fabric │ │ └── fabric.ts │ │ ├── legacyfabric │ │ └── legacyFabric.ts │ │ └── quilt │ │ └── quilt.ts └── utils │ ├── unzipper.ts │ ├── Downloader.ts │ └── Index.ts ├── test ├── offline.js ├── index.js └── AZauth.js ├── package.json ├── LICENSE.md └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ['luuxis'] -------------------------------------------------------------------------------- /webfiles/instances/aynor/README.md: -------------------------------------------------------------------------------- 1 | # files .minecraft 2 | 3 | -------------------------------------------------------------------------------- /webfiles/instances/hypixel/README.md: -------------------------------------------------------------------------------- 1 | # files .minecraft 2 | 3 | -------------------------------------------------------------------------------- /webfiles/instances/selvania/README.md: -------------------------------------------------------------------------------- 1 | # files .minecraft 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | test/Minecraft 4 | test/*.json* 5 | webfiles/instances/* 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /assets/icons/microsoft.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luuxis/minecraft-java-core/HEAD/assets/icons/microsoft.icns -------------------------------------------------------------------------------- /assets/icons/microsoft.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luuxis/minecraft-java-core/HEAD/assets/icons/microsoft.ico -------------------------------------------------------------------------------- /assets/icons/microsoft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luuxis/minecraft-java-core/HEAD/assets/icons/microsoft.png -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/build": true, 4 | "**/node_modules": true, 5 | "**/package-lock.json": true 6 | } 7 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Nous rejoindre sur Discord, pour toutes questions ou demandes 4 | url: http://discord.luuxis.fr 5 | about: Veuillez ne pas ouvrir d'issue autre que pour signaler des bugs 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "src/**/*" 4 | ], 5 | "compilerOptions": { 6 | "sourceMap": true, 7 | "target": "ES2020", 8 | "module": "CommonJS", 9 | "lib": [ 10 | "ES2021", 11 | "DOM" 12 | ], 13 | "declaration": true, 14 | "outDir": "build", 15 | "esModuleInterop": true 16 | } 17 | } -------------------------------------------------------------------------------- /src/Authenticator/GUI/Terminal.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import prompt from 'prompt'; 7 | 8 | module.exports = async function (url: string) { 9 | console.log(`Open brosser ${url}`); 10 | prompt.start(); 11 | let result = await prompt.get(['copy-URL']); 12 | return result['copy-URL'].split("code=")[1].split("&")[0]; 13 | } -------------------------------------------------------------------------------- /src/Index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import AZauth from './Authenticator/AZauth.js'; 7 | import Launch from './Launch.js'; 8 | import Microsoft from './Authenticator/Microsoft.js'; 9 | import * as Mojang from './Authenticator/Mojang.js'; 10 | import Status from './StatusServer/status.js'; 11 | import Downloader from './utils/Downloader.js'; 12 | 13 | export { 14 | AZauth as AZauth, 15 | Launch as Launch, 16 | Microsoft as Microsoft, 17 | Mojang as Mojang, 18 | Status as Status, 19 | Downloader as Downloader 20 | }; -------------------------------------------------------------------------------- /webfiles/php/instances.php: -------------------------------------------------------------------------------- 1 | array( 4 | "minecraft_version" => "1.16.5", 5 | "loadder_type" => "forge", 6 | "loadder_version" => "latest" 7 | ) 8 | )); 9 | 10 | $instance['hypixel'] = array_merge($instance['hypixel'], array( 11 | "loadder" => array( 12 | "minecraft_version" => "1.8.9", 13 | "loadder_type" => "forge", 14 | "loadder_version" => "latest" 15 | ) 16 | )); 17 | 18 | $instance['selvania'] = array_merge($instance['selvania'], array( 19 | "loadder" => array( 20 | "minecraft_version" => "latest_release", 21 | "loadder_type" => "none", 22 | "loadder_version" => "latest" 23 | ) 24 | )); 25 | ?> -------------------------------------------------------------------------------- /test/offline.js: -------------------------------------------------------------------------------- 1 | const { Launch, Mojang } = require('minecraft-java-core'); 2 | const launcher = new Launch(); 3 | (async () => { 4 | await launcher.Launch({ 5 | path: './minecraft', 6 | authenticator: await Mojang.login('Luuxis'), 7 | version: '1.16.5', 8 | intelEnabledMac: true, 9 | bypassOffline: true, 10 | loader: { 11 | path: './', 12 | type: 'fabric', 13 | build: 'latest', 14 | enable: true 15 | }, 16 | memory: { 17 | min: '2G', 18 | max: '4G' 19 | } 20 | }); 21 | 22 | launcher.on('progress', (progress, size) => console.log(`[DL] ${((progress / size) * 100).toFixed(2)}%`)) 23 | .on('patch', pacth => process.stdout.write(pacth)) 24 | .on('data', line => process.stdout.write(line)) 25 | .on('close', () => console.log('Game exited.')); 26 | })(); 27 | -------------------------------------------------------------------------------- /webfiles/index.php: -------------------------------------------------------------------------------- 1 | $value, "url" => $url); 27 | } 28 | 29 | include 'php/instances.php'; 30 | echo str_replace("\\", "", json_encode($instance)); 31 | exit; 32 | } 33 | 34 | echo dirToArray("instances/$instance_param"); 35 | ?> -------------------------------------------------------------------------------- /webfiles/php/scandir.php: -------------------------------------------------------------------------------- 1 | $value) { 33 | $hash = hash_file('sha1', $dir . "/" . $value); 34 | $size = filesize($dir . "/" . $value); 35 | $path = str_replace("$dir/", "", $dir . "/" . $value); 36 | 37 | $url_req = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); 38 | $url = "http://$_SERVER[HTTP_HOST]$url_req$dir/$path"; 39 | $res[] = array("url" => $url, "size" => $size, "hash" => $hash, "path" => $path); 40 | } 41 | return str_replace("\\", "", json_encode($res)); 42 | } 43 | ?> -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minecraft-java-core", 3 | "version": "4.2.3", 4 | "types": "./build/Index.d.ts", 5 | "exports": { 6 | ".": { 7 | "import": "./build/Index.js", 8 | "require": "./build/Index.js" 9 | } 10 | }, 11 | "description": "A library starting minecraft game NW.js and Electron.js", 12 | "scripts": { 13 | "dev": "rimraf ./build && tsc -w", 14 | "build": "rimraf ./build && tsc", 15 | "prepublishOnly": "npm i && npm run build" 16 | }, 17 | "engines": { 18 | "node": ">=18.0.0" 19 | }, 20 | "files": [ 21 | "assets/**", 22 | "build/**", 23 | "LICENSE", 24 | "README.md" 25 | ], 26 | "keywords": [ 27 | "Minecraft", 28 | "Launcher", 29 | "Node-Minecraft", 30 | "Game", 31 | "Minecraft-Launcher", 32 | "Forge", 33 | "Minecraft-Forge" 34 | ], 35 | "author": "Luuxis", 36 | "license": "CCANC", 37 | "dependencies": { 38 | "prompt": "^1.3.0", 39 | "semver": "^7.7.2", 40 | "tslib": "^2.8.1" 41 | }, 42 | "devDependencies": { 43 | "@types/node": "^22.10.2", 44 | "@types/semver": "^7.7.0", 45 | "rimraf": "^6.0.1", 46 | "typescript": "^5.7.2" 47 | }, 48 | "bugs": { 49 | "url": "https://github.com/luuxis/minecraft-java-core/issues" 50 | }, 51 | "homepage": "https://github.com/luuxis/minecraft-java-core#readme", 52 | "repository": { 53 | "type": "git", 54 | "url": "git+https://github.com/luuxis/minecraft-java-core.git" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const { Launch, Microsoft } = require('minecraft-java-core'); 2 | const launcher = new Launch(); 3 | 4 | const fs = require('fs'); 5 | let mc 6 | 7 | (async () => { 8 | if (!fs.existsSync('./account.json')) { 9 | mc = await new Microsoft().getAuth(); 10 | fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); 11 | } else { 12 | mc = JSON.parse(fs.readFileSync('./account.json')); 13 | if (!mc.refresh_token) { 14 | mc = await new Microsoft().getAuth(); 15 | fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); 16 | } else { 17 | mc = await new Microsoft().refresh(mc); 18 | fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); 19 | if (mc.error) process.exit(1); 20 | } 21 | } 22 | 23 | const opt = { 24 | url: "https://luuxcraft.fr/api/user/48c74227-13d1-48d6-931b-0f12b73da340/instance", 25 | path: './minecraft', 26 | authenticator: mc, 27 | version: '1.8.9', 28 | intelEnabledMac: true, 29 | instance: "Hypixel", 30 | 31 | ignored: [ 32 | "config", 33 | "logs", 34 | "resourcepacks", 35 | "options.txt", 36 | "optionsof.txt" 37 | ], 38 | 39 | loader: { 40 | type: 'forge', 41 | build: 'latest', 42 | enable: true 43 | }, 44 | memory: { 45 | min: '14G', 46 | max: '16G' 47 | }, 48 | }; 49 | 50 | launcher.Launch(opt); 51 | launcher.on('progress', (progress, size) => console.log(`[DL] ${((progress / size) * 100).toFixed(2)}%`)); 52 | launcher.on('patch', pacth => process.stdout.write(pacth)); 53 | launcher.on('data', line => process.stdout.write(line)); 54 | launcher.on('error', err => console.error(err)); 55 | })(); 56 | -------------------------------------------------------------------------------- /src/StatusServer/buffer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | function CustomBuffer(existingBuffer: any = Buffer.alloc(48)) { 7 | let buffer = existingBuffer; 8 | let offset = 0; 9 | 10 | this.writeletInt = (val: any) => { 11 | while (true) { 12 | if ((val & 0xFFFFFF80) == 0) { 13 | return this.writeUByte(val); 14 | } 15 | this.writeUByte(val & 0x7F | 0x80); 16 | val = val >>> 7; 17 | } 18 | }; 19 | 20 | this.writeString = (string: any) => { 21 | this.writeletInt(string.length); 22 | if (offset + string.length >= buffer.length) Buffer.concat([buffer, new Buffer(string.length)]); 23 | buffer.write(string, offset, string.length, "UTF-8"); 24 | offset += string.length; 25 | }; 26 | 27 | this.writeUShort = (val: any) => { 28 | this.writeUByte(val >> 8); 29 | this.writeUByte(val & 0xFF); 30 | }; 31 | 32 | this.writeUByte = (val: any) => { 33 | if (offset >= buffer.length) { 34 | buffer = Buffer.concat([buffer, new Buffer(50)]); 35 | } 36 | 37 | buffer.writeUInt8(val, offset++); 38 | }; 39 | 40 | this.readletInt = function () { 41 | let val = 0; 42 | let count = 0; 43 | 44 | while (true) { 45 | let i = buffer.readUInt8(offset++); 46 | val |= (i & 0x7F) << count++ * 7; 47 | if ((i & 0x80) != 128) break 48 | } 49 | return val; 50 | }; 51 | 52 | this.readString = () => { 53 | let length = this.readletInt(); 54 | let str = buffer.toString("UTF-8", offset, offset + length); 55 | offset += length; 56 | return str; 57 | }; 58 | 59 | this.buffer = () => { 60 | return buffer.slice(0, offset); 61 | }; 62 | 63 | this.offset = () => { 64 | return offset; 65 | }; 66 | } 67 | 68 | export default CustomBuffer -------------------------------------------------------------------------------- /src/Authenticator/GUI/NW.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import path from 'path'; 7 | 8 | const defaultProperties = { 9 | width: 1000, 10 | height: 650, 11 | resizable: false, 12 | position: "center", 13 | frame: true, 14 | icon: path.join(__dirname, '../../../assets/icons/Microsoft.png') 15 | } 16 | 17 | module.exports = async function (url: string, redirect_uri: string = "https://login.live.com/oauth20_desktop.srf") { 18 | await new Promise((resolve: any) => { 19 | //@ts-ignore 20 | nw.Window.get().cookies.getAll({ domain: "live.com" }, async (cookies) => { 21 | for await (let cookie of cookies) { 22 | let url = `http${cookie.secure ? "s" : ""}://${cookie.domain.replace(/$\./, "") + cookie.path}`; 23 | //@ts-ignore 24 | nw.Window.get().cookies.remove({ url: url, name: cookie.name }); 25 | } 26 | return resolve(); 27 | }); 28 | }); 29 | 30 | let code = await new Promise((resolve) => { 31 | //@ts-ignore 32 | nw.Window.open(url, defaultProperties, (Window: any) => { 33 | let interval = null; 34 | let code; 35 | interval = setInterval(() => { 36 | if (Window.window.document.location.href.startsWith(redirect_uri)) { 37 | clearInterval(interval); 38 | try { 39 | code = Window.window.document.location.href.split("code=")[1].split("&")[0]; 40 | } catch (e) { 41 | code = "cancel"; 42 | } 43 | Window.close(); 44 | } 45 | }, 100); 46 | 47 | Window.on('closed', () => { 48 | if (!code) code = "cancel"; 49 | if (interval) clearInterval(interval); 50 | resolve(code); 51 | }); 52 | }); 53 | }); 54 | return code 55 | } -------------------------------------------------------------------------------- /src/Authenticator/GUI/Electron.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | const path = require('path') 7 | const { app, BrowserWindow, session } = require('electron') 8 | 9 | const defaultProperties = { 10 | width: 1000, 11 | height: 650, 12 | resizable: false, 13 | center: true, 14 | icon: path.join(__dirname, '../../../assets/icons', `Microsoft.${(process.platform === 'win32') ? 'ico' : 'png'}`), 15 | } 16 | 17 | module.exports = async function (url: string, redirect_uri: string = "https://login.live.com/oauth20_desktop.srf") { 18 | await new Promise((resolve: any) => { 19 | app.whenReady().then(() => { 20 | session.defaultSession.cookies.get({ domain: 'live.com' }).then((cookies: any) => { 21 | for (let cookie of cookies) { 22 | let urlcookie = `http${cookie.secure ? "s" : ""}://${cookie.domain.replace(/$\./, "") + cookie.path}`; 23 | session.defaultSession.cookies.remove(urlcookie, cookie.name) 24 | } 25 | }) 26 | return resolve(); 27 | }) 28 | }) 29 | 30 | return new Promise(resolve => { 31 | app.whenReady().then(() => { 32 | const mainWindow = new BrowserWindow(defaultProperties) 33 | mainWindow.setMenu(null); 34 | mainWindow.loadURL(url); 35 | var loading = false; 36 | 37 | mainWindow.on("close", () => { 38 | if (!loading) resolve("cancel"); 39 | }) 40 | 41 | mainWindow.webContents.on("did-finish-load", () => { 42 | const loc = mainWindow.webContents.getURL(); 43 | if (loc.startsWith(redirect_uri)) { 44 | const urlParams = new URLSearchParams(loc.substr(loc.indexOf("?") + 1)).get("code"); 45 | if (urlParams) { 46 | resolve(urlParams); 47 | loading = true; 48 | } else { 49 | resolve("cancel"); 50 | } 51 | try { 52 | mainWindow.close(); 53 | } catch { 54 | console.error("Failed to close window!"); 55 | } 56 | } 57 | }) 58 | }) 59 | }) 60 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Signaler un bug 2 | description: Vous avez rencontré un bug? Signalez-le ici 3 | title: "[Bug] " 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Un grand merci d'avance pour votre aide. Néanmoins, nous avons besoin d'un certain nombre d'informations, pour nous aider. 10 | - type: checkboxes 11 | attributes: 12 | label: "Liste des vérifications à faire avant de valider l'ouverture du signalement de bug" 13 | description: Assurez que vous avez complété ce qui suit, dans le cas contraire, votre rapport peut être refusé 14 | options: 15 | - label: J'ai réussi à reproduire le bug sur le Selvania Launcher (sans mes modifications) 16 | required: true 17 | - label: Mon code respecte la licence Creative Commons Zero v1.0 Universal 18 | required: true 19 | - label: Mon code respecte les conditions d'utilisation du Selvania Launcher 20 | required: true 21 | - label: J'arrive à reproduire le bug sur la dernière version du Selvania Launcher 22 | required: true 23 | - type: dropdown 24 | attributes: 25 | label: Système d'exploitation 26 | options: 27 | - Windows 28 | - macOS 29 | - Linux (Basé sur Debian/Ubuntu) 30 | - Linux (Autres) 31 | validations: 32 | required: true 33 | - type: input 34 | attributes: 35 | label: Version du système d'exploitation 36 | placeholder: "Exemple: Windows 11 Professionnel 21H2 Build 22000.739" 37 | validations: 38 | required: true 39 | - type: input 40 | attributes: 41 | label: Hash du commit sur lequel le bug est rencontré 42 | placeholder: 84d7881b67ecf6088205eca6723bfb19bf2a5f0d 43 | - type: textarea 44 | attributes: 45 | label: Comportement attendu 46 | description: Une description de ce qui devrait se passer 47 | placeholder: Le launcher devrait... 48 | validations: 49 | required: true 50 | - type: textarea 51 | attributes: 52 | label: Comportement actuel 53 | description: Une description de ce qui se passe avec le bug 54 | validations: 55 | required: true 56 | - type: textarea 57 | attributes: 58 | label: Instructions pour reproduire le but 59 | placeholder: | 60 | 1. Ouvrir le launcher 61 | 2. Aller dans le menu xyz 62 | 3. Cliquer sur abc 63 | 4. Observer 64 | validations: 65 | required: true 66 | - type: textarea 67 | attributes: 68 | label: Notes additionnelles 69 | placeholder: Détails supplémentaires concernant le bug, tout ce qui pourrait être utile 70 | validations: 71 | required: false 72 | -------------------------------------------------------------------------------- /src/StatusServer/status.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import net from 'net' 7 | import createBuffer from './buffer.js'; 8 | 9 | function ping(server: any, port: any, callback: any, timeout: any, protocol: any = '') { 10 | let start: any = new Date(); 11 | let socket = net.connect({ 12 | port: port, 13 | host: server 14 | }, () => { 15 | let handshakeBuffer = new createBuffer(); 16 | 17 | handshakeBuffer.writeletInt(0); 18 | handshakeBuffer.writeletInt(protocol); 19 | handshakeBuffer.writeString(server); 20 | handshakeBuffer.writeUShort(port); 21 | handshakeBuffer.writeletInt(1); 22 | 23 | writePCBuffer(socket, handshakeBuffer); 24 | 25 | let setModeBuffer = new createBuffer(); 26 | 27 | setModeBuffer.writeletInt(0); 28 | 29 | writePCBuffer(socket, setModeBuffer); 30 | }); 31 | 32 | socket.setTimeout(timeout, () => { 33 | if (callback) callback(new Error("Socket timed out when connecting to " + server + ":" + port), null); 34 | socket.destroy(); 35 | }); 36 | 37 | let readingBuffer = Buffer.alloc(0); 38 | 39 | socket.on('data', data => { 40 | readingBuffer = Buffer.concat([readingBuffer, data]); 41 | 42 | let buffer = new createBuffer(readingBuffer); 43 | let length: any; 44 | 45 | try { 46 | length = buffer.readletInt(); 47 | } catch (err) { 48 | return; 49 | } 50 | 51 | if (readingBuffer.length < length - buffer.offset()) return; 52 | 53 | buffer.readletInt(); 54 | 55 | try { 56 | let end: any = new Date() 57 | let json = JSON.parse(buffer.readString()); 58 | callback(null, { 59 | error: false, 60 | ms: Math.round(end - start), 61 | version: json.version.name, 62 | playersConnect: json.players.online, 63 | playersMax: json.players.max 64 | }); 65 | } catch (err) { 66 | return callback(err, null); 67 | } 68 | 69 | socket.destroy(); 70 | }); 71 | 72 | socket.once('error', err => { 73 | if (callback) callback(err, null); 74 | socket.destroy(); 75 | }); 76 | }; 77 | 78 | function writePCBuffer(client: any, buffer: any) { 79 | let length = new createBuffer(); 80 | length.writeletInt(buffer.buffer().length); 81 | client.write(Buffer.concat([length.buffer(), buffer.buffer()])); 82 | } 83 | 84 | export default class status { 85 | ip: string 86 | port: number 87 | constructor(ip = '0.0.0.0', port = 25565) { 88 | this.ip = ip 89 | this.port = port 90 | } 91 | 92 | async getStatus() { 93 | return await new Promise((resolve, reject) => { 94 | ping(this.ip, this.port, (err: any, res: any) => { 95 | if (err) return reject({ error: err }); 96 | return resolve(res); 97 | }, 3000); 98 | }) 99 | } 100 | } -------------------------------------------------------------------------------- /src/Authenticator/Mojang.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import crypto from 'crypto'; 7 | 8 | let api_url = 'https://authserver.mojang.com'; 9 | 10 | 11 | async function login(username: string, password?: string) { 12 | let UUID = crypto.randomBytes(16).toString('hex'); 13 | if (!password) { 14 | return { 15 | access_token: UUID, 16 | client_token: UUID, 17 | uuid: UUID, 18 | name: username, 19 | user_properties: '{}', 20 | meta: { 21 | online: false, 22 | type: 'Mojang' 23 | } 24 | } 25 | } 26 | 27 | let message = await fetch(`${api_url}/authenticate`, { 28 | method: 'POST', 29 | headers: { 30 | 'Content-Type': 'application/json' 31 | }, 32 | body: JSON.stringify({ 33 | agent: { 34 | name: "Minecraft", 35 | version: 1 36 | }, 37 | username, 38 | password, 39 | clientToken: UUID, 40 | requestUser: true 41 | }) 42 | }).then(res => res.json()); 43 | 44 | if (message.error) { 45 | return message; 46 | }; 47 | let user = { 48 | access_token: message.accessToken, 49 | client_token: message.clientToken, 50 | uuid: message.selectedProfile.id, 51 | name: message.selectedProfile.name, 52 | user_properties: '{}', 53 | meta: { 54 | online: true, 55 | type: 'Mojang' 56 | } 57 | } 58 | return user; 59 | } 60 | 61 | async function refresh(acc: any) { 62 | let message = await fetch(`${api_url}/refresh`, { 63 | method: 'POST', 64 | headers: { 65 | 'Content-Type': 'application/json' 66 | }, 67 | body: JSON.stringify({ 68 | accessToken: acc.access_token, 69 | clientToken: acc.client_token, 70 | requestUser: true 71 | }) 72 | }).then(res => res.json()); 73 | 74 | if (message.error) { 75 | return message; 76 | }; 77 | 78 | let user = { 79 | access_token: message.accessToken, 80 | client_token: message.clientToken, 81 | uuid: message.selectedProfile.id, 82 | name: message.selectedProfile.name, 83 | user_properties: '{}', 84 | meta: { 85 | online: true, 86 | type: 'Mojang' 87 | } 88 | } 89 | return user; 90 | } 91 | 92 | async function validate(acc: any) { 93 | let message = await fetch(`${api_url}/validate`, { 94 | method: 'POST', 95 | headers: { 96 | 'Content-Type': 'application/json' 97 | }, 98 | body: JSON.stringify({ 99 | accessToken: acc.access_token, 100 | clientToken: acc.client_token, 101 | }) 102 | }); 103 | 104 | if (message.status == 204) { 105 | return true; 106 | } else { 107 | return false; 108 | } 109 | } 110 | 111 | async function signout(acc: any) { 112 | let message = await fetch(`${api_url}/invalidate`, { 113 | method: 'POST', 114 | headers: { 115 | 'Content-Type': 'application/json' 116 | }, 117 | body: JSON.stringify({ 118 | accessToken: acc.access_token, 119 | clientToken: acc.client_token, 120 | }) 121 | }).then(res => res.text()); 122 | 123 | if (message == "") { 124 | return true; 125 | } else { 126 | return false; 127 | } 128 | } 129 | 130 | function ChangeAuthApi(url: string) { 131 | api_url = url 132 | } 133 | 134 | export { 135 | login as login, 136 | refresh as refresh, 137 | validate as validate, 138 | signout as signout, 139 | ChangeAuthApi as ChangeAuthApi 140 | } -------------------------------------------------------------------------------- /test/AZauth.js: -------------------------------------------------------------------------------- 1 | const prompt = require('prompt') 2 | const { AZauth, Launch } = require('../build/Index'); 3 | const launch = new Launch(); 4 | const auth = new AZauth('https://nincraft.fr'); 5 | const fs = require('fs'); 6 | 7 | let mc 8 | async function login() { 9 | console.log('set your email or username'); 10 | prompt.start(); 11 | let { email } = await prompt.get(['email']); 12 | console.log('set your password'); 13 | let { password } = await prompt.get(['password']); 14 | let azauth = await auth.login(email, password); 15 | 16 | if (azauth.A2F) { 17 | console.log('set your 2FA code'); 18 | let { code } = await prompt.get(['code']); 19 | azauth = await auth.login(email, password, code); 20 | } 21 | 22 | if (azauth.error) { 23 | console.log(azauth); 24 | process.exit(1); 25 | } 26 | return azauth; 27 | } 28 | 29 | async function main() { 30 | if (!fs.existsSync('./AZauth.json')) { 31 | mc = await login(); 32 | fs.writeFileSync('./AZauth.json', JSON.stringify(mc, null, 4)); 33 | } else { 34 | mc = JSON.parse(fs.readFileSync('./AZauth.json')); 35 | 36 | if (!mc.access_token) { 37 | mc = await login(); 38 | fs.writeFileSync('./AZauth.json', JSON.stringify(mc, null, 4)); 39 | } else { 40 | mc = await auth.verify(mc); 41 | if (mc.error) mc = await login(); 42 | fs.writeFileSync('./AZauth.json', JSON.stringify(mc, null, 4)); 43 | } 44 | } 45 | 46 | let opt = { 47 | // url: 'https://luuxis.fr/api/user/893bbc-a0bc41-da8568-ef56dd-7f2df8/files', 48 | authenticator: mc, 49 | timeout: 10000, 50 | path: './Minecraft', 51 | version: '1.16.5', 52 | detached: false, 53 | intelEnabledMac: true, 54 | downloadFileMultiple: 10, 55 | 56 | loader: { 57 | type: 'forge', 58 | build: 'latest', 59 | enable: true 60 | }, 61 | 62 | verify: false, 63 | ignored: ['loader', 'options.txt'], 64 | args: [], 65 | 66 | javaPath: null, 67 | java: true, 68 | 69 | screen: { 70 | width: null, 71 | height: null, 72 | fullscreen: null, 73 | }, 74 | 75 | memory: { 76 | min: '2G', 77 | max: '4G' 78 | } 79 | } 80 | 81 | await launch.Launch(opt); 82 | 83 | launch.on('extract', extract => { 84 | console.log(extract); 85 | }); 86 | 87 | launch.on('progress', (progress, size, element) => { 88 | console.log(`Downloading ${element} ${Math.round((progress / size) * 100)}%`); 89 | }); 90 | 91 | launch.on('check', (progress, size, element) => { 92 | console.log(`Checking ${element} ${Math.round((progress / size) * 100)}%`); 93 | }); 94 | 95 | launch.on('estimated', (time) => { 96 | let hours = Math.floor(time / 3600); 97 | let minutes = Math.floor((time - hours * 3600) / 60); 98 | let seconds = Math.floor(time - hours * 3600 - minutes * 60); 99 | console.log(`${hours}h ${minutes}m ${seconds}s`); 100 | }) 101 | 102 | launch.on('speed', (speed) => { 103 | console.log(`${(speed / 1067008).toFixed(2)} Mb/s`) 104 | }) 105 | 106 | launch.on('patch', patch => { 107 | console.log(patch); 108 | }); 109 | 110 | launch.on('data', (e) => { 111 | console.log(e); 112 | }) 113 | 114 | launch.on('close', code => { 115 | console.log(code); 116 | }); 117 | 118 | launch.on('error', err => { 119 | console.log(err); 120 | }); 121 | } 122 | 123 | main() -------------------------------------------------------------------------------- /assets/LWJGL/aarch/2.9.4.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "classifiers": { 6 | "natives-linux": { 7 | "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar", 8 | "sha1": "f3c455b71c5146acb5f8a9513247fc06db182fd5", 9 | "size": 4521, 10 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-2.9.4/jinput-platform-2.0.5-natives-linux.jar" 11 | } 12 | } 13 | }, 14 | "extract": { 15 | "exclude": [ 16 | "META-INF/" 17 | ] 18 | }, 19 | "name": "net.java.jinput:jinput-platform:2.0.5", 20 | "natives": { 21 | "linux": "natives-linux" 22 | } 23 | }, 24 | { 25 | "downloads": { 26 | "artifact": { 27 | "path": "net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar", 28 | "sha1": "c2e322bbec2345f1b93b96000f93e3a4c3b2bf96", 29 | "size": 216945, 30 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-2.9.4/jinput-2.0.5.jar" 31 | } 32 | }, 33 | "name": "net.java.jinput:jinput:2.0.5" 34 | }, 35 | { 36 | "downloads": { 37 | "artifact": { 38 | "path": "net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar", 39 | "sha1": "e12fe1fda814bd348c1579329c86943d2cd3c6a6", 40 | "size": 7508, 41 | "url": "https://libraries.minecraft.net/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar" 42 | } 43 | }, 44 | "name": "net.java.jutils:jutils:1.0.0" 45 | }, 46 | { 47 | "downloads": { 48 | "artifact": { 49 | "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar", 50 | "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33", 51 | "size": 22, 52 | "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" 53 | }, 54 | "classifiers": { 55 | "natives-linux": { 56 | "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar", 57 | "sha1": "fa483e540a9a753a5ffbb23dcf7879a5bf752611", 58 | "size": 475177, 59 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-2.9.4/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" 60 | } 61 | } 62 | }, 63 | "extract": { 64 | "exclude": [ 65 | "META-INF/" 66 | ] 67 | }, 68 | "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209", 69 | "natives": { 70 | "linux": "natives-linux" 71 | } 72 | }, 73 | { 74 | "downloads": { 75 | "artifact": { 76 | "path": "org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar", 77 | "sha1": "697517568c68e78ae0b4544145af031c81082dfe", 78 | "size": 1047168, 79 | "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar" 80 | } 81 | }, 82 | "name": "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209" 83 | }, 84 | { 85 | "downloads": { 86 | "artifact": { 87 | "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar", 88 | "sha1": "d51a7c040a721d13efdfbd34f8b257b2df882ad0", 89 | "size": 173887, 90 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-2.9.4/lwjgl_util-2.9.4-nightly-20150209.jar" 91 | } 92 | }, 93 | "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209" 94 | } 95 | ] 96 | } -------------------------------------------------------------------------------- /assets/LWJGL/aarch64/2.9.4.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "classifiers": { 6 | "natives-linux": { 7 | "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar", 8 | "sha1": "42b388ccb7c63cec4e9f24f4dddef33325f8b212", 9 | "size": 10932, 10 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-2.9.4/jinput-platform-2.0.5-natives-linux.jar" 11 | } 12 | } 13 | }, 14 | "extract": { 15 | "exclude": [ 16 | "META-INF/" 17 | ] 18 | }, 19 | "name": "net.java.jinput:jinput-platform:2.0.5", 20 | "natives": { 21 | "linux": "natives-linux" 22 | } 23 | }, 24 | { 25 | "downloads": { 26 | "artifact": { 27 | "path": "net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar", 28 | "sha1": "47f50f20c60495069c5a3cab65f5f87b44c1069e", 29 | "size": 216970, 30 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-2.9.4/jinput-2.0.5.jar" 31 | } 32 | }, 33 | "name": "net.java.jinput:jinput:2.0.5" 34 | }, 35 | { 36 | "downloads": { 37 | "artifact": { 38 | "path": "net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar", 39 | "sha1": "e12fe1fda814bd348c1579329c86943d2cd3c6a6", 40 | "size": 7508, 41 | "url": "https://libraries.minecraft.net/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar" 42 | } 43 | }, 44 | "name": "net.java.jutils:jutils:1.0.0" 45 | }, 46 | { 47 | "downloads": { 48 | "artifact": { 49 | "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar", 50 | "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33", 51 | "size": 22, 52 | "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" 53 | }, 54 | "classifiers": { 55 | "natives-linux": { 56 | "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar", 57 | "sha1": "63ac7da0f4a4785c7eadc0f8edc1e9dcc4dd08cb", 58 | "size": 579979, 59 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-2.9.4/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" 60 | } 61 | } 62 | }, 63 | "extract": { 64 | "exclude": [ 65 | "META-INF/" 66 | ] 67 | }, 68 | "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209", 69 | "natives": { 70 | "linux": "natives-linux" 71 | } 72 | }, 73 | { 74 | "downloads": { 75 | "artifact": { 76 | "path": "org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar", 77 | "sha1": "697517568c68e78ae0b4544145af031c81082dfe", 78 | "size": 1047168, 79 | "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar" 80 | } 81 | }, 82 | "name": "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209" 83 | }, 84 | { 85 | "downloads": { 86 | "artifact": { 87 | "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar", 88 | "sha1": "d51a7c040a721d13efdfbd34f8b257b2df882ad0", 89 | "size": 173887, 90 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-2.9.4/lwjgl_util-2.9.4-nightly-20150209.jar" 91 | } 92 | }, 93 | "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209" 94 | } 95 | ] 96 | } -------------------------------------------------------------------------------- /src/Minecraft/Minecraft-Json.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import os from 'os'; 7 | import MinecraftNativeLinuxARM from './Minecraft-Lwjgl-Native.js'; 8 | 9 | /** 10 | * Basic structure for options passed to the Json class. 11 | * Modify or expand based on your actual usage. 12 | */ 13 | export interface JsonOptions { 14 | version: string; // The targeted Minecraft version (e.g. "1.19", "latest_release", etc.) 15 | [key: string]: any; // Include any additional fields needed by your code 16 | } 17 | 18 | /** 19 | * Represents a single version entry from Mojang's version manifest. 20 | */ 21 | export interface VersionEntry { 22 | id: string; 23 | type: string; 24 | url: string; 25 | time: string; 26 | releaseTime: string; 27 | } 28 | 29 | /** 30 | * Structure of the Mojang version manifest (simplified). 31 | */ 32 | export interface MojangVersionManifest { 33 | latest: { 34 | release: string; 35 | snapshot: string; 36 | }; 37 | versions: VersionEntry[]; 38 | } 39 | 40 | /** 41 | * Structure returned by the getInfoVersion method on success. 42 | */ 43 | export interface GetInfoVersionResult { 44 | InfoVersion: VersionEntry; 45 | json: any; // The specific version JSON fetched from Mojang 46 | version: string; // The final resolved version (e.g., "1.19" if "latest_release" was given) 47 | } 48 | 49 | /** 50 | * Structure returned by getInfoVersion if an error occurs (version not found). 51 | */ 52 | export interface GetInfoVersionError { 53 | error: true; 54 | message: string; 55 | } 56 | 57 | /** 58 | * This class retrieves Minecraft version information from Mojang's 59 | * version manifest, and optionally processes the JSON for ARM-based Linux. 60 | */ 61 | export default class Json { 62 | private readonly options: JsonOptions; 63 | 64 | constructor(options: JsonOptions) { 65 | this.options = options; 66 | } 67 | 68 | /** 69 | * Fetches the Mojang version manifest, resolves the intended version (release, snapshot, etc.), 70 | * and returns the associated JSON object for that version. 71 | * If the system is Linux ARM, it will run additional processing on the JSON. 72 | * 73 | * @returns An object containing { InfoVersion, json, version }, or an error object. 74 | */ 75 | public async GetInfoVersion(): Promise { 76 | let { version } = this.options; 77 | 78 | // Fetch the version manifest 79 | const response = await fetch( 80 | `https://launchermeta.mojang.com/mc/game/version_manifest_v2.json?_t=${new Date().toISOString()}` 81 | ); 82 | const manifest: MojangVersionManifest = await response.json(); 83 | 84 | // Resolve "latest_release"/"latest_snapshot" shorthands 85 | if (version === 'latest_release' || version === 'r' || version === 'lr') { 86 | version = manifest.latest.release; 87 | } else if (version === 'latest_snapshot' || version === 's' || version === 'ls') { 88 | version = manifest.latest.snapshot; 89 | } 90 | 91 | // Find the matching version info from the manifest 92 | const matchedVersion = manifest.versions.find((v) => v.id === version); 93 | if (!matchedVersion) { 94 | return { 95 | error: true, 96 | message: `Minecraft ${version} is not found.` 97 | }; 98 | } 99 | 100 | // Fetch the detailed version JSON from Mojang 101 | const jsonResponse = await fetch(matchedVersion.url); 102 | let versionJson = await jsonResponse.json(); 103 | 104 | // If on Linux ARM, run additional processing 105 | if (os.platform() === 'linux' && os.arch().startsWith('arm')) { 106 | versionJson = await new MinecraftNativeLinuxARM(this.options).ProcessJson(versionJson); 107 | } 108 | 109 | return { 110 | InfoVersion: matchedVersion, 111 | json: versionJson, 112 | version 113 | }; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Minecraft/Minecraft-Lwjgl-Native.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import path from 'path'; 7 | import fs from 'fs'; 8 | import os from 'os'; 9 | 10 | /** 11 | * Minimal interface describing a single library entry in the version JSON. 12 | * Adjust as needed to reflect your actual library data structure. 13 | */ 14 | interface MinecraftLibrary { 15 | name: string; 16 | // You may include other fields like 'downloads', 'natives', etc. if needed 17 | } 18 | 19 | /** 20 | * Represents the structure of a Minecraft version JSON. 21 | * You can expand this interface based on your actual usage. 22 | */ 23 | interface MinecraftVersion { 24 | libraries: MinecraftLibrary[]; 25 | // Additional fields like 'id', 'mainClass', etc. can go here 26 | } 27 | 28 | /** 29 | * Options for constructing the MinecraftLoader, if needed. 30 | * Extend or remove fields to match your actual requirements. 31 | */ 32 | interface LoaderOptions { 33 | // Add any relevant configuration fields if needed 34 | [key: string]: unknown; 35 | } 36 | 37 | /** 38 | * This class modifies the version JSON for ARM-based Linux systems, 39 | * specifically handling LWJGL library replacements for versions 2.9.x or custom LWJGL versions. 40 | */ 41 | export default class MinecraftLoader { 42 | private options: LoaderOptions; 43 | 44 | constructor(options: LoaderOptions) { 45 | this.options = options; 46 | } 47 | 48 | /** 49 | * Processes a Minecraft version JSON, removing default JInput and LWJGL entries 50 | * if needed, then injecting ARM-compatible LWJGL libraries from local JSON files. 51 | * 52 | * @param version A MinecraftVersion object containing a list of libraries 53 | * @returns The same version object, but with updated libraries for ARM-based Linux 54 | */ 55 | public async ProcessJson(version: MinecraftVersion): Promise { 56 | // Maps Node's arm architecture to the expected LWJGL naming 57 | const archMapping: Record = { 58 | arm64: 'aarch64', 59 | arm: 'aarch' 60 | }; 61 | const currentArch = os.arch(); 62 | const mappedArch = archMapping[currentArch]; 63 | 64 | // If running on a non-ARM environment, or if the mapping doesn't exist, no changes are needed 65 | if (!mappedArch) { 66 | return version; 67 | } 68 | 69 | // Path to the directory containing LWJGL JSON files for ARM 70 | const pathLWJGL = path.join(__dirname, '../../assets/LWJGL', mappedArch); 71 | 72 | // Identify the version strings for JInput and LWJGL from the existing libraries 73 | const versionJinput = version.libraries.find(lib => 74 | lib.name.startsWith('net.java.jinput:jinput-platform:') || 75 | lib.name.startsWith('net.java.jinput:jinput:') 76 | )?.name.split(':').pop(); 77 | 78 | const versionLWJGL = version.libraries.find(lib => 79 | lib.name.startsWith('org.lwjgl:lwjgl:') || 80 | lib.name.startsWith('org.lwjgl.lwjgl:lwjgl:') 81 | )?.name.split(':').pop(); 82 | 83 | // Remove all JInput-related libraries if a JInput version is found 84 | if (versionJinput) { 85 | version.libraries = version.libraries.filter(lib => !lib.name.includes('jinput')); 86 | } 87 | 88 | // Remove all LWJGL-related libraries if an LWJGL version is found 89 | if (versionLWJGL) { 90 | version.libraries = version.libraries.filter(lib => !lib.name.includes('lwjgl')); 91 | 92 | // Inject ARM-compatible LWJGL libraries 93 | let lwjglJsonFile = versionLWJGL.includes('2.9') 94 | ? '2.9.4.json' 95 | : `${versionLWJGL}.json`; 96 | 97 | const lwjglPath = path.join(pathLWJGL, lwjglJsonFile); 98 | 99 | // Read the appropriate LWJGL JSON (e.g., "2.9.4.json" or ".json") 100 | const lwjglNativesContent = fs.readFileSync(lwjglPath, 'utf-8'); 101 | const lwjglNatives = JSON.parse(lwjglNativesContent) as MinecraftVersion; 102 | 103 | // Append the ARM-compatible libraries 104 | version.libraries.push(...lwjglNatives.libraries); 105 | } 106 | 107 | return version; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | LUUXIS LICENSE v1.0 – LICENCE PERSONNALISÉE / CUSTOM LICENSE 2 | 3 | Copyright © 2025 Luuxis 4 | 5 | FRANÇAIS 🇫🇷 6 | ──────────────────────────────────────────────────────────── 7 | 8 | Ce logiciel et son code source sont la propriété exclusive de l’auteur (ci-après « le Titulaire »). 9 | L’utilisation, la modification et la redistribution sont autorisées sous réserve du respect strict des conditions suivantes : 10 | 11 | 1. 🛑 Interdiction d’usage commercial par des tiers 12 | - Il est strictement interdit de vendre, louer ou redistribuer ce code (ou ses dérivés) à des fins commerciales. 13 | - Toute utilisation commerciale directe du code est interdite, sauf autorisation écrite explicite du Titulaire. 14 | 15 | 2. 💰 Microtransactions autorisées 16 | - La monétisation par microtransactions en jeu est autorisée, tant que : 17 | - le code n’est pas vendu ni monétisé directement, 18 | - le code source reste public et accessible. 19 | 20 | 3. 📂 Code source obligatoire et public 21 | - Toute version redistribuée ou modifiée doit publier son code source complet, librement et gratuitement accessible, sans restriction. 22 | 23 | 4. 🧾 Attribution 24 | - Le nom de l’auteur original (« Luuxis ») doit être clairement mentionné dans toute redistribution ou version modifiée. 25 | 26 | 5. 🔐 Droit exclusif de revente par le créateur 27 | - Seul le Titulaire (Luuxis) est autorisé à vendre ou concéder une licence commerciale du code source. 28 | 29 | 6. 🚫 Interdiction de modifier cette licence 30 | - Il est interdit de modifier, supprimer ou remplacer cette licence. Toute redistribution doit inclure cette licence sans altération. 31 | 32 | 7. ⚠️ Absence de garantie 33 | - Le logiciel est fourni "tel quel", sans garantie. L’auteur décline toute responsabilité pour tout dommage lié à son usage. 34 | 35 | 8. ⚖️ Violation et poursuites 36 | - Tout non-respect entraîne la résiliation immédiate de cette licence. L’usage non autorisé peut entraîner des poursuites conformément aux articles L.122-1 et L.335-3 du Code de la propriété intellectuelle (France). 37 | 38 | Par l’utilisation de ce logiciel, vous acceptez toutes les conditions de cette licence. 39 | 40 | ──────────────────────────────────────────────────────────── 41 | 42 | ENGLISH 🇬🇧 43 | ──────────────────────────────────────────────────────────── 44 | 45 | This software and its source code are the exclusive property of the author (hereinafter “the Licensor”). 46 | Use, modification, and redistribution are permitted under the following strict conditions: 47 | 48 | 1. 🛑 Prohibition of commercial use by third parties 49 | - It is strictly forbidden to sell, rent or redistribute this code (or any derivative) for commercial purposes. 50 | - Any direct commercial use of the code is forbidden unless explicitly authorized in writing by the Licensor. 51 | 52 | 2. 💰 Microtransactions allowed 53 | - In-game microtransaction monetization is allowed, as long as: 54 | - the code itself is not sold or directly monetized, 55 | - the source code remains public and freely accessible. 56 | 57 | 3. 📂 Mandatory public source code 58 | - Any redistributed or modified version must make its complete source code freely and publicly accessible, without restrictions. 59 | 60 | 4. 🧾 Attribution 61 | - The original author’s name (“Luuxis”) must be clearly credited in any redistribution or modified version. 62 | 63 | 5. 🔐 Exclusive resale rights reserved by the creator 64 | - Only the Licensor (Luuxis) is allowed to sell or license this software or its source code for commercial purposes. 65 | 66 | 6. 🚫 License modification is forbidden 67 | - This license must not be altered, replaced, or removed. Any redistribution must include this license as-is. 68 | 69 | 7. ⚠️ No warranty 70 | - This software is provided “as is”, without any warranties. The author accepts no liability for any damage resulting from its use. 71 | 72 | 8. ⚖️ Violation and legal consequences 73 | - Any violation results in immediate termination of this license. Unauthorized use may lead to legal action under applicable copyright law. 74 | 75 | By using this software, you fully and irrevocably agree to the terms of this license. 76 | 77 | ──────────────────────────────────────────────────────────── 78 | -------------------------------------------------------------------------------- /src/Minecraft/Minecraft-Assets.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | import fs from 'fs'; 6 | 7 | /** 8 | * Represents the general structure of the options passed to MinecraftAssets. 9 | * You can expand or modify these fields as necessary for your specific use case. 10 | */ 11 | export interface MinecraftAssetsOptions { 12 | path: string; // Base path to the Minecraft data folder 13 | instance?: string; // Instance name (if using multi-instance setup) 14 | } 15 | 16 | /** 17 | * Represents a simplified version of the Minecraft version JSON structure. 18 | */ 19 | export interface VersionJSON { 20 | assetIndex?: { 21 | id: string; // e.g. "1.19" 22 | url: string; // URL where the asset index JSON can be fetched 23 | }; 24 | assets?: string; // e.g. "1.19" 25 | } 26 | 27 | /** 28 | * Represents a single asset object in the final array returned by getAssets(). 29 | */ 30 | export interface AssetItem { 31 | type: 'CFILE' | 'Assets'; 32 | path: string; 33 | content?: string; // Used if type = "CFILE" 34 | sha1?: string; // Used if type = "Assets" 35 | size?: number; // Used if type = "Assets" 36 | url?: string; // Used if type = "Assets" 37 | } 38 | 39 | /** 40 | * Class responsible for handling Minecraft asset index fetching 41 | * and optionally copying legacy assets to the correct directory. 42 | */ 43 | export default class MinecraftAssets { 44 | private assetIndex: { id: string; url: string } | undefined; 45 | private readonly options: MinecraftAssetsOptions; 46 | 47 | constructor(options: MinecraftAssetsOptions) { 48 | this.options = options; 49 | } 50 | 51 | /** 52 | * Fetches the asset index from the provided JSON object, then constructs 53 | * and returns an array of asset download objects. These can be processed 54 | * by a downloader to ensure all assets are present locally. 55 | * 56 | * @param versionJson A JSON object containing an "assetIndex" field. 57 | * @returns An array of AssetItem objects with download info. 58 | */ 59 | public async getAssets(versionJson: VersionJSON): Promise { 60 | this.assetIndex = versionJson.assetIndex; 61 | if (!this.assetIndex) { 62 | // If there's no assetIndex, there's nothing to download. 63 | return []; 64 | } 65 | 66 | // Fetch the asset index JSON from the remote URL 67 | let data; 68 | try { 69 | const response = await fetch(this.assetIndex.url); 70 | data = await response.json(); 71 | } catch (err: any) { 72 | throw new Error(`Failed to fetch asset index: ${err.message}`); 73 | } 74 | 75 | // First item is the index file itself, which we'll store locally 76 | const assetsArray: AssetItem[] = [ 77 | { 78 | type: 'CFILE', 79 | path: `assets/indexes/${this.assetIndex.id}.json`, 80 | content: JSON.stringify(data) 81 | } 82 | ]; 83 | 84 | // Convert the "objects" property into a list of individual assets 85 | const objects = Object.values(data.objects || {}); 86 | for (const obj of objects as Array<{ hash: string; size: number }>) { 87 | assetsArray.push({ 88 | type: 'Assets', 89 | sha1: obj.hash, 90 | size: obj.size, 91 | path: `assets/objects/${obj.hash.substring(0, 2)}/${obj.hash}`, 92 | url: `https://resources.download.minecraft.net/${obj.hash.substring(0, 2)}/${obj.hash}` 93 | }); 94 | } 95 | 96 | return assetsArray; 97 | } 98 | 99 | /** 100 | * Copies legacy assets (when using older versions of Minecraft) from 101 | * the main "objects" folder to a "resources" folder, preserving the 102 | * directory structure. 103 | * 104 | * @param versionJson A JSON object that has an "assets" property for the index name. 105 | */ 106 | public copyAssets(versionJson: VersionJSON): void { 107 | // Determine the legacy directory where resources should go 108 | let legacyDirectory = `${this.options.path}/resources`; 109 | if (this.options.instance) { 110 | legacyDirectory = `${this.options.path}/instances/${this.options.instance}/resources`; 111 | } 112 | 113 | // The path to the local asset index JSON 114 | const pathAssets = `${this.options.path}/assets/indexes/${versionJson.assets}.json`; 115 | if (!fs.existsSync(pathAssets)) { 116 | return; // Nothing to copy if the file doesn't exist 117 | } 118 | 119 | // Parse the asset index JSON 120 | let assetsData; 121 | try { 122 | assetsData = JSON.parse(fs.readFileSync(pathAssets, 'utf-8')); 123 | } catch (err: any) { 124 | throw new Error(`Failed to read assets index file: ${err.message}`); 125 | } 126 | 127 | // Each entry is [filePath, { hash, size }] 128 | const assetsEntries = Object.entries(assetsData.objects || {}); 129 | for (const [filePath, hashData] of assetsEntries) { 130 | const hashObj = hashData as { hash: string; size: number }; 131 | const fullHash = hashObj.hash; 132 | const subHash = fullHash.substring(0, 2); 133 | 134 | // Directory where the hashed file is stored 135 | const subAssetDir = `${this.options.path}/assets/objects/${subHash}`; 136 | 137 | // If needed, create the corresponding directories in the legacy folder 138 | const pathSegments = filePath.split('/'); 139 | pathSegments.pop(); // Remove the last segment (the filename itself) 140 | if (!fs.existsSync(`${legacyDirectory}/${pathSegments.join('/')}`)) { 141 | fs.mkdirSync(`${legacyDirectory}/${pathSegments.join('/')}`, { recursive: true }); 142 | } 143 | 144 | // Copy the file if it doesn't already exist in the legacy location 145 | const sourceFile = `${subAssetDir}/${fullHash}`; 146 | const targetFile = `${legacyDirectory}/${filePath}`; 147 | if (!fs.existsSync(targetFile)) { 148 | fs.copyFileSync(sourceFile, targetFile); 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/Minecraft-Loader/patcher.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import { spawn } from 'child_process'; 7 | import fs from 'fs'; 8 | import path from 'path'; 9 | import { EventEmitter } from 'events'; 10 | import { getPathLibraries, getFileFromArchive } from '../utils/Index.js'; 11 | 12 | interface ForgePatcherOptions { 13 | path: string; 14 | loader: { 15 | type: string; 16 | }; 17 | } 18 | 19 | interface Config { 20 | java: string; 21 | minecraft: string; 22 | minecraftJson: string; 23 | } 24 | 25 | interface ProfileData { 26 | client: string; 27 | [key: string]: any; 28 | } 29 | 30 | interface Processor { 31 | jar: string; 32 | args: string[]; 33 | classpath: string[]; 34 | sides?: string[]; 35 | } 36 | 37 | export interface Profile { 38 | data: Record; 39 | processors?: any[]; 40 | libraries?: Array<{ name?: string }>; // The universal jar/libraries reference 41 | path?: string; 42 | } 43 | 44 | export default class ForgePatcher extends EventEmitter { 45 | private readonly options: ForgePatcherOptions; 46 | 47 | constructor(options: ForgePatcherOptions) { 48 | super(); 49 | this.options = options; 50 | } 51 | 52 | public async patcher(profile: Profile, config: Config, neoForgeOld: boolean = true): Promise { 53 | const { processors } = profile; 54 | 55 | for (const [_, processor] of Object.entries(processors)) { 56 | if (processor.sides && !processor.sides.includes('client')) continue; 57 | 58 | const jarInfo = getPathLibraries(processor.jar); 59 | const jarPath = path.resolve(this.options.path, 'libraries', jarInfo.path, jarInfo.name); 60 | 61 | const args = processor.args 62 | .map(arg => this.setArgument(arg, profile, config, neoForgeOld)) 63 | .map(arg => this.computePath(arg)); 64 | 65 | const classPaths = processor.classpath.map(cp => { 66 | const cpInfo = getPathLibraries(cp); 67 | return `"${path.join(this.options.path, 'libraries', cpInfo.path, cpInfo.name)}"`; 68 | }); 69 | 70 | const mainClass = await this.readJarManifest(jarPath); 71 | if (!mainClass) { 72 | this.emit('error', `Impossible de déterminer la classe principale dans le JAR: ${jarPath}`); 73 | continue; 74 | } 75 | 76 | await new Promise((resolve) => { 77 | const spawned = spawn( 78 | `"${path.resolve(config.java)}"`, 79 | [ 80 | '-classpath', 81 | [`"${jarPath}"`, ...classPaths].join(path.delimiter), 82 | mainClass, 83 | ...args 84 | ], 85 | { shell: true } 86 | ); 87 | 88 | spawned.stdout.on('data', data => { 89 | this.emit('patch', data.toString('utf-8')); 90 | }); 91 | 92 | spawned.stderr.on('data', data => { 93 | this.emit('patch', data.toString('utf-8')); 94 | }); 95 | 96 | spawned.on('close', code => { 97 | if (code !== 0) { 98 | this.emit('error', `Le patcher Forge s'est terminé avec le code ${code}`); 99 | } 100 | resolve(); 101 | }); 102 | }); 103 | } 104 | } 105 | 106 | public check(profile: Profile): boolean { 107 | const { processors } = profile; 108 | let files: string[] = []; 109 | 110 | for (const processor of Object.values(processors)) { 111 | if (processor.sides && !processor.sides.includes('client')) continue; 112 | 113 | processor.args.forEach(arg => { 114 | const finalArg = arg.replace('{', '').replace('}', ''); 115 | if (profile.data[finalArg]) { 116 | if (finalArg === 'BINPATCH') return; 117 | files.push(profile.data[finalArg].client); 118 | } 119 | }); 120 | } 121 | 122 | files = Array.from(new Set(files)); 123 | 124 | for (const file of files) { 125 | const lib = getPathLibraries(file.replace('[', '').replace(']', '')); 126 | const filePath = path.resolve(this.options.path, 'libraries', lib.path, lib.name); 127 | if (!fs.existsSync(filePath)) return false; 128 | } 129 | return true; 130 | } 131 | 132 | private setArgument(arg: string, profile: Profile, config: Config, neoForgeOld: boolean): string { 133 | const finalArg = arg.replace('{', '').replace('}', ''); 134 | 135 | const universalLib = profile.libraries.find(lib => { 136 | if (this.options.loader.type === 'forge') return lib.name.startsWith('net.minecraftforge:forge'); 137 | else return lib.name.startsWith(neoForgeOld ? 'net.neoforged:forge' : 'net.neoforged:neoforge'); 138 | }); 139 | 140 | if (profile.data[finalArg]) { 141 | if (finalArg === 'BINPATCH') { 142 | const jarInfo = getPathLibraries(profile.path || (universalLib?.name ?? '')); 143 | return `"${path.join(this.options.path, 'libraries', jarInfo.path, jarInfo.name).replace('.jar', '-clientdata.lzma')}"`; 144 | } 145 | return profile.data[finalArg].client; 146 | } 147 | 148 | return arg 149 | .replace('{SIDE}', 'client') 150 | .replace('{ROOT}', `"${path.dirname(path.resolve(this.options.path, 'forge'))}"`) 151 | .replace('{MINECRAFT_JAR}', `"${config.minecraft}"`) 152 | .replace('{MINECRAFT_VERSION}', `"${config.minecraftJson}"`) 153 | .replace('{INSTALLER}', `"${path.join(this.options.path, 'libraries')}"`) 154 | .replace('{LIBRARY_DIR}', `"${path.join(this.options.path, 'libraries')}"`); 155 | } 156 | 157 | private computePath(arg: string): string { 158 | if (arg.startsWith('[')) { 159 | const libInfo = getPathLibraries(arg.replace('[', '').replace(']', '')); 160 | return `"${path.join(this.options.path, 'libraries', libInfo.path, libInfo.name)}"`; 161 | } 162 | return arg; 163 | } 164 | 165 | private async readJarManifest(jarPath: string): Promise { 166 | const manifestContent = await getFileFromArchive(jarPath, 'META-INF/MANIFEST.MF'); 167 | if (!manifestContent) return null; 168 | 169 | const content = manifestContent.toString(); 170 | const mainClassLine = content.split('Main-Class: ')[1]; 171 | if (!mainClassLine) return null; 172 | return mainClassLine.split('\r\n')[0]; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/Minecraft/Minecraft-Loader.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import { EventEmitter } from 'events'; 7 | import path from 'path'; 8 | // Note: Adjust the import path according to your actual TypeScript setup. 9 | import LoaderDownloader, { LoaderType } from '../Minecraft-Loader/index.js'; 10 | 11 | /** 12 | * Describes the loader options, including a path and other configurations. 13 | * You can expand this interface if your real code requires more fields. 14 | */ 15 | interface LoaderOptions { 16 | path: string; // Base path for the Minecraft data or installation 17 | loader: { 18 | path?: string; // Path to store loader files (e.g. Forge, Fabric) 19 | type?: string; // Type of loader (forge, fabric, etc.) 20 | build?: string; // Build number if applicable (e.g., for Forge) 21 | }; 22 | downloadFileMultiple?: number; // If your downloader can handle multiple files 23 | } 24 | 25 | /** 26 | * Represents the MinecraftLoader class options, merging LoaderOptions 27 | * with any additional fields your code might require. 28 | */ 29 | interface MinecraftLoaderOptions extends LoaderOptions { 30 | // Additional fields that might be needed by your application 31 | [key: string]: any; 32 | } 33 | 34 | /** 35 | * A simple interface describing the JSON structure returned by loader installation. 36 | * Adjust to reflect the actual fields from your loader JSON. 37 | */ 38 | interface LoaderJSON { 39 | libraries: Array<{ 40 | loader?: string; 41 | name?: string; // Or any other required fields 42 | }>; 43 | arguments?: { 44 | game?: string[]; 45 | jvm?: string[]; 46 | }; 47 | mainClass?: string; 48 | [key: string]: any; 49 | } 50 | 51 | /** 52 | * This class manages the installation and argument-building for a Minecraft 53 | * mod loader (e.g. Forge, Fabric). It wraps a `loaderDownloader` and emits 54 | * the same events for progress, extraction, patching, etc. 55 | */ 56 | export default class MinecraftLoader extends EventEmitter { 57 | private options: MinecraftLoaderOptions; 58 | private loaderPath: string; 59 | 60 | constructor(options: MinecraftLoaderOptions) { 61 | super(); 62 | this.options = options; 63 | this.loaderPath = path.join(this.options.path, this.options.loader.path); 64 | } 65 | 66 | /** 67 | * Installs the loader for a given Minecraft version using a LoaderDownloader, 68 | * returning the loader's JSON on completion. This function emits several events 69 | * for progress reporting and patch notifications. 70 | * 71 | * @param version The Minecraft version (e.g. "1.19.2") 72 | * @param javaPath Path to the Java executable used by the loader for patching 73 | * @returns A Promise that resolves to the loader's JSON configuration 74 | */ 75 | public async GetLoader(version: string, javaPath: string): Promise { 76 | const loader = new LoaderDownloader({ 77 | path: this.loaderPath, 78 | downloadFileMultiple: this.options.downloadFileMultiple, 79 | loader: { 80 | type: this.options.loader.type as LoaderType, 81 | version: version, 82 | build: this.options.loader.build, 83 | config: { 84 | javaPath, 85 | minecraftJar: `${this.options.path}/versions/${version}/${version}.jar`, 86 | minecraftJson: `${this.options.path}/versions/${version}/${version}.json` 87 | } 88 | } 89 | }); 90 | 91 | return new Promise((resolve, reject) => { 92 | loader.install(); 93 | 94 | loader.on('json', (json: LoaderJSON) => { 95 | // Inject the loader path into each library if needed 96 | const modifiedJson = json; 97 | modifiedJson.libraries = modifiedJson.libraries.map(lib => { 98 | lib.loader = this.loaderPath; 99 | return lib; 100 | }); 101 | resolve(modifiedJson); 102 | }); 103 | 104 | loader.on('extract', (extract: any) => { 105 | // Forward the "extract" event 106 | this.emit('extract', extract); 107 | }); 108 | 109 | loader.on('progress', (progress: any, size: any, element: any) => { 110 | // Forward the "progress" event 111 | this.emit('progress', progress, size, element); 112 | }); 113 | 114 | loader.on('check', (progress: any, size: any, element: any) => { 115 | // Forward the "check" event 116 | this.emit('check', progress, size, element); 117 | }); 118 | 119 | loader.on('patch', (patch: any) => { 120 | // Forward the "patch" event 121 | this.emit('patch', patch); 122 | }); 123 | 124 | loader.on('error', (err: any) => { 125 | reject(err); 126 | }); 127 | }); 128 | } 129 | 130 | /** 131 | * Builds the game and JVM arguments based on the loader's JSON data. 132 | * This may involve placeholder replacements for the main class, library directories, etc. 133 | * 134 | * @param json The loader JSON previously returned by GetLoader (or null) 135 | * @param version The targeted Minecraft version (used for placeholder substitution) 136 | * @returns An object with `game`, `jvm`, and an optional `mainClass` property 137 | */ 138 | public async GetArguments(json: LoaderJSON | null, version: string): Promise<{ 139 | game: string[]; 140 | jvm: string[]; 141 | mainClass?: string; 142 | }> { 143 | // If no loader JSON is provided, return empty arrays 144 | if (json === null) { 145 | return { game: [], jvm: [] }; 146 | } 147 | 148 | const moddedArgs = json.arguments; 149 | // If no arguments field is present, return empty arrays 150 | if (!moddedArgs) return { game: [], jvm: [] }; 151 | 152 | const args: { 153 | game?: string[]; 154 | jvm?: string[]; 155 | mainClass?: string; 156 | } = {}; 157 | 158 | if (moddedArgs.game) { 159 | args.game = moddedArgs.game; 160 | } 161 | 162 | if (moddedArgs.jvm) { 163 | // Replace placeholders in the JVM arguments 164 | args.jvm = moddedArgs.jvm.map((jvmArg) => 165 | jvmArg 166 | .replace(/\${version_name}/g, version) 167 | .replace(/\${library_directory}/g, `${this.loaderPath}/libraries`) 168 | .replace(/\${classpath_separator}/g, process.platform === 'win32' ? ';' : ':') 169 | ); 170 | } 171 | 172 | args.mainClass = json.mainClass; 173 | return { 174 | game: args.game || [], 175 | jvm: args.jvm || [], 176 | mainClass: args.mainClass 177 | }; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/Authenticator/AZauth.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import { Buffer } from 'node:buffer'; 7 | 8 | // This interface defines the structure of the user object 9 | // returned by the AZauth service. You can adapt it to your needs. 10 | interface AZauthUser { 11 | access_token?: string; 12 | client_token?: string; 13 | uuid?: string; 14 | name?: string; 15 | user_properties?: string; 16 | user_info?: { 17 | id?: string; 18 | banned?: boolean; 19 | money?: number; 20 | role?: string; 21 | verified?: boolean; 22 | }; 23 | meta?: { 24 | online: boolean; 25 | type: string; 26 | }; 27 | profile?: { 28 | skins: Array<{ 29 | url: string; 30 | base64?: string; 31 | }>; 32 | }; 33 | // Error-related fields 34 | error?: boolean; 35 | reason?: string; 36 | message?: string; 37 | A2F?: boolean; 38 | } 39 | 40 | export default class AZauth { 41 | private url: string; 42 | private skinAPI: string; 43 | 44 | /** 45 | * The constructor prepares the authentication and skin URLs from the base URL. 46 | * @param url The base URL of the AZauth server 47 | */ 48 | constructor(url: string) { 49 | // '/api/auth' for authentication, '/api/skin-api/skins' for skin data 50 | this.url = new URL('/api/auth', url).toString(); 51 | this.skinAPI = new URL('/api/skin-api/skins', url).toString(); 52 | } 53 | 54 | /** 55 | * Authenticates a user using their username/email and password. 56 | * Optionally, a 2FA code can be provided. 57 | * 58 | * @param username The email or username for authentication 59 | * @param password The password 60 | * @param A2F Optional 2FA code 61 | * @returns A Promise that resolves to an AZauthUser object 62 | */ 63 | public async login(username: string, password: string, A2F: string | null = null): Promise { 64 | const response = await fetch(`${this.url}/authenticate`, { 65 | method: 'POST', 66 | headers: { 'Content-Type': 'application/json' }, 67 | body: JSON.stringify({ 68 | email: username, 69 | password, 70 | code: A2F 71 | }) 72 | }); 73 | 74 | const data = await response.json(); 75 | 76 | // If the server indicates that 2FA is required 77 | if (data.status === 'pending' && data.reason === '2fa') { 78 | return { A2F: true }; 79 | } 80 | 81 | // If the server returns an error status 82 | if (data.status === 'error') { 83 | return { 84 | error: true, 85 | reason: data.reason, 86 | message: data.message 87 | }; 88 | } 89 | 90 | // If authentication is successful, return the complete user object 91 | return { 92 | access_token: data.access_token, 93 | client_token: data.uuid, 94 | uuid: data.uuid, 95 | name: data.username, 96 | user_properties: '{}', 97 | user_info: { 98 | id: data.id, 99 | banned: data.banned, 100 | money: data.money, 101 | role: data.role, 102 | verified: data.email_verified 103 | }, 104 | meta: { 105 | online: false, 106 | type: 'AZauth' 107 | }, 108 | profile: { 109 | skins: [await this.skin(data.id)] 110 | } 111 | }; 112 | } 113 | 114 | /** 115 | * Verifies an existing session (e.g., for refreshing tokens). 116 | * @param user An AZauthUser object containing at least the access token 117 | * @returns A Promise that resolves to an updated AZauthUser object or an error object 118 | */ 119 | public async verify(user: AZauthUser): Promise { 120 | const response = await fetch(`${this.url}/verify`, { 121 | method: 'POST', 122 | headers: { 'Content-Type': 'application/json' }, 123 | body: JSON.stringify({ 124 | access_token: user.access_token 125 | }) 126 | }); 127 | 128 | const data = await response.json(); 129 | 130 | // If the server indicates an error 131 | if (data.status === 'error') { 132 | return { 133 | error: true, 134 | reason: data.reason, 135 | message: data.message 136 | }; 137 | } 138 | 139 | // Return the updated user session object 140 | return { 141 | access_token: data.access_token, 142 | client_token: data.uuid, 143 | uuid: data.uuid, 144 | name: data.username, 145 | user_properties: '{}', 146 | user_info: { 147 | id: data.id, 148 | banned: data.banned, 149 | money: data.money, 150 | role: data.role, 151 | verified: data.email_verified 152 | }, 153 | meta: { 154 | online: false, 155 | type: 'AZauth' 156 | }, 157 | profile: { 158 | skins: [await this.skin(data.id)] 159 | } 160 | }; 161 | } 162 | 163 | /** 164 | * Logs out a user from the AZauth service (invalidates the token). 165 | * @param user The AZauthUser object with a valid access token 166 | * @returns A Promise that resolves to true if logout is successful, otherwise false 167 | */ 168 | public async signout(user: AZauthUser): Promise { 169 | const response = await fetch(`${this.url}/logout`, { 170 | method: 'POST', 171 | headers: { 'Content-Type': 'application/json' }, 172 | body: JSON.stringify({ 173 | access_token: user.access_token 174 | }) 175 | }); 176 | 177 | const data = await response.json(); 178 | if (data.error) return false; 179 | return true; 180 | } 181 | 182 | /** 183 | * Retrieves the skin of a user by their ID (UUID). 184 | * If the skin exists, returns both the direct URL and a base64-encoded PNG. 185 | * If the skin doesn't exist, only the URL is returned. 186 | * 187 | * @param uuid The UUID or ID of the user 188 | * @returns A Promise resolving to an object with the skin URL (and optional base64 data) 189 | */ 190 | private async skin(uuid: string): Promise<{ url: string; base64?: string }> { 191 | let response = await fetch(`${this.skinAPI}/${uuid}`, { 192 | method: 'GET', 193 | headers: { 'Content-Type': 'application/json' } 194 | }); 195 | 196 | // If the skin is not found (404), return only the URL 197 | if (response.status === 404) { 198 | return { 199 | url: `${this.skinAPI}/${uuid}` 200 | }; 201 | } 202 | 203 | // Otherwise, convert the skin image to a base64-encoded string 204 | const arrayBuffer = await response.arrayBuffer(); 205 | const buffer = Buffer.from(arrayBuffer); 206 | return { 207 | url: `${this.skinAPI}/${uuid}`, 208 | base64: `data:image/png;base64,${buffer.toString('base64')}` 209 | }; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/utils/unzipper.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import zlib from 'zlib'; 3 | 4 | interface ZipEntry { 5 | entryName: string; 6 | isDirectory: boolean; 7 | getData: () => Buffer; 8 | } 9 | 10 | export default class Unzipper { 11 | private entries: ZipEntry[] = []; 12 | 13 | constructor(zipFilePath: string) { 14 | const fileBuffer = fs.readFileSync(zipFilePath); 15 | 16 | const eocdSig = Buffer.from([0x50, 0x4B, 0x05, 0x06]); 17 | const maxCommentLength = 0xFFFF; 18 | const scanStart = Math.max(0, fileBuffer.length - (maxCommentLength + 22)); 19 | 20 | let eocdPos = -1; 21 | for (let i = fileBuffer.length - 22; i >= scanStart; i--) { 22 | if ( 23 | fileBuffer[i] === 0x50 && 24 | fileBuffer[i + 1] === 0x4B && 25 | fileBuffer[i + 2] === 0x05 && 26 | fileBuffer[i + 3] === 0x06 27 | ) { 28 | eocdPos = i; 29 | break; 30 | } 31 | } 32 | 33 | if (eocdPos !== -1) { 34 | const cdOffset = fileBuffer.readUInt32LE(eocdPos + 16); 35 | const cdSize = fileBuffer.readUInt32LE(eocdPos + 12); 36 | const cdEnd = cdOffset + cdSize; 37 | 38 | let cdCursor = cdOffset; 39 | 40 | while (cdCursor < cdEnd) { 41 | if (cdCursor + 46 > fileBuffer.length) break; // sécurité 42 | 43 | if (fileBuffer.readUInt32LE(cdCursor) !== 0x02014b50) break; 44 | 45 | const compressionMethod = fileBuffer.readUInt16LE(cdCursor + 10); 46 | const compressedSize = fileBuffer.readUInt32LE(cdCursor + 20); 47 | const uncompressedSize = fileBuffer.readUInt32LE(cdCursor + 24); 48 | const fileNameLength = fileBuffer.readUInt16LE(cdCursor + 28); 49 | const extraFieldLength = fileBuffer.readUInt16LE(cdCursor + 30); 50 | const fileCommentLength = fileBuffer.readUInt16LE(cdCursor + 32); 51 | const relativeOffset = fileBuffer.readUInt32LE(cdCursor + 42); 52 | 53 | const fileName = fileBuffer.toString( 54 | 'utf-8', 55 | cdCursor + 46, 56 | cdCursor + 46 + fileNameLength 57 | ); 58 | 59 | const headerOffset = relativeOffset; 60 | // Sécurité sur l'offset du header 61 | if (headerOffset + 30 > fileBuffer.length) { 62 | cdCursor += 46 + fileNameLength + extraFieldLength + fileCommentLength; 63 | continue; 64 | } 65 | if (fileBuffer.readUInt32LE(headerOffset) !== 0x04034b50) { 66 | cdCursor += 46 + fileNameLength + extraFieldLength + fileCommentLength; 67 | continue; 68 | } 69 | 70 | const lfFileNameLength = fileBuffer.readUInt16LE(headerOffset + 26); 71 | const lfExtraFieldLength = fileBuffer.readUInt16LE(headerOffset + 28); 72 | const dataStart = headerOffset + 30 + lfFileNameLength + lfExtraFieldLength; 73 | const dataEnd = dataStart + compressedSize; 74 | 75 | // Sécurité: ne pas dépasser la taille du buffer 76 | if (dataEnd > fileBuffer.length) { 77 | cdCursor += 46 + fileNameLength + extraFieldLength + fileCommentLength; 78 | continue; 79 | } 80 | 81 | const compressedData = fileBuffer.slice(dataStart, dataEnd); 82 | 83 | this.entries.push({ 84 | entryName: fileName, 85 | isDirectory: fileName.endsWith('/'), 86 | getData: () => { 87 | if (compressionMethod === 8) { 88 | return zlib.inflateRawSync(compressedData); 89 | } else if (compressionMethod === 0) { 90 | return compressedData; 91 | } else { 92 | throw new Error(`Unsupported compression method: ${compressionMethod}`); 93 | } 94 | } 95 | }); 96 | 97 | cdCursor += 46 + fileNameLength + extraFieldLength + fileCommentLength; 98 | } 99 | } else { 100 | let currentOffset = 0; 101 | while (currentOffset < fileBuffer.length - 4) { 102 | const signaturePos = fileBuffer.indexOf( 103 | Buffer.from([0x50, 0x4B, 0x03, 0x04]), 104 | currentOffset 105 | ); 106 | if (signaturePos === -1) break; 107 | 108 | const headerOffset = signaturePos; 109 | if (headerOffset + 30 > fileBuffer.length) break; 110 | 111 | const compressionMethod = fileBuffer.readUInt16LE(headerOffset + 8); 112 | const compressedSize = fileBuffer.readUInt32LE(headerOffset + 18); 113 | const uncompressedSize = fileBuffer.readUInt32LE(headerOffset + 22); 114 | const fileNameLength = fileBuffer.readUInt16LE(headerOffset + 26); 115 | const extraFieldLength = fileBuffer.readUInt16LE(headerOffset + 28); 116 | 117 | const fileNameStart = headerOffset + 30; 118 | const fileNameEnd = fileNameStart + fileNameLength; 119 | if (fileNameEnd > fileBuffer.length) break; 120 | 121 | const fileName = fileBuffer.toString( 122 | 'utf-8', 123 | fileNameStart, 124 | fileNameEnd 125 | ); 126 | 127 | const dataStart = fileNameEnd + extraFieldLength; 128 | const dataEnd = dataStart + compressedSize; 129 | 130 | if (dataEnd > fileBuffer.length) { 131 | currentOffset = dataEnd; 132 | continue; 133 | } 134 | 135 | const compressedData = fileBuffer.slice(dataStart, dataEnd); 136 | 137 | this.entries.push({ 138 | entryName: fileName, 139 | isDirectory: fileName.endsWith('/'), 140 | getData: () => { 141 | if (compressionMethod === 8) { 142 | return zlib.inflateRawSync(compressedData); 143 | } else if (compressionMethod === 0) { 144 | return compressedData; 145 | } else { 146 | throw new Error(`Unsupported compression method: ${compressionMethod}`); 147 | } 148 | } 149 | }); 150 | 151 | currentOffset = dataEnd; 152 | } 153 | } 154 | } 155 | 156 | getEntries(): ZipEntry[] { 157 | return this.entries; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/Minecraft-Loader/loader/fabric/fabric.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import { EventEmitter } from 'events'; 7 | import fs from 'fs'; 8 | import path from 'path'; 9 | 10 | import { getPathLibraries } from '../../../utils/Index.js'; 11 | import Downloader from '../../../utils/Downloader.js'; 12 | 13 | /** 14 | * Represents the options needed by the FabricMC class. 15 | * You can expand this if your code requires more specific fields. 16 | */ 17 | interface FabricOptions { 18 | path: string; // Base path to your game or library folder 19 | downloadFileMultiple?: number; // Max simultaneous downloads (if supported by Downloader) 20 | loader: { 21 | version: string; // Minecraft version 22 | build: string; // Fabric build (e.g. "latest", "recommended", or a specific version) 23 | }; 24 | } 25 | 26 | /** 27 | * Represents the Loader object that holds metadata URLs and JSON paths. 28 | * For instance, it might look like: 29 | * { 30 | * metaData: 'https://meta.fabricmc.net/v2/versions', 31 | * json: 'https://meta.fabricmc.net/v2/versions/loader/${version}/${build}/profile/json' 32 | * } 33 | */ 34 | interface LoaderObject { 35 | metaData: string; 36 | json: string; // Template string with placeholders like ${version} and ${build} 37 | } 38 | 39 | /** 40 | * Represents the structure of your metadata, including 41 | * game versions and loader builds. Adapt as needed. 42 | */ 43 | interface MetaData { 44 | game: Array<{ 45 | version: string; 46 | stable: boolean; 47 | }>; 48 | loader: Array<{ 49 | version: string; 50 | stable: boolean; 51 | }>; 52 | } 53 | 54 | /** 55 | * Structure of a library entry in the Fabric JSON manifest. 56 | * Extend this interface if you have additional fields like "rules", etc. 57 | */ 58 | interface FabricLibrary { 59 | name: string; 60 | url: string; 61 | rules?: Array; 62 | } 63 | 64 | /** 65 | * The JSON object returned by Fabric metadata endpoints. 66 | */ 67 | interface FabricJSON { 68 | libraries: FabricLibrary[]; 69 | [key: string]: any; 70 | } 71 | 72 | /** 73 | * This class handles downloading Fabric loader JSON metadata, 74 | * resolving the correct build, and downloading the required libraries. 75 | */ 76 | export default class FabricMC extends EventEmitter { 77 | private readonly options: FabricOptions; 78 | 79 | constructor(options: FabricOptions) { 80 | super(); 81 | this.options = options; 82 | } 83 | 84 | /** 85 | * Fetches the Fabric loader metadata to find the correct build for the given 86 | * Minecraft version. If the specified build is "latest" or "recommended", 87 | * it uses the first (most recent) entry. Otherwise, it looks up a specific build. 88 | * 89 | * @param Loader A LoaderObject describing metadata and json URL templates. 90 | * @returns A JSON object representing the Fabric loader profile, or an error object. 91 | */ 92 | public async downloadJson(Loader: LoaderObject): Promise { 93 | let buildInfo: { version: string; stable: boolean } | undefined; 94 | 95 | // Fetch the metadata 96 | let response = await fetch(Loader.metaData); 97 | let metaData: MetaData = await response.json(); 98 | 99 | // Check if the Minecraft version is supported 100 | const version = metaData.game.find(v => v.version === this.options.loader.version); 101 | if (!version) { 102 | return { error: `FabricMC doesn't support Minecraft ${this.options.loader.version}` }; 103 | } 104 | 105 | // Determine the loader build 106 | const availableBuilds = metaData.loader.map(b => b.version); 107 | if (this.options.loader.build === 'latest' || this.options.loader.build === 'recommended') { 108 | buildInfo = metaData.loader[0]; // The first entry is presumably the latest 109 | } else { 110 | buildInfo = metaData.loader.find(l => l.version === this.options.loader.build); 111 | } 112 | 113 | if (!buildInfo) { 114 | return { 115 | error: `Fabric Loader ${this.options.loader.build} not found, Available builds: ${availableBuilds.join(', ')}` 116 | }; 117 | } 118 | 119 | // Build the URL for the Fabric JSON using placeholders 120 | const url = Loader.json 121 | .replace('${build}', buildInfo.version) 122 | .replace('${version}', this.options.loader.version); 123 | 124 | // Fetch the Fabric loader JSON 125 | try { 126 | const result = await fetch(url); 127 | const fabricJson: FabricJSON = await result.json(); 128 | return fabricJson; 129 | } catch (err: any) { 130 | return { error: err.message || 'An error occurred while fetching Fabric JSON' }; 131 | } 132 | } 133 | 134 | /** 135 | * Downloads any missing libraries defined in the Fabric JSON manifest, 136 | * skipping those that already exist locally (or that have rules preventing download). 137 | * 138 | * @param fabricJson The Fabric JSON object with a `libraries` array. 139 | * @returns The same `libraries` array after downloading as needed. 140 | */ 141 | public async downloadLibraries(fabricJson: FabricJSON): Promise { 142 | const { libraries } = fabricJson; 143 | const downloader = new Downloader(); 144 | const downloadQueue: Array<{ 145 | url: string; 146 | folder: string; 147 | path: string; 148 | name: string; 149 | size: number; 150 | }> = []; 151 | 152 | let checkedLibraries = 0; 153 | let totalSize = 0; 154 | 155 | // Identify which libraries need downloading 156 | for (const lib of libraries) { 157 | // Skip if there are any rules that prevent downloading 158 | if (lib.rules) { 159 | this.emit('check', checkedLibraries++, libraries.length, 'libraries'); 160 | continue; 161 | } 162 | 163 | // Parse out the library path 164 | const libInfo = getPathLibraries(lib.name); 165 | const libFolderPath = path.resolve(this.options.path, 'libraries', libInfo.path); 166 | const libFilePath = path.resolve(libFolderPath, libInfo.name); 167 | 168 | // If the file doesn't exist locally, we prepare a download item 169 | if (!fs.existsSync(libFilePath)) { 170 | const libUrl = `${lib.url}${libInfo.path}/${libInfo.name}`; 171 | 172 | let sizeFile = 0; 173 | // Check if the file is accessible and retrieve its size 174 | const res = await downloader.checkURL(libUrl); 175 | if (res && typeof res === 'object' && 'status' in res && res.status === 200) { 176 | sizeFile = res.size; 177 | totalSize += res.size; 178 | } 179 | 180 | downloadQueue.push({ 181 | url: libUrl, 182 | folder: libFolderPath, 183 | path: libFilePath, 184 | name: libInfo.name, 185 | size: sizeFile 186 | }); 187 | } 188 | 189 | // Emit a "check" event for progress tracking 190 | this.emit('check', checkedLibraries++, libraries.length, 'libraries'); 191 | } 192 | 193 | // If there are files to download, do so now 194 | if (downloadQueue.length > 0) { 195 | downloader.on('progress', (downloaded: number, total: number) => { 196 | this.emit('progress', downloaded, total, 'libraries'); 197 | }); 198 | 199 | await downloader.downloadFileMultiple(downloadQueue, totalSize, this.options.downloadFileMultiple); 200 | } 201 | 202 | return libraries; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/Minecraft-Loader/loader/legacyfabric/legacyFabric.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import fs from 'fs'; 7 | import path from 'path'; 8 | import { EventEmitter } from 'events'; 9 | 10 | import { getPathLibraries } from '../../../utils/Index.js'; 11 | import Downloader from '../../../utils/Downloader.js'; 12 | 13 | /** 14 | * Represents the "loader" part of the user's options, containing version info for Minecraft and Fabric. 15 | */ 16 | interface FabricLoaderConfig { 17 | version: string; // e.g., "1.19.2" 18 | build: string; // e.g., "latest", "recommended" or a specific build like "0.14.8" 19 | } 20 | 21 | /** 22 | * Overall options passed to FabricMC. 23 | * Adjust or extend according to your project needs. 24 | */ 25 | interface FabricOptions { 26 | path: string; // Base path where libraries and files should be placed 27 | loader: FabricLoaderConfig; // Configuration for the Fabric loader 28 | downloadFileMultiple?: number; // Number of concurrent downloads (if your Downloader supports it) 29 | [key: string]: any; // Allow extra fields as needed 30 | } 31 | 32 | /** 33 | * This object typically references the metadata and JSON URLs for the Fabric API, 34 | * for example: 35 | * { 36 | * metaData: 'https://meta.fabricmc.net/v2/versions', 37 | * json: 'https://meta.fabricmc.net/v2/versions/loader/${version}/${build}/profile/json' 38 | * } 39 | */ 40 | interface LoaderObject { 41 | metaData: string; // URL to fetch general Fabric metadata 42 | json: string; // Template URL to fetch the final Fabric loader JSON 43 | } 44 | 45 | /** 46 | * Represents one library entry in the Fabric loader JSON. 47 | */ 48 | interface FabricLibrary { 49 | name: string; 50 | url: string; 51 | rules?: Array; 52 | } 53 | 54 | /** 55 | * Represents the final JSON object fetched for the Fabric loader, 56 | * containing an array of libraries. 57 | */ 58 | interface FabricJSON { 59 | libraries: FabricLibrary[]; 60 | [key: string]: any; // Extend or adapt based on actual structure 61 | } 62 | 63 | /** 64 | * A class that handles downloading the Fabric loader JSON metadata 65 | * and the libraries needed to launch Fabric. 66 | */ 67 | export default class FabricMC extends EventEmitter { 68 | private readonly options: FabricOptions; 69 | 70 | constructor(options: FabricOptions = { path: '', loader: { version: '', build: '' } }) { 71 | super(); 72 | this.options = options; 73 | } 74 | 75 | /** 76 | * Fetches metadata from the Fabric API to identify the correct build for the given version. 77 | * If the build is "latest" or "recommended", it picks the first entry from the loader array. 78 | * Otherwise, it tries to match the specific build requested by the user. 79 | * 80 | * @param Loader A LoaderObject with metaData and json URLs for Fabric. 81 | * @returns A FabricJSON object on success, or an error object. 82 | */ 83 | public async downloadJson(Loader: LoaderObject): Promise { 84 | let selectedBuild: { version: string } | undefined; 85 | 86 | // Fetch overall metadata 87 | const metaResponse = await fetch(Loader.metaData); 88 | const metaData = await metaResponse.json(); 89 | 90 | // Check if the requested Minecraft version is supported 91 | const versionExists = metaData.game.find((ver: any) => ver.version === this.options.loader.version); 92 | if (!versionExists) { 93 | return { error: `FabricMC doesn't support Minecraft ${this.options.loader.version}` }; 94 | } 95 | 96 | // Extract all possible loader builds 97 | const availableBuilds = metaData.loader.map((b: any) => b.version); 98 | 99 | // If user wants the "latest" or "recommended" build, use the first in the array 100 | if (this.options.loader.build === 'latest' || this.options.loader.build === 'recommended') { 101 | selectedBuild = metaData.loader[0]; 102 | } else { 103 | // Otherwise, search for a matching build 104 | selectedBuild = metaData.loader.find((loaderBuild: any) => loaderBuild.version === this.options.loader.build); 105 | } 106 | 107 | if (!selectedBuild) { 108 | return { 109 | error: `Fabric Loader ${this.options.loader.build} not found, Available builds: ${availableBuilds.join(', ')}` 110 | }; 111 | } 112 | 113 | // Construct the final URL for fetching the Fabric JSON 114 | const url = Loader.json 115 | .replace('${build}', selectedBuild.version) 116 | .replace('${version}', this.options.loader.version); 117 | 118 | // Fetch and parse the JSON 119 | try { 120 | const response = await fetch(url); 121 | const fabricJson: FabricJSON = await response.json(); 122 | return fabricJson; 123 | } catch (err: any) { 124 | return { error: err.message || 'Failed to fetch or parse Fabric loader JSON' }; 125 | } 126 | } 127 | 128 | /** 129 | * Iterates over the libraries in the Fabric JSON, checks if they exist locally, 130 | * and if not, downloads them. Skips libraries that have "rules" (usually platform-specific). 131 | * 132 | * @param json The Fabric loader JSON object with a "libraries" array. 133 | * @returns The same libraries array after downloads, or an error object if something fails. 134 | */ 135 | public async downloadLibraries(json: FabricJSON): Promise { 136 | const { libraries } = json; 137 | const downloader = new Downloader(); 138 | let pendingDownloads: Array<{ 139 | url: string; 140 | folder: string; 141 | path: string; 142 | name: string; 143 | size: number; 144 | }> = []; 145 | 146 | let checkedCount = 0; 147 | let totalSize = 0; 148 | 149 | // Evaluate each library for possible download 150 | for (const lib of libraries) { 151 | // Skip if library has rules that might disqualify it for this platform 152 | if (lib.rules) { 153 | this.emit('check', checkedCount++, libraries.length, 'libraries'); 154 | continue; 155 | } 156 | 157 | // Build the local file path 158 | const libInfo = getPathLibraries(lib.name); 159 | const libFolder = path.resolve(this.options.path, 'libraries', libInfo.path); 160 | const libFilePath = path.resolve(libFolder, libInfo.name); 161 | 162 | // If it doesn't exist, prepare to download 163 | if (!fs.existsSync(libFilePath)) { 164 | const libUrl = `${lib.url}${libInfo.path}/${libInfo.name}`; 165 | 166 | let fileSize = 0; 167 | // Check if the file is available and get its size 168 | const checkRes = await downloader.checkURL(libUrl); 169 | if (checkRes && typeof checkRes === 'object' && 'status' in checkRes && checkRes.status === 200) { 170 | fileSize = checkRes.size; 171 | totalSize += fileSize; 172 | } 173 | 174 | pendingDownloads.push({ 175 | url: libUrl, 176 | folder: libFolder, 177 | path: libFilePath, 178 | name: libInfo.name, 179 | size: fileSize 180 | }); 181 | } 182 | 183 | this.emit('check', checkedCount++, libraries.length, 'libraries'); 184 | } 185 | 186 | // Download all missing libraries in bulk 187 | if (pendingDownloads.length > 0) { 188 | downloader.on('progress', (downloaded: number, total: number) => { 189 | this.emit('progress', downloaded, total, 'libraries'); 190 | }); 191 | 192 | await downloader.downloadFileMultiple(pendingDownloads, totalSize, this.options.downloadFileMultiple); 193 | } 194 | 195 | return libraries; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/Minecraft-Loader/loader/quilt/quilt.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import fs from 'fs'; 7 | import path from 'path'; 8 | import { EventEmitter } from 'events'; 9 | 10 | import { getPathLibraries } from '../../../utils/Index.js'; 11 | import Downloader from '../../../utils/Downloader.js'; 12 | 13 | /** 14 | * Represents the Quilt loader configuration within the user's options. 15 | */ 16 | interface QuiltLoaderConfig { 17 | version: string; // e.g., "1.19.2" 18 | build: string; // e.g., "latest", "recommended", or a specific build ID 19 | } 20 | 21 | /** 22 | * The main options object passed to the Quilt class. 23 | * You can extend this as needed by your application. 24 | */ 25 | interface QuiltOptions { 26 | path: string; // Base path for storing downloaded libraries, etc. 27 | loader: QuiltLoaderConfig; // Loader configuration for Quilt 28 | downloadFileMultiple?: number; // Number of concurrent downloads 29 | [key: string]: any; // Allow additional fields as needed 30 | } 31 | 32 | /** 33 | * Describes the data needed for fetching Quilt metadata and loader JSON. 34 | * For example: 35 | * { 36 | * metaData: "https://meta.quiltmc.org/v3/versions", 37 | * json: "https://meta.quiltmc.org/v3/versions/loader/${version}/${build}/profile/json" 38 | * } 39 | */ 40 | interface LoaderObject { 41 | metaData: string; 42 | json: string; // URL pattern with placeholders like ${version} and ${build} 43 | } 44 | 45 | /** 46 | * A structure for one library entry in the Quilt loader JSON. 47 | */ 48 | interface QuiltLibrary { 49 | name: string; 50 | url: string; 51 | rules?: Array; 52 | } 53 | 54 | /** 55 | * The JSON object typically returned by the Quilt API, 56 | * containing an array of libraries and possibly other fields. 57 | */ 58 | interface QuiltJSON { 59 | libraries: QuiltLibrary[]; 60 | [key: string]: any; 61 | } 62 | 63 | /** 64 | * This class handles fetching the Quilt loader metadata, 65 | * identifying the appropriate build for a given Minecraft version, 66 | * and downloading required libraries. 67 | */ 68 | export default class Quilt extends EventEmitter { 69 | private readonly options: QuiltOptions; 70 | private versionMinecraft: string | undefined; 71 | 72 | constructor(options: QuiltOptions = { path: '', loader: { version: '', build: '' } }) { 73 | super(); 74 | this.options = options; 75 | } 76 | 77 | /** 78 | * Fetches the Quilt loader metadata to identify the correct build for the specified 79 | * Minecraft version. If "latest" or "recommended" is requested, picks the most 80 | * recent or stable build accordingly. 81 | * 82 | * @param Loader An object describing where to fetch Quilt metadata and JSON. 83 | * @returns A QuiltJSON object on success, or an error object if something fails. 84 | */ 85 | public async downloadJson(Loader: LoaderObject): Promise { 86 | let selectedBuild: any; 87 | 88 | // Fetch the metadata 89 | const metaResponse = await fetch(Loader.metaData); 90 | const metaData = await metaResponse.json(); 91 | 92 | // Check if the requested Minecraft version is supported 93 | const mcVersionExists = metaData.game.find((ver: any) => ver.version === this.options.loader.version); 94 | if (!mcVersionExists) { 95 | return { error: `QuiltMC doesn't support Minecraft ${this.options.loader.version}` }; 96 | } 97 | 98 | // Gather all available builds for this version 99 | const availableBuilds = metaData.loader.map((b: any) => b.version); 100 | 101 | // Determine which build to use 102 | if (this.options.loader.build === 'latest') { 103 | selectedBuild = metaData.loader[0]; 104 | } else if (this.options.loader.build === 'recommended') { 105 | // Attempt to find a build that isn't labeled "beta" 106 | selectedBuild = metaData.loader.find((b: any) => !b.version.includes('beta')); 107 | } else { 108 | // Otherwise, match a specific build 109 | selectedBuild = metaData.loader.find( 110 | (loaderItem: any) => loaderItem.version === this.options.loader.build 111 | ); 112 | } 113 | 114 | if (!selectedBuild) { 115 | return { 116 | error: `QuiltMC Loader ${this.options.loader.build} not found, Available builds: ${availableBuilds.join(', ')}` 117 | }; 118 | } 119 | 120 | // Build the URL for the Quilt loader profile JSON 121 | const url = Loader.json 122 | .replace('${build}', selectedBuild.version) 123 | .replace('${version}', this.options.loader.version); 124 | 125 | // Fetch the JSON profile 126 | try { 127 | const response = await fetch(url); 128 | const quiltJson: QuiltJSON = await response.json(); 129 | return quiltJson; 130 | } catch (err: any) { 131 | return { error: err.message || 'Failed to fetch or parse Quilt loader JSON' }; 132 | } 133 | } 134 | 135 | /** 136 | * Parses the Quilt JSON to determine which libraries need downloading, skipping 137 | * any that already exist or that are disqualified by "rules". Downloads them 138 | * in bulk using the Downloader utility. 139 | * 140 | * @param quiltJson A QuiltJSON object containing a list of libraries. 141 | * @returns The final list of libraries, or an error if something fails. 142 | */ 143 | public async downloadLibraries(quiltJson: QuiltJSON): Promise { 144 | const { libraries } = quiltJson; 145 | const downloader = new Downloader(); 146 | 147 | let filesToDownload: Array<{ 148 | url: string; 149 | folder: string; 150 | path: string; 151 | name: string; 152 | size: number; 153 | }> = []; 154 | 155 | let checkedLibraries = 0; 156 | let totalSize = 0; 157 | 158 | for (const lib of libraries) { 159 | // If rules exist, skip it (likely platform-specific logic) 160 | if (lib.rules) { 161 | this.emit('check', checkedLibraries++, libraries.length, 'libraries'); 162 | continue; 163 | } 164 | 165 | // Construct the local path where this library should reside 166 | const libInfo = getPathLibraries(lib.name); 167 | const libFolder = path.resolve(this.options.path, 'libraries', libInfo.path); 168 | const libFilePath = path.resolve(libFolder, libInfo.name); 169 | 170 | // If the library doesn't exist locally, prepare to download 171 | if (!fs.existsSync(libFilePath)) { 172 | const libUrl = `${lib.url}${libInfo.path}/${libInfo.name}`; 173 | 174 | let fileSize = 0; 175 | const checkResult = await downloader.checkURL(libUrl); 176 | 177 | if (checkResult && checkResult.status === 200) { 178 | fileSize = checkResult.size; 179 | totalSize += fileSize; 180 | } 181 | 182 | filesToDownload.push({ 183 | url: libUrl, 184 | folder: libFolder, 185 | path: libFilePath, 186 | name: libInfo.name, 187 | size: fileSize 188 | }); 189 | } 190 | 191 | 192 | // Emit a "check" event for each library 193 | this.emit('check', checkedLibraries++, libraries.length, 'libraries'); 194 | } 195 | 196 | // If there are libraries to download, proceed with the bulk download 197 | if (filesToDownload.length > 0) { 198 | downloader.on('progress', (downloaded: number, total: number) => { 199 | this.emit('progress', downloaded, total, 'libraries'); 200 | }); 201 | 202 | await downloader.downloadFileMultiple(filesToDownload, totalSize, this.options.downloadFileMultiple); 203 | } 204 | 205 | return libraries; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/utils/Downloader.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import fs from 'fs'; 7 | import { EventEmitter } from 'events'; 8 | import { fromAnyReadable } from './Index.js'; 9 | 10 | /** 11 | * Describes a single file to be downloaded by the Downloader class. 12 | */ 13 | export interface DownloadOptions { 14 | /** The URL to download from */ 15 | url: string; 16 | /** Local path (including filename) where the file will be saved */ 17 | path: string; 18 | /** The total length of the file (in bytes), if known */ 19 | length?: number; 20 | /** Local folder in which the file's path resides */ 21 | folder: string; 22 | /** Optional type descriptor, used when emitting 'progress' events */ 23 | type?: string; 24 | } 25 | 26 | /** 27 | * A class responsible for downloading single or multiple files, 28 | * emitting events for progress, speed, estimated time, and errors. 29 | */ 30 | export default class Downloader extends EventEmitter { 31 | /** 32 | * Downloads a single file from the given URL to the specified local path. 33 | * Emits "progress" events with the number of bytes downloaded and total size. 34 | * 35 | * @param url - The remote URL to download from 36 | * @param dirPath - Local folder path where the file is saved 37 | * @param fileName - Name of the file (e.g., "mod.jar") 38 | */ 39 | public async downloadFile(url: string, dirPath: string, fileName: string): Promise { 40 | if (!fs.existsSync(dirPath)) { 41 | fs.mkdirSync(dirPath, { recursive: true }); 42 | } 43 | 44 | const writer = fs.createWriteStream(`${dirPath}/${fileName}`); 45 | const response = await fetch(url); 46 | 47 | const contentLength = response.headers.get('content-length'); 48 | const totalSize = contentLength ? parseInt(contentLength, 10) : 0; 49 | 50 | let downloaded = 0; 51 | 52 | return new Promise((resolve, reject) => { 53 | const body = fromAnyReadable(response.body as any); 54 | 55 | body.on('data', (chunk: Buffer) => { 56 | downloaded += chunk.length; 57 | // Emit progress with the current number of bytes vs. total size 58 | this.emit('progress', downloaded, totalSize); 59 | writer.write(chunk); 60 | }); 61 | 62 | body.on('end', () => { 63 | writer.end(); 64 | resolve(); 65 | }); 66 | 67 | body.on('error', (err: Error) => { 68 | writer.destroy(); 69 | this.emit('error', err); 70 | reject(err); 71 | }); 72 | }); 73 | } 74 | 75 | /** 76 | * Downloads multiple files concurrently (up to the specified limit). 77 | * Emits "progress" events with cumulative bytes downloaded vs. total size, 78 | * as well as "speed" and "estimated" events for speed and ETA calculations. 79 | * 80 | * @param files - An array of DownloadOptions describing each file 81 | * @param size - The total size (in bytes) of all files to be downloaded 82 | * @param limit - The maximum number of simultaneous downloads 83 | * @param timeout - A timeout in milliseconds for each fetch request 84 | */ 85 | public async downloadFileMultiple( 86 | files: DownloadOptions[], 87 | size: number, 88 | limit: number = 1, 89 | timeout: number = 10000 90 | ): Promise { 91 | if (limit > files.length) limit = files.length; 92 | 93 | let completed = 0; // Number of downloads completed 94 | let downloaded = 0; // Cumulative bytes downloaded 95 | let queued = 0; // Index of the next file to download 96 | 97 | let start = Date.now(); 98 | let before = 0; 99 | const speeds: number[] = []; 100 | 101 | const estimated = setInterval(() => { 102 | const duration = (Date.now() - start) / 1000; 103 | const chunkDownloaded = downloaded - before; 104 | if (speeds.length >= 5) speeds.shift(); 105 | speeds.push(chunkDownloaded / duration); 106 | 107 | const avgSpeed = speeds.reduce((a, b) => a + b, 0) / speeds.length; 108 | this.emit('speed', avgSpeed); 109 | 110 | const timeRemaining = (size - downloaded) / avgSpeed; 111 | this.emit('estimated', timeRemaining); 112 | 113 | start = Date.now(); 114 | before = downloaded; 115 | }, 500); 116 | 117 | const downloadNext = async (): Promise => { 118 | if (queued >= files.length) return; 119 | 120 | const file = files[queued++]; 121 | if (!fs.existsSync(file.folder)) { 122 | fs.mkdirSync(file.folder, { recursive: true, mode: 0o777 }); 123 | } 124 | 125 | const writer = fs.createWriteStream(file.path, { flags: 'w', mode: 0o777 }); 126 | const controller = new AbortController(); 127 | const timeoutId = setTimeout(() => controller.abort(), timeout); 128 | 129 | try { 130 | const response = await fetch(file.url, { signal: controller.signal }); 131 | 132 | clearTimeout(timeoutId); 133 | 134 | const stream = fromAnyReadable(response.body as any); 135 | 136 | stream.on('data', (chunk: Buffer) => { 137 | downloaded += chunk.length; 138 | this.emit('progress', downloaded, size, file.type); 139 | writer.write(chunk); 140 | }); 141 | 142 | stream.on('end', () => { 143 | writer.end(); 144 | completed++; 145 | downloadNext(); 146 | }); 147 | 148 | stream.on('error', (err) => { 149 | writer.destroy(); 150 | this.emit('error', err); 151 | completed++; 152 | downloadNext(); 153 | }); 154 | } catch (e) { 155 | clearTimeout(timeoutId); 156 | writer.destroy(); 157 | this.emit('error', e); 158 | completed++; 159 | downloadNext(); 160 | } 161 | }; 162 | 163 | // Start "limit" concurrent downloads 164 | for (let i = 0; i < limit; i++) { 165 | downloadNext(); 166 | } 167 | 168 | // Wait until all downloads complete 169 | return new Promise((resolve) => { 170 | const interval = setInterval(() => { 171 | if (completed === files.length) { 172 | clearInterval(estimated); 173 | clearInterval(interval); 174 | resolve(); 175 | } 176 | }, 100); 177 | }); 178 | } 179 | 180 | /** 181 | * Performs a HEAD request on the given URL to check if it is valid (status=200) 182 | * and retrieves the "content-length" if available. 183 | * 184 | * @param url The URL to check 185 | * @param timeout Time in ms before the request times out 186 | * @returns An object containing { size, status } or rejects with false 187 | */ 188 | public async checkURL( 189 | url: string, 190 | timeout: number = 10000 191 | ): Promise<{ size: number; status: number } | false> { 192 | const controller = new AbortController(); 193 | const timeoutId = setTimeout(() => controller.abort(), timeout); 194 | 195 | try { 196 | const res = await fetch(url, { 197 | method: 'HEAD', 198 | signal: controller.signal 199 | }); 200 | 201 | clearTimeout(timeoutId); 202 | 203 | if (res.status === 200) { 204 | const contentLength = res.headers.get('content-length'); 205 | const size = contentLength ? parseInt(contentLength, 10) : 0; 206 | return { size, status: 200 }; 207 | } 208 | return false; 209 | } catch (e: any) { 210 | clearTimeout(timeoutId); 211 | return false; 212 | } 213 | } 214 | 215 | 216 | 217 | /** 218 | * Tries each mirror in turn, constructing an URL (mirror + baseURL). If a valid 219 | * response is found (status=200), it returns the final URL and size. Otherwise, returns false. 220 | * 221 | * @param baseURL The relative path (e.g. "group/id/artifact.jar") 222 | * @param mirrors An array of possible mirror base URLs 223 | * @returns An object { url, size, status } if found, or false if all mirrors fail 224 | */ 225 | public async checkMirror( 226 | baseURL: string, 227 | mirrors: string[] 228 | ): Promise<{ url: string; size: number; status: number } | false> { 229 | 230 | for (const mirror of mirrors) { 231 | const testURL = `${mirror}/${baseURL}`; 232 | const res = await this.checkURL(testURL); 233 | 234 | if (res !== false && res.status === 200) { 235 | return { 236 | url: testURL, 237 | size: res.size, 238 | status: 200 239 | }; 240 | } 241 | } 242 | return false; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /assets/LWJGL/aarch/3.1.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "artifact": { 6 | "sha1": "d2df40234fd576e708feee78bccadaf02c5712a3", 7 | "size": 300107, 8 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-core.jar", 9 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" 10 | } 11 | }, 12 | "name": "org.lwjgl:lwjgl:3.1.6" 13 | }, 14 | { 15 | "downloads": { 16 | "artifact": { 17 | "sha1": "d2df40234fd576e708feee78bccadaf02c5712a3", 18 | "size": 300107, 19 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-core.jar", 20 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" 21 | }, 22 | "classifiers": { 23 | "natives-linux": { 24 | "sha1": "1d4e6397a4ba0df30f7477ee5ba308d4dad3387e", 25 | "size": 59165, 26 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-natives-linux-arm32.jar", 27 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6-natives-linux.jar" 28 | } 29 | } 30 | }, 31 | "name": "org.lwjgl:lwjgl:3.1.6", 32 | "natives": { 33 | "linux": "natives-linux" 34 | } 35 | }, 36 | { 37 | "downloads": { 38 | "artifact": { 39 | "sha1": "9b0884b8fc763ebf9abe2fd747feaf6bc6968d8c", 40 | "size": 39899, 41 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", 42 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" 43 | } 44 | }, 45 | "name": "org.lwjgl:lwjgl-jemalloc:3.1.6" 46 | }, 47 | { 48 | "downloads": { 49 | "artifact": { 50 | "sha1": "9b0884b8fc763ebf9abe2fd747feaf6bc6968d8c", 51 | "size": 39899, 52 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", 53 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" 54 | }, 55 | "classifiers": { 56 | "natives-linux": { 57 | "sha1": "e3c8c26f433efc2e135b9f02a275742825560bfc", 58 | "size": 134237, 59 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc-natives-linux-arm32.jar", 60 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6-natives-linux.jar" 61 | } 62 | } 63 | }, 64 | "name": "org.lwjgl:lwjgl-jemalloc:3.1.6", 65 | "natives": { 66 | "linux": "natives-linux" 67 | } 68 | }, 69 | { 70 | "downloads": { 71 | "artifact": { 72 | "sha1": "71cbf1f0f60dc7a9c522c5b76e7e62453ca7c77c", 73 | "size": 78718, 74 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal.jar", 75 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" 76 | } 77 | }, 78 | "name": "org.lwjgl:lwjgl-openal:3.1.6" 79 | }, 80 | { 81 | "downloads": { 82 | "artifact": { 83 | "sha1": "71cbf1f0f60dc7a9c522c5b76e7e62453ca7c77c", 84 | "size": 78718, 85 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal.jar", 86 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" 87 | }, 88 | "classifiers": { 89 | "natives-linux": { 90 | "sha1": "fafb2af9deeb64b52a1642ae853493e1f13f98a2", 91 | "size": 398418, 92 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal-natives-linux-arm32.jar", 93 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6-natives-linux.jar" 94 | } 95 | } 96 | }, 97 | "name": "org.lwjgl:lwjgl-openal:3.1.6", 98 | "natives": { 99 | "linux": "natives-linux" 100 | } 101 | }, 102 | { 103 | "downloads": { 104 | "artifact": { 105 | "sha1": "9a6b6e74b41c15b70964a79a32d4a9b04f614d9f", 106 | "size": 830047, 107 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl.jar", 108 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" 109 | } 110 | }, 111 | "name": "org.lwjgl:lwjgl-opengl:3.1.6" 112 | }, 113 | { 114 | "downloads": { 115 | "artifact": { 116 | "sha1": "9a6b6e74b41c15b70964a79a32d4a9b04f614d9f", 117 | "size": 830047, 118 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl.jar", 119 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" 120 | }, 121 | "classifiers": { 122 | "natives-linux": { 123 | "sha1": "a7922f4e7be6aae0483432cac5ee7da7b0748346", 124 | "size": 65526, 125 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl-natives-linux-arm32.jar", 126 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6-natives-linux.jar" 127 | } 128 | } 129 | }, 130 | "name": "org.lwjgl:lwjgl-opengl:3.1.6", 131 | "natives": { 132 | "linux": "natives-linux" 133 | } 134 | }, 135 | { 136 | "downloads": { 137 | "artifact": { 138 | "sha1": "f978d3bf16a4ba09dafeb3d5563786cb7002129c", 139 | "size": 114229, 140 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw.jar", 141 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" 142 | } 143 | }, 144 | "name": "org.lwjgl:lwjgl-glfw:3.1.6" 145 | }, 146 | { 147 | "downloads": { 148 | "artifact": { 149 | "sha1": "f978d3bf16a4ba09dafeb3d5563786cb7002129c", 150 | "size": 114229, 151 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw.jar", 152 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" 153 | }, 154 | "classifiers": { 155 | "natives-linux": { 156 | "sha1": "0c1dc55fed2fe336c256206a2b49fc54107115f5", 157 | "size": 80186, 158 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw-natives-linux-arm32.jar", 159 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6-natives-linux.jar" 160 | } 161 | } 162 | }, 163 | "name": "org.lwjgl:lwjgl-glfw:3.1.6", 164 | "natives": { 165 | "linux": "natives-linux" 166 | } 167 | }, 168 | { 169 | "downloads": { 170 | "artifact": { 171 | "sha1": "bc9e649120027fc78cfebabc17b00ba50e16a86a", 172 | "size": 104372, 173 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb.jar", 174 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" 175 | } 176 | }, 177 | "name": "org.lwjgl:lwjgl-stb:3.1.6" 178 | }, 179 | { 180 | "downloads": { 181 | "artifact": { 182 | "sha1": "bc9e649120027fc78cfebabc17b00ba50e16a86a", 183 | "size": 104372, 184 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb.jar", 185 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" 186 | }, 187 | "classifiers": { 188 | "natives-linux": { 189 | "sha1": "bdf6e31bf49d466cc497444f1be31dbd522e108a", 190 | "size": 143311, 191 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb-natives-linux-arm32.jar", 192 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6-natives-linux.jar" 193 | } 194 | } 195 | }, 196 | "name": "org.lwjgl:lwjgl-stb:3.1.6", 197 | "natives": { 198 | "linux": "natives-linux" 199 | } 200 | } 201 | ] 202 | } -------------------------------------------------------------------------------- /assets/LWJGL/aarch/3.1.6.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "artifact": { 6 | "sha1": "d2df40234fd576e708feee78bccadaf02c5712a3", 7 | "size": 300107, 8 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-core.jar", 9 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" 10 | } 11 | }, 12 | "name": "org.lwjgl:lwjgl:3.1.6" 13 | }, 14 | { 15 | "downloads": { 16 | "artifact": { 17 | "sha1": "d2df40234fd576e708feee78bccadaf02c5712a3", 18 | "size": 300107, 19 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-core.jar", 20 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" 21 | }, 22 | "classifiers": { 23 | "natives-linux": { 24 | "sha1": "1d4e6397a4ba0df30f7477ee5ba308d4dad3387e", 25 | "size": 59165, 26 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-natives-linux-arm32.jar", 27 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6-natives-linux.jar" 28 | } 29 | } 30 | }, 31 | "name": "org.lwjgl:lwjgl:3.1.6", 32 | "natives": { 33 | "linux": "natives-linux" 34 | } 35 | }, 36 | { 37 | "downloads": { 38 | "artifact": { 39 | "sha1": "9b0884b8fc763ebf9abe2fd747feaf6bc6968d8c", 40 | "size": 39899, 41 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", 42 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" 43 | } 44 | }, 45 | "name": "org.lwjgl:lwjgl-jemalloc:3.1.6" 46 | }, 47 | { 48 | "downloads": { 49 | "artifact": { 50 | "sha1": "9b0884b8fc763ebf9abe2fd747feaf6bc6968d8c", 51 | "size": 39899, 52 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", 53 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" 54 | }, 55 | "classifiers": { 56 | "natives-linux": { 57 | "sha1": "e3c8c26f433efc2e135b9f02a275742825560bfc", 58 | "size": 134237, 59 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc-natives-linux-arm32.jar", 60 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6-natives-linux.jar" 61 | } 62 | } 63 | }, 64 | "name": "org.lwjgl:lwjgl-jemalloc:3.1.6", 65 | "natives": { 66 | "linux": "natives-linux" 67 | } 68 | }, 69 | { 70 | "downloads": { 71 | "artifact": { 72 | "sha1": "71cbf1f0f60dc7a9c522c5b76e7e62453ca7c77c", 73 | "size": 78718, 74 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal.jar", 75 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" 76 | } 77 | }, 78 | "name": "org.lwjgl:lwjgl-openal:3.1.6" 79 | }, 80 | { 81 | "downloads": { 82 | "artifact": { 83 | "sha1": "71cbf1f0f60dc7a9c522c5b76e7e62453ca7c77c", 84 | "size": 78718, 85 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal.jar", 86 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" 87 | }, 88 | "classifiers": { 89 | "natives-linux": { 90 | "sha1": "fafb2af9deeb64b52a1642ae853493e1f13f98a2", 91 | "size": 398418, 92 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal-natives-linux-arm32.jar", 93 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6-natives-linux.jar" 94 | } 95 | } 96 | }, 97 | "name": "org.lwjgl:lwjgl-openal:3.1.6", 98 | "natives": { 99 | "linux": "natives-linux" 100 | } 101 | }, 102 | { 103 | "downloads": { 104 | "artifact": { 105 | "sha1": "9a6b6e74b41c15b70964a79a32d4a9b04f614d9f", 106 | "size": 830047, 107 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl.jar", 108 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" 109 | } 110 | }, 111 | "name": "org.lwjgl:lwjgl-opengl:3.1.6" 112 | }, 113 | { 114 | "downloads": { 115 | "artifact": { 116 | "sha1": "9a6b6e74b41c15b70964a79a32d4a9b04f614d9f", 117 | "size": 830047, 118 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl.jar", 119 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" 120 | }, 121 | "classifiers": { 122 | "natives-linux": { 123 | "sha1": "a7922f4e7be6aae0483432cac5ee7da7b0748346", 124 | "size": 65526, 125 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl-natives-linux-arm32.jar", 126 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6-natives-linux.jar" 127 | } 128 | } 129 | }, 130 | "name": "org.lwjgl:lwjgl-opengl:3.1.6", 131 | "natives": { 132 | "linux": "natives-linux" 133 | } 134 | }, 135 | { 136 | "downloads": { 137 | "artifact": { 138 | "sha1": "f978d3bf16a4ba09dafeb3d5563786cb7002129c", 139 | "size": 114229, 140 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw.jar", 141 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" 142 | } 143 | }, 144 | "name": "org.lwjgl:lwjgl-glfw:3.1.6" 145 | }, 146 | { 147 | "downloads": { 148 | "artifact": { 149 | "sha1": "f978d3bf16a4ba09dafeb3d5563786cb7002129c", 150 | "size": 114229, 151 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw.jar", 152 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" 153 | }, 154 | "classifiers": { 155 | "natives-linux": { 156 | "sha1": "0c1dc55fed2fe336c256206a2b49fc54107115f5", 157 | "size": 80186, 158 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw-natives-linux-arm32.jar", 159 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6-natives-linux.jar" 160 | } 161 | } 162 | }, 163 | "name": "org.lwjgl:lwjgl-glfw:3.1.6", 164 | "natives": { 165 | "linux": "natives-linux" 166 | } 167 | }, 168 | { 169 | "downloads": { 170 | "artifact": { 171 | "sha1": "bc9e649120027fc78cfebabc17b00ba50e16a86a", 172 | "size": 104372, 173 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb.jar", 174 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" 175 | } 176 | }, 177 | "name": "org.lwjgl:lwjgl-stb:3.1.6" 178 | }, 179 | { 180 | "downloads": { 181 | "artifact": { 182 | "sha1": "bc9e649120027fc78cfebabc17b00ba50e16a86a", 183 | "size": 104372, 184 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb.jar", 185 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" 186 | }, 187 | "classifiers": { 188 | "natives-linux": { 189 | "sha1": "bdf6e31bf49d466cc497444f1be31dbd522e108a", 190 | "size": 143311, 191 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb-natives-linux-arm32.jar", 192 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6-natives-linux.jar" 193 | } 194 | } 195 | }, 196 | "name": "org.lwjgl:lwjgl-stb:3.1.6", 197 | "natives": { 198 | "linux": "natives-linux" 199 | } 200 | } 201 | ] 202 | } -------------------------------------------------------------------------------- /assets/LWJGL/aarch/3.2.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "artifact": { 6 | "sha1": "c1f8d244dda855a936e136844a5c80611a5f36fe", 7 | "size": 314536, 8 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-core.jar", 9 | "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar" 10 | } 11 | }, 12 | "name": "org.lwjgl:lwjgl:3.2.1" 13 | }, 14 | { 15 | "downloads": { 16 | "artifact": { 17 | "sha1": "c1f8d244dda855a936e136844a5c80611a5f36fe", 18 | "size": 314536, 19 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-core.jar", 20 | "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar" 21 | }, 22 | "classifiers": { 23 | "natives-linux": { 24 | "sha1": "a50f6e12091dccd4901e783b168b428f7653d254", 25 | "size": 65481, 26 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-natives-linux-arm32.jar", 27 | "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-linux.jar" 28 | } 29 | } 30 | }, 31 | "name": "org.lwjgl:lwjgl:3.2.1", 32 | "natives": { 33 | "linux": "natives-linux" 34 | } 35 | }, 36 | { 37 | "downloads": { 38 | "artifact": { 39 | "sha1": "f8f7801d8b82af7705f06c40bbea16b066479531", 40 | "size": 37712, 41 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-jemalloc.jar", 42 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar" 43 | } 44 | }, 45 | "name": "org.lwjgl:lwjgl-jemalloc:3.2.1" 46 | }, 47 | { 48 | "downloads": { 49 | "artifact": { 50 | "sha1": "f8f7801d8b82af7705f06c40bbea16b066479531", 51 | "size": 37712, 52 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-jemalloc.jar", 53 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar" 54 | }, 55 | "classifiers": { 56 | "natives-linux": { 57 | "sha1": "e19e402887625f15a982426156976badcdffaadc", 58 | "size": 134237, 59 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-jemalloc-natives-linux-arm32.jar", 60 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-natives-linux.jar" 61 | } 62 | } 63 | }, 64 | "name": "org.lwjgl:lwjgl-jemalloc:3.2.1", 65 | "natives": { 66 | "linux": "natives-linux" 67 | } 68 | }, 69 | { 70 | "downloads": { 71 | "artifact": { 72 | "sha1": "3dcb4c151a8ccf65f50f4aee9f6ff30a79bb7439", 73 | "size": 79683, 74 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-openal.jar", 75 | "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar" 76 | } 77 | }, 78 | "name": "org.lwjgl:lwjgl-openal:3.2.1" 79 | }, 80 | { 81 | "downloads": { 82 | "artifact": { 83 | "sha1": "3dcb4c151a8ccf65f50f4aee9f6ff30a79bb7439", 84 | "size": 79683, 85 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-openal.jar", 86 | "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar" 87 | }, 88 | "classifiers": { 89 | "natives-linux": { 90 | "sha1": "0dce4b9a444fcb0a4e5b75ea758b0094049daea3", 91 | "size": 398418, 92 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-openal-natives-linux-arm32.jar", 93 | "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-natives-linux.jar" 94 | } 95 | } 96 | }, 97 | "name": "org.lwjgl:lwjgl-openal:3.2.1", 98 | "natives": { 99 | "linux": "natives-linux" 100 | } 101 | }, 102 | { 103 | "downloads": { 104 | "artifact": { 105 | "sha1": "2350cc2bd1fe80e14ff2c39a81477b57dccfe09a", 106 | "size": 939248, 107 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-opengl.jar", 108 | "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar" 109 | } 110 | }, 111 | "name": "org.lwjgl:lwjgl-opengl:3.2.1" 112 | }, 113 | { 114 | "downloads": { 115 | "artifact": { 116 | "sha1": "2350cc2bd1fe80e14ff2c39a81477b57dccfe09a", 117 | "size": 939248, 118 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-opengl.jar", 119 | "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar" 120 | }, 121 | "classifiers": { 122 | "natives-linux": { 123 | "sha1": "a6d3ff86fe3e07bd055032e93ce3b8952574a427", 124 | "size": 56387, 125 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-opengl-natives-linux-arm32.jar", 126 | "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-natives-linux.jar" 127 | } 128 | } 129 | }, 130 | "name": "org.lwjgl:lwjgl-opengl:3.2.1", 131 | "natives": { 132 | "linux": "natives-linux" 133 | } 134 | }, 135 | { 136 | "downloads": { 137 | "artifact": { 138 | "sha1": "f245bcd89c484e05b524c25ea9935b6aa1e6d7ac", 139 | "size": 116839, 140 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-glfw.jar", 141 | "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar" 142 | } 143 | }, 144 | "name": "org.lwjgl:lwjgl-glfw:3.2.1" 145 | }, 146 | { 147 | "downloads": { 148 | "artifact": { 149 | "sha1": "f245bcd89c484e05b524c25ea9935b6aa1e6d7ac", 150 | "size": 116839, 151 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-glfw.jar", 152 | "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar" 153 | }, 154 | "classifiers": { 155 | "natives-linux": { 156 | "sha1": "14533fb79a7077b2eb1d156067956d2da9f3403f", 157 | "size": 80186, 158 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-glfw-natives-linux-arm32.jar", 159 | "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-natives-linux.jar" 160 | } 161 | } 162 | }, 163 | "name": "org.lwjgl:lwjgl-glfw:3.2.1", 164 | "natives": { 165 | "linux": "natives-linux" 166 | } 167 | }, 168 | { 169 | "downloads": { 170 | "artifact": { 171 | "sha1": "acd384013d30ea9ddddd805e9a5996b2b6058c5c", 172 | "size": 105432, 173 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-stb.jar", 174 | "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar" 175 | } 176 | }, 177 | "name": "org.lwjgl:lwjgl-stb:3.2.1" 178 | }, 179 | { 180 | "downloads": { 181 | "artifact": { 182 | "sha1": "acd384013d30ea9ddddd805e9a5996b2b6058c5c", 183 | "size": 105432, 184 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-stb.jar", 185 | "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar" 186 | }, 187 | "classifiers": { 188 | "natives-linux": { 189 | "sha1": "bc5bb97dbb328df0c82375637030f692a161d082", 190 | "size": 144685, 191 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-stb-natives-linux-arm32.jar", 192 | "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-natives-linux.jar" 193 | } 194 | } 195 | }, 196 | "name": "org.lwjgl:lwjgl-stb:3.2.1", 197 | "natives": { 198 | "linux": "natives-linux" 199 | } 200 | } 201 | ] 202 | } -------------------------------------------------------------------------------- /assets/LWJGL/aarch64/3.1.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "artifact": { 6 | "sha1": "55b9dbe63745835ddde8b4c22be8da6520e8274f", 7 | "size": 300107, 8 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-core.jar", 9 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" 10 | } 11 | }, 12 | "name": "org.lwjgl:lwjgl:3.1.6" 13 | }, 14 | { 15 | "downloads": { 16 | "artifact": { 17 | "sha1": "55b9dbe63745835ddde8b4c22be8da6520e8274f", 18 | "size": 300107, 19 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-core.jar", 20 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" 21 | }, 22 | "classifiers": { 23 | "natives-linux": { 24 | "sha1": "68c8151938f33c702528208d32084e1c18b2dc9e", 25 | "size": 54802, 26 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-natives-linux-arm64.jar", 27 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6-natives-linux.jar" 28 | } 29 | } 30 | }, 31 | "name": "org.lwjgl:lwjgl:3.1.6", 32 | "natives": { 33 | "linux": "natives-linux" 34 | } 35 | }, 36 | { 37 | "downloads": { 38 | "artifact": { 39 | "sha1": "6e9ee82494343aee0737c391e151d0147c992584", 40 | "size": 39899, 41 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", 42 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" 43 | } 44 | }, 45 | "name": "org.lwjgl:lwjgl-jemalloc:3.1.6" 46 | }, 47 | { 48 | "downloads": { 49 | "artifact": { 50 | "sha1": "6e9ee82494343aee0737c391e151d0147c992584", 51 | "size": 39899, 52 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", 53 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" 54 | }, 55 | "classifiers": { 56 | "natives-linux": { 57 | "sha1": "762d7d80c9cdf3a3f3fc80c8a5f86612255edfe0", 58 | "size": 156343, 59 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc-patched-natives-linux-arm64.jar", 60 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6-natives-linux.jar" 61 | } 62 | } 63 | }, 64 | "name": "org.lwjgl:lwjgl-jemalloc:3.1.6", 65 | "natives": { 66 | "linux": "natives-linux" 67 | } 68 | }, 69 | { 70 | "downloads": { 71 | "artifact": { 72 | "sha1": "fa243387070b806da104e3746828968b07ca737f", 73 | "size": 78718, 74 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal.jar", 75 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" 76 | } 77 | }, 78 | "name": "org.lwjgl:lwjgl-openal:3.1.6" 79 | }, 80 | { 81 | "downloads": { 82 | "artifact": { 83 | "sha1": "fa243387070b806da104e3746828968b07ca737f", 84 | "size": 78718, 85 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal.jar", 86 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" 87 | }, 88 | "classifiers": { 89 | "natives-linux": { 90 | "sha1": "5d1f9e7c633044a5700f2a80454d2a717251f675", 91 | "size": 469432, 92 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal-natives-linux-arm64.jar", 93 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6-natives-linux.jar" 94 | } 95 | } 96 | }, 97 | "name": "org.lwjgl:lwjgl-openal:3.1.6", 98 | "natives": { 99 | "linux": "natives-linux" 100 | } 101 | }, 102 | { 103 | "downloads": { 104 | "artifact": { 105 | "sha1": "862a9e64741dfab2f3a48638ebd58c35ba5e43cd", 106 | "size": 830047, 107 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl.jar", 108 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" 109 | } 110 | }, 111 | "name": "org.lwjgl:lwjgl-opengl:3.1.6" 112 | }, 113 | { 114 | "downloads": { 115 | "artifact": { 116 | "sha1": "862a9e64741dfab2f3a48638ebd58c35ba5e43cd", 117 | "size": 830047, 118 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl.jar", 119 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" 120 | }, 121 | "classifiers": { 122 | "natives-linux": { 123 | "sha1": "f9dec4cedbe2d98a92dd933d030c7e3efa99411b", 124 | "size": 67010, 125 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl-natives-linux-arm64.jar", 126 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6-natives-linux.jar" 127 | } 128 | } 129 | }, 130 | "name": "org.lwjgl:lwjgl-opengl:3.1.6", 131 | "natives": { 132 | "linux": "natives-linux" 133 | } 134 | }, 135 | { 136 | "downloads": { 137 | "artifact": { 138 | "sha1": "b3ae7bad5b7e7d771ba9423521fc10f4ed67bbab", 139 | "size": 114229, 140 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw.jar", 141 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" 142 | } 143 | }, 144 | "name": "org.lwjgl:lwjgl-glfw:3.1.6" 145 | }, 146 | { 147 | "downloads": { 148 | "artifact": { 149 | "sha1": "b3ae7bad5b7e7d771ba9423521fc10f4ed67bbab", 150 | "size": 114229, 151 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw.jar", 152 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" 153 | }, 154 | "classifiers": { 155 | "natives-linux": { 156 | "sha1": "645aeac6de1deb1b77c7cf3abe84674f77ef1aea", 157 | "size": 85072, 158 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw-natives-linux-arm64.jar", 159 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6-natives-linux.jar" 160 | } 161 | } 162 | }, 163 | "name": "org.lwjgl:lwjgl-glfw:3.1.6", 164 | "natives": { 165 | "linux": "natives-linux" 166 | } 167 | }, 168 | { 169 | "downloads": { 170 | "artifact": { 171 | "sha1": "688e32efb1ad7a09b60f0f4b7131e3dc33b286ca", 172 | "size": 104372, 173 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb.jar", 174 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" 175 | } 176 | }, 177 | "name": "org.lwjgl:lwjgl-stb:3.1.6" 178 | }, 179 | { 180 | "downloads": { 181 | "artifact": { 182 | "sha1": "688e32efb1ad7a09b60f0f4b7131e3dc33b286ca", 183 | "size": 104372, 184 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb.jar", 185 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" 186 | }, 187 | "classifiers": { 188 | "natives-linux": { 189 | "sha1": "7852b75b40a470face01cbb9f37ebc5e715944bd", 190 | "size": 192935, 191 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb-natives-linux-arm64.jar", 192 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6-natives-linux.jar" 193 | } 194 | } 195 | }, 196 | "name": "org.lwjgl:lwjgl-stb:3.1.6", 197 | "natives": { 198 | "linux": "natives-linux" 199 | } 200 | } 201 | ] 202 | } -------------------------------------------------------------------------------- /assets/LWJGL/aarch64/3.1.6.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "artifact": { 6 | "sha1": "55b9dbe63745835ddde8b4c22be8da6520e8274f", 7 | "size": 300107, 8 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-core.jar", 9 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" 10 | } 11 | }, 12 | "name": "org.lwjgl:lwjgl:3.1.6" 13 | }, 14 | { 15 | "downloads": { 16 | "artifact": { 17 | "sha1": "55b9dbe63745835ddde8b4c22be8da6520e8274f", 18 | "size": 300107, 19 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-core.jar", 20 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" 21 | }, 22 | "classifiers": { 23 | "natives-linux": { 24 | "sha1": "68c8151938f33c702528208d32084e1c18b2dc9e", 25 | "size": 54802, 26 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-natives-linux-arm64.jar", 27 | "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6-natives-linux.jar" 28 | } 29 | } 30 | }, 31 | "name": "org.lwjgl:lwjgl:3.1.6", 32 | "natives": { 33 | "linux": "natives-linux" 34 | } 35 | }, 36 | { 37 | "downloads": { 38 | "artifact": { 39 | "sha1": "6e9ee82494343aee0737c391e151d0147c992584", 40 | "size": 39899, 41 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", 42 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" 43 | } 44 | }, 45 | "name": "org.lwjgl:lwjgl-jemalloc:3.1.6" 46 | }, 47 | { 48 | "downloads": { 49 | "artifact": { 50 | "sha1": "6e9ee82494343aee0737c391e151d0147c992584", 51 | "size": 39899, 52 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", 53 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" 54 | }, 55 | "classifiers": { 56 | "natives-linux": { 57 | "sha1": "762d7d80c9cdf3a3f3fc80c8a5f86612255edfe0", 58 | "size": 156343, 59 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc-patched-natives-linux-arm64.jar", 60 | "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6-natives-linux.jar" 61 | } 62 | } 63 | }, 64 | "name": "org.lwjgl:lwjgl-jemalloc:3.1.6", 65 | "natives": { 66 | "linux": "natives-linux" 67 | } 68 | }, 69 | { 70 | "downloads": { 71 | "artifact": { 72 | "sha1": "fa243387070b806da104e3746828968b07ca737f", 73 | "size": 78718, 74 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal.jar", 75 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" 76 | } 77 | }, 78 | "name": "org.lwjgl:lwjgl-openal:3.1.6" 79 | }, 80 | { 81 | "downloads": { 82 | "artifact": { 83 | "sha1": "fa243387070b806da104e3746828968b07ca737f", 84 | "size": 78718, 85 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal.jar", 86 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" 87 | }, 88 | "classifiers": { 89 | "natives-linux": { 90 | "sha1": "5d1f9e7c633044a5700f2a80454d2a717251f675", 91 | "size": 469432, 92 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal-natives-linux-arm64.jar", 93 | "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6-natives-linux.jar" 94 | } 95 | } 96 | }, 97 | "name": "org.lwjgl:lwjgl-openal:3.1.6", 98 | "natives": { 99 | "linux": "natives-linux" 100 | } 101 | }, 102 | { 103 | "downloads": { 104 | "artifact": { 105 | "sha1": "862a9e64741dfab2f3a48638ebd58c35ba5e43cd", 106 | "size": 830047, 107 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl.jar", 108 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" 109 | } 110 | }, 111 | "name": "org.lwjgl:lwjgl-opengl:3.1.6" 112 | }, 113 | { 114 | "downloads": { 115 | "artifact": { 116 | "sha1": "862a9e64741dfab2f3a48638ebd58c35ba5e43cd", 117 | "size": 830047, 118 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl.jar", 119 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" 120 | }, 121 | "classifiers": { 122 | "natives-linux": { 123 | "sha1": "f9dec4cedbe2d98a92dd933d030c7e3efa99411b", 124 | "size": 67010, 125 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl-natives-linux-arm64.jar", 126 | "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6-natives-linux.jar" 127 | } 128 | } 129 | }, 130 | "name": "org.lwjgl:lwjgl-opengl:3.1.6", 131 | "natives": { 132 | "linux": "natives-linux" 133 | } 134 | }, 135 | { 136 | "downloads": { 137 | "artifact": { 138 | "sha1": "b3ae7bad5b7e7d771ba9423521fc10f4ed67bbab", 139 | "size": 114229, 140 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw.jar", 141 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" 142 | } 143 | }, 144 | "name": "org.lwjgl:lwjgl-glfw:3.1.6" 145 | }, 146 | { 147 | "downloads": { 148 | "artifact": { 149 | "sha1": "b3ae7bad5b7e7d771ba9423521fc10f4ed67bbab", 150 | "size": 114229, 151 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw.jar", 152 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" 153 | }, 154 | "classifiers": { 155 | "natives-linux": { 156 | "sha1": "645aeac6de1deb1b77c7cf3abe84674f77ef1aea", 157 | "size": 85072, 158 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw-natives-linux-arm64.jar", 159 | "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6-natives-linux.jar" 160 | } 161 | } 162 | }, 163 | "name": "org.lwjgl:lwjgl-glfw:3.1.6", 164 | "natives": { 165 | "linux": "natives-linux" 166 | } 167 | }, 168 | { 169 | "downloads": { 170 | "artifact": { 171 | "sha1": "688e32efb1ad7a09b60f0f4b7131e3dc33b286ca", 172 | "size": 104372, 173 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb.jar", 174 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" 175 | } 176 | }, 177 | "name": "org.lwjgl:lwjgl-stb:3.1.6" 178 | }, 179 | { 180 | "downloads": { 181 | "artifact": { 182 | "sha1": "688e32efb1ad7a09b60f0f4b7131e3dc33b286ca", 183 | "size": 104372, 184 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb.jar", 185 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" 186 | }, 187 | "classifiers": { 188 | "natives-linux": { 189 | "sha1": "7852b75b40a470face01cbb9f37ebc5e715944bd", 190 | "size": 192935, 191 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb-natives-linux-arm64.jar", 192 | "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6-natives-linux.jar" 193 | } 194 | } 195 | }, 196 | "name": "org.lwjgl:lwjgl-stb:3.1.6", 197 | "natives": { 198 | "linux": "natives-linux" 199 | } 200 | } 201 | ] 202 | } -------------------------------------------------------------------------------- /assets/LWJGL/aarch64/3.2.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "artifact": { 6 | "sha1": "4add49f642c6f6d0bf51e1b6fea388d5101267b9", 7 | "size": 314115, 8 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-core.jar", 9 | "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar" 10 | } 11 | }, 12 | "name": "org.lwjgl:lwjgl:3.2.1" 13 | }, 14 | { 15 | "downloads": { 16 | "artifact": { 17 | "sha1": "4add49f642c6f6d0bf51e1b6fea388d5101267b9", 18 | "size": 314115, 19 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-core.jar", 20 | "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar" 21 | }, 22 | "classifiers": { 23 | "natives-linux": { 24 | "sha1": "03c691efeac999530f4b350312a5a9d85e52cf89", 25 | "size": 60186, 26 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-natives-linux-arm64.jar", 27 | "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-linux.jar" 28 | } 29 | } 30 | }, 31 | "name": "org.lwjgl:lwjgl:3.2.1", 32 | "natives": { 33 | "linux": "natives-linux" 34 | } 35 | }, 36 | { 37 | "downloads": { 38 | "artifact": { 39 | "sha1": "5621e055b542f6caab937b8346de5fd02105f9b2", 40 | "size": 37712, 41 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-jemalloc.jar", 42 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar" 43 | } 44 | }, 45 | "name": "org.lwjgl:lwjgl-jemalloc:3.2.1" 46 | }, 47 | { 48 | "downloads": { 49 | "artifact": { 50 | "sha1": "5621e055b542f6caab937b8346de5fd02105f9b2", 51 | "size": 37712, 52 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-jemalloc.jar", 53 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar" 54 | }, 55 | "classifiers": { 56 | "natives-linux": { 57 | "sha1": "762d7d80c9cdf3a3f3fc80c8a5f86612255edfe0", 58 | "size": 156343, 59 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-jemalloc-patched-natives-linux-arm64.jar", 60 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-natives-linux.jar" 61 | } 62 | } 63 | }, 64 | "name": "org.lwjgl:lwjgl-jemalloc:3.2.1", 65 | "natives": { 66 | "linux": "natives-linux" 67 | } 68 | }, 69 | { 70 | "downloads": { 71 | "artifact": { 72 | "sha1": "b6ef8efb60c888a14feebcd09addc265f1eca1a2", 73 | "size": 79683, 74 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-openal.jar", 75 | "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar" 76 | } 77 | }, 78 | "name": "org.lwjgl:lwjgl-openal:3.2.1" 79 | }, 80 | { 81 | "downloads": { 82 | "artifact": { 83 | "sha1": "b6ef8efb60c888a14feebcd09addc265f1eca1a2", 84 | "size": 79683, 85 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-openal.jar", 86 | "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar" 87 | }, 88 | "classifiers": { 89 | "natives-linux": { 90 | "sha1": "222a583cbfdcc3a81d5d9ad807c3d68196c8ec52", 91 | "size": 469432, 92 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-openal-natives-linux-arm64.jar", 93 | "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-natives-linux.jar" 94 | } 95 | } 96 | }, 97 | "name": "org.lwjgl:lwjgl-openal:3.2.1", 98 | "natives": { 99 | "linux": "natives-linux" 100 | } 101 | }, 102 | { 103 | "downloads": { 104 | "artifact": { 105 | "sha1": "282139e3a8d1402fc25e18eb6a05eb090a1e81b4", 106 | "size": 939248, 107 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-opengl.jar", 108 | "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar" 109 | } 110 | }, 111 | "name": "org.lwjgl:lwjgl-opengl:3.2.1" 112 | }, 113 | { 114 | "downloads": { 115 | "artifact": { 116 | "sha1": "282139e3a8d1402fc25e18eb6a05eb090a1e81b4", 117 | "size": 939248, 118 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-opengl.jar", 119 | "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar" 120 | }, 121 | "classifiers": { 122 | "natives-linux": { 123 | "sha1": "1d5e0092e54efd3f100f2c307261155573fa78f6", 124 | "size": 56110, 125 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-opengl-natives-linux-arm64.jar", 126 | "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-natives-linux.jar" 127 | } 128 | } 129 | }, 130 | "name": "org.lwjgl:lwjgl-opengl:3.2.1", 131 | "natives": { 132 | "linux": "natives-linux" 133 | } 134 | }, 135 | { 136 | "downloads": { 137 | "artifact": { 138 | "sha1": "ccc4ba7e8521b5981343bcdbc3cf6492f35a5aa5", 139 | "size": 116839, 140 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-glfw.jar", 141 | "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar" 142 | } 143 | }, 144 | "name": "org.lwjgl:lwjgl-glfw:3.2.1" 145 | }, 146 | { 147 | "downloads": { 148 | "artifact": { 149 | "sha1": "ccc4ba7e8521b5981343bcdbc3cf6492f35a5aa5", 150 | "size": 116839, 151 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-glfw.jar", 152 | "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar" 153 | }, 154 | "classifiers": { 155 | "natives-linux": { 156 | "sha1": "d52b2a26fafb6d4eebeef74ee4f6c3ebd65004b2", 157 | "size": 85072, 158 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-glfw-natives-linux-arm64.jar", 159 | "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-natives-linux.jar" 160 | } 161 | } 162 | }, 163 | "name": "org.lwjgl:lwjgl-glfw:3.2.1", 164 | "natives": { 165 | "linux": "natives-linux" 166 | } 167 | }, 168 | { 169 | "downloads": { 170 | "artifact": { 171 | "sha1": "e6898a15a8067c89771ae2949bea8f5ff7874fcc", 172 | "size": 105432, 173 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-stb.jar", 174 | "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar" 175 | } 176 | }, 177 | "name": "org.lwjgl:lwjgl-stb:3.2.1" 178 | }, 179 | { 180 | "downloads": { 181 | "artifact": { 182 | "sha1": "e6898a15a8067c89771ae2949bea8f5ff7874fcc", 183 | "size": 105432, 184 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-stb.jar", 185 | "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar" 186 | }, 187 | "classifiers": { 188 | "natives-linux": { 189 | "sha1": "b8f7ace2cb31887254bbcde88e0dc705f5de2c3f", 190 | "size": 193998, 191 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-stb-natives-linux-arm64.jar", 192 | "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-natives-linux.jar" 193 | } 194 | } 195 | }, 196 | "name": "org.lwjgl:lwjgl-stb:3.2.1", 197 | "natives": { 198 | "linux": "natives-linux" 199 | } 200 | } 201 | ] 202 | } -------------------------------------------------------------------------------- /src/Minecraft/Minecraft-Bundle.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import fs from 'fs'; 7 | import path from 'path'; 8 | import { getFileHash } from '../utils/Index.js'; 9 | 10 | /** 11 | * Represents a single file or object that may need to be downloaded or checked. 12 | */ 13 | export interface BundleItem { 14 | type?: 'CFILE' | 'Assets' | string; // e.g., "CFILE" for direct content files 15 | path: string; // Local path where file is or should be stored 16 | folder?: string; // Directory path (derived from 'path') 17 | content?: string; // File content if type === "CFILE" 18 | sha1?: string; // Expected SHA-1 hash for the file 19 | size?: number; // Size in bytes if relevant 20 | url?: string; // Download URL if relevant 21 | } 22 | 23 | /** 24 | * Options for the MinecraftBundle class, indicating paths and ignored files. 25 | */ 26 | export interface MinecraftBundleOptions { 27 | path: string; // The main Minecraft directory or root path 28 | instance?: string; // Instance name, if working with multiple instances 29 | ignored: string[]; // Files or directories to ignore when cleaning 30 | } 31 | 32 | /** 33 | * This class manages checking, downloading, and cleaning up Minecraft files. 34 | * It compares local files with a provided bundle, identifies missing or 35 | * outdated files, and can remove extraneous files. 36 | */ 37 | export default class MinecraftBundle { 38 | private options: MinecraftBundleOptions; 39 | 40 | constructor(options: MinecraftBundleOptions) { 41 | this.options = options; 42 | } 43 | 44 | /** 45 | * Checks each item in the provided bundle to see if it needs to be 46 | * downloaded or updated (e.g., if hashes don't match). 47 | * 48 | * @param bundle Array of file items describing what needs to be on disk. 49 | * @returns Array of BundleItem objects that require downloading. 50 | */ 51 | public async checkBundle(bundle: BundleItem[]): Promise { 52 | const toDownload: BundleItem[] = []; 53 | 54 | for (const file of bundle) { 55 | if (!file.path) continue; 56 | 57 | // Convert path to absolute, consistent format 58 | file.path = path.resolve(this.options.path, file.path).replace(/\\/g, '/'); 59 | file.folder = file.path.split('/').slice(0, -1).join('/'); 60 | 61 | // If it's a direct content file (CFILE), we create/write the content immediately 62 | if (file.type === 'CFILE') { 63 | if (!fs.existsSync(file.folder)) { 64 | fs.mkdirSync(file.folder, { recursive: true, mode: 0o777 }); 65 | } 66 | fs.writeFileSync(file.path, file.content ?? '', { encoding: 'utf8', mode: 0o755 }); 67 | continue; 68 | } 69 | 70 | // If the file is supposed to have a certain hash, check it. 71 | if (fs.existsSync(file.path)) { 72 | // Build the instance path prefix for ignoring checks 73 | let replaceName = `${this.options.path}/`; 74 | if (this.options.instance) { 75 | replaceName = `${this.options.path}/instances/${this.options.instance}/`; 76 | } 77 | 78 | // If file is in "ignored" list, skip checks 79 | const relativePath = file.path.replace(replaceName, ''); 80 | if (this.options.ignored.includes(relativePath)) { 81 | continue; 82 | } 83 | 84 | // If the file has a hash and doesn't match, mark it for download 85 | if (file.sha1) { 86 | const localHash = await getFileHash(file.path); 87 | if (localHash !== file.sha1) { 88 | toDownload.push(file); 89 | } 90 | } 91 | } else { 92 | // The file doesn't exist at all, mark it for download 93 | toDownload.push(file); 94 | } 95 | } 96 | 97 | return toDownload; 98 | } 99 | 100 | /** 101 | * Calculates the total download size of all files in the bundle. 102 | * 103 | * @param bundle Array of items in the bundle (with a 'size' field). 104 | * @returns Sum of all file sizes in bytes. 105 | */ 106 | public async getTotalSize(bundle: BundleItem[]): Promise { 107 | let totalSize = 0; 108 | for (const file of bundle) { 109 | if (file.size) { 110 | totalSize += file.size; 111 | } 112 | } 113 | return totalSize; 114 | } 115 | 116 | /** 117 | * Removes files or directories that should not be present, i.e., those 118 | * not listed in the bundle and not in the "ignored" list. 119 | * If the file is a directory, it's removed recursively. 120 | * 121 | * @param bundle Array of BundleItems representing valid files. 122 | */ 123 | public async checkFiles(bundle: BundleItem[]): Promise { 124 | // If using instances, ensure the 'instances' directory exists 125 | let instancePath = ''; 126 | if (this.options.instance) { 127 | if (!fs.existsSync(`${this.options.path}/instances`)) { 128 | fs.mkdirSync(`${this.options.path}/instances`, { recursive: true }); 129 | } 130 | instancePath = `/instances/${this.options.instance}`; 131 | } 132 | 133 | // Gather all existing files in the relevant directory 134 | const allFiles = this.options.instance 135 | ? this.getFiles(`${this.options.path}${instancePath}`) 136 | : this.getFiles(this.options.path); 137 | 138 | // Also gather files from "loader" and "runtime" directories to ignore 139 | const ignoredFiles = [ 140 | ...this.getFiles(`${this.options.path}/loader`), 141 | ...this.getFiles(`${this.options.path}/runtime`) 142 | ]; 143 | 144 | // Convert custom ignored paths to actual file paths 145 | for (let ignoredPath of this.options.ignored) { 146 | ignoredPath = `${this.options.path}${instancePath}/${ignoredPath}`; 147 | if (fs.existsSync(ignoredPath)) { 148 | if (fs.statSync(ignoredPath).isDirectory()) { 149 | // If it's a directory, add all files within it 150 | ignoredFiles.push(...this.getFiles(ignoredPath)); 151 | } else { 152 | // If it's a single file, just add that file 153 | ignoredFiles.push(ignoredPath); 154 | } 155 | } 156 | } 157 | 158 | // Mark bundle paths as ignored (so we don't delete them) 159 | bundle.forEach(file => { 160 | ignoredFiles.push(file.path); 161 | }); 162 | 163 | // Filter out all ignored files from the main file list 164 | const filesToDelete = allFiles.filter(file => !ignoredFiles.includes(file)); 165 | 166 | // Remove each file or directory 167 | for (const filePath of filesToDelete) { 168 | try { 169 | const stats = fs.statSync(filePath); 170 | if (stats.isDirectory()) { 171 | fs.rmSync(filePath, { recursive: true }); 172 | } else { 173 | fs.unlinkSync(filePath); 174 | 175 | // Clean up empty folders going upward until we hit the main path 176 | let currentDir = path.dirname(filePath); 177 | while (true) { 178 | if (currentDir === this.options.path) break; 179 | const dirContents = fs.readdirSync(currentDir); 180 | if (dirContents.length === 0) { 181 | fs.rmSync(currentDir); 182 | } 183 | currentDir = path.dirname(currentDir); 184 | } 185 | } 186 | } catch { 187 | // If an error occurs (e.g. file locked or non-existent), skip it 188 | continue; 189 | } 190 | } 191 | } 192 | 193 | /** 194 | * Recursively gathers all files in a given directory path. 195 | * If a directory is empty, it is also added to the returned array. 196 | * 197 | * @param dirPath The starting directory path to walk. 198 | * @param collectedFiles Used internally to store file paths. 199 | * @returns The array of all file paths (and empty directories) under dirPath. 200 | */ 201 | private getFiles(dirPath: string, collectedFiles: string[] = []): string[] { 202 | if (fs.existsSync(dirPath)) { 203 | const entries = fs.readdirSync(dirPath); 204 | // If the directory is empty, store it as a "file" so it can be processed 205 | if (entries.length === 0) { 206 | collectedFiles.push(dirPath); 207 | } 208 | // Explore each child entry 209 | for (const entry of entries) { 210 | const fullPath = `${dirPath}/${entry}`; 211 | const stats = fs.statSync(fullPath); 212 | if (stats.isDirectory()) { 213 | this.getFiles(fullPath, collectedFiles); 214 | } else { 215 | collectedFiles.push(fullPath); 216 | } 217 | } 218 | } 219 | return collectedFiles; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/Minecraft/Minecraft-Libraries.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import os from 'os'; 7 | import fs from 'fs'; 8 | import { getFileFromArchive } from '../utils/Index.js'; 9 | 10 | /** 11 | * Maps Node.js platforms to Mojang's naming scheme for OS in library natives. 12 | */ 13 | const MojangLib: Record = { 14 | win32: 'windows', 15 | darwin: 'osx', 16 | linux: 'linux' 17 | }; 18 | 19 | /** 20 | * Maps Node.js architecture strings to Mojang's arch replacements (e.g., "${arch}" => 64). 21 | */ 22 | const Arch: Record = { 23 | x32: '32', 24 | x64: '64', 25 | arm: '32', 26 | arm64: '64' 27 | }; 28 | 29 | /** 30 | * Represents a single library entry in the version JSON. 31 | * Adjust or extend this interface based on your actual JSON structure. 32 | */ 33 | interface MinecraftLibrary { 34 | name?: string; 35 | rules?: Array<{ 36 | os?: { name: string }; 37 | action?: string; 38 | }>; 39 | natives?: Record; 40 | downloads: { 41 | artifact?: { 42 | sha1: string; 43 | size: number; 44 | path: string; 45 | url: string; 46 | }; 47 | classifiers?: Record< 48 | string, 49 | { 50 | sha1: string; 51 | size: number; 52 | path: string; 53 | url: string; 54 | } 55 | >; 56 | }; 57 | } 58 | 59 | /** 60 | * Represents a Minecraft version JSON structure. 61 | * Extend this interface to reflect any additional fields you use. 62 | */ 63 | interface MinecraftVersionJSON { 64 | id: string; 65 | libraries: MinecraftLibrary[]; 66 | downloads: { 67 | client: { 68 | sha1: string; 69 | size: number; 70 | url: string; 71 | }; 72 | }; 73 | [key: string]: any; 74 | } 75 | 76 | /** 77 | * Represents an item in the optional "asset" array fetched from a custom URL. 78 | */ 79 | interface CustomAssetItem { 80 | path: string; 81 | hash: string; 82 | size: number; 83 | url: string; 84 | } 85 | 86 | /** 87 | * Represents the user-provided options for the Libraries class. 88 | * Adjust as needed for your codebase. 89 | */ 90 | interface LibrariesOptions { 91 | path: string; // Base path to the Minecraft folder 92 | instance?: string; // Instance name if using multi-instances 93 | [key: string]: any; // Other fields your code might need 94 | } 95 | 96 | /** 97 | * Represents a file or library entry that needs to be downloaded and stored. 98 | */ 99 | interface LibraryDownload { 100 | sha1?: string; 101 | size?: number; 102 | path: string; 103 | type: string; 104 | url?: string; 105 | content?: string; // For CFILE entries (JSON content) 106 | } 107 | 108 | /** 109 | * This class is responsible for: 110 | * - Gathering library download info from the version JSON 111 | * - Handling custom asset entries if provided 112 | * - Extracting native libraries for the current OS into the appropriate folder 113 | */ 114 | export default class Libraries { 115 | private json!: MinecraftVersionJSON; 116 | private readonly options: LibrariesOptions; 117 | 118 | constructor(options: LibrariesOptions) { 119 | this.options = options; 120 | } 121 | 122 | /** 123 | * Processes the provided Minecraft version JSON to build a list of libraries 124 | * that need to be downloaded (including the main client jar and the version JSON itself). 125 | * 126 | * @param json A MinecraftVersionJSON object (containing libraries, downloads, etc.) 127 | * @returns An array of LibraryDownload items describing each file. 128 | */ 129 | public async Getlibraries(json: MinecraftVersionJSON): Promise { 130 | this.json = json; 131 | const libraries: LibraryDownload[] = []; 132 | 133 | for (const lib of this.json.libraries) { 134 | let artifact: { sha1: string; size: number; path: string; url: string } | undefined; 135 | let type = 'Libraries'; 136 | 137 | if (lib.natives) { 138 | // If this library has OS natives, pick the correct classifier 139 | const classifiers = lib.downloads.classifiers; 140 | let native = lib.natives[MojangLib[os.platform()]] || lib.natives[os.platform()]; 141 | type = 'Native'; 142 | if (native) { 143 | // Replace "${arch}" if present, e.g. "natives-windows-${arch}" 144 | const archReplaced = native.replace('${arch}', Arch[os.arch()] || ''); 145 | artifact = classifiers ? classifiers[archReplaced] : undefined; 146 | } else { 147 | // No valid native for the current platform 148 | continue; 149 | } 150 | } else { 151 | // If there are rules restricting OS, skip if not matching 152 | if (lib.rules && lib.rules[0]?.os?.name) { 153 | if (lib.rules[0].os.name !== MojangLib[os.platform()]) { 154 | continue; 155 | } 156 | } 157 | artifact = lib.downloads.artifact; 158 | } 159 | 160 | if (!artifact) continue; 161 | 162 | libraries.push({ 163 | sha1: artifact.sha1, 164 | size: artifact.size, 165 | path: `libraries/${artifact.path}`, 166 | type: type, 167 | url: artifact.url 168 | }); 169 | } 170 | 171 | // Add the main Minecraft client JAR to the list 172 | libraries.push({ 173 | sha1: this.json.downloads.client.sha1, 174 | size: this.json.downloads.client.size, 175 | path: `versions/${this.json.id}/${this.json.id}.jar`, 176 | type: 'Libraries', 177 | url: this.json.downloads.client.url 178 | }); 179 | 180 | // Add the JSON file for this version as a "CFILE" 181 | libraries.push({ 182 | path: `versions/${this.json.id}/${this.json.id}.json`, 183 | type: 'CFILE', 184 | content: JSON.stringify(this.json) 185 | }); 186 | 187 | return libraries; 188 | } 189 | 190 | /** 191 | * Fetches custom assets or libraries from a remote URL if provided. 192 | * This method expects the response to be an array of objects with 193 | * "path", "hash", "size", and "url". 194 | * 195 | * @param url The remote URL that returns a JSON array of CustomAssetItem 196 | * @returns An array of LibraryDownload entries describing each item 197 | */ 198 | public async GetAssetsOthers(url: string | null): Promise { 199 | if (!url) return []; 200 | 201 | const response = await fetch(url); 202 | const data: CustomAssetItem[] = await response.json(); 203 | 204 | const assets: LibraryDownload[] = []; 205 | for (const asset of data) { 206 | if (!asset.path) continue; 207 | 208 | // The 'type' is deduced from the first part of the path 209 | const fileType = asset.path.split('/')[0]; 210 | assets.push({ 211 | sha1: asset.hash, 212 | size: asset.size, 213 | type: fileType, 214 | path: this.options.instance 215 | ? `instances/${this.options.instance}/${asset.path}` 216 | : asset.path, 217 | url: asset.url 218 | }); 219 | } 220 | return assets; 221 | } 222 | 223 | /** 224 | * Extracts native libraries from the downloaded jars (those marked type="Native") 225 | * and places them into the "natives" folder under "versions//natives". 226 | * 227 | * @param bundle An array of library entries (some of which may be natives) 228 | * @returns The paths of the native files that were extracted 229 | */ 230 | public async natives(bundle: LibraryDownload[]): Promise { 231 | // Gather only the native library files 232 | const natives = bundle 233 | .filter((item) => item.type === 'Native') 234 | .map((item) => `${item.path}`); 235 | 236 | if (natives.length === 0) { 237 | return []; 238 | } 239 | 240 | // Create the natives folder if it doesn't already exist 241 | const nativesFolder = `${this.options.path}/versions/${this.json.id}/natives`.replace(/\\/g, '/'); 242 | if (!fs.existsSync(nativesFolder)) { 243 | fs.mkdirSync(nativesFolder, { recursive: true, mode: 0o777 }); 244 | } 245 | 246 | // For each native jar, extract its contents (excluding META-INF) 247 | for (const native of natives) { 248 | const entries = await getFileFromArchive(native, null, null, true); 249 | 250 | 251 | for (const entry of entries) { 252 | if (entry.name.startsWith('META-INF')) continue; 253 | 254 | if (entry.isDirectory) { 255 | fs.mkdirSync(`${nativesFolder}/${entry.name}`, { recursive: true, mode: 0o777 }); 256 | continue; 257 | } 258 | 259 | // Write the file to the natives folder 260 | fs.writeFileSync(`${nativesFolder}/${entry.name}`, entry.data, { mode: 0o777 }); 261 | } 262 | } 263 | return natives; 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /assets/LWJGL/aarch/3.2.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "artifact": { 6 | "sha1": "16ea3934fca417368250d1ddac01a30c1809d317", 7 | "size": 318413, 8 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-core.jar", 9 | "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar" 10 | } 11 | }, 12 | "name": "org.lwjgl:lwjgl:3.2.2" 13 | }, 14 | { 15 | "downloads": { 16 | "artifact": { 17 | "sha1": "16ea3934fca417368250d1ddac01a30c1809d317", 18 | "size": 318413, 19 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-core.jar", 20 | "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar" 21 | }, 22 | "classifiers": { 23 | "natives-linux": { 24 | "sha1": "6bd0b37fef777a309936a72dc7f63126e8c79ea5", 25 | "size": 90296, 26 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-natives-linux-arm32.jar", 27 | "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2-natives-linux.jar" 28 | } 29 | } 30 | }, 31 | "name": "org.lwjgl:lwjgl:3.2.2", 32 | "natives": { 33 | "linux": "natives-linux" 34 | } 35 | }, 36 | { 37 | "downloads": { 38 | "artifact": { 39 | "sha1": "8224ae2e8fc6d8e1a0fc7d84dc917aa3c440620c", 40 | "size": 33790, 41 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-jemalloc.jar", 42 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar" 43 | } 44 | }, 45 | "name": "org.lwjgl:lwjgl-jemalloc:3.2.2" 46 | }, 47 | { 48 | "downloads": { 49 | "artifact": { 50 | "sha1": "8224ae2e8fc6d8e1a0fc7d84dc917aa3c440620c", 51 | "size": 33790, 52 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-jemalloc.jar", 53 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar" 54 | }, 55 | "classifiers": { 56 | "natives-linux": { 57 | "sha1": "9163a2a5559ef87bc13ead8fea84417ea3928748", 58 | "size": 134237, 59 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-jemalloc-natives-linux-arm32.jar", 60 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2-natives-linux.jar" 61 | } 62 | } 63 | }, 64 | "name": "org.lwjgl:lwjgl-jemalloc:3.2.2", 65 | "natives": { 66 | "linux": "natives-linux" 67 | } 68 | }, 69 | { 70 | "downloads": { 71 | "artifact": { 72 | "sha1": "304f0571fd5971621ee6da86a4c1e90f6f52e2ee", 73 | "size": 79582, 74 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-openal.jar", 75 | "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar" 76 | } 77 | }, 78 | "name": "org.lwjgl:lwjgl-openal:3.2.2" 79 | }, 80 | { 81 | "downloads": { 82 | "artifact": { 83 | "sha1": "304f0571fd5971621ee6da86a4c1e90f6f52e2ee", 84 | "size": 79582, 85 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-openal.jar", 86 | "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar" 87 | }, 88 | "classifiers": { 89 | "natives-linux": { 90 | "sha1": "ecbc981fdd996492a1f6334f003ed62e5a8c0cd5", 91 | "size": 398418, 92 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-openal-natives-linux-arm32.jar", 93 | "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2-natives-linux.jar" 94 | } 95 | } 96 | }, 97 | "name": "org.lwjgl:lwjgl-openal:3.2.2", 98 | "natives": { 99 | "linux": "natives-linux" 100 | } 101 | }, 102 | { 103 | "downloads": { 104 | "artifact": { 105 | "sha1": "9762ae928d02147e716cd82e929b74a97ea9600a", 106 | "size": 937609, 107 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-opengl.jar", 108 | "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar" 109 | } 110 | }, 111 | "name": "org.lwjgl:lwjgl-opengl:3.2.2" 112 | }, 113 | { 114 | "downloads": { 115 | "artifact": { 116 | "sha1": "9762ae928d02147e716cd82e929b74a97ea9600a", 117 | "size": 937609, 118 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-opengl.jar", 119 | "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar" 120 | }, 121 | "classifiers": { 122 | "natives-linux": { 123 | "sha1": "3af5599c74dd76dd8dbb567b3f9b4963a6abeed5", 124 | "size": 56388, 125 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-opengl-natives-linux-arm32.jar", 126 | "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2-natives-linux.jar" 127 | } 128 | } 129 | }, 130 | "name": "org.lwjgl:lwjgl-opengl:3.2.2", 131 | "natives": { 132 | "linux": "natives-linux" 133 | } 134 | }, 135 | { 136 | "downloads": { 137 | "artifact": { 138 | "sha1": "99e9a39fa8ed4167e3ff9e04d47eb32c9e69804d", 139 | "size": 108691, 140 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-glfw.jar", 141 | "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar" 142 | } 143 | }, 144 | "name": "org.lwjgl:lwjgl-glfw:3.2.2" 145 | }, 146 | { 147 | "downloads": { 148 | "artifact": { 149 | "sha1": "99e9a39fa8ed4167e3ff9e04d47eb32c9e69804d", 150 | "size": 108691, 151 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-glfw.jar", 152 | "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar" 153 | }, 154 | "classifiers": { 155 | "natives-linux": { 156 | "sha1": "4265f2fbe3b9d642591165165a17cf406cf7b98e", 157 | "size": 80186, 158 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-glfw-natives-linux-arm32.jar", 159 | "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2-natives-linux.jar" 160 | } 161 | } 162 | }, 163 | "name": "org.lwjgl:lwjgl-glfw:3.2.2", 164 | "natives": { 165 | "linux": "natives-linux" 166 | } 167 | }, 168 | { 169 | "downloads": { 170 | "artifact": { 171 | "sha1": "ea979b0af45b8e689f5f47c989aa8550c148d8a2", 172 | "size": 104075, 173 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-stb.jar", 174 | "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar" 175 | } 176 | }, 177 | "name": "org.lwjgl:lwjgl-stb:3.2.2" 178 | }, 179 | { 180 | "downloads": { 181 | "artifact": { 182 | "sha1": "ea979b0af45b8e689f5f47c989aa8550c148d8a2", 183 | "size": 104075, 184 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-stb.jar", 185 | "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar" 186 | }, 187 | "classifiers": { 188 | "natives-linux": { 189 | "sha1": "ec9d70aaebd0ff76dfeecf8f00b56118bf3706b1", 190 | "size": 149387, 191 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-stb-natives-linux-arm32.jar", 192 | "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2-natives-linux.jar" 193 | } 194 | } 195 | }, 196 | "name": "org.lwjgl:lwjgl-stb:3.2.2", 197 | "natives": { 198 | "linux": "natives-linux" 199 | } 200 | }, 201 | { 202 | "downloads": { 203 | "artifact": { 204 | "sha1": "a8c09f5b7fa24bd53ec329c231b566497a163d5b", 205 | "size": 5571, 206 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-tinyfd.jar", 207 | "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar" 208 | } 209 | }, 210 | "name": "org.lwjgl:lwjgl-tinyfd:3.2.2" 211 | }, 212 | { 213 | "downloads": { 214 | "artifact": { 215 | "sha1": "a8c09f5b7fa24bd53ec329c231b566497a163d5b", 216 | "size": 5571, 217 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-tinyfd.jar", 218 | "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar" 219 | }, 220 | "classifiers": { 221 | "natives-linux": { 222 | "sha1": "82d16054ada6633297a3108fb6d8bae98800c76f", 223 | "size": 41663, 224 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-tinyfd-natives-linux-arm32.jar", 225 | "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-natives-linux.jar" 226 | } 227 | } 228 | }, 229 | "name": "org.lwjgl:lwjgl-tinyfd:3.2.2", 230 | "natives": { 231 | "linux": "natives-linux" 232 | } 233 | } 234 | ] 235 | } -------------------------------------------------------------------------------- /assets/LWJGL/aarch64/3.2.2.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "artifact": { 6 | "sha1": "360899386df83d6a8407844a94478607af937f97", 7 | "size": 318833, 8 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-core.jar", 9 | "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar" 10 | } 11 | }, 12 | "name": "org.lwjgl:lwjgl:3.2.2" 13 | }, 14 | { 15 | "downloads": { 16 | "artifact": { 17 | "sha1": "360899386df83d6a8407844a94478607af937f97", 18 | "size": 318833, 19 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-core.jar", 20 | "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar" 21 | }, 22 | "classifiers": { 23 | "natives-linux": { 24 | "sha1": "612efd57d12b2e48e554858eb35e7e2eb46ebb4c", 25 | "size": 87121, 26 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-natives-linux-arm64.jar", 27 | "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2-natives-linux.jar" 28 | } 29 | } 30 | }, 31 | "name": "org.lwjgl:lwjgl:3.2.2", 32 | "natives": { 33 | "linux": "natives-linux" 34 | } 35 | }, 36 | { 37 | "downloads": { 38 | "artifact": { 39 | "sha1": "cc04eec29b2fa8c298791af9800a3766d9617954", 40 | "size": 33790, 41 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-jemalloc.jar", 42 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar" 43 | } 44 | }, 45 | "name": "org.lwjgl:lwjgl-jemalloc:3.2.2" 46 | }, 47 | { 48 | "downloads": { 49 | "artifact": { 50 | "sha1": "cc04eec29b2fa8c298791af9800a3766d9617954", 51 | "size": 33790, 52 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-jemalloc.jar", 53 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar" 54 | }, 55 | "classifiers": { 56 | "natives-linux": { 57 | "sha1": "762d7d80c9cdf3a3f3fc80c8a5f86612255edfe0", 58 | "size": 156343, 59 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-jemalloc-patched-natives-linux-arm64.jar", 60 | "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2-natives-linux.jar" 61 | } 62 | } 63 | }, 64 | "name": "org.lwjgl:lwjgl-jemalloc:3.2.2", 65 | "natives": { 66 | "linux": "natives-linux" 67 | } 68 | }, 69 | { 70 | "downloads": { 71 | "artifact": { 72 | "sha1": "6dfce9dc6a9629c75b2ae01a8df7e7be80ba0261", 73 | "size": 79582, 74 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-openal.jar", 75 | "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar" 76 | } 77 | }, 78 | "name": "org.lwjgl:lwjgl-openal:3.2.2" 79 | }, 80 | { 81 | "downloads": { 82 | "artifact": { 83 | "sha1": "6dfce9dc6a9629c75b2ae01a8df7e7be80ba0261", 84 | "size": 79582, 85 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-openal.jar", 86 | "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar" 87 | }, 88 | "classifiers": { 89 | "natives-linux": { 90 | "sha1": "948e415b5b2a2c650c25b377a4a9f443b21ce92e", 91 | "size": 469432, 92 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-openal-natives-linux-arm64.jar", 93 | "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2-natives-linux.jar" 94 | } 95 | } 96 | }, 97 | "name": "org.lwjgl:lwjgl-openal:3.2.2", 98 | "natives": { 99 | "linux": "natives-linux" 100 | } 101 | }, 102 | { 103 | "downloads": { 104 | "artifact": { 105 | "sha1": "198bc2f72e0b2eb401eb6f5999aea52909b31ac4", 106 | "size": 937609, 107 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-opengl.jar", 108 | "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar" 109 | } 110 | }, 111 | "name": "org.lwjgl:lwjgl-opengl:3.2.2" 112 | }, 113 | { 114 | "downloads": { 115 | "artifact": { 116 | "sha1": "198bc2f72e0b2eb401eb6f5999aea52909b31ac4", 117 | "size": 937609, 118 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-opengl.jar", 119 | "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar" 120 | }, 121 | "classifiers": { 122 | "natives-linux": { 123 | "sha1": "bd40897077bf7d12f562da898b18ac2c68e1f9d7", 124 | "size": 56109, 125 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-opengl-natives-linux-arm64.jar", 126 | "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2-natives-linux.jar" 127 | } 128 | } 129 | }, 130 | "name": "org.lwjgl:lwjgl-opengl:3.2.2", 131 | "natives": { 132 | "linux": "natives-linux" 133 | } 134 | }, 135 | { 136 | "downloads": { 137 | "artifact": { 138 | "sha1": "155d175037efc76630940c197ca6dea2b17d7e18", 139 | "size": 108691, 140 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-glfw.jar", 141 | "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar" 142 | } 143 | }, 144 | "name": "org.lwjgl:lwjgl-glfw:3.2.2" 145 | }, 146 | { 147 | "downloads": { 148 | "artifact": { 149 | "sha1": "155d175037efc76630940c197ca6dea2b17d7e18", 150 | "size": 108691, 151 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-glfw.jar", 152 | "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar" 153 | }, 154 | "classifiers": { 155 | "natives-linux": { 156 | "sha1": "074ad243761147df0d060fbefc814614d2ff75cc", 157 | "size": 85072, 158 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-glfw-natives-linux-arm64.jar", 159 | "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2-natives-linux.jar" 160 | } 161 | } 162 | }, 163 | "name": "org.lwjgl:lwjgl-glfw:3.2.2", 164 | "natives": { 165 | "linux": "natives-linux" 166 | } 167 | }, 168 | { 169 | "downloads": { 170 | "artifact": { 171 | "sha1": "46a5735f3eb9d17eb5dcbdd5afa194066d2a6555", 172 | "size": 104075, 173 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-stb.jar", 174 | "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar" 175 | } 176 | }, 177 | "name": "org.lwjgl:lwjgl-stb:3.2.2" 178 | }, 179 | { 180 | "downloads": { 181 | "artifact": { 182 | "sha1": "46a5735f3eb9d17eb5dcbdd5afa194066d2a6555", 183 | "size": 104075, 184 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-stb.jar", 185 | "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar" 186 | }, 187 | "classifiers": { 188 | "natives-linux": { 189 | "sha1": "077efa7d7ea41b32df5c6078e912e724cccd06db", 190 | "size": 202038, 191 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-stb-natives-linux-arm64.jar", 192 | "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2-natives-linux.jar" 193 | } 194 | } 195 | }, 196 | "name": "org.lwjgl:lwjgl-stb:3.2.2", 197 | "natives": { 198 | "linux": "natives-linux" 199 | } 200 | }, 201 | { 202 | "downloads": { 203 | "artifact": { 204 | "sha1": "3a75b9811607633bf33c978f53964df1534a4bc1", 205 | "size": 5571, 206 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-tinyfd.jar", 207 | "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar" 208 | } 209 | }, 210 | "name": "org.lwjgl:lwjgl-tinyfd:3.2.2" 211 | }, 212 | { 213 | "downloads": { 214 | "artifact": { 215 | "sha1": "3a75b9811607633bf33c978f53964df1534a4bc1", 216 | "size": 5571, 217 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-tinyfd.jar", 218 | "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar" 219 | }, 220 | "classifiers": { 221 | "natives-linux": { 222 | "sha1": "37c744ca289b5d7ae155d79e39029488b3254e5b", 223 | "size": 37893, 224 | "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-tinyfd-natives-linux-arm64.jar", 225 | "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-natives-linux.jar" 226 | } 227 | } 228 | }, 229 | "name": "org.lwjgl:lwjgl-tinyfd:3.2.2", 230 | "natives": { 231 | "linux": "natives-linux" 232 | } 233 | } 234 | ] 235 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##### v4 • **minecraft‑java‑core** 2 | [![License: CC‑BY‑NC 4.0](https://img.shields.io/badge/License-CC--BY--NC%204.0-yellow.svg)](https://creativecommons.org/licenses/by-nc/4.0/) 3 | ![stable version](https://img.shields.io/npm/v/minecraft-java-core?logo=nodedotjs) 4 | 5 | **minecraft‑java‑core** is a **NodeJS/TypeScript** solution for launching both vanilla *and* modded Minecraft Java Edition without juggling JSON manifests, assets, libraries or Java runtimes yourself. Think of it as the *core* of an Electron/NW.js/CLI launcher. 6 | 7 | --- 8 | 9 | ### Getting support 10 | Need help or just want to chat? Join the community Discord! 11 | 12 |

13 | 14 | 15 | 16 |

17 | 18 | --- 19 | 20 | ### Installing 21 | 22 | ```bash 23 | npm i minecraft-java-core 24 | # or 25 | yarn add minecraft-java-core 26 | ``` 27 | 28 | *Requirements:* Node ≥ 18, TypeScript (only if you import *.ts*), 7‑Zip embedded binary. 29 | 30 | --- 31 | 32 | ### Standard Example (ESM) 33 | ```ts 34 | const { Launch, Microsoft } = require('minecraft-java-core'); 35 | const launcher = new Launch(); 36 | 37 | const fs = require('fs'); 38 | let mc 39 | 40 | (async () => { 41 | if (!fs.existsSync('./account.json')) { 42 | mc = await new Microsoft().getAuth(); 43 | fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); 44 | } else { 45 | mc = JSON.parse(fs.readFileSync('./account.json')); 46 | if (!mc.refresh_token) { 47 | mc = await new Microsoft().getAuth(); 48 | fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); 49 | } else { 50 | mc = await new Microsoft().refresh(mc); 51 | fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); 52 | if (mc.error) process.exit(1); 53 | } 54 | } 55 | 56 | const opt = { 57 | url: "https://luuxcraft.fr/api/user/48c74227-13d1-48d6-931b-0f12b73da340/instance", 58 | path: './minecraft', 59 | authenticator: mc, 60 | version: '1.8.9', 61 | intelEnabledMac: true, 62 | instance: "Hypixel", 63 | 64 | ignored: [ 65 | "config", 66 | "logs", 67 | "resourcepacks", 68 | "options.txt", 69 | "optionsof.txt" 70 | ], 71 | 72 | loader: { 73 | type: 'forge', 74 | build: 'latest', 75 | enable: true 76 | }, 77 | memory: { 78 | min: '14G', 79 | max: '16G' 80 | }, 81 | }; 82 | 83 | launcher.Launch(opt); 84 | launcher.on('progress', (progress, size) => console.log(`[DL] ${((progress / size) * 100).toFixed(2)}%`)); 85 | launcher.on('patch', pacth => process.stdout.write(pacth)); 86 | launcher.on('data', line => process.stdout.write(line)); 87 | launcher.on('error', err => console.error(err)); 88 | })(); 89 | ``` 90 | 91 | --- 92 | 93 | ## Documentation 94 | 95 | ### Launch class 96 | 97 | | Function | Type | Description | 98 | |----------|---------|------------------------------------------------------------------------| 99 | | `launch` | Promise | Launches Minecraft with the given **`LaunchOptions`** (see below). | 100 | 101 | #### LaunchOptions 102 | 103 | | Parameter | Type | Description | Required | 104 | |-----------|------|-------------|----------| 105 | | `path` | String | Working directory where game files are stored (usually `.minecraft`). | ✔︎ | 106 | | `url` | String \| null | Custom version manifest base URL (only for mirror setups). | — | 107 | | `authenticator` | Object | Microsoft / Mojang / AZauth profile returned by the authenticator. | ✔︎ | 108 | | `timeout` | Integer | Network timeout in **milliseconds** for downloads. | — | 109 | | `version` | String | `'latest_release'`, `'latest_snapshot'`, `'1.21.1'`. | — | 110 | | `instance` | String \| null | Name of the instance if you manage multiple profiles. | — | 111 | | `detached` | Boolean | Detach the Java process from the launcher. | — | 112 | | `intelEnabledMac` | Boolean | Force Rosetta when running on Apple Silicon. | — | 113 | | `downloadFileMultiple` | Integer | Max parallel downloads. | — | 114 | | `loader.enable` | Boolean | Whether to install a mod‑loader (Forge/Fabric/…). | — | 115 | | `loader.type` | String \| null | `forge`, `neoforge`, `fabric`, `legacyfabric`, `quilt`. | — | 116 | | `loader.build` | String | Loader build tag (e.g. `latest`, `0.15.9`). | — | 117 | | `loader.path` | String | Destination folder for loader files. Defaults to `./loader`. | — | 118 | | `mcp` | String \| null | Path to MCP configuration for legacy mods. | — | 119 | | `verify` | Boolean | Verify SHA‑1 of downloaded files. | — | 120 | | `ignored` | Array | List of files to skip during verification. | — | 121 | | `JVM_ARGS` | Array | Extra JVM arguments. | — | 122 | | `GAME_ARGS` | Array | Extra Minecraft arguments. | — | 123 | | `java.path` | String \| null | Absolute path to Java runtime. | — | 124 | | `java.version` | String \| null | Force a specific Java version (e.g. `17`). | — | 125 | | `java.type` | String | `jre` or `jdk`. | — | 126 | | `screen.width` | Number \| null | Width of game window. | — | 127 | | `screen.height` | Number \| null | Height of game window. | — | 128 | | `screen.fullscreen` | Boolean | Start the game in fullscreen mode. | — | 129 | | `memory.min` | String | Minimum RAM (e.g. `1G`). | ✔︎ | 130 | | `memory.max` | String | Maximum RAM (e.g. `2G`). | ✔︎ | 131 | 132 | > **Recommendation:** Start with the minimal set (`authenticator`, `path`, `version`, `memory`) and gradually add overrides only when you need them. 133 | 134 | #### Default configuration 135 | 136 | Below is the complete **default** `LaunchOptions` object returned by 137 | `minecraft‑java‑core` when you don’t override any field. Use it as a quick 138 | reference for every available parameter and its default value. 139 | (Parameters marked *nullable* can be left `null`/`undefined` and the library 140 | will figure out sane values.) 141 | 142 | ```ts 143 | const defaultOptions = { 144 | url: null, // Optional custom manifest URL 145 | authenticator: null, // Microsoft/Mojang/AZauth profile 146 | timeout: 10000, // Network timeout in ms 147 | path: '.Minecraft', // Root directory (alias: root) 148 | version: 'latest_release', // Minecraft version (string or 'latest_…') 149 | instance: null, // Multi‑instance name (optional) 150 | detached: false, // Detach Java process from parent 151 | intelEnabledMac: false, // Rosetta toggle for Apple Silicon 152 | downloadFileMultiple: 5, // Parallel downloads 153 | 154 | loader: { 155 | path: './loader', // Where to install loaders 156 | type: null, // forge | neoforge | fabric | … 157 | build: 'latest', // Build number / tag 158 | enable: false, // Whether to install the loader 159 | }, 160 | 161 | mcp: null, // Path to MCP config (legacy mods) 162 | 163 | verify: false, // SHA‑1 check after download 164 | ignored: [], // Files to skip verification 165 | JVM_ARGS: [], // Extra JVM arguments 166 | GAME_ARGS: [], // Extra game arguments 167 | 168 | java: { 169 | path: null, // Custom JVM path 170 | version: null, // Explicit Java version 171 | type: 'jre', // jre | jdk 172 | }, 173 | 174 | screen: { 175 | width: null, 176 | height: null, 177 | fullscreen: false, 178 | }, 179 | 180 | memory: { 181 | min: '1G', 182 | max: '2G', 183 | }, 184 | } as const; 185 | ``` 186 | 187 | > **Note** : Any field you provide when calling `Launch.launch()` will be 188 | > merged on top of these defaults; you rarely need to specify more than 189 | > `authenticator`, `path`, `version` and `memory`. 190 | 191 | --- 192 | 193 | #### Events 194 | 195 | | Event Name | Payload | Description | 196 | |-------------|---------|--------------------------------------------------------------| 197 | | `data` | String | Raw output from the Java process. | 198 | | `progress` | Number | Global download progress percentage. | 199 | | `speed` | Number | Current download speed (kB/s). | 200 | | `estimated` | Number | Estimated time remaining (s). | 201 | | `extract` | String | Name of the file currently being extracted. | 202 | | `patch` | String | Loader patch currently applied. | 203 | | `close` | void | Emitted when the Java process exits. | 204 | | `error` | Error | Something went wrong. | 205 | 206 | --- 207 | 208 | ### Authentication *(built‑in)* 209 | 210 | * **Microsoft** — OAuth 2 Device Code flow via Xbox Live → XSTS → Minecraft. 211 | * **Mojang** *(legacy)* — classic Yggdrasil endpoint. 212 | * **AZauth** — community Yggdrasil‑compatible server. 213 | 214 | > The authenticator returns a profile object that you pass directly to `Launch.launch()`. 215 | 216 | --- 217 | 218 | ### Utilities 219 | 220 | * **Downloader** — resilient downloader with resume, integrity check & `progress`/`speed` events. 221 | * **Status** — simple TCP ping that returns MOTD, player count & latency. 222 | 223 | --- 224 | 225 | ### File structure (simplified) 226 | ``` 227 | src/ 228 | Authenticator/ Microsoft, Mojang, AZauth flows 229 | Minecraft/ Version JSON, assets, libraries, args builder 230 | Minecraft-Loader/ Forge, NeoForge, Fabric, Quilt, … installers 231 | StatusServer/ Server ping implementation 232 | utils/ Downloader & helpers 233 | Launch.ts Main entry point 234 | assets/ LWJGL native indexes 235 | ``` 236 | 237 | --- 238 | 239 | ### Contributors 240 | See the commit history for a full list. Special thanks to: 241 | 242 | * **Luuxis** — original author. 243 | * Community testers & issue reporters. 244 | 245 | --- 246 | 247 | ### License 248 | Released under **Creative Commons Attribution‑NonCommercial 4.0 International**. -------------------------------------------------------------------------------- /src/utils/Index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Luuxis 3 | * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) 4 | */ 5 | 6 | import crypto from 'crypto'; 7 | import fs from 'fs'; 8 | import { Readable } from 'node:stream'; 9 | import Unzipper from './unzipper.js'; 10 | 11 | // This interface defines the structure of a Minecraft library rule. 12 | interface LibraryRule { 13 | action: 'allow' | 'disallow'; 14 | os?: { 15 | name?: string; 16 | }; 17 | features?: any; // Adjust or remove if not used in your code 18 | } 19 | 20 | /** 21 | * Represents a Library object, possibly containing rules or additional fields. 22 | * Adjust according to your actual library structure. 23 | */ 24 | interface MinecraftLibrary { 25 | name: string; 26 | rules?: LibraryRule[]; 27 | downloads?: { 28 | artifact?: { 29 | url?: string; 30 | size?: number; 31 | }; 32 | }; 33 | natives?: Record; 34 | [key: string]: any; // Extend if needed 35 | } 36 | 37 | /** 38 | * Represents a minimal version JSON structure to check if it's considered "old" (pre-1.6 or legacy). 39 | */ 40 | interface MinecraftVersionJSON { 41 | assets?: string; // "legacy" or "pre-1.6" indicates older assets 42 | [key: string]: any; 43 | } 44 | 45 | /** 46 | * Parses a Gradle/Maven identifier string (like "net.minecraftforge:forge:1.19-41.0.63") 47 | * into a local file path (group/artifact/version) and final filename (artifact-version.jar). 48 | * Optionally allows specifying a native string suffix or forcing an extension. 49 | * 50 | * @param main A Gradle-style coordinate (group:artifact:version[:classifier]) 51 | * @param nativeString A suffix for native libraries (e.g., "-natives-linux") 52 | * @param forceExt A forced file extension (default is ".jar") 53 | * @returns An object with `path` and `name`, where `path` is the directory path and `name` is the filename 54 | */ 55 | function getPathLibraries(main: string, nativeString?: string, forceExt?: string) { 56 | // Example "net.minecraftforge:forge:1.19-41.0.63" 57 | const libSplit = main.split(':'); 58 | 59 | // If there's a fourth element, it's typically a classifier appended to version 60 | const fileName = libSplit[3] ? `${libSplit[2]}-${libSplit[3]}` : libSplit[2]; 61 | 62 | // Replace '@' in versions if present (e.g., "1.0@beta" => "1.0.beta") 63 | let finalFileName = fileName.includes('@') 64 | ? fileName.replace('@', '.') 65 | : `${fileName}${nativeString || ''}${forceExt || '.jar'}`; 66 | 67 | // Construct the path: "net.minecraftforge" => "net/minecraftforge" 68 | // artifact => "forge" 69 | // version => "1.19-41.0.63" 70 | const pathLib = `${libSplit[0].replace(/\./g, '/')}/${libSplit[1]}/${libSplit[2].split('@')[0]}`; 71 | 72 | return { 73 | path: pathLib, 74 | name: `${libSplit[1]}-${finalFileName}`, 75 | version: libSplit[2], 76 | }; 77 | } 78 | 79 | /** 80 | * Computes a hash (default SHA-1) of the given file by streaming its contents. 81 | * 82 | * @param filePath Full path to the file on disk 83 | * @param algorithm Hashing algorithm (default: "sha1") 84 | * @returns A Promise resolving to the hex string of the file's hash 85 | */ 86 | async function getFileHash(filePath: string, algorithm: string = 'sha1'): Promise { 87 | const shasum = crypto.createHash(algorithm); 88 | const fileStream = fs.createReadStream(filePath); 89 | 90 | return new Promise((resolve) => { 91 | fileStream.on('data', (data) => { 92 | shasum.update(data); 93 | }); 94 | 95 | fileStream.on('end', () => { 96 | resolve(shasum.digest('hex')); 97 | }); 98 | }); 99 | } 100 | 101 | /** 102 | * Determines if a given Minecraft version JSON is considered "old" 103 | * by checking its assets field (e.g., "legacy" or "pre-1.6"). 104 | * 105 | * @param json The Minecraft version JSON 106 | * @returns true if it's an older version, false otherwise 107 | */ 108 | function isold(json: MinecraftVersionJSON): boolean { 109 | return json.assets === 'legacy' || json.assets === 'pre-1.6'; 110 | } 111 | 112 | /** 113 | * Returns metadata necessary to download specific loaders (Forge, Fabric, etc.) 114 | * based on a loader type string (e.g., "forge", "fabric"). 115 | * If the loader type is unrecognized, returns undefined. 116 | * 117 | * @param type A string representing the loader type 118 | */ 119 | function loader(type: string) { 120 | if (type === 'forge') { 121 | return { 122 | metaData: 'https://files.minecraftforge.net/net/minecraftforge/forge/maven-metadata.json', 123 | meta: 'https://files.minecraftforge.net/net/minecraftforge/forge/${build}/meta.json', 124 | promotions: 'https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json', 125 | install: 'https://maven.minecraftforge.net/net/minecraftforge/forge/${version}/forge-${version}-installer', 126 | universal: 'https://maven.minecraftforge.net/net/minecraftforge/forge/${version}/forge-${version}-universal', 127 | client: 'https://maven.minecraftforge.net/net/minecraftforge/forge/${version}/forge-${version}-client' 128 | }; 129 | } else if (type === 'neoforge') { 130 | return { 131 | legacyMetaData: 'https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge', 132 | metaData: 'https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge', 133 | legacyInstall: 'https://maven.neoforged.net/releases/net/neoforged/forge/${version}/forge-${version}-installer.jar', 134 | install: 'https://maven.neoforged.net/releases/net/neoforged/neoforge/${version}/neoforge-${version}-installer.jar' 135 | }; 136 | } else if (type === 'fabric') { 137 | return { 138 | metaData: 'https://meta.fabricmc.net/v2/versions', 139 | json: 'https://meta.fabricmc.net/v2/versions/loader/${version}/${build}/profile/json' 140 | }; 141 | } else if (type === 'legacyfabric') { 142 | return { 143 | metaData: 'https://meta.legacyfabric.net/v2/versions', 144 | json: 'https://meta.legacyfabric.net/v2/versions/loader/${version}/${build}/profile/json' 145 | }; 146 | } else if (type === 'quilt') { 147 | return { 148 | metaData: 'https://meta.quiltmc.org/v3/versions', 149 | json: 'https://meta.quiltmc.org/v3/versions/loader/${version}/${build}/profile/json' 150 | }; 151 | } 152 | // If none match, return undefined 153 | } 154 | 155 | /** 156 | * A list of potential Maven mirrors for downloading libraries. 157 | */ 158 | const mirrors = [ 159 | 'https://maven.minecraftforge.net', 160 | 'https://maven.neoforged.net/releases', 161 | 'https://maven.creeperhost.net', 162 | 'https://libraries.minecraft.net', 163 | 'https://repo1.maven.org/maven2' 164 | ]; 165 | 166 | /** 167 | * Reads a .jar or .zip file, returning specific entries or listing file entries in the archive. 168 | * 169 | * @param jar Full path to the jar/zip file 170 | * @param file The file entry to extract data from (e.g., "install_profile.json"). If null, returns all entries or partial lists. 171 | * @param prefix A path prefix filter (e.g., "maven/org/lwjgl/") if you want a list of matching files instead of direct extraction 172 | * @returns A buffer or an array of { name, data }, or a list of filenames if prefix is given 173 | */ 174 | async function getFileFromArchive(jar: string, file: string | null = null, prefix: string | null = null, includeDirs: boolean = false): Promise { 175 | const result: any[] = []; 176 | const zip = new Unzipper(jar); 177 | const entries = zip.getEntries(); 178 | 179 | return new Promise((resolve) => { 180 | for (const entry of entries) { 181 | if (includeDirs ? !prefix : (!entry.isDirectory && !prefix)) { 182 | // If no prefix is given, either return a specific file if 'file' is set, 183 | // or accumulate all entries if 'file' is null 184 | if (entry.entryName === file) { 185 | return resolve(entry.getData()); 186 | } else if (!file) { 187 | result.push({ name: entry.entryName, data: entry.getData(), isDirectory: entry.isDirectory }); 188 | } 189 | } 190 | 191 | // If a prefix is given, collect all entry names under that prefix 192 | if (!entry.isDirectory && entry.entryName.includes(prefix)) { 193 | result.push(entry.entryName); 194 | } 195 | } 196 | 197 | if (file && !prefix) { 198 | // If a specific file was requested but not found, return undefined or empty 199 | return resolve(undefined); 200 | } 201 | 202 | // Otherwise, resolve the array of results 203 | resolve(result); 204 | }); 205 | } 206 | 207 | /** 208 | * Determines if a library should be skipped based on its 'rules' property. 209 | * For example, it might skip libraries if action='disallow' for the current OS, 210 | * or if there are specific conditions not met. 211 | * 212 | * @param lib A library object (with optional 'rules' array) 213 | * @returns true if the library should be skipped, false otherwise 214 | */ 215 | function skipLibrary(lib: MinecraftLibrary): boolean { 216 | // Map Node.js platform strings to Mojang's naming 217 | const LibMap: Record = { 218 | win32: 'windows', 219 | darwin: 'osx', 220 | linux: 'linux' 221 | }; 222 | 223 | // If no rules, it's not skipped 224 | if (!lib.rules) { 225 | return false; 226 | } 227 | 228 | let shouldSkip = true; 229 | 230 | for (const rule of lib.rules) { 231 | // If features exist, your logic can handle them here 232 | if (rule.features) { 233 | // Implementation is up to your usage 234 | continue; 235 | } 236 | 237 | // "allow" means it can be used if OS matches (or no OS specified) 238 | // "disallow" means skip if OS matches (or no OS specified) 239 | if ( 240 | rule.action === 'allow' && 241 | ((rule.os && rule.os.name === LibMap[process.platform]) || !rule.os) 242 | ) { 243 | shouldSkip = false; 244 | } else if ( 245 | rule.action === 'disallow' && 246 | ((rule.os && rule.os.name === LibMap[process.platform]) || !rule.os) 247 | ) { 248 | shouldSkip = true; 249 | } 250 | } 251 | 252 | return shouldSkip; 253 | } 254 | 255 | function fromAnyReadable(webStream: ReadableStream): import('node:stream').Readable { 256 | let NodeReadableStreamCtor: typeof ReadableStream | undefined; 257 | if (!NodeReadableStreamCtor && typeof globalThis?.navigator === 'undefined') { 258 | import('node:stream/web').then((mod) => { NodeReadableStreamCtor = mod.ReadableStream; }); 259 | } 260 | if (NodeReadableStreamCtor && webStream instanceof NodeReadableStreamCtor && typeof (Readable as any).fromWeb === 'function') { 261 | return Readable.fromWeb(webStream as any); 262 | } 263 | 264 | const nodeStream = new Readable({ read() { } }); 265 | const reader = webStream.getReader(); 266 | 267 | (function pump() { 268 | reader.read().then(({ done, value }) => { 269 | if (done) return nodeStream.push(null); 270 | nodeStream.push(Buffer.from(value)); 271 | pump(); 272 | }).catch(err => nodeStream.destroy(err)); 273 | })(); 274 | 275 | return nodeStream; 276 | } 277 | 278 | // Export all utility functions and constants 279 | export { 280 | getPathLibraries, 281 | getFileHash, 282 | isold, 283 | loader, 284 | mirrors, 285 | getFileFromArchive, 286 | skipLibrary, 287 | fromAnyReadable 288 | }; 289 | -------------------------------------------------------------------------------- /assets/LWJGL/aarch/3.3.1.json: -------------------------------------------------------------------------------- 1 | { 2 | "libraries": [ 3 | { 4 | "downloads": { 5 | "artifact": { 6 | "sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0", 7 | "size": 128801, 8 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar", 9 | "path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar" 10 | } 11 | }, 12 | "name": "org.lwjgl:lwjgl-glfw:3.3.1" 13 | }, 14 | { 15 | "downloads": { 16 | "artifact": { 17 | "sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0", 18 | "size": 128801, 19 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar", 20 | "path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar" 21 | }, 22 | "classifiers": { 23 | "natives-linux": { 24 | "sha1": "816d935933f2dd743074c4e717cc25b55720f294", 25 | "size": 104027, 26 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-linux-arm32.jar", 27 | "path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-linux.jar" 28 | }, 29 | "sources": { 30 | "sha1": "e502700e6a1a0d02bddb8b4ef85afcdc15c88358", 31 | "size": 125778, 32 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-sources.jar" 33 | } 34 | } 35 | }, 36 | "name": "org.lwjgl:lwjgl-glfw:3.3.1", 37 | "natives": { 38 | "linux": "natives-linux" 39 | } 40 | }, 41 | { 42 | "downloads": { 43 | "artifact": { 44 | "sha1": "a817bcf213db49f710603677457567c37d53e103", 45 | "size": 36601, 46 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar", 47 | "path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar" 48 | } 49 | }, 50 | "name": "org.lwjgl:lwjgl-jemalloc:3.3.1" 51 | }, 52 | { 53 | "downloads": { 54 | "artifact": { 55 | "sha1": "a817bcf213db49f710603677457567c37d53e103", 56 | "size": 36601, 57 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar", 58 | "path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar" 59 | }, 60 | "classifiers": { 61 | "natives-linux": { 62 | "sha1": "a96a6d6cb3876d7813fcee53c3c24f246aeba3b3", 63 | "size": 136157, 64 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-linux-arm32.jar", 65 | "path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-linux.jar" 66 | }, 67 | "sources": { 68 | "sha1": "f5858d34e06053b1866858fed7a685cf0c6b5926", 69 | "size": 32306, 70 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-sources.jar" 71 | } 72 | } 73 | }, 74 | "name": "org.lwjgl:lwjgl-jemalloc:3.3.1", 75 | "natives": { 76 | "linux": "natives-linux" 77 | } 78 | }, 79 | { 80 | "downloads": { 81 | "artifact": { 82 | "sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd", 83 | "size": 88237, 84 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar", 85 | "path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar" 86 | } 87 | }, 88 | "name": "org.lwjgl:lwjgl-openal:3.3.1" 89 | }, 90 | { 91 | "downloads": { 92 | "artifact": { 93 | "sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd", 94 | "size": 88237, 95 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar", 96 | "path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar" 97 | }, 98 | "classifiers": { 99 | "natives-linux": { 100 | "sha1": "ffbe35d7fa5ec9b7eca136a7c71f24d4025a510b", 101 | "size": 400129, 102 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-linux-arm32.jar", 103 | "path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-linux.jar" 104 | }, 105 | "sources": { 106 | "sha1": "9c563bf7c10b71c6609b9f96a7c7859bdf05d21f", 107 | "size": 85417, 108 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-sources.jar" 109 | } 110 | } 111 | }, 112 | "name": "org.lwjgl:lwjgl-openal:3.3.1", 113 | "natives": { 114 | "linux": "natives-linux" 115 | } 116 | }, 117 | { 118 | "downloads": { 119 | "artifact": { 120 | "sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411", 121 | "size": 921563, 122 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar", 123 | "path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar" 124 | } 125 | }, 126 | "name": "org.lwjgl:lwjgl-opengl:3.3.1" 127 | }, 128 | { 129 | "downloads": { 130 | "artifact": { 131 | "sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411", 132 | "size": 921563, 133 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar", 134 | "path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar" 135 | }, 136 | "classifiers": { 137 | "natives-linux": { 138 | "sha1": "e3550fa91097fd56e361b4370fa822220fef3595", 139 | "size": 58474, 140 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-linux-arm32.jar", 141 | "path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-linux.jar" 142 | }, 143 | "sources": { 144 | "sha1": "1a827bc02651fa44d32f424c380edc6d53f94a62", 145 | "size": 1274449, 146 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-sources.jar" 147 | } 148 | } 149 | }, 150 | "name": "org.lwjgl:lwjgl-opengl:3.3.1", 151 | "natives": { 152 | "linux": "natives-linux" 153 | } 154 | }, 155 | { 156 | "downloads": { 157 | "artifact": { 158 | "sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f", 159 | "size": 112380, 160 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar", 161 | "path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar" 162 | } 163 | }, 164 | "name": "org.lwjgl:lwjgl-stb:3.3.1" 165 | }, 166 | { 167 | "downloads": { 168 | "artifact": { 169 | "sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f", 170 | "size": 112380, 171 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar", 172 | "path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar" 173 | }, 174 | "classifiers": { 175 | "natives-linux": { 176 | "sha1": "b08226bab162c06ae69337d8a1b0ee0a3fdf0b90", 177 | "size": 153889, 178 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-linux-arm32.jar", 179 | "path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-linux.jar" 180 | }, 181 | "sources": { 182 | "sha1": "22cb295464f44068add8443204ec8c85fd379cbe", 183 | "size": 103489, 184 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-sources.jar" 185 | } 186 | } 187 | }, 188 | "name": "org.lwjgl:lwjgl-stb:3.3.1", 189 | "natives": { 190 | "linux": "natives-linux" 191 | } 192 | }, 193 | { 194 | "downloads": { 195 | "artifact": { 196 | "sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347", 197 | "size": 6767, 198 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar", 199 | "path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar" 200 | } 201 | }, 202 | "name": "org.lwjgl:lwjgl-tinyfd:3.3.1" 203 | }, 204 | { 205 | "downloads": { 206 | "artifact": { 207 | "sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347", 208 | "size": 6767, 209 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar", 210 | "path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar" 211 | }, 212 | "classifiers": { 213 | "natives-linux": { 214 | "sha1": "d53d331e859217a61298fcbcf8d79137f3df345c", 215 | "size": 48061, 216 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-linux-arm32.jar", 217 | "path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar" 218 | }, 219 | "sources": { 220 | "sha1": "4784c20508b51386ce9d572632524a5bf47ccb40", 221 | "size": 5530, 222 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-sources.jar" 223 | } 224 | } 225 | }, 226 | "name": "org.lwjgl:lwjgl-tinyfd:3.3.1", 227 | "natives": { 228 | "linux": "natives-linux" 229 | } 230 | }, 231 | { 232 | "downloads": { 233 | "artifact": { 234 | "sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb", 235 | "size": 724243, 236 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar", 237 | "path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar" 238 | } 239 | }, 240 | "name": "org.lwjgl:lwjgl:3.3.1" 241 | }, 242 | { 243 | "downloads": { 244 | "artifact": { 245 | "sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb", 246 | "size": 724243, 247 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar", 248 | "path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar" 249 | }, 250 | "classifiers": { 251 | "natives-linux": { 252 | "sha1": "41a3c1dd15d6b964eb8196dde69720a3e3e5e969", 253 | "size": 82374, 254 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-linux-arm32.jar", 255 | "path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-linux.jar" 256 | }, 257 | "sources": { 258 | "sha1": "e918fb595d1ca293a68807a9da8b519ea348a67a", 259 | "size": 572854, 260 | "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-sources.jar" 261 | } 262 | } 263 | }, 264 | "name": "org.lwjgl:lwjgl:3.3.1", 265 | "natives": { 266 | "linux": "natives-linux" 267 | } 268 | } 269 | ] 270 | } --------------------------------------------------------------------------------