diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..1b9f3199 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: ['luuxis'] \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..8c9d5677 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,71 @@ +name: Signaler un bug +description: Vous avez rencontré un bug? Signalez-le ici +title: "[Bug] " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Un grand merci d'avance pour votre aide. Néanmoins, nous avons besoin d'un certain nombre d'informations, pour nous aider. + - type: checkboxes + attributes: + label: "Liste des vérifications à faire avant de valider l'ouverture du signalement de bug" + description: Assurez que vous avez complété ce qui suit, dans le cas contraire, votre rapport peut être refusé + options: + - label: J'ai réussi à reproduire le bug sur le Selvania Launcher (sans mes modifications) + required: true + - label: Mon code respecte la licence Creative Commons Zero v1.0 Universal + required: true + - label: Mon code respecte les conditions d'utilisation du Selvania Launcher + required: true + - label: J'arrive à reproduire le bug sur la dernière version du Selvania Launcher + required: true + - type: dropdown + attributes: + label: Système d'exploitation + options: + - Windows + - macOS + - Linux (Basé sur Debian/Ubuntu) + - Linux (Autres) + validations: + required: true + - type: input + attributes: + label: Version du système d'exploitation + placeholder: "Exemple: Windows 11 Professionnel 21H2 Build 22000.739" + validations: + required: true + - type: input + attributes: + label: Hash du commit sur lequel le bug est rencontré + placeholder: 84d7881b67ecf6088205eca6723bfb19bf2a5f0d + - type: textarea + attributes: + label: Comportement attendu + description: Une description de ce qui devrait se passer + placeholder: Le launcher devrait... + validations: + required: true + - type: textarea + attributes: + label: Comportement actuel + description: Une description de ce qui se passe avec le bug + validations: + required: true + - type: textarea + attributes: + label: Instructions pour reproduire le but + placeholder: | + 1. Ouvrir le launcher + 2. Aller dans le menu xyz + 3. Cliquer sur abc + 4. Observer + validations: + required: true + - type: textarea + attributes: + label: Notes additionnelles + placeholder: Détails supplémentaires concernant le bug, tout ce qui pourrait être utile + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..574a5e0f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Nous rejoindre sur Discord, pour toutes questions ou demandes + url: http://discord.luuxis.fr + about: Veuillez ne pas ouvrir d'issue autre que pour signaler des bugs diff --git a/.gitignore b/.gitignore index 08dc1a24..82791e76 100755 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ node_modules build -files -.Minecraft -test/*.json +test/Minecraft +test/*.json* webfiles/instances/* +.DS_Store diff --git a/LICENSE.md b/LICENSE.md index c94c4001..82901b88 100755 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,160 +1,77 @@ -# Creative Commons Attribution-NonCommercial 4.0 International +LUUXIS LICENSE v1.0 – LICENCE PERSONNALISÉE / CUSTOM LICENSE -Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. +Copyright © 2025 Luuxis -### Using Creative Commons Public Licenses +FRANÇAIS 🇫🇷 +──────────────────────────────────────────────────────────── -Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. +Ce logiciel et son code source sont la propriété exclusive de l’auteur (ci-après « le Titulaire »). +L’utilisation, la modification et la redistribution sont autorisées sous réserve du respect strict des conditions suivantes : -* __Considerations for licensors:__ Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. [More considerations for licensors](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors). +1. 🛑 Interdiction d’usage commercial par des tiers + - Il est strictement interdit de vendre, louer ou redistribuer ce code (ou ses dérivés) à des fins commerciales. + - Toute utilisation commerciale directe du code est interdite, sauf autorisation écrite explicite du Titulaire. -* __Considerations for the public:__ By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. [More considerations for the public](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees). +2. 💰 Microtransactions autorisées + - La monétisation par microtransactions en jeu est autorisée, tant que : + - le code n’est pas vendu ni monétisé directement, + - le code source reste public et accessible. -## Creative Commons Attribution-NonCommercial 4.0 International Public License +3. 📂 Code source obligatoire et public + - Toute version redistribuée ou modifiée doit publier son code source complet, librement et gratuitement accessible, sans restriction. -By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. +4. 🧾 Attribution + - Le nom de l’auteur original (« Luuxis ») doit être clairement mentionné dans toute redistribution ou version modifiée. -### Section 1 – Definitions. +5. 🔐 Droit exclusif de revente par le créateur + - Seul le Titulaire (Luuxis) est autorisé à vendre ou concéder une licence commerciale du code source. -a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. +6. 🚫 Interdiction de modifier cette licence + - Il est interdit de modifier, supprimer ou remplacer cette licence. Toute redistribution doit inclure cette licence sans altération. -b. __Adapter's License__ means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. +7. ⚠️ Absence de garantie + - Le logiciel est fourni "tel quel", sans garantie. L’auteur décline toute responsabilité pour tout dommage lié à son usage. -c. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. +8. ⚖️ Violation et poursuites + - 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). -d. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. +Par l’utilisation de ce logiciel, vous acceptez toutes les conditions de cette licence. -e. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. +──────────────────────────────────────────────────────────── -f. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License. +ENGLISH 🇬🇧 +──────────────────────────────────────────────────────────── -g. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. +This software and its source code are the exclusive property of the author (hereinafter “the Licensor”). +Use, modification, and redistribution are permitted under the following strict conditions: -h. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License. +1. 🛑 Prohibition of commercial use by third parties + - It is strictly forbidden to sell, rent or redistribute this code (or any derivative) for commercial purposes. + - Any direct commercial use of the code is forbidden unless explicitly authorized in writing by the Licensor. -i. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. +2. 💰 Microtransactions allowed + - In-game microtransaction monetization is allowed, as long as: + - the code itself is not sold or directly monetized, + - the source code remains public and freely accessible. -j. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. +3. 📂 Mandatory public source code + - Any redistributed or modified version must make its complete source code freely and publicly accessible, without restrictions. -k. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. +4. 🧾 Attribution + - The original author’s name (“Luuxis”) must be clearly credited in any redistribution or modified version. -l. __You__ means the individual or entity exercising the Licensed Rights under this Public License. __Your__ has a corresponding meaning. +5. 🔐 Exclusive resale rights reserved by the creator + - Only the Licensor (Luuxis) is allowed to sell or license this software or its source code for commercial purposes. -### Section 2 – Scope. +6. 🚫 License modification is forbidden + - This license must not be altered, replaced, or removed. Any redistribution must include this license as-is. -a. ___License grant.___ +7. ⚠️ No warranty + - This software is provided “as is”, without any warranties. The author accepts no liability for any damage resulting from its use. - 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: +8. ⚖️ Violation and legal consequences + - Any violation results in immediate termination of this license. Unauthorized use may lead to legal action under applicable copyright law. - A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and - - B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only. - - 2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. - - 3. __Term.__ The term of this Public License is specified in Section 6(a). - - 4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. - - 5. __Downstream recipients.__ - - A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. - - B. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. - - 6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). - -b. ___Other rights.___ - - 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this Public License. - - 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. - -### Section 3 – License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the following conditions. - -a. ___Attribution.___ - - 1. If You Share the Licensed Material (including in modified form), You must: - - A. retain the following if it is supplied by the Licensor with the Licensed Material: - - i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of warranties; - - v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; - - B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and - - C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. - - 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. - -### Section 4 – Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: - -a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only; - -b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and - -c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. - -### Section 5 – Disclaimer of Warranties and Limitation of Liability. - -a. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__ - -b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__ - -c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. - -### Section 6 – Term and Termination. - -a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. - -b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. - -c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. - -d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. - -### Section 7 – Other Terms and Conditions. - -a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. - -b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. - -### Section 8 – Interpretation. - -a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. - -b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. - -c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. - -d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. - -> Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. -> -> Creative Commons may be contacted at creativecommons.org +By using this software, you fully and irrevocably agree to the terms of this license. +──────────────────────────────────────────────────────────── diff --git a/README.md b/README.md index dec625ec..f10f0f13 100755 --- a/README.md +++ b/README.md @@ -1,127 +1,248 @@ -# minecraft-java-core -NodeJS Module for Minecraft launcher -
-[![Number](https://img.shields.io/npm/v/minecraft-java-core?style=social&logo=appveyor)](https://npmjs.com/minecraft-java-core) -
-[![Install](https://img.shields.io/npm/dm/minecraft-java-core.svg?style=social&logo=appveyor)](https://npmjs.com/minecraft-java-core) -
-[![size](https://img.shields.io/github/languages/code-size/luuxis/minecraft-java-core?style=social&logo=appveyor)](https://npmjs.com/minecraft-java-core) -
-[![sizeinstall](https://badgen.net/packagephobia/install/minecraft-java-core)](https://npmjs.com/minecraft-java-core) +##### v4 • **minecraft‑java‑core** +[![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/) +![stable version](https://img.shields.io/npm/v/minecraft-java-core?logo=nodedotjs) + +**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. --- -## Avantages :dizzy: -- Auto check & downloading compatible java version -- Support 100% custom minecraft version -- Work with ftp without any zip file, juste drop folder in your ftp -- Auto check & delete file with bad hash & size - -# Install Client - -## Quick Start :zap: -```npm -git clone https://github.com/luuxis/Selvania-Launcher.git -cd Selvania-Launcher -npm install -npm start -``` -## Installation :package: -```npm +### Getting support +Need help or just want to chat? Join the community Discord! + +

+ + + +

+ +--- + +### Installing + +```bash npm i minecraft-java-core +# or +yarn add minecraft-java-core ``` -## Usage :triangular_flag_on_post: -Require library -```javascript -const { Launch, Mojang } = require('minecraft-java-core'); -``` +*Requirements:* Node ≥ 18, TypeScript (only if you import *.ts*), 7‑Zip embedded binary. -## Launch :rocket: -### Options -```javascript -const { Mojang, Launch } = require('minecraft-java-core'); -const launch = new Launch(); - -async function main() { - let opt = { - authenticator: await Mojang.login('Luuxis'), - timeout: 10000, - path: './.Minecraft test', - version: '1.19.3', - detached: false, - downloadFileMultiple: 100, +--- + +### Standard Example (ESM) +```ts +const { Launch, Microsoft } = require('minecraft-java-core'); +const launcher = new Launch(); + +const fs = require('fs'); +let mc + +(async () => { + if (!fs.existsSync('./account.json')) { + mc = await new Microsoft().getAuth(); + fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); + } else { + mc = JSON.parse(fs.readFileSync('./account.json')); + if (!mc.refresh_token) { + mc = await new Microsoft().getAuth(); + fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); + } else { + mc = await new Microsoft().refresh(mc); + fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); + if (mc.error) process.exit(1); + } + } + + const opt = { + url: "https://luuxcraft.fr/api/user/48c74227-13d1-48d6-931b-0f12b73da340/instance", + path: './minecraft', + authenticator: mc, + version: '1.8.9', + intelEnabledMac: true, + instance: "Hypixel", + + ignored: [ + "config", + "logs", + "resourcepacks", + "options.txt", + "optionsof.txt" + ], loader: { type: 'forge', build: 'latest', enable: true }, + memory: { + min: '14G', + max: '16G' + }, + }; + + launcher.Launch(opt); + launcher.on('progress', (progress, size) => console.log(`[DL] ${((progress / size) * 100).toFixed(2)}%`)); + launcher.on('patch', pacth => process.stdout.write(pacth)); + launcher.on('data', line => process.stdout.write(line)); + launcher.on('error', err => console.error(err)); +})(); +``` - verify: false, - ignored: ['loader', 'options.txt'], - args: [], +--- - javaPath: null, - java: true, +## Documentation + +### Launch class + +| Function | Type | Description | +|----------|---------|------------------------------------------------------------------------| +| `launch` | Promise | Launches Minecraft with the given **`LaunchOptions`** (see below). | + +#### LaunchOptions + +| Parameter | Type | Description | Required | +|-----------|------|-------------|----------| +| `path` | String | Working directory where game files are stored (usually `.minecraft`). | ✔︎ | +| `url` | String \| null | Custom version manifest base URL (only for mirror setups). | — | +| `authenticator` | Object | Microsoft / Mojang / AZauth profile returned by the authenticator. | ✔︎ | +| `timeout` | Integer | Network timeout in **milliseconds** for downloads. | — | +| `version` | String | `'latest_release'`, `'latest_snapshot'`, `'1.21.1'`. | — | +| `instance` | String \| null | Name of the instance if you manage multiple profiles. | — | +| `detached` | Boolean | Detach the Java process from the launcher. | — | +| `intelEnabledMac` | Boolean | Force Rosetta when running on Apple Silicon. | — | +| `downloadFileMultiple` | Integer | Max parallel downloads. | — | +| `loader.enable` | Boolean | Whether to install a mod‑loader (Forge/Fabric/…). | — | +| `loader.type` | String \| null | `forge`, `neoforge`, `fabric`, `legacyfabric`, `quilt`. | — | +| `loader.build` | String | Loader build tag (e.g. `latest`, `0.15.9`). | — | +| `loader.path` | String | Destination folder for loader files. Defaults to `./loader`. | — | +| `mcp` | String \| null | Path to MCP configuration for legacy mods. | — | +| `verify` | Boolean | Verify SHA‑1 of downloaded files. | — | +| `ignored` | Array | List of files to skip during verification. | — | +| `JVM_ARGS` | Array | Extra JVM arguments. | — | +| `GAME_ARGS` | Array | Extra Minecraft arguments. | — | +| `java.path` | String \| null | Absolute path to Java runtime. | — | +| `java.version` | String \| null | Force a specific Java version (e.g. `17`). | — | +| `java.type` | String | `jre` or `jdk`. | — | +| `screen.width` | Number \| null | Width of game window. | — | +| `screen.height` | Number \| null | Height of game window. | — | +| `screen.fullscreen` | Boolean | Start the game in fullscreen mode. | — | +| `memory.min` | String | Minimum RAM (e.g. `1G`). | ✔︎ | +| `memory.max` | String | Maximum RAM (e.g. `2G`). | ✔︎ | + +> **Recommendation:** Start with the minimal set (`authenticator`, `path`, `version`, `memory`) and gradually add overrides only when you need them. + +#### Default configuration + +Below is the complete **default** `LaunchOptions` object returned by +`minecraft‑java‑core` when you don’t override any field. Use it as a quick +reference for every available parameter and its default value. +(Parameters marked *nullable* can be left `null`/`undefined` and the library +will figure out sane values.) + +```ts +const defaultOptions = { + url: null, // Optional custom manifest URL + authenticator: null, // Microsoft/Mojang/AZauth profile + timeout: 10000, // Network timeout in ms + path: '.Minecraft', // Root directory (alias: root) + version: 'latest_release', // Minecraft version (string or 'latest_…') + instance: null, // Multi‑instance name (optional) + detached: false, // Detach Java process from parent + intelEnabledMac: false, // Rosetta toggle for Apple Silicon + downloadFileMultiple: 5, // Parallel downloads + + loader: { + path: './loader', // Where to install loaders + type: null, // forge | neoforge | fabric | … + build: 'latest', // Build number / tag + enable: false, // Whether to install the loader + }, + + mcp: null, // Path to MCP config (legacy mods) + + verify: false, // SHA‑1 check after download + ignored: [], // Files to skip verification + JVM_ARGS: [], // Extra JVM arguments + GAME_ARGS: [], // Extra game arguments + + java: { + path: null, // Custom JVM path + version: null, // Explicit Java version + type: 'jre', // jre | jdk + }, + + screen: { + width: null, + height: null, + fullscreen: false, + }, + + memory: { + min: '1G', + max: '2G', + }, +} as const; +``` - screen: { - width: null, - height: null, - fullscreen: null, - }, +> **Note** : Any field you provide when calling `Launch.launch()` will be +> merged on top of these defaults; you rarely need to specify more than +> `authenticator`, `path`, `version` and `memory`. - memory: { - min: '2G', - max: '4G' - } - } +--- - await launch.Launch(opt); +#### Events - launch.on('extract', extract => { - console.log(extract); - }); +| Event Name | Payload | Description | +|-------------|---------|--------------------------------------------------------------| +| `data` | String | Raw output from the Java process. | +| `progress` | Number | Global download progress percentage. | +| `speed` | Number | Current download speed (kB/s). | +| `estimated` | Number | Estimated time remaining (s). | +| `extract` | String | Name of the file currently being extracted. | +| `patch` | String | Loader patch currently applied. | +| `close` | void | Emitted when the Java process exits. | +| `error` | Error | Something went wrong. | - launch.on('progress', (progress, size, element) => { - console.log(`Downloading ${element} ${Math.round((progress / size) * 100)}%`); - }); +--- - launch.on('check', (progress, size, element) => { - console.log(`Checking ${element} ${Math.round((progress / size) * 100)}%`); - }); +### Authentication *(built‑in)* - launch.on('estimated', (time) => { - let hours = Math.floor(time / 3600); - let minutes = Math.floor((time - hours * 3600) / 60); - let seconds = Math.floor(time - hours * 3600 - minutes * 60); - console.log(`${hours}h ${minutes}m ${seconds}s`); - }) +* **Microsoft** — OAuth 2 Device Code flow via Xbox Live → XSTS → Minecraft. +* **Mojang** *(legacy)* — classic Yggdrasil endpoint. +* **AZauth** — community Yggdrasil‑compatible server. - launch.on('speed', (speed) => { - console.log(`${(speed / 1067008).toFixed(2)} Mb/s`) - }) +> The authenticator returns a profile object that you pass directly to `Launch.launch()`. - launch.on('patch', patch => { - console.log(patch); - }); +--- - launch.on('data', (e) => { - console.log(e); - }) +### Utilities - launch.on('close', code => { - console.log(code); - }); +* **Downloader** — resilient downloader with resume, integrity check & `progress`/`speed` events. +* **Status** — simple TCP ping that returns MOTD, player count & latency. - launch.on('error', err => { - console.log(err); - }); -} +--- -main() +### File structure (simplified) ``` +src/ + Authenticator/ Microsoft, Mojang, AZauth flows + Minecraft/ Version JSON, assets, libraries, args builder + Minecraft-Loader/ Forge, NeoForge, Fabric, Quilt, … installers + StatusServer/ Server ping implementation + utils/ Downloader & helpers + Launch.ts Main entry point +assets/ LWJGL native indexes +``` + +--- + +### Contributors +See the commit history for a full list. Special thanks to: + +* **Luuxis** — original author. +* Community testers & issue reporters. + --- -
-[

discord](https://discord.gg/e9q7Yr2cuQ) +### License +Released under **Creative Commons Attribution‑NonCommercial 4.0 International**. \ No newline at end of file diff --git a/assets/LWJGL/aarch/2.9.4.json b/assets/LWJGL/aarch/2.9.4.json new file mode 100644 index 00000000..9a6bd338 --- /dev/null +++ b/assets/LWJGL/aarch/2.9.4.json @@ -0,0 +1,96 @@ +{ + "libraries": [ + { + "downloads": { + "classifiers": { + "natives-linux": { + "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar", + "sha1": "f3c455b71c5146acb5f8a9513247fc06db182fd5", + "size": 4521, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-2.9.4/jinput-platform-2.0.5-natives-linux.jar" + } + } + }, + "extract": { + "exclude": [ + "META-INF/" + ] + }, + "name": "net.java.jinput:jinput-platform:2.0.5", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "path": "net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar", + "sha1": "c2e322bbec2345f1b93b96000f93e3a4c3b2bf96", + "size": 216945, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-2.9.4/jinput-2.0.5.jar" + } + }, + "name": "net.java.jinput:jinput:2.0.5" + }, + { + "downloads": { + "artifact": { + "path": "net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar", + "sha1": "e12fe1fda814bd348c1579329c86943d2cd3c6a6", + "size": 7508, + "url": "https://libraries.minecraft.net/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar" + } + }, + "name": "net.java.jutils:jutils:1.0.0" + }, + { + "downloads": { + "artifact": { + "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar", + "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33", + "size": 22, + "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" + }, + "classifiers": { + "natives-linux": { + "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar", + "sha1": "fa483e540a9a753a5ffbb23dcf7879a5bf752611", + "size": 475177, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-2.9.4/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" + } + } + }, + "extract": { + "exclude": [ + "META-INF/" + ] + }, + "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "path": "org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar", + "sha1": "697517568c68e78ae0b4544145af031c81082dfe", + "size": 1047168, + "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar" + } + }, + "name": "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209" + }, + { + "downloads": { + "artifact": { + "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar", + "sha1": "d51a7c040a721d13efdfbd34f8b257b2df882ad0", + "size": 173887, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-2.9.4/lwjgl_util-2.9.4-nightly-20150209.jar" + } + }, + "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209" + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch/3.1.2.json b/assets/LWJGL/aarch/3.1.2.json new file mode 100644 index 00000000..a9577cde --- /dev/null +++ b/assets/LWJGL/aarch/3.1.2.json @@ -0,0 +1,202 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "d2df40234fd576e708feee78bccadaf02c5712a3", + "size": 300107, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "d2df40234fd576e708feee78bccadaf02c5712a3", + "size": 300107, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "1d4e6397a4ba0df30f7477ee5ba308d4dad3387e", + "size": 59165, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "9b0884b8fc763ebf9abe2fd747feaf6bc6968d8c", + "size": 39899, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "9b0884b8fc763ebf9abe2fd747feaf6bc6968d8c", + "size": 39899, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "e3c8c26f433efc2e135b9f02a275742825560bfc", + "size": 134237, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "71cbf1f0f60dc7a9c522c5b76e7e62453ca7c77c", + "size": 78718, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "71cbf1f0f60dc7a9c522c5b76e7e62453ca7c77c", + "size": 78718, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "fafb2af9deeb64b52a1642ae853493e1f13f98a2", + "size": 398418, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "9a6b6e74b41c15b70964a79a32d4a9b04f614d9f", + "size": 830047, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "9a6b6e74b41c15b70964a79a32d4a9b04f614d9f", + "size": 830047, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "a7922f4e7be6aae0483432cac5ee7da7b0748346", + "size": 65526, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "f978d3bf16a4ba09dafeb3d5563786cb7002129c", + "size": 114229, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "f978d3bf16a4ba09dafeb3d5563786cb7002129c", + "size": 114229, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "0c1dc55fed2fe336c256206a2b49fc54107115f5", + "size": 80186, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "bc9e649120027fc78cfebabc17b00ba50e16a86a", + "size": 104372, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "bc9e649120027fc78cfebabc17b00ba50e16a86a", + "size": 104372, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "bdf6e31bf49d466cc497444f1be31dbd522e108a", + "size": 143311, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.1.6", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch/3.1.6.json b/assets/LWJGL/aarch/3.1.6.json new file mode 100644 index 00000000..a9577cde --- /dev/null +++ b/assets/LWJGL/aarch/3.1.6.json @@ -0,0 +1,202 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "d2df40234fd576e708feee78bccadaf02c5712a3", + "size": 300107, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "d2df40234fd576e708feee78bccadaf02c5712a3", + "size": 300107, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "1d4e6397a4ba0df30f7477ee5ba308d4dad3387e", + "size": 59165, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "9b0884b8fc763ebf9abe2fd747feaf6bc6968d8c", + "size": 39899, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "9b0884b8fc763ebf9abe2fd747feaf6bc6968d8c", + "size": 39899, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "e3c8c26f433efc2e135b9f02a275742825560bfc", + "size": 134237, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-jemalloc-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "71cbf1f0f60dc7a9c522c5b76e7e62453ca7c77c", + "size": 78718, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "71cbf1f0f60dc7a9c522c5b76e7e62453ca7c77c", + "size": 78718, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "fafb2af9deeb64b52a1642ae853493e1f13f98a2", + "size": 398418, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-openal-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "9a6b6e74b41c15b70964a79a32d4a9b04f614d9f", + "size": 830047, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "9a6b6e74b41c15b70964a79a32d4a9b04f614d9f", + "size": 830047, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "a7922f4e7be6aae0483432cac5ee7da7b0748346", + "size": 65526, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-opengl-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "f978d3bf16a4ba09dafeb3d5563786cb7002129c", + "size": 114229, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "f978d3bf16a4ba09dafeb3d5563786cb7002129c", + "size": 114229, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "0c1dc55fed2fe336c256206a2b49fc54107115f5", + "size": 80186, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-glfw-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "bc9e649120027fc78cfebabc17b00ba50e16a86a", + "size": 104372, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "bc9e649120027fc78cfebabc17b00ba50e16a86a", + "size": 104372, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "bdf6e31bf49d466cc497444f1be31dbd522e108a", + "size": 143311, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.1.6/lwjgl-stb-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.1.6", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch/3.2.1.json b/assets/LWJGL/aarch/3.2.1.json new file mode 100644 index 00000000..7cfebef2 --- /dev/null +++ b/assets/LWJGL/aarch/3.2.1.json @@ -0,0 +1,202 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "c1f8d244dda855a936e136844a5c80611a5f36fe", + "size": 314536, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "c1f8d244dda855a936e136844a5c80611a5f36fe", + "size": 314536, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "a50f6e12091dccd4901e783b168b428f7653d254", + "size": 65481, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.2.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "f8f7801d8b82af7705f06c40bbea16b066479531", + "size": 37712, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "f8f7801d8b82af7705f06c40bbea16b066479531", + "size": 37712, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "e19e402887625f15a982426156976badcdffaadc", + "size": 134237, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-jemalloc-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.2.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "3dcb4c151a8ccf65f50f4aee9f6ff30a79bb7439", + "size": 79683, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "3dcb4c151a8ccf65f50f4aee9f6ff30a79bb7439", + "size": 79683, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "0dce4b9a444fcb0a4e5b75ea758b0094049daea3", + "size": 398418, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-openal-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.2.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "2350cc2bd1fe80e14ff2c39a81477b57dccfe09a", + "size": 939248, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "2350cc2bd1fe80e14ff2c39a81477b57dccfe09a", + "size": 939248, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "a6d3ff86fe3e07bd055032e93ce3b8952574a427", + "size": 56387, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-opengl-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.2.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "f245bcd89c484e05b524c25ea9935b6aa1e6d7ac", + "size": 116839, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "f245bcd89c484e05b524c25ea9935b6aa1e6d7ac", + "size": 116839, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "14533fb79a7077b2eb1d156067956d2da9f3403f", + "size": 80186, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-glfw-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.2.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "acd384013d30ea9ddddd805e9a5996b2b6058c5c", + "size": 105432, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "acd384013d30ea9ddddd805e9a5996b2b6058c5c", + "size": 105432, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "bc5bb97dbb328df0c82375637030f692a161d082", + "size": 144685, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.1/lwjgl-stb-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.2.1", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch/3.2.2.json b/assets/LWJGL/aarch/3.2.2.json new file mode 100644 index 00000000..e19ae72a --- /dev/null +++ b/assets/LWJGL/aarch/3.2.2.json @@ -0,0 +1,235 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "16ea3934fca417368250d1ddac01a30c1809d317", + "size": 318413, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "16ea3934fca417368250d1ddac01a30c1809d317", + "size": 318413, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "6bd0b37fef777a309936a72dc7f63126e8c79ea5", + "size": 90296, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "8224ae2e8fc6d8e1a0fc7d84dc917aa3c440620c", + "size": 33790, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "8224ae2e8fc6d8e1a0fc7d84dc917aa3c440620c", + "size": 33790, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "9163a2a5559ef87bc13ead8fea84417ea3928748", + "size": 134237, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-jemalloc-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "304f0571fd5971621ee6da86a4c1e90f6f52e2ee", + "size": 79582, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "304f0571fd5971621ee6da86a4c1e90f6f52e2ee", + "size": 79582, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "ecbc981fdd996492a1f6334f003ed62e5a8c0cd5", + "size": 398418, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-openal-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "9762ae928d02147e716cd82e929b74a97ea9600a", + "size": 937609, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "9762ae928d02147e716cd82e929b74a97ea9600a", + "size": 937609, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "3af5599c74dd76dd8dbb567b3f9b4963a6abeed5", + "size": 56388, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-opengl-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "99e9a39fa8ed4167e3ff9e04d47eb32c9e69804d", + "size": 108691, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "99e9a39fa8ed4167e3ff9e04d47eb32c9e69804d", + "size": 108691, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "4265f2fbe3b9d642591165165a17cf406cf7b98e", + "size": 80186, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-glfw-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "ea979b0af45b8e689f5f47c989aa8550c148d8a2", + "size": 104075, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "ea979b0af45b8e689f5f47c989aa8550c148d8a2", + "size": 104075, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "ec9d70aaebd0ff76dfeecf8f00b56118bf3706b1", + "size": 149387, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-stb-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "a8c09f5b7fa24bd53ec329c231b566497a163d5b", + "size": 5571, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-tinyfd.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "a8c09f5b7fa24bd53ec329c231b566497a163d5b", + "size": 5571, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-tinyfd.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "82d16054ada6633297a3108fb6d8bae98800c76f", + "size": 41663, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm32/raw/lwjgl-3.2.2/lwjgl-tinyfd-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.2.2", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch/3.3.1.json b/assets/LWJGL/aarch/3.3.1.json new file mode 100644 index 00000000..a76adcaa --- /dev/null +++ b/assets/LWJGL/aarch/3.3.1.json @@ -0,0 +1,270 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0", + "size": 128801, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0", + "size": 128801, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "816d935933f2dd743074c4e717cc25b55720f294", + "size": 104027, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "e502700e6a1a0d02bddb8b4ef85afcdc15c88358", + "size": 125778, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "a817bcf213db49f710603677457567c37d53e103", + "size": 36601, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "a817bcf213db49f710603677457567c37d53e103", + "size": 36601, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "a96a6d6cb3876d7813fcee53c3c24f246aeba3b3", + "size": 136157, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "f5858d34e06053b1866858fed7a685cf0c6b5926", + "size": 32306, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd", + "size": 88237, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd", + "size": 88237, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "ffbe35d7fa5ec9b7eca136a7c71f24d4025a510b", + "size": 400129, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "9c563bf7c10b71c6609b9f96a7c7859bdf05d21f", + "size": 85417, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411", + "size": 921563, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411", + "size": 921563, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "e3550fa91097fd56e361b4370fa822220fef3595", + "size": 58474, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "1a827bc02651fa44d32f424c380edc6d53f94a62", + "size": 1274449, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f", + "size": 112380, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f", + "size": 112380, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "b08226bab162c06ae69337d8a1b0ee0a3fdf0b90", + "size": 153889, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "22cb295464f44068add8443204ec8c85fd379cbe", + "size": 103489, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347", + "size": 6767, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347", + "size": 6767, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "d53d331e859217a61298fcbcf8d79137f3df345c", + "size": 48061, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar" + }, + "sources": { + "sha1": "4784c20508b51386ce9d572632524a5bf47ccb40", + "size": 5530, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb", + "size": 724243, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar", + "path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb", + "size": 724243, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar", + "path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "41a3c1dd15d6b964eb8196dde69720a3e3e5e969", + "size": 82374, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "e918fb595d1ca293a68807a9da8b519ea348a67a", + "size": 572854, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.3.1", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch/3.3.2.json b/assets/LWJGL/aarch/3.3.2.json new file mode 100644 index 00000000..360f5c24 --- /dev/null +++ b/assets/LWJGL/aarch/3.3.2.json @@ -0,0 +1,270 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "757920418805fb90bfebb3d46b1d9e7669fca2eb", + "size": 135828, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "757920418805fb90bfebb3d46b1d9e7669fca2eb", + "size": 135828, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "5907d9a6b7c44fb0612a63bb1cff5992588f65be", + "size": 110067, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "0bdd2ae91adb35fd7809b7ecf2d677546b26fe4f", + "size": 126160, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "877e17e39ebcd58a9c956dc3b5b777813de0873a", + "size": 43233, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "877e17e39ebcd58a9c956dc3b5b777813de0873a", + "size": 43233, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "9367437ce192e4d6f5725d53d85520644c0b0d6f", + "size": 177571, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "1d953086a319cfb09d0703e50011849a95ab2277", + "size": 32303, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "ae5357ed6d934546d3533993ea84c0cfb75eed95", + "size": 108230, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "ae5357ed6d934546d3533993ea84c0cfb75eed95", + "size": 108230, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "7c82bbc33ef49ee4094b216c940db564b2998224", + "size": 503352, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "534195bc70b8ff83c270c1d618cdafe76e9be2c4", + "size": 100606, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "ee8e95be0b438602038bc1f02dc5e3d011b1b216", + "size": 928871, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "ee8e95be0b438602038bc1f02dc5e3d011b1b216", + "size": 928871, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "821f9a2d1d583c44893f42b96f6977682b48a99b", + "size": 59265, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "1301ff0d9814ac96d7020f5912d5f0f72c039fca", + "size": 1275908, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "a2550795014d622b686e9caac50b14baa87d2c70", + "size": 118874, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "a2550795014d622b686e9caac50b14baa87d2c70", + "size": 118874, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "ca9333da184aade20757151f4615f1e27ca521ae", + "size": 154928, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "dda437f20ae0c920c1c744984b4093889982b994", + "size": 103496, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "9f65c248dd77934105274fcf8351abb75b34327c", + "size": 13404, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "9f65c248dd77934105274fcf8351abb75b34327c", + "size": 13404, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "807e220913aa0740449ff90d3b3d825cf5f359ed", + "size": 48788, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2.jar" + }, + "sources": { + "sha1": "bd33407b8cdbac1161c759656034d283f4708051", + "size": 5527, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "4421d94af68e35dcaa31737a6fc59136a1e61b94", + "size": 786196, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2.jar", + "path": "org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "4421d94af68e35dcaa31737a6fc59136a1e61b94", + "size": 786196, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2.jar", + "path": "org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "afcbfaaa46f217e98a6da4208550f71de1f2a225", + "size": 89347, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2-natives-linux-arm32.jar", + "path": "org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "aff949f8180d6d1e26a47c7a2bca8163f5b987fe", + "size": 624340, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.3.2", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch64/2.9.4.json b/assets/LWJGL/aarch64/2.9.4.json new file mode 100644 index 00000000..aa3324da --- /dev/null +++ b/assets/LWJGL/aarch64/2.9.4.json @@ -0,0 +1,96 @@ +{ + "libraries": [ + { + "downloads": { + "classifiers": { + "natives-linux": { + "path": "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-linux.jar", + "sha1": "42b388ccb7c63cec4e9f24f4dddef33325f8b212", + "size": 10932, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-2.9.4/jinput-platform-2.0.5-natives-linux.jar" + } + } + }, + "extract": { + "exclude": [ + "META-INF/" + ] + }, + "name": "net.java.jinput:jinput-platform:2.0.5", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "path": "net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar", + "sha1": "47f50f20c60495069c5a3cab65f5f87b44c1069e", + "size": 216970, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-2.9.4/jinput-2.0.5.jar" + } + }, + "name": "net.java.jinput:jinput:2.0.5" + }, + { + "downloads": { + "artifact": { + "path": "net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar", + "sha1": "e12fe1fda814bd348c1579329c86943d2cd3c6a6", + "size": 7508, + "url": "https://libraries.minecraft.net/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar" + } + }, + "name": "net.java.jutils:jutils:1.0.0" + }, + { + "downloads": { + "artifact": { + "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar", + "sha1": "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33", + "size": 22, + "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar" + }, + "classifiers": { + "natives-linux": { + "path": "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar", + "sha1": "63ac7da0f4a4785c7eadc0f8edc1e9dcc4dd08cb", + "size": 579979, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-2.9.4/lwjgl-platform-2.9.4-nightly-20150209-natives-linux.jar" + } + } + }, + "extract": { + "exclude": [ + "META-INF/" + ] + }, + "name": "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "path": "org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar", + "sha1": "697517568c68e78ae0b4544145af031c81082dfe", + "size": 1047168, + "url": "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar" + } + }, + "name": "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209" + }, + { + "downloads": { + "artifact": { + "path": "org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar", + "sha1": "d51a7c040a721d13efdfbd34f8b257b2df882ad0", + "size": 173887, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-2.9.4/lwjgl_util-2.9.4-nightly-20150209.jar" + } + }, + "name": "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209" + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch64/3.1.2.json b/assets/LWJGL/aarch64/3.1.2.json new file mode 100644 index 00000000..fab1028f --- /dev/null +++ b/assets/LWJGL/aarch64/3.1.2.json @@ -0,0 +1,202 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "55b9dbe63745835ddde8b4c22be8da6520e8274f", + "size": 300107, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "55b9dbe63745835ddde8b4c22be8da6520e8274f", + "size": 300107, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "68c8151938f33c702528208d32084e1c18b2dc9e", + "size": 54802, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "6e9ee82494343aee0737c391e151d0147c992584", + "size": 39899, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "6e9ee82494343aee0737c391e151d0147c992584", + "size": 39899, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "762d7d80c9cdf3a3f3fc80c8a5f86612255edfe0", + "size": 156343, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc-patched-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "fa243387070b806da104e3746828968b07ca737f", + "size": 78718, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "fa243387070b806da104e3746828968b07ca737f", + "size": 78718, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "5d1f9e7c633044a5700f2a80454d2a717251f675", + "size": 469432, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "862a9e64741dfab2f3a48638ebd58c35ba5e43cd", + "size": 830047, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "862a9e64741dfab2f3a48638ebd58c35ba5e43cd", + "size": 830047, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "f9dec4cedbe2d98a92dd933d030c7e3efa99411b", + "size": 67010, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "b3ae7bad5b7e7d771ba9423521fc10f4ed67bbab", + "size": 114229, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "b3ae7bad5b7e7d771ba9423521fc10f4ed67bbab", + "size": 114229, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "645aeac6de1deb1b77c7cf3abe84674f77ef1aea", + "size": 85072, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "688e32efb1ad7a09b60f0f4b7131e3dc33b286ca", + "size": 104372, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "688e32efb1ad7a09b60f0f4b7131e3dc33b286ca", + "size": 104372, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "7852b75b40a470face01cbb9f37ebc5e715944bd", + "size": 192935, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.1.6", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch64/3.1.6.json b/assets/LWJGL/aarch64/3.1.6.json new file mode 100644 index 00000000..fab1028f --- /dev/null +++ b/assets/LWJGL/aarch64/3.1.6.json @@ -0,0 +1,202 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "55b9dbe63745835ddde8b4c22be8da6520e8274f", + "size": 300107, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "55b9dbe63745835ddde8b4c22be8da6520e8274f", + "size": 300107, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "68c8151938f33c702528208d32084e1c18b2dc9e", + "size": 54802, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl/3.1.6/lwjgl-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "6e9ee82494343aee0737c391e151d0147c992584", + "size": 39899, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "6e9ee82494343aee0737c391e151d0147c992584", + "size": 39899, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "762d7d80c9cdf3a3f3fc80c8a5f86612255edfe0", + "size": 156343, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-jemalloc-patched-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.1.6/lwjgl-jemalloc-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "fa243387070b806da104e3746828968b07ca737f", + "size": 78718, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "fa243387070b806da104e3746828968b07ca737f", + "size": 78718, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "5d1f9e7c633044a5700f2a80454d2a717251f675", + "size": 469432, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-openal-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-openal/3.1.6/lwjgl-openal-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "862a9e64741dfab2f3a48638ebd58c35ba5e43cd", + "size": 830047, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "862a9e64741dfab2f3a48638ebd58c35ba5e43cd", + "size": 830047, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "f9dec4cedbe2d98a92dd933d030c7e3efa99411b", + "size": 67010, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-opengl-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-opengl/3.1.6/lwjgl-opengl-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "b3ae7bad5b7e7d771ba9423521fc10f4ed67bbab", + "size": 114229, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "b3ae7bad5b7e7d771ba9423521fc10f4ed67bbab", + "size": 114229, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "645aeac6de1deb1b77c7cf3abe84674f77ef1aea", + "size": 85072, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-glfw-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-glfw/3.1.6/lwjgl-glfw-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.1.6", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "688e32efb1ad7a09b60f0f4b7131e3dc33b286ca", + "size": 104372, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.1.6" + }, + { + "downloads": { + "artifact": { + "sha1": "688e32efb1ad7a09b60f0f4b7131e3dc33b286ca", + "size": 104372, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "7852b75b40a470face01cbb9f37ebc5e715944bd", + "size": 192935, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.1.6/lwjgl-stb-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-stb/3.1.6/lwjgl-stb-3.1.6-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.1.6", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch64/3.2.1.json b/assets/LWJGL/aarch64/3.2.1.json new file mode 100644 index 00000000..c395b0bd --- /dev/null +++ b/assets/LWJGL/aarch64/3.2.1.json @@ -0,0 +1,202 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "4add49f642c6f6d0bf51e1b6fea388d5101267b9", + "size": 314115, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "4add49f642c6f6d0bf51e1b6fea388d5101267b9", + "size": 314115, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "03c691efeac999530f4b350312a5a9d85e52cf89", + "size": 60186, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl/3.2.1/lwjgl-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.2.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "5621e055b542f6caab937b8346de5fd02105f9b2", + "size": 37712, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "5621e055b542f6caab937b8346de5fd02105f9b2", + "size": 37712, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "762d7d80c9cdf3a3f3fc80c8a5f86612255edfe0", + "size": 156343, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-jemalloc-patched-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.1/lwjgl-jemalloc-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.2.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "b6ef8efb60c888a14feebcd09addc265f1eca1a2", + "size": 79683, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "b6ef8efb60c888a14feebcd09addc265f1eca1a2", + "size": 79683, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "222a583cbfdcc3a81d5d9ad807c3d68196c8ec52", + "size": 469432, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-openal-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.1/lwjgl-openal-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.2.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "282139e3a8d1402fc25e18eb6a05eb090a1e81b4", + "size": 939248, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "282139e3a8d1402fc25e18eb6a05eb090a1e81b4", + "size": 939248, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "1d5e0092e54efd3f100f2c307261155573fa78f6", + "size": 56110, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-opengl-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.1/lwjgl-opengl-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.2.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "ccc4ba7e8521b5981343bcdbc3cf6492f35a5aa5", + "size": 116839, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "ccc4ba7e8521b5981343bcdbc3cf6492f35a5aa5", + "size": 116839, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "d52b2a26fafb6d4eebeef74ee4f6c3ebd65004b2", + "size": 85072, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-glfw-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.1/lwjgl-glfw-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.2.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "e6898a15a8067c89771ae2949bea8f5ff7874fcc", + "size": 105432, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.2.1" + }, + { + "downloads": { + "artifact": { + "sha1": "e6898a15a8067c89771ae2949bea8f5ff7874fcc", + "size": 105432, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "b8f7ace2cb31887254bbcde88e0dc705f5de2c3f", + "size": 193998, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.1/lwjgl-stb-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.1/lwjgl-stb-3.2.1-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.2.1", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch64/3.2.2.json b/assets/LWJGL/aarch64/3.2.2.json new file mode 100644 index 00000000..d15ca007 --- /dev/null +++ b/assets/LWJGL/aarch64/3.2.2.json @@ -0,0 +1,235 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "360899386df83d6a8407844a94478607af937f97", + "size": 318833, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "360899386df83d6a8407844a94478607af937f97", + "size": 318833, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-core.jar", + "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "612efd57d12b2e48e554858eb35e7e2eb46ebb4c", + "size": 87121, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl/3.2.2/lwjgl-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "cc04eec29b2fa8c298791af9800a3766d9617954", + "size": 33790, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "cc04eec29b2fa8c298791af9800a3766d9617954", + "size": 33790, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-jemalloc.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "762d7d80c9cdf3a3f3fc80c8a5f86612255edfe0", + "size": 156343, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-jemalloc-patched-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.2.2/lwjgl-jemalloc-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "6dfce9dc6a9629c75b2ae01a8df7e7be80ba0261", + "size": 79582, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "6dfce9dc6a9629c75b2ae01a8df7e7be80ba0261", + "size": 79582, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-openal.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "948e415b5b2a2c650c25b377a4a9f443b21ce92e", + "size": 469432, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-openal-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-openal/3.2.2/lwjgl-openal-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "198bc2f72e0b2eb401eb6f5999aea52909b31ac4", + "size": 937609, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "198bc2f72e0b2eb401eb6f5999aea52909b31ac4", + "size": 937609, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-opengl.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "bd40897077bf7d12f562da898b18ac2c68e1f9d7", + "size": 56109, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-opengl-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-opengl/3.2.2/lwjgl-opengl-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "155d175037efc76630940c197ca6dea2b17d7e18", + "size": 108691, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "155d175037efc76630940c197ca6dea2b17d7e18", + "size": 108691, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-glfw.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "074ad243761147df0d060fbefc814614d2ff75cc", + "size": 85072, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-glfw-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-glfw/3.2.2/lwjgl-glfw-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "46a5735f3eb9d17eb5dcbdd5afa194066d2a6555", + "size": 104075, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "46a5735f3eb9d17eb5dcbdd5afa194066d2a6555", + "size": 104075, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-stb.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "077efa7d7ea41b32df5c6078e912e724cccd06db", + "size": 202038, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-stb-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-stb/3.2.2/lwjgl-stb-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.2.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "3a75b9811607633bf33c978f53964df1534a4bc1", + "size": 5571, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-tinyfd.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.2.2" + }, + { + "downloads": { + "artifact": { + "sha1": "3a75b9811607633bf33c978f53964df1534a4bc1", + "size": 5571, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-tinyfd.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "37c744ca289b5d7ae155d79e39029488b3254e5b", + "size": 37893, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.2.2/lwjgl-tinyfd-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.2.2/lwjgl-tinyfd-3.2.2-natives-linux.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.2.2", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/LWJGL/aarch64/3.3.1.json b/assets/LWJGL/aarch64/3.3.1.json new file mode 100644 index 00000000..f9bbf8e0 --- /dev/null +++ b/assets/LWJGL/aarch64/3.3.1.json @@ -0,0 +1,270 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0", + "size": 128801, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "cbac1b8d30cb4795149c1ef540f912671a8616d0", + "size": 128801, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "513eb39b866d0fe131a18d5c517087805433b029", + "size": 112350, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "e502700e6a1a0d02bddb8b4ef85afcdc15c88358", + "size": 125778, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.1/lwjgl-glfw-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "a817bcf213db49f710603677457567c37d53e103", + "size": 36601, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "a817bcf213db49f710603677457567c37d53e103", + "size": 36601, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "749be48a9b86ee2c3a2da5fd77511208adcfb33b", + "size": 159993, + "url": "https://github.com/theofficialgman/lwjgl3-binaries-arm64/raw/lwjgl-3.3.1/lwjgl-jemalloc-patched-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "f5858d34e06053b1866858fed7a685cf0c6b5926", + "size": 32306, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.1/lwjgl-jemalloc-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd", + "size": 88237, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "2623a6b8ae1dfcd880738656a9f0243d2e6840bd", + "size": 88237, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "cf4e303257e82981b8b2e31bba3d7f8f7b8f42b2", + "size": 470743, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "9c563bf7c10b71c6609b9f96a7c7859bdf05d21f", + "size": 85417, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.1/lwjgl-openal-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411", + "size": 921563, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "831a5533a21a5f4f81bbc51bb13e9899319b5411", + "size": 921563, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "1c528fb258a6e63e8fceb4482d8db0f3af10a634", + "size": 57908, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "1a827bc02651fa44d32f424c380edc6d53f94a62", + "size": 1274449, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.1/lwjgl-opengl-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f", + "size": 112380, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "b119297cf8ed01f247abe8685857f8e7fcf5980f", + "size": 112380, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "8e8348a1813aad7f30aaf75ea197151ebb7beba9", + "size": 205491, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "22cb295464f44068add8443204ec8c85fd379cbe", + "size": 103489, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.1/lwjgl-stb-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347", + "size": 6767, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "0ff1914111ef2e3e0110ef2dabc8d8cdaad82347", + "size": 6767, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "964f628b7a82fd909def086c0dd9a4b84bb259ae", + "size": 42654, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1.jar" + }, + "sources": { + "sha1": "4784c20508b51386ce9d572632524a5bf47ccb40", + "size": 5530, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.1/lwjgl-tinyfd-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.3.1", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb", + "size": 724243, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar", + "path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.3.1" + }, + { + "downloads": { + "artifact": { + "sha1": "ae58664f88e18a9bb2c77b063833ca7aaec484cb", + "size": 724243, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar", + "path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "b597401014acb7196c76d97e15a6288f54f1f692", + "size": 86308, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-natives-linux.jar" + }, + "sources": { + "sha1": "e918fb595d1ca293a68807a9da8b519ea348a67a", + "size": 572854, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.1/lwjgl-3.3.1-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.3.1", + "natives": { + "linux": "natives-linux" + } + } + ] +} diff --git a/assets/LWJGL/aarch64/3.3.2.json b/assets/LWJGL/aarch64/3.3.2.json new file mode 100644 index 00000000..37cb2ba3 --- /dev/null +++ b/assets/LWJGL/aarch64/3.3.2.json @@ -0,0 +1,270 @@ +{ + "libraries": [ + { + "downloads": { + "artifact": { + "sha1": "757920418805fb90bfebb3d46b1d9e7669fca2eb", + "size": 135828, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "757920418805fb90bfebb3d46b1d9e7669fca2eb", + "size": 135828, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "bc49e64bae0f7ff103a312ee8074a34c4eb034c7", + "size": 120168, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "0bdd2ae91adb35fd7809b7ecf2d677546b26fe4f", + "size": 126160, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-glfw/3.3.2/lwjgl-glfw-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-glfw:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "877e17e39ebcd58a9c956dc3b5b777813de0873a", + "size": 43233, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "877e17e39ebcd58a9c956dc3b5b777813de0873a", + "size": 43233, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "5249f18a9ae20ea86c5816bc3107a888ce7a17d2", + "size": 206402, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "1d953086a319cfb09d0703e50011849a95ab2277", + "size": 32303, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-jemalloc/3.3.2/lwjgl-jemalloc-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-jemalloc:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "ae5357ed6d934546d3533993ea84c0cfb75eed95", + "size": 108230, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "ae5357ed6d934546d3533993ea84c0cfb75eed95", + "size": 108230, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "22408980cc579709feaf9acb807992d3ebcf693f", + "size": 590865, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "534195bc70b8ff83c270c1d618cdafe76e9be2c4", + "size": 100606, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-openal/3.3.2/lwjgl-openal-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-openal:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "ee8e95be0b438602038bc1f02dc5e3d011b1b216", + "size": 928871, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "ee8e95be0b438602038bc1f02dc5e3d011b1b216", + "size": 928871, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "bb9eb56da6d1d549d6a767218e675e36bc568eb9", + "size": 58627, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "1301ff0d9814ac96d7020f5912d5f0f72c039fca", + "size": 1275908, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-opengl/3.3.2/lwjgl-opengl-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-opengl:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "a2550795014d622b686e9caac50b14baa87d2c70", + "size": 118874, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "a2550795014d622b686e9caac50b14baa87d2c70", + "size": 118874, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "11a380c37b0f03cb46db235e064528f84d736ff7", + "size": 207419, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "dda437f20ae0c920c1c744984b4093889982b994", + "size": 103496, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-stb/3.3.2/lwjgl-stb-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-stb:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "9f65c248dd77934105274fcf8351abb75b34327c", + "size": 13404, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "9f65c248dd77934105274fcf8351abb75b34327c", + "size": 13404, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "93f8c5bc1984963cd79109891fb5a9d1e580373e", + "size": 43381, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2.jar" + }, + "sources": { + "sha1": "bd33407b8cdbac1161c759656034d283f4708051", + "size": 5527, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl-tinyfd/3.3.2/lwjgl-tinyfd-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl-tinyfd:3.3.2", + "natives": { + "linux": "natives-linux" + } + }, + { + "downloads": { + "artifact": { + "sha1": "4421d94af68e35dcaa31737a6fc59136a1e61b94", + "size": 786196, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2.jar", + "path": "org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2.jar" + } + }, + "name": "org.lwjgl:lwjgl:3.3.2" + }, + { + "downloads": { + "artifact": { + "sha1": "4421d94af68e35dcaa31737a6fc59136a1e61b94", + "size": 786196, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2.jar", + "path": "org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2.jar" + }, + "classifiers": { + "natives-linux": { + "sha1": "8bd89332c90a90e6bc4aa997a25c05b7db02c90a", + "size": 90795, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2-natives-linux-arm64.jar", + "path": "org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2-natives-linux.jar" + }, + "sources": { + "sha1": "aff949f8180d6d1e26a47c7a2bca8163f5b987fe", + "size": 624340, + "url": "https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2-sources.jar" + } + } + }, + "name": "org.lwjgl:lwjgl:3.3.2", + "natives": { + "linux": "natives-linux" + } + } + ] +} \ No newline at end of file diff --git a/assets/forge/maven-metadata.json b/assets/forge/maven-metadata.json new file mode 100644 index 00000000..a8182810 --- /dev/null +++ b/assets/forge/maven-metadata.json @@ -0,0 +1,4969 @@ +{ + "1.1": [ + "1.1-1.3.2.1", + "1.1-1.3.2.2", + "1.1-1.3.2.3", + "1.1-1.3.2.4", + "1.1-1.3.2.5", + "1.1-1.3.2.6", + "1.1-1.3.2.7", + "1.1-1.3.2.8", + "1.1-1.3.2.9", + "1.1-1.3.2.10", + "1.1-1.3.3.12", + "1.1-1.3.3.13", + "1.1-1.3.3.14", + "1.1-1.3.3.15", + "1.1-1.3.3.16", + "1.1-1.3.3.18", + "1.1-1.3.3.19", + "1.1-1.3.3.20", + "1.1-1.3.3.21", + "1.1-1.3.3.22", + "1.1-1.3.3.23", + "1.1-1.3.3.24", + "1.1-1.3.3.26", + "1.1-1.3.3.27", + "1.1-1.3.3.28", + "1.1-1.3.4.29" + ], + "1.2.3": [ + "1.2.3-1.3.4.30", + "1.2.3-1.3.4.31", + "1.2.3-1.3.4.32", + "1.2.3-1.3.4.33", + "1.2.3-1.3.4.34", + "1.2.3-1.3.4.35", + "1.2.3-1.3.4.36", + "1.2.3-1.3.4.37", + "1.2.3-1.3.4.38", + "1.2.3-1.3.4.39", + "1.2.3-1.3.4.41", + "1.2.3-1.4.0.44", + "1.2.3-1.4.0.45", + "1.2.3-1.4.0.46", + "1.2.3-1.4.0.47", + "1.2.3-1.4.0.48", + "1.2.3-1.4.0.50", + "1.2.3-1.4.0.51", + "1.2.3-1.4.0.52", + "1.2.3-1.4.0.55", + "1.2.3-1.4.0.56", + "1.2.3-1.4.0.57", + "1.2.3-1.4.1.58", + "1.2.3-1.4.1.59", + "1.2.3-1.4.1.60", + "1.2.3-1.4.1.61", + "1.2.3-1.4.1.62", + "1.2.3-1.4.1.63", + "1.2.3-1.4.1.64" + ], + "1.2.4": [ + "1.2.4-2.0.0.65", + "1.2.4-2.0.0.66", + "1.2.4-2.0.0.67", + "1.2.4-2.0.0.68" + ], + "1.2.5": [ + "1.2.5-3.0.0.69", + "1.2.5-3.0.0.70", + "1.2.5-3.0.0.71", + "1.2.5-3.0.0.72", + "1.2.5-3.0.1.73", + "1.2.5-3.0.1.74", + "1.2.5-3.0.1.75", + "1.2.5-3.0.1.77", + "1.2.5-3.0.1.78", + "1.2.5-3.0.1.79", + "1.2.5-3.0.1.80", + "1.2.5-3.0.1.81", + "1.2.5-3.0.1.82", + "1.2.5-3.0.1.83", + "1.2.5-3.0.1.84", + "1.2.5-3.0.1.85", + "1.2.5-3.0.1.86", + "1.2.5-3.0.1.87", + "1.2.5-3.0.1.88", + "1.2.5-3.0.1.89", + "1.2.5-3.1.2.90", + "1.2.5-3.1.2.91", + "1.2.5-3.1.2.92", + "1.2.5-3.1.2.93", + "1.2.5-3.1.2.94", + "1.2.5-3.1.2.95", + "1.2.5-3.1.2.96", + "1.2.5-3.1.2.97", + "1.2.5-3.1.2.98", + "1.2.5-3.1.3.99", + "1.2.5-3.1.3.100", + "1.2.5-3.1.3.101", + "1.2.5-3.1.3.102", + "1.2.5-3.1.3.103", + "1.2.5-3.1.3.104", + "1.2.5-3.1.3.105", + "1.2.5-3.1.3.106", + "1.2.5-3.1.3.107", + "1.2.5-3.2.3.108", + "1.2.5-3.2.4.110", + "1.2.5-3.2.4.111", + "1.2.5-3.2.4.114", + "1.2.5-3.2.4.115", + "1.2.5-3.2.4.116", + "1.2.5-3.2.5.117", + "1.2.5-3.2.5.118", + "1.2.5-3.2.5.119", + "1.2.5-3.2.5.120", + "1.2.5-3.2.5.121", + "1.2.5-3.2.5.122", + "1.2.5-3.2.5.123", + "1.2.5-3.2.5.124", + "1.2.5-3.2.5.125", + "1.2.5-3.2.5.126", + "1.2.5-3.2.5.127", + "1.2.5-3.2.5.128", + "1.2.5-3.2.6.129", + "1.2.5-3.2.6.130", + "1.2.5-3.2.6.131", + "1.2.5-3.2.6.132", + "1.2.5-3.3.7.133", + "1.2.5-3.3.7.134", + "1.2.5-3.3.7.135", + "1.2.5-3.3.7.136", + "1.2.5-3.3.7.137", + "1.2.5-3.3.7.138", + "1.2.5-3.3.7.139", + "1.2.5-3.3.7.140", + "1.2.5-3.3.8.141", + "1.2.5-3.3.8.142", + "1.2.5-3.3.8.143", + "1.2.5-3.3.8.144", + "1.2.5-3.3.8.145", + "1.2.5-3.3.8.146", + "1.2.5-3.3.8.147", + "1.2.5-3.3.8.148", + "1.2.5-3.3.8.150", + "1.2.5-3.3.8.151", + "1.2.5-3.3.8.152", + "1.2.5-3.3.8.153", + "1.2.5-3.3.8.154", + "1.2.5-3.3.8.155", + "1.2.5-3.3.8.156", + "1.2.5-3.3.8.157", + "1.2.5-3.3.8.158", + "1.2.5-3.3.8.159", + "1.2.5-3.3.8.160", + "1.2.5-3.3.8.161", + "1.2.5-3.3.8.162", + "1.2.5-3.3.8.163", + "1.2.5-3.3.8.164", + "1.2.5-3.3.8.168", + "1.2.5-3.3.8.170", + "1.2.5-3.4.9.171" + ], + "1.3.2": [ + "1.3.2-4.0.0.172", + "1.3.2-4.0.0.173", + "1.3.2-4.0.0.176", + "1.3.2-4.0.0.177", + "1.3.2-4.0.0.178", + "1.3.2-4.0.0.179", + "1.3.2-4.0.0.180", + "1.3.2-4.0.0.181", + "1.3.2-4.0.0.182", + "1.3.2-4.0.0.183", + "1.3.2-4.0.0.184", + "1.3.2-4.0.0.185", + "1.3.2-4.0.0.186", + "1.3.2-4.0.0.187", + "1.3.2-4.0.0.188", + "1.3.2-4.0.0.189", + "1.3.2-4.0.0.190", + "1.3.2-4.0.0.191", + "1.3.2-4.0.0.192", + "1.3.2-4.0.0.193", + "1.3.2-4.0.0.194", + "1.3.2-4.0.0.195", + "1.3.2-4.0.0.196", + "1.3.2-4.0.0.197", + "1.3.2-4.0.0.198", + "1.3.2-4.0.0.199", + "1.3.2-4.0.0.200", + "1.3.2-4.0.0.204", + "1.3.2-4.0.0.205", + "1.3.2-4.0.0.206", + "1.3.2-4.0.0.207", + "1.3.2-4.0.0.208", + "1.3.2-4.0.0.209", + "1.3.2-4.0.0.210", + "1.3.2-4.0.0.211", + "1.3.2-4.0.0.212", + "1.3.2-4.0.0.213", + "1.3.2-4.0.0.214", + "1.3.2-4.0.0.215", + "1.3.2-4.0.0.216", + "1.3.2-4.0.0.217", + "1.3.2-4.0.0.220", + "1.3.2-4.0.0.221", + "1.3.2-4.0.0.222", + "1.3.2-4.0.0.223", + "1.3.2-4.0.0.224", + "1.3.2-4.0.0.225", + "1.3.2-4.0.0.226", + "1.3.2-4.0.0.227", + "1.3.2-4.0.0.228", + "1.3.2-4.0.0.229", + "1.3.2-4.0.0.230", + "1.3.2-4.0.0.231", + "1.3.2-4.0.0.232", + "1.3.2-4.0.0.233", + "1.3.2-4.0.0.234", + "1.3.2-4.0.0.235", + "1.3.2-4.0.0.236", + "1.3.2-4.0.0.237", + "1.3.2-4.0.0.238", + "1.3.2-4.0.0.239", + "1.3.2-4.0.0.240", + "1.3.2-4.0.0.241", + "1.3.2-4.0.0.242", + "1.3.2-4.0.0.243", + "1.3.2-4.0.0.245", + "1.3.2-4.0.0.246", + "1.3.2-4.0.0.247", + "1.3.2-4.0.0.248", + "1.3.2-4.0.0.249", + "1.3.2-4.0.0.250", + "1.3.2-4.1.1.251", + "1.3.2-4.1.1.252", + "1.3.2-4.1.1.253", + "1.3.2-4.1.1.254", + "1.3.2-4.1.1.255", + "1.3.2-4.1.1.256", + "1.3.2-4.1.1.257", + "1.3.2-4.1.1.258", + "1.3.2-4.1.2.259", + "1.3.2-4.1.2.260", + "1.3.2-4.1.2.261", + "1.3.2-4.1.2.262", + "1.3.2-4.1.2.263", + "1.3.2-4.1.2.264", + "1.3.2-4.1.2.265", + "1.3.2-4.1.2.266", + "1.3.2-4.1.2.267", + "1.3.2-4.1.2.268", + "1.3.2-4.1.2.269", + "1.3.2-4.1.3.270", + "1.3.2-4.1.4.271", + "1.3.2-4.1.4.272", + "1.3.2-4.1.4.274", + "1.3.2-4.1.4.275", + "1.3.2-4.1.4.276", + "1.3.2-4.1.4.277", + "1.3.2-4.1.4.278", + "1.3.2-4.1.4.279", + "1.3.2-4.1.4.280", + "1.3.2-4.1.4.281", + "1.3.2-4.1.4.282", + "1.3.2-4.1.4.284", + "1.3.2-4.1.4.285", + "1.3.2-4.1.4.286", + "1.3.2-4.1.4.287", + "1.3.2-4.1.4.288", + "1.3.2-4.1.4.289", + "1.3.2-4.1.4.290", + "1.3.2-4.1.4.291", + "1.3.2-4.1.4.292", + "1.3.2-4.1.4.294", + "1.3.2-4.1.4.295", + "1.3.2-4.1.4.296", + "1.3.2-4.1.4.297", + "1.3.2-4.1.4.298", + "1.3.2-4.2.5.299", + "1.3.2-4.2.5.300", + "1.3.2-4.2.5.301", + "1.3.2-4.2.5.302", + "1.3.2-4.2.5.303", + "1.3.2-4.2.5.305", + "1.3.2-4.2.5.306", + "1.3.2-4.2.5.307", + "1.3.2-4.2.5.310", + "1.3.2-4.2.5.311", + "1.3.2-4.2.5.312", + "1.3.2-4.2.5.313", + "1.3.2-4.2.5.314", + "1.3.2-4.2.5.315", + "1.3.2-4.2.5.316", + "1.3.2-4.2.5.317", + "1.3.2-4.3.5.318" + ], + "1.4.0": [ + "1.4.0-5.0.0.320", + "1.4.0-5.0.0.321", + "1.4.0-5.0.0.322", + "1.4.0-5.0.0.323", + "1.4.0-5.0.0.324", + "1.4.0-5.0.0.325", + "1.4.0-5.0.0.326" + ], + "1.4.1": [ + "1.4.1-6.0.0.327", + "1.4.1-6.0.0.328", + "1.4.1-6.0.0.329" + ], + "1.4.2": [ + "1.4.2-6.0.1.330", + "1.4.2-6.0.1.331", + "1.4.2-6.0.1.332", + "1.4.2-6.0.1.336", + "1.4.2-6.0.1.337", + "1.4.2-6.0.1.338", + "1.4.2-6.0.1.339", + "1.4.2-6.0.1.341", + "1.4.2-6.0.1.342", + "1.4.2-6.0.1.343", + "1.4.2-6.0.1.345", + "1.4.2-6.0.1.347", + "1.4.2-6.0.1.348", + "1.4.2-6.0.1.349", + "1.4.2-6.0.1.350", + "1.4.2-6.0.1.351", + "1.4.2-6.0.1.353", + "1.4.2-6.0.1.354", + "1.4.2-6.0.1.355" + ], + "1.4.3": [ + "1.4.3-6.2.1.356", + "1.4.3-6.2.1.357", + "1.4.3-6.2.1.358" + ], + "1.4.4": [ + "1.4.4-6.3.0.360", + "1.4.4-6.3.0.361", + "1.4.4-6.3.0.362", + "1.4.4-6.3.0.363", + "1.4.4-6.3.0.364", + "1.4.4-6.3.0.365", + "1.4.4-6.3.0.366", + "1.4.4-6.3.0.367", + "1.4.4-6.3.0.368", + "1.4.4-6.3.0.369", + "1.4.4-6.3.0.370", + "1.4.4-6.3.0.371", + "1.4.4-6.3.0.372", + "1.4.4-6.3.0.373", + "1.4.4-6.3.0.374", + "1.4.4-6.3.0.375", + "1.4.4-6.3.0.376", + "1.4.4-6.3.0.377", + "1.4.4-6.3.0.378" + ], + "1.4.5": [ + "1.4.5-6.4.0.379", + "1.4.5-6.4.0.380", + "1.4.5-6.4.0.381", + "1.4.5-6.4.0.382", + "1.4.5-6.4.0.383", + "1.4.5-6.4.0.384", + "1.4.5-6.4.0.385", + "1.4.5-6.4.0.386", + "1.4.5-6.4.0.387", + "1.4.5-6.4.0.388", + "1.4.5-6.4.0.390", + "1.4.5-6.4.0.393", + "1.4.5-6.4.0.394", + "1.4.5-6.4.0.395", + "1.4.5-6.4.0.396", + "1.4.5-6.4.0.397", + "1.4.5-6.4.0.398", + "1.4.5-6.4.0.399", + "1.4.5-6.4.1.400", + "1.4.5-6.4.1.401", + "1.4.5-6.4.1.402", + "1.4.5-6.4.1.403", + "1.4.5-6.4.1.404", + "1.4.5-6.4.1.405", + "1.4.5-6.4.1.406", + "1.4.5-6.4.1.407", + "1.4.5-6.4.1.408", + "1.4.5-6.4.1.409", + "1.4.5-6.4.1.410", + "1.4.5-6.4.1.411", + "1.4.5-6.4.1.413", + "1.4.5-6.4.1.414", + "1.4.5-6.4.1.416", + "1.4.5-6.4.1.424", + "1.4.5-6.4.1.425", + "1.4.5-6.4.1.426", + "1.4.5-6.4.1.428", + "1.4.5-6.4.1.430", + "1.4.5-6.4.1.432", + "1.4.5-6.4.1.433", + "1.4.5-6.4.1.434", + "1.4.5-6.4.1.435", + "1.4.5-6.4.1.436", + "1.4.5-6.4.1.437", + "1.4.5-6.4.1.438", + "1.4.5-6.4.1.439", + "1.4.5-6.4.1.441", + "1.4.5-6.4.1.442", + "1.4.5-6.4.2.443", + "1.4.5-6.4.2.444", + "1.4.5-6.4.2.445", + "1.4.5-6.4.2.446", + "1.4.5-6.4.2.447", + "1.4.5-6.4.2.448" + ], + "1.4.6": [ + "1.4.6-6.5.0.451", + "1.4.6-6.5.0.452", + "1.4.6-6.5.0.453", + "1.4.6-6.5.0.454", + "1.4.6-6.5.0.455", + "1.4.6-6.5.0.456", + "1.4.6-6.5.0.457", + "1.4.6-6.5.0.458", + "1.4.6-6.5.0.459", + "1.4.6-6.5.0.460", + "1.4.6-6.5.0.461", + "1.4.6-6.5.0.462", + "1.4.6-6.5.0.463", + "1.4.6-6.5.0.464", + "1.4.6-6.5.0.465", + "1.4.6-6.5.0.466", + "1.4.6-6.5.0.467", + "1.4.6-6.5.0.468", + "1.4.6-6.5.0.469", + "1.4.6-6.5.0.470", + "1.4.6-6.5.0.471", + "1.4.6-6.5.0.472", + "1.4.6-6.5.0.473", + "1.4.6-6.5.0.474", + "1.4.6-6.5.0.475", + "1.4.6-6.5.0.476", + "1.4.6-6.5.0.477", + "1.4.6-6.5.0.478", + "1.4.6-6.5.0.479", + "1.4.6-6.5.0.480", + "1.4.6-6.5.0.481", + "1.4.6-6.5.0.482", + "1.4.6-6.5.0.483", + "1.4.6-6.5.0.484", + "1.4.6-6.5.0.486", + "1.4.6-6.5.0.487", + "1.4.6-6.5.0.488", + "1.4.6-6.5.0.489" + ], + "1.4.7": [ + "1.4.7-6.6.0.490", + "1.4.7-6.6.0.491", + "1.4.7-6.6.0.492", + "1.4.7-6.6.0.493", + "1.4.7-6.6.0.494", + "1.4.7-6.6.0.495", + "1.4.7-6.6.0.496", + "1.4.7-6.6.0.497", + "1.4.7-6.6.0.499", + "1.4.7-6.6.0.501", + "1.4.7-6.6.0.502", + "1.4.7-6.6.0.503", + "1.4.7-6.6.0.504", + "1.4.7-6.6.0.505", + "1.4.7-6.6.0.506", + "1.4.7-6.6.0.507", + "1.4.7-6.6.0.509", + "1.4.7-6.6.0.510", + "1.4.7-6.6.0.511", + "1.4.7-6.6.0.515", + "1.4.7-6.6.0.516", + "1.4.7-6.6.0.517", + "1.4.7-6.6.0.518", + "1.4.7-6.6.1.521", + "1.4.7-6.6.1.522", + "1.4.7-6.6.1.523", + "1.4.7-6.6.1.524", + "1.4.7-6.6.1.527", + "1.4.7-6.6.1.528", + "1.4.7-6.6.1.529", + "1.4.7-6.6.1.530", + "1.4.7-6.6.1.531", + "1.4.7-6.6.1.532", + "1.4.7-6.6.2.533", + "1.4.7-6.6.2.534" + ], + "1.5": [ + "1.5-7.7.0.559", + "1.5-7.7.0.560", + "1.5-7.7.0.561", + "1.5-7.7.0.562", + "1.5-7.7.0.563", + "1.5-7.7.0.565", + "1.5-7.7.0.566", + "1.5-7.7.0.567", + "1.5-7.7.0.568", + "1.5-7.7.0.569", + "1.5-7.7.0.571", + "1.5-7.7.0.572", + "1.5-7.7.0.573", + "1.5-7.7.0.574", + "1.5-7.7.0.575", + "1.5-7.7.0.576", + "1.5-7.7.0.577", + "1.5-7.7.0.578", + "1.5-7.7.0.579", + "1.5-7.7.0.580", + "1.5-7.7.0.581", + "1.5-7.7.0.582", + "1.5-7.7.0.583", + "1.5-7.7.0.584", + "1.5-7.7.0.585", + "1.5-7.7.0.586", + "1.5-7.7.0.587", + "1.5-7.7.0.588", + "1.5-7.7.0.589", + "1.5-7.7.0.590", + "1.5-7.7.0.591", + "1.5-7.7.0.592", + "1.5-7.7.0.593", + "1.5-7.7.0.594", + "1.5-7.7.0.595", + "1.5-7.7.0.598" + ], + "1.5.1": [ + "1.5.1-7.7.0.600", + "1.5.1-7.7.0.601", + "1.5.1-7.7.0.602", + "1.5.1-7.7.0.603", + "1.5.1-7.7.0.604", + "1.5.1-7.7.0.605", + "1.5.1-7.7.0.608", + "1.5.1-7.7.0.609", + "1.5.1-7.7.0.610", + "1.5.1-7.7.1.611", + "1.5.1-7.7.1.614", + "1.5.1-7.7.1.615", + "1.5.1-7.7.1.616", + "1.5.1-7.7.1.617", + "1.5.1-7.7.1.618", + "1.5.1-7.7.1.620", + "1.5.1-7.7.1.621", + "1.5.1-7.7.1.622", + "1.5.1-7.7.1.623", + "1.5.1-7.7.1.624", + "1.5.1-7.7.1.625", + "1.5.1-7.7.1.627", + "1.5.1-7.7.1.628", + "1.5.1-7.7.1.629", + "1.5.1-7.7.1.630", + "1.5.1-7.7.1.631", + "1.5.1-7.7.1.632", + "1.5.1-7.7.1.633", + "1.5.1-7.7.1.634", + "1.5.1-7.7.1.635", + "1.5.1-7.7.1.636", + "1.5.1-7.7.1.637", + "1.5.1-7.7.1.638", + "1.5.1-7.7.1.639", + "1.5.1-7.7.1.640", + "1.5.1-7.7.1.642", + "1.5.1-7.7.1.643", + "1.5.1-7.7.1.644", + "1.5.1-7.7.1.645", + "1.5.1-7.7.1.646", + "1.5.1-7.7.1.647", + "1.5.1-7.7.1.648", + "1.5.1-7.7.1.649", + "1.5.1-7.7.1.650", + "1.5.1-7.7.1.651", + "1.5.1-7.7.1.652", + "1.5.1-7.7.1.653", + "1.5.1-7.7.1.654", + "1.5.1-7.7.1.655", + "1.5.1-7.7.1.656", + "1.5.1-7.7.1.657", + "1.5.1-7.7.1.659", + "1.5.1-7.7.1.660", + "1.5.1-7.7.1.661", + "1.5.1-7.7.1.662", + "1.5.1-7.7.1.663", + "1.5.1-7.7.1.664", + "1.5.1-7.7.1.665", + "1.5.1-7.7.1.666", + "1.5.1-7.7.1.667", + "1.5.1-7.7.1.672", + "1.5.1-7.7.1.673", + "1.5.1-7.7.1.674", + "1.5.1-7.7.1.675", + "1.5.1-7.7.1.676", + "1.5.1-7.7.2.678", + "1.5.1-7.7.2.679", + "1.5.1-7.7.2.682" + ], + "1.5.2": [ + "1.5.2-7.8.0.684", + "1.5.2-7.8.0.685", + "1.5.2-7.8.0.686", + "1.5.2-7.8.0.687", + "1.5.2-7.8.0.688", + "1.5.2-7.8.0.689", + "1.5.2-7.8.0.690", + "1.5.2-7.8.0.691", + "1.5.2-7.8.0.692", + "1.5.2-7.8.0.693", + "1.5.2-7.8.0.694", + "1.5.2-7.8.0.695", + "1.5.2-7.8.0.696", + "1.5.2-7.8.0.697", + "1.5.2-7.8.0.698", + "1.5.2-7.8.0.699", + "1.5.2-7.8.0.700", + "1.5.2-7.8.0.701", + "1.5.2-7.8.0.702", + "1.5.2-7.8.0.703", + "1.5.2-7.8.0.704", + "1.5.2-7.8.0.705", + "1.5.2-7.8.0.706", + "1.5.2-7.8.0.707", + "1.5.2-7.8.0.708", + "1.5.2-7.8.0.710", + "1.5.2-7.8.0.711", + "1.5.2-7.8.0.712", + "1.5.2-7.8.0.713", + "1.5.2-7.8.0.715", + "1.5.2-7.8.0.716", + "1.5.2-7.8.0.719", + "1.5.2-7.8.0.720", + "1.5.2-7.8.0.721", + "1.5.2-7.8.0.722", + "1.5.2-7.8.0.723", + "1.5.2-7.8.0.725", + "1.5.2-7.8.0.726", + "1.5.2-7.8.0.727", + "1.5.2-7.8.0.728", + "1.5.2-7.8.0.729", + "1.5.2-7.8.0.730", + "1.5.2-7.8.0.731", + "1.5.2-7.8.0.732", + "1.5.2-7.8.0.733", + "1.5.2-7.8.0.734", + "1.5.2-7.8.0.735", + "1.5.2-7.8.0.736", + "1.5.2-7.8.1.737", + "1.5.2-7.8.1.738" + ], + "1.6.1": [ + "1.6.1-8.9.0.749", + "1.6.1-8.9.0.751", + "1.6.1-8.9.0.753", + "1.6.1-8.9.0.755", + "1.6.1-8.9.0.756", + "1.6.1-8.9.0.757", + "1.6.1-8.9.0.758", + "1.6.1-8.9.0.759", + "1.6.1-8.9.0.760", + "1.6.1-8.9.0.761", + "1.6.1-8.9.0.762", + "1.6.1-8.9.0.763", + "1.6.1-8.9.0.764", + "1.6.1-8.9.0.765", + "1.6.1-8.9.0.766", + "1.6.1-8.9.0.767", + "1.6.1-8.9.0.768", + "1.6.1-8.9.0.771", + "1.6.1-8.9.0.772", + "1.6.1-8.9.0.773", + "1.6.1-8.9.0.774", + "1.6.1-8.9.0.775" + ], + "1.6.2": [ + "1.6.2-9.10.0.776", + "1.6.2-9.10.0.777", + "1.6.2-9.10.0.778", + "1.6.2-9.10.0.779", + "1.6.2-9.10.0.780", + "1.6.2-9.10.0.781", + "1.6.2-9.10.0.784", + "1.6.2-9.10.0.785", + "1.6.2-9.10.0.786", + "1.6.2-9.10.0.787", + "1.6.2-9.10.0.789", + "1.6.2-9.10.0.790", + "1.6.2-9.10.0.791", + "1.6.2-9.10.0.792", + "1.6.2-9.10.0.793", + "1.6.2-9.10.0.794", + "1.6.2-9.10.0.795", + "1.6.2-9.10.0.796", + "1.6.2-9.10.0.797", + "1.6.2-9.10.0.798", + "1.6.2-9.10.0.799", + "1.6.2-9.10.0.800", + "1.6.2-9.10.0.801", + "1.6.2-9.10.0.802", + "1.6.2-9.10.0.803", + "1.6.2-9.10.0.804", + "1.6.2-9.10.0.816", + "1.6.2-9.10.0.817", + "1.6.2-9.10.0.818", + "1.6.2-9.10.0.819", + "1.6.2-9.10.0.820", + "1.6.2-9.10.0.821", + "1.6.2-9.10.0.822", + "1.6.2-9.10.0.823", + "1.6.2-9.10.0.824", + "1.6.2-9.10.0.825", + "1.6.2-9.10.0.826", + "1.6.2-9.10.0.827", + "1.6.2-9.10.0.828", + "1.6.2-9.10.0.829", + "1.6.2-9.10.0.830", + "1.6.2-9.10.0.831", + "1.6.2-9.10.0.832", + "1.6.2-9.10.0.833", + "1.6.2-9.10.0.834", + "1.6.2-9.10.0.835", + "1.6.2-9.10.0.836", + "1.6.2-9.10.0.837", + "1.6.2-9.10.0.838", + "1.6.2-9.10.0.839", + "1.6.2-9.10.0.840", + "1.6.2-9.10.0.841", + "1.6.2-9.10.0.842", + "1.6.2-9.10.0.843", + "1.6.2-9.10.0.844", + "1.6.2-9.10.0.845", + "1.6.2-9.10.0.846", + "1.6.2-9.10.0.847", + "1.6.2-9.10.0.848", + "1.6.2-9.10.1.849", + "1.6.2-9.10.1.850", + "1.6.2-9.10.1.851", + "1.6.2-9.10.1.852", + "1.6.2-9.10.1.853", + "1.6.2-9.10.1.854", + "1.6.2-9.10.1.855", + "1.6.2-9.10.1.856", + "1.6.2-9.10.1.857", + "1.6.2-9.10.1.858", + "1.6.2-9.10.1.859", + "1.6.2-9.10.1.860", + "1.6.2-9.10.1.861", + "1.6.2-9.10.1.862", + "1.6.2-9.10.1.863", + "1.6.2-9.10.1.864", + "1.6.2-9.10.1.865", + "1.6.2-9.10.1.866", + "1.6.2-9.10.1.867", + "1.6.2-9.10.1.869", + "1.6.2-9.10.1.870", + "1.6.2-9.10.1.871" + ], + "1.6.3": [ + "1.6.3-9.11.0.873", + "1.6.3-9.11.0.874", + "1.6.3-9.11.0.875", + "1.6.3-9.11.0.876", + "1.6.3-9.11.0.877", + "1.6.3-9.11.0.878" + ], + "1.6.4": [ + "1.6.4-9.11.0.879", + "1.6.4-9.11.0.880", + "1.6.4-9.11.0.881", + "1.6.4-9.11.0.882", + "1.6.4-9.11.0.883", + "1.6.4-9.11.0.884", + "1.6.4-9.11.0.885", + "1.6.4-9.11.0.886", + "1.6.4-9.11.0.891", + "1.6.4-9.11.0.892", + "1.6.4-9.11.0.893", + "1.6.4-9.11.0.894", + "1.6.4-9.11.0.895", + "1.6.4-9.11.0.896", + "1.6.4-9.11.0.897", + "1.6.4-9.11.0.898", + "1.6.4-9.11.0.899", + "1.6.4-9.11.0.900", + "1.6.4-9.11.0.901", + "1.6.4-9.11.0.902", + "1.6.4-9.11.0.903", + "1.6.4-9.11.0.904", + "1.6.4-9.11.0.905", + "1.6.4-9.11.0.906", + "1.6.4-9.11.0.907", + "1.6.4-9.11.0.908", + "1.6.4-9.11.0.909", + "1.6.4-9.11.0.910", + "1.6.4-9.11.0.911", + "1.6.4-9.11.0.912", + "1.6.4-9.11.0.913", + "1.6.4-9.11.1.914", + "1.6.4-9.11.1.915", + "1.6.4-9.11.1.916", + "1.6.4-9.11.1.917", + "1.6.4-9.11.1.918", + "1.6.4-9.11.1.919", + "1.6.4-9.11.1.920", + "1.6.4-9.11.1.921", + "1.6.4-9.11.1.922", + "1.6.4-9.11.1.923", + "1.6.4-9.11.1.924", + "1.6.4-9.11.1.925", + "1.6.4-9.11.1.926", + "1.6.4-9.11.1.928", + "1.6.4-9.11.1.930", + "1.6.4-9.11.1.931", + "1.6.4-9.11.1.933", + "1.6.4-9.11.1.934", + "1.6.4-9.11.1.935", + "1.6.4-9.11.1.937", + "1.6.4-9.11.1.938", + "1.6.4-9.11.1.939", + "1.6.4-9.11.1.940", + "1.6.4-9.11.1.941", + "1.6.4-9.11.1.942", + "1.6.4-9.11.1.943", + "1.6.4-9.11.1.944", + "1.6.4-9.11.1.945", + "1.6.4-9.11.1.946", + "1.6.4-9.11.1.947", + "1.6.4-9.11.1.948", + "1.6.4-9.11.1.949", + "1.6.4-9.11.1.951", + "1.6.4-9.11.1.952", + "1.6.4-9.11.1.960", + "1.6.4-9.11.1.961", + "1.6.4-9.11.1.963", + "1.6.4-9.11.1.964", + "1.6.4-9.11.1.953", + "1.6.4-9.11.1.965", + "1.6.4-9.11.1.1345" + ], + "1.7.2": [ + "1.7.2-10.12.0.967", + "1.7.2-10.12.0.968", + "1.7.2-10.12.0.969", + "1.7.2-10.12.0.970", + "1.7.2-10.12.0.971", + "1.7.2-10.12.0.972", + "1.7.2-10.12.0.973", + "1.7.2-10.12.0.974", + "1.7.2-10.12.0.975", + "1.7.2-10.12.0.976", + "1.7.2-10.12.0.977", + "1.7.2-10.12.0.979", + "1.7.2-10.12.0.980", + "1.7.2-10.12.0.981", + "1.7.2-10.12.0.982", + "1.7.2-10.12.0.984", + "1.7.2-10.12.0.985", + "1.7.2-10.12.0.986", + "1.7.2-10.12.0.987", + "1.7.2-10.12.0.989", + "1.7.2-10.12.0.990", + "1.7.2-10.12.0.991", + "1.7.2-10.12.0.993", + "1.7.2-10.12.0.994", + "1.7.2-10.12.0.995", + "1.7.2-10.12.0.996", + "1.7.2-10.12.0.997", + "1.7.2-10.12.0.998", + "1.7.2-10.12.0.999", + "1.7.2-10.12.0.1000", + "1.7.2-10.12.0.1001", + "1.7.2-10.12.0.1002", + "1.7.2-10.12.0.1003", + "1.7.2-10.12.0.1004", + "1.7.2-10.12.0.1005", + "1.7.2-10.12.0.1006", + "1.7.2-10.12.0.1007", + "1.7.2-10.12.0.1008", + "1.7.2-10.12.0.1009", + "1.7.2-10.12.0.1010", + "1.7.2-10.12.0.1011", + "1.7.2-10.12.0.1012", + "1.7.2-10.12.0.1013", + "1.7.2-10.12.0.1014", + "1.7.2-10.12.0.1015", + "1.7.2-10.12.0.1016", + "1.7.2-10.12.0.1017", + "1.7.2-10.12.0.1018", + "1.7.2-10.12.0.1019", + "1.7.2-10.12.0.1020", + "1.7.2-10.12.0.1021", + "1.7.2-10.12.0.1022", + "1.7.2-10.12.0.1023", + "1.7.2-10.12.0.1024", + "1.7.2-10.12.0.1025", + "1.7.2-10.12.0.1026", + "1.7.2-10.12.0.1027", + "1.7.2-10.12.0.1028", + "1.7.2-10.12.0.1029", + "1.7.2-10.12.0.1030", + "1.7.2-10.12.0.1031", + "1.7.2-10.12.0.1032", + "1.7.2-10.12.0.1033", + "1.7.2-10.12.0.1034", + "1.7.2-10.12.0.1039", + "1.7.2-10.12.0.1040", + "1.7.2-10.12.0.1041", + "1.7.2-10.12.0.1042", + "1.7.2-10.12.0.1043", + "1.7.2-10.12.0.1044", + "1.7.2-10.12.0.1045", + "1.7.2-10.12.0.1046", + "1.7.2-10.12.0.1047", + "1.7.2-10.12.0.1048", + "1.7.2-10.12.0.1049", + "1.7.2-10.12.0.1050", + "1.7.2-10.12.0.1051", + "1.7.2-10.12.0.1052", + "1.7.2-10.12.0.1053", + "1.7.2-10.12.0.1054", + "1.7.2-10.12.0.1055", + "1.7.2-10.12.0.1056", + "1.7.2-10.12.0.1057", + "1.7.2-10.12.0.1058", + "1.7.2-10.12.0.1059", + "1.7.2-10.12.1.1060", + "1.7.2-10.12.1.1061", + "1.7.2-10.12.1.1063", + "1.7.2-10.12.1.1065", + "1.7.2-10.12.1.1066", + "1.7.2-10.12.1.1067", + "1.7.2-10.12.1.1068", + "1.7.2-10.12.1.1069", + "1.7.2-10.12.1.1070", + "1.7.2-10.12.1.1071", + "1.7.2-10.12.1.1072", + "1.7.2-10.12.1.1073", + "1.7.2-10.12.1.1074", + "1.7.2-10.12.1.1075", + "1.7.2-10.12.1.1076", + "1.7.2-10.12.1.1077", + "1.7.2-10.12.1.1078", + "1.7.2-10.12.1.1079", + "1.7.2-10.12.1.1080", + "1.7.2-10.12.1.1081", + "1.7.2-10.12.1.1082", + "1.7.2-10.12.1.1083", + "1.7.2-10.12.1.1084", + "1.7.2-10.12.1.1085", + "1.7.2-10.12.1.1087", + "1.7.2-10.12.1.1088", + "1.7.2-10.12.1.1090", + "1.7.2-10.12.1.1091", + "1.7.2-10.12.1.1092", + "1.7.2-10.12.1.1093", + "1.7.2-10.12.1.1094", + "1.7.2-10.12.1.1095", + "1.7.2-10.12.1.1096", + "1.7.2-10.12.1.1097", + "1.7.2-10.12.1.1098", + "1.7.2-10.12.1.1099", + "1.7.2-10.12.1.1100", + "1.7.2-10.12.1.1101", + "1.7.2-10.12.1.1103", + "1.7.2-10.12.1.1104", + "1.7.2-10.12.1.1105", + "1.7.2-10.12.1.1106", + "1.7.2-10.12.1.1107", + "1.7.2-10.12.1.1108", + "1.7.2-10.12.1.1109", + "1.7.2-10.12.1.1110", + "1.7.2-10.12.1.1111", + "1.7.2-10.12.1.1112", + "1.7.2-10.12.1.1113", + "1.7.2-10.12.1.1114", + "1.7.2-10.12.1.1115", + "1.7.2-10.12.1.1116", + "1.7.2-10.12.1.1117", + "1.7.2-10.12.1.1118", + "1.7.2-10.12.1.1119", + "1.7.2-10.12.1.1120", + "1.7.2-10.12.2.1121", + "1.7.2-10.12.2.1122", + "1.7.2-10.12.2.1123", + "1.7.2-10.12.2.1124", + "1.7.2-10.12.2.1125", + "1.7.2-10.12.2.1126", + "1.7.2-10.12.2.1127", + "1.7.2-10.12.2.1128", + "1.7.2-10.12.2.1129", + "1.7.2-10.12.2.1130", + "1.7.2-10.12.2.1131", + "1.7.2-10.12.2.1132", + "1.7.2-10.12.2.1133", + "1.7.2-10.12.2.1145", + "1.7.2-10.12.2.1147", + "1.7.2-10.12.2.1154-mc172", + "1.7.2-10.12.2.1155-mc172", + "1.7.2-10.12.2.1161-mc172" + ], + "1.7.10_pre4": [ + "1.7.10_pre4-10.12.2.1137-prerelease", + "1.7.10_pre4-10.12.2.1138-prerelease", + "1.7.10_pre4-10.12.2.1139-prerelease", + "1.7.10_pre4-10.12.2.1141-prerelease", + "1.7.10_pre4-10.12.2.1142-prerelease", + "1.7.10_pre4-10.12.2.1143-prerelease", + "1.7.10_pre4-10.12.2.1144-prerelease", + "1.7.10_pre4-10.12.2.1146-prerelease", + "1.7.10_pre4-10.12.2.1148-prerelease", + "1.7.10_pre4-10.12.2.1149-prerelease" + ], + "1.7.10": [ + "1.7.10-10.13.0.1150", + "1.7.10-10.13.0.1151", + "1.7.10-10.13.0.1152", + "1.7.10-10.13.0.1153", + "1.7.10-10.13.0.1156", + "1.7.10-10.13.0.1157", + "1.7.10-10.13.0.1158", + "1.7.10-10.13.0.1159", + "1.7.10-10.13.0.1160", + "1.7.10-10.13.0.1162", + "1.7.10-10.13.0.1166", + "1.7.10-10.13.0.1167", + "1.7.10-10.13.0.1168", + "1.7.10-10.13.0.1169", + "1.7.10-10.13.0.1170", + "1.7.10-10.13.0.1171", + "1.7.10-10.13.0.1172", + "1.7.10-10.13.0.1174", + "1.7.10-10.13.0.1175", + "1.7.10-10.13.0.1176", + "1.7.10-10.13.0.1177", + "1.7.10-10.13.0.1178", + "1.7.10-10.13.0.1179", + "1.7.10-10.13.0.1180", + "1.7.10-10.13.0.1181", + "1.7.10-10.13.0.1182", + "1.7.10-10.13.0.1183", + "1.7.10-10.13.0.1184", + "1.7.10-10.13.0.1185", + "1.7.10-10.13.0.1186", + "1.7.10-10.13.0.1187", + "1.7.10-10.13.0.1188", + "1.7.10-10.13.0.1189", + "1.7.10-10.13.0.1190", + "1.7.10-10.13.0.1191", + "1.7.10-10.13.0.1194", + "1.7.10-10.13.0.1195", + "1.7.10-10.13.0.1197", + "1.7.10-10.13.0.1198", + "1.7.10-10.13.0.1199", + "1.7.10-10.13.0.1200", + "1.7.10-10.13.0.1201", + "1.7.10-10.13.0.1202", + "1.7.10-10.13.0.1203", + "1.7.10-10.13.0.1204", + "1.7.10-10.13.0.1205", + "1.7.10-10.13.0.1206", + "1.7.10-10.13.0.1207", + "1.7.10-10.13.0.1208", + "1.7.10-10.13.1.1210-new", + "1.7.10-10.13.1.1211-new", + "1.7.10-10.13.1.1212-new", + "1.7.10-10.13.1.1213-new", + "1.7.10-10.13.1.1214-new", + "1.7.10-10.13.1.1215-new", + "1.7.10-10.13.1.1216-new", + "1.7.10-10.13.1.1217", + "1.7.10-10.13.1.1219", + "1.7.10-10.13.1.1220", + "1.7.10-10.13.1.1221", + "1.7.10-10.13.1.1222", + "1.7.10-10.13.1.1223", + "1.7.10-10.13.1.1224", + "1.7.10-10.13.1.1225", + "1.7.10-10.13.1.1226", + "1.7.10-10.13.1.1229", + "1.7.10-10.13.2.1230", + "1.7.10-10.13.2.1231", + "1.7.10-10.13.2.1232", + "1.7.10-10.13.2.1233", + "1.7.10-10.13.2.1234", + "1.7.10-10.13.2.1235", + "1.7.10-10.13.2.1236", + "1.7.10-10.13.2.1240", + "1.7.10-10.13.2.1253", + "1.7.10-10.13.2.1254", + "1.7.10-10.13.2.1256", + "1.7.10-10.13.2.1258", + "1.7.10-10.13.2.1263", + "1.7.10-10.13.2.1264", + "1.7.10-10.13.2.1270", + "1.7.10-10.13.2.1272", + "1.7.10-10.13.2.1275", + "1.7.10-10.13.2.1276", + "1.7.10-10.13.2.1277", + "1.7.10-10.13.2.1283", + "1.7.10-10.13.2.1284", + "1.7.10-10.13.2.1286", + "1.7.10-10.13.2.1291", + "1.7.10-10.13.2.1300-1.7.10", + "1.7.10-10.13.2.1307-1.7.10", + "1.7.10-10.13.2.1340-1.7.10", + "1.7.10-10.13.2.1342-1.7.10", + "1.7.10-10.13.2.1343-1.7.10", + "1.7.10-10.13.2.1346-1.7.10", + "1.7.10-10.13.2.1347-1.7.10", + "1.7.10-10.13.2.1351-1.7.10", + "1.7.10-10.13.2.1352-1.7.10", + "1.7.10-10.13.3.1355-1.7.10", + "1.7.10-10.13.3.1356-1.7.10", + "1.7.10-10.13.3.1358-1.7.10", + "1.7.10-10.13.3.1360-1.7.10", + "1.7.10-10.13.3.1362-1.7.10", + "1.7.10-10.13.3.1363-1.7.10", + "1.7.10-10.13.3.1364-1.7.10", + "1.7.10-10.13.3.1365-1.7.10", + "1.7.10-10.13.3.1366-1.7.10", + "1.7.10-10.13.3.1367-1.7.10", + "1.7.10-10.13.3.1368-1.7.10", + "1.7.10-10.13.3.1369-1.7.10", + "1.7.10-10.13.3.1370-1.7.10", + "1.7.10-10.13.3.1372-1.7.10", + "1.7.10-10.13.3.1373-1.7.10", + "1.7.10-10.13.3.1374-1.7.10", + "1.7.10-10.13.3.1376-1.7.10", + "1.7.10-10.13.3.1377-1.7.10", + "1.7.10-10.13.3.1378-1.7.10", + "1.7.10-10.13.3.1379-1.7.10", + "1.7.10-10.13.3.1380-1.7.10", + "1.7.10-10.13.3.1381-1.7.10", + "1.7.10-10.13.3.1382-1.7.10", + "1.7.10-10.13.3.1383-1.7.10", + "1.7.10-10.13.3.1384-1.7.10", + "1.7.10-10.13.3.1385-1.7.10", + "1.7.10-10.13.3.1387-1.7.10", + "1.7.10-10.13.3.1388-1.7.10", + "1.7.10-10.13.3.1389-1710ls", + "1.7.10-10.13.3.1391-1710ls", + "1.7.10-10.13.3.1393-1710ls", + "1.7.10-10.13.3.1394-1710ls", + "1.7.10-10.13.3.1395-1710ls", + "1.7.10-10.13.3.1399-1.7.10", + "1.7.10-10.13.3.1400-1.7.10", + "1.7.10-10.13.3.1401-1710ls", + "1.7.10-10.13.3.1403-1.7.10", + "1.7.10-10.13.3.1406-1.7.10", + "1.7.10-10.13.3.1407-1.7.10", + "1.7.10-10.13.3.1408-1.7.10", + "1.7.10-10.13.3.1420-1.7.10", + "1.7.10-10.13.3.1422-1.7.10", + "1.7.10-10.13.3.1424-1.7.10", + "1.7.10-10.13.3.1428-1.7.10", + "1.7.10-10.13.4.1445-1.7.10", + "1.7.10-10.13.4.1447-1.7.10", + "1.7.10-10.13.4.1448-1.7.10", + "1.7.10-10.13.4.1451-1.7.10", + "1.7.10-10.13.4.1452-1.7.10", + "1.7.10-10.13.4.1456-1.7.10", + "1.7.10-10.13.4.1469-1.7.10", + "1.7.10-10.13.4.1470-1.7.10", + "1.7.10-10.13.4.1472-1.7.10", + "1.7.10-10.13.4.1481-1.7.10", + "1.7.10-10.13.4.1490-1.7.10", + "1.7.10-10.13.4.1492-1.7.10", + "1.7.10-10.13.4.1517-1.7.10", + "1.7.10-10.13.4.1539-1.7.10", + "1.7.10-10.13.4.1540-1.7.10", + "1.7.10-10.13.4.1541-1.7.10", + "1.7.10-10.13.4.1557-1.7.10", + "1.7.10-10.13.4.1558-1.7.10", + "1.7.10-10.13.4.1564-1.7.10", + "1.7.10-10.13.4.1566-1.7.10", + "1.7.10-10.13.4.1614-1.7.10" + ], + "1.8": [ + "1.8-11.14.0.1237-1.8", + "1.8-11.14.0.1238-1.8", + "1.8-11.14.0.1239-1.8", + "1.8-11.14.0.1241-1.8", + "1.8-11.14.0.1242-1.8", + "1.8-11.14.0.1243-1.8", + "1.8-11.14.0.1244-1.8", + "1.8-11.14.0.1245-1.8", + "1.8-11.14.0.1246-1.8", + "1.8-11.14.0.1247-1.8", + "1.8-11.14.0.1248-1.8", + "1.8-11.14.0.1249-1.8", + "1.8-11.14.0.1251-1.8", + "1.8-11.14.0.1252-1.8", + "1.8-11.14.0.1255-1.8", + "1.8-11.14.0.1257-1.8", + "1.8-11.14.0.1259-1.8", + "1.8-11.14.0.1260-1.8", + "1.8-11.14.0.1261-1.8", + "1.8-11.14.0.1262-1.8", + "1.8-11.14.0.1265-1.8", + "1.8-11.14.0.1266-1.8", + "1.8-11.14.0.1267-1.8", + "1.8-11.14.0.1268-1.8", + "1.8-11.14.0.1269-1.8", + "1.8-11.14.0.1271-1.8", + "1.8-11.14.0.1273-1.8", + "1.8-11.14.0.1274-1.8", + "1.8-11.14.0.1278-1.8", + "1.8-11.14.0.1279-1.8", + "1.8-11.14.0.1280-1.8", + "1.8-11.14.0.1281-1.8", + "1.8-11.14.0.1282-1.8", + "1.8-11.14.0.1285-1.8", + "1.8-11.14.0.1287-1.8", + "1.8-11.14.0.1288-1.8", + "1.8-11.14.0.1289-1.8", + "1.8-11.14.0.1290-1.8", + "1.8-11.14.0.1292-1.8", + "1.8-11.14.0.1293-1.8", + "1.8-11.14.0.1294-1.8", + "1.8-11.14.0.1295-1.8", + "1.8-11.14.0.1296", + "1.8-11.14.0.1297", + "1.8-11.14.0.1298", + "1.8-11.14.0.1299", + "1.8-11.14.1.1301", + "1.8-11.14.1.1302", + "1.8-11.14.1.1303", + "1.8-11.14.1.1305", + "1.8-11.14.1.1306", + "1.8-11.14.1.1308", + "1.8-11.14.1.1309", + "1.8-11.14.1.1310", + "1.8-11.14.1.1311", + "1.8-11.14.1.1312", + "1.8-11.14.1.1313", + "1.8-11.14.1.1314", + "1.8-11.14.1.1315", + "1.8-11.14.1.1316", + "1.8-11.14.1.1317", + "1.8-11.14.1.1318", + "1.8-11.14.1.1319", + "1.8-11.14.1.1320", + "1.8-11.14.1.1321", + "1.8-11.14.1.1322", + "1.8-11.14.1.1323", + "1.8-11.14.1.1324", + "1.8-11.14.1.1325", + "1.8-11.14.1.1326", + "1.8-11.14.1.1327", + "1.8-11.14.1.1328", + "1.8-11.14.1.1329", + "1.8-11.14.1.1332", + "1.8-11.14.1.1333", + "1.8-11.14.1.1334", + "1.8-11.14.1.1335", + "1.8-11.14.1.1336", + "1.8-11.14.1.1337", + "1.8-11.14.1.1338", + "1.8-11.14.1.1339", + "1.8-11.14.1.1341", + "1.8-11.14.1.1349", + "1.8-11.14.1.1350", + "1.8-11.14.1.1353", + "1.8-11.14.1.1354", + "1.8-11.14.1.1357", + "1.8-11.14.1.1359", + "1.8-11.14.1.1361", + "1.8-11.14.1.1371", + "1.8-11.14.1.1375", + "1.8-11.14.1.1390", + "1.8-11.14.1.1392", + "1.8-11.14.1.1396", + "1.8-11.14.1.1397", + "1.8-11.14.1.1398", + "1.8-11.14.1.1402", + "1.8-11.14.1.1404", + "1.8-11.14.1.1405", + "1.8-11.14.1.1409", + "1.8-11.14.1.1410", + "1.8-11.14.1.1411", + "1.8-11.14.1.1412", + "1.8-11.14.1.1413", + "1.8-11.14.1.1414", + "1.8-11.14.1.1415", + "1.8-11.14.1.1416", + "1.8-11.14.1.1417", + "1.8-11.14.1.1418", + "1.8-11.14.1.1419", + "1.8-11.14.2.1421", + "1.8-11.14.2.1423", + "1.8-11.14.2.1426", + "1.8-11.14.2.1427", + "1.8-11.14.2.1429", + "1.8-11.14.2.1430", + "1.8-11.14.2.1431", + "1.8-11.14.2.1433", + "1.8-11.14.2.1434", + "1.8-11.14.2.1435", + "1.8-11.14.2.1436", + "1.8-11.14.2.1437", + "1.8-11.14.2.1439", + "1.8-11.14.2.1440", + "1.8-11.14.2.1441", + "1.8-11.14.2.1442", + "1.8-11.14.2.1443", + "1.8-11.14.2.1444", + "1.8-11.14.3.1446", + "1.8-11.14.3.1449", + "1.8-11.14.3.1450", + "1.8-11.14.3.1453", + "1.8-11.14.3.1457", + "1.8-11.14.3.1458", + "1.8-11.14.3.1459", + "1.8-11.14.3.1460", + "1.8-11.14.3.1461", + "1.8-11.14.3.1462", + "1.8-11.14.3.1463", + "1.8-11.14.3.1464", + "1.8-11.14.3.1465", + "1.8-11.14.3.1466", + "1.8-11.14.3.1467", + "1.8-11.14.3.1468", + "1.8-11.14.3.1473", + "1.8-11.14.3.1474", + "1.8-11.14.3.1475", + "1.8-11.14.3.1476", + "1.8-11.14.3.1479", + "1.8-11.14.3.1480", + "1.8-11.14.3.1482", + "1.8-11.14.3.1483", + "1.8-11.14.3.1484", + "1.8-11.14.3.1485", + "1.8-11.14.3.1486", + "1.8-11.14.3.1487", + "1.8-11.14.3.1491", + "1.8-11.14.3.1493", + "1.8-11.14.3.1494", + "1.8-11.14.3.1495", + "1.8-11.14.3.1496", + "1.8-11.14.3.1497", + "1.8-11.14.3.1498", + "1.8-11.14.3.1499", + "1.8-11.14.3.1500", + "1.8-11.14.3.1501", + "1.8-11.14.3.1502", + "1.8-11.14.3.1503", + "1.8-11.14.3.1504", + "1.8-11.14.3.1505", + "1.8-11.14.3.1506", + "1.8-11.14.3.1507", + "1.8-11.14.3.1508", + "1.8-11.14.3.1509", + "1.8-11.14.3.1510", + "1.8-11.14.3.1511", + "1.8-11.14.3.1512", + "1.8-11.14.3.1513", + "1.8-11.14.3.1514", + "1.8-11.14.3.1515", + "1.8-11.14.3.1516", + "1.8-11.14.3.1518", + "1.8-11.14.3.1519", + "1.8-11.14.3.1520", + "1.8-11.14.3.1521", + "1.8-11.14.3.1523", + "1.8-11.14.3.1524", + "1.8-11.14.3.1525", + "1.8-11.14.3.1526", + "1.8-11.14.3.1529", + "1.8-11.14.3.1530", + "1.8-11.14.3.1531", + "1.8-11.14.3.1532", + "1.8-11.14.3.1533", + "1.8-11.14.3.1534", + "1.8-11.14.3.1535", + "1.8-11.14.3.1542", + "1.8-11.14.3.1543", + "1.8-11.14.3.1544", + "1.8-11.14.3.1545", + "1.8-11.14.3.1546", + "1.8-11.14.3.1547", + "1.8-11.14.3.1548", + "1.8-11.14.3.1549", + "1.8-11.14.3.1550", + "1.8-11.14.3.1551", + "1.8-11.14.3.1552", + "1.8-11.14.3.1553", + "1.8-11.14.3.1554", + "1.8-11.14.3.1555", + "1.8-11.14.3.1556", + "1.8-11.14.3.1559", + "1.8-11.14.3.1560", + "1.8-11.14.3.1561", + "1.8-11.14.3.1562", + "1.8-11.14.4.1563", + "1.8-11.14.4.1565", + "1.8-11.14.4.1568", + "1.8-11.14.4.1569", + "1.8-11.14.4.1570", + "1.8-11.14.4.1571", + "1.8-11.14.4.1572", + "1.8-11.14.4.1577" + ], + "1.8.8": [ + "1.8.8-11.14.4.1575-1.8.8", + "1.8.8-11.14.4.1576-1.8.8", + "1.8.8-11.14.4.1579-1.8.8", + "1.8.8-11.14.4.1580-1.8.8", + "1.8.8-11.14.4.1581-1.8.8", + "1.8.8-11.14.4.1582-1.8.8", + "1.8.8-11.14.4.1583-1.8.8", + "1.8.8-11.14.4.1584-1.8.8", + "1.8.8-11.14.4.1585-1.8.8", + "1.8.8-11.14.4.1586-1.8.8", + "1.8.8-11.14.4.1587-1.8.8", + "1.8.8-11.14.4.1588-1.8.8", + "1.8.8-11.14.4.1589-1.8.8", + "1.8.8-11.14.4.1590-1.8.8", + "1.8.8-11.15.0.1591-1.8.8", + "1.8.8-11.15.0.1592-1.8.8", + "1.8.8-11.15.0.1594-1.8.8", + "1.8.8-11.15.0.1595-1.8.8", + "1.8.8-11.15.0.1596-1.8.8", + "1.8.8-11.15.0.1600-1.8.8", + "1.8.8-11.15.0.1601-1.8.8", + "1.8.8-11.15.0.1602-1.8.8", + "1.8.8-11.15.0.1603-1.8.8", + "1.8.8-11.15.0.1604-1.8.8", + "1.8.8-11.15.0.1605-1.8.8", + "1.8.8-11.15.0.1606-1.8.8", + "1.8.8-11.15.0.1607-1.8.8", + "1.8.8-11.15.0.1608-1.8.8", + "1.8.8-11.15.0.1609-1.8.8", + "1.8.8-11.15.0.1610-1.8.8", + "1.8.8-11.15.0.1611-1.8.8", + "1.8.8-11.15.0.1612-1.8.8", + "1.8.8-11.15.0.1613-1.8.8", + "1.8.8-11.15.0.1615-1.8.8", + "1.8.8-11.15.0.1616-1.8.8", + "1.8.8-11.15.0.1617-1.8.8", + "1.8.8-11.15.0.1618-1.8.8", + "1.8.8-11.15.0.1619-1.8.8", + "1.8.8-11.15.0.1620-1.8.8", + "1.8.8-11.15.0.1621-1.8.8", + "1.8.8-11.15.0.1622-1.8.8", + "1.8.8-11.15.0.1623-1.8.8", + "1.8.8-11.15.0.1624-1.8.8", + "1.8.8-11.15.0.1625-1.8.8", + "1.8.8-11.15.0.1626-1.8.8", + "1.8.8-11.15.0.1627-1.8.8", + "1.8.8-11.15.0.1628-1.8.8", + "1.8.8-11.15.0.1630-1.8.8", + "1.8.8-11.15.0.1632-1.8.8", + "1.8.8-11.15.0.1633-1.8.8", + "1.8.8-11.15.0.1634-1.8.8", + "1.8.8-11.15.0.1635-1.8.8", + "1.8.8-11.15.0.1636-1.8.8", + "1.8.8-11.15.0.1637-1.8.8", + "1.8.8-11.15.0.1638-1.8.8", + "1.8.8-11.15.0.1639-1.8.8", + "1.8.8-11.15.0.1640-1.8.8", + "1.8.8-11.15.0.1641-1.8.8", + "1.8.8-11.15.0.1642-1.8.8", + "1.8.8-11.15.0.1643-1.8.8", + "1.8.8-11.15.0.1644-1.8.8", + "1.8.8-11.15.0.1645-1.8.8", + "1.8.8-11.15.0.1646-1.8.8", + "1.8.8-11.15.0.1647-1.8.8", + "1.8.8-11.15.0.1649-1.8.8", + "1.8.8-11.15.0.1650-1.8.8", + "1.8.8-11.15.0.1651-1.8.8", + "1.8.8-11.15.0.1652-1.8.8", + "1.8.8-11.15.0.1653-1.8.8", + "1.8.8-11.15.0.1654-1.8.8", + "1.8.8-11.15.0.1655" + ], + "1.8.9": [ + "1.8.9-11.15.0.1656", + "1.8.9-11.15.0.1657", + "1.8.9-11.15.0.1658", + "1.8.9-11.15.0.1659", + "1.8.9-11.15.0.1661", + "1.8.9-11.15.0.1662", + "1.8.9-11.15.0.1663", + "1.8.9-11.15.0.1664", + "1.8.9-11.15.0.1665", + "1.8.9-11.15.0.1666", + "1.8.9-11.15.0.1668", + "1.8.9-11.15.0.1669", + "1.8.9-11.15.0.1670", + "1.8.9-11.15.0.1671", + "1.8.9-11.15.0.1672", + "1.8.9-11.15.0.1673", + "1.8.9-11.15.0.1674", + "1.8.9-11.15.0.1675", + "1.8.9-11.15.0.1676", + "1.8.9-11.15.0.1677", + "1.8.9-11.15.0.1681", + "1.8.9-11.15.0.1682", + "1.8.9-11.15.0.1683", + "1.8.9-11.15.0.1684", + "1.8.9-11.15.0.1686", + "1.8.9-11.15.0.1687", + "1.8.9-11.15.0.1688", + "1.8.9-11.15.0.1689", + "1.8.9-11.15.0.1690", + "1.8.9-11.15.0.1691", + "1.8.9-11.15.0.1692", + "1.8.9-11.15.0.1693", + "1.8.9-11.15.0.1694", + "1.8.9-11.15.0.1695", + "1.8.9-11.15.0.1696", + "1.8.9-11.15.0.1697", + "1.8.9-11.15.0.1698", + "1.8.9-11.15.0.1699", + "1.8.9-11.15.0.1700", + "1.8.9-11.15.0.1701", + "1.8.9-11.15.0.1702", + "1.8.9-11.15.0.1703", + "1.8.9-11.15.0.1705", + "1.8.9-11.15.0.1706", + "1.8.9-11.15.0.1707", + "1.8.9-11.15.0.1708", + "1.8.9-11.15.0.1709", + "1.8.9-11.15.0.1710", + "1.8.9-11.15.0.1711", + "1.8.9-11.15.0.1712", + "1.8.9-11.15.0.1713", + "1.8.9-11.15.0.1714", + "1.8.9-11.15.0.1715", + "1.8.9-11.15.0.1716", + "1.8.9-11.15.0.1718", + "1.8.9-11.15.0.1719", + "1.8.9-11.15.0.1720", + "1.8.9-11.15.0.1721", + "1.8.9-11.15.1.1722", + "1.8.9-11.15.1.1723", + "1.8.9-11.15.1.1724", + "1.8.9-11.15.1.1725", + "1.8.9-11.15.1.1726", + "1.8.9-11.15.1.1727", + "1.8.9-11.15.1.1729", + "1.8.9-11.15.1.1730", + "1.8.9-11.15.1.1731", + "1.8.9-11.15.1.1732", + "1.8.9-11.15.1.1733", + "1.8.9-11.15.1.1734", + "1.8.9-11.15.1.1735", + "1.8.9-11.15.1.1736", + "1.8.9-11.15.1.1737", + "1.8.9-11.15.1.1738", + "1.8.9-11.15.1.1739", + "1.8.9-11.15.1.1740", + "1.8.9-11.15.1.1741", + "1.8.9-11.15.1.1742", + "1.8.9-11.15.1.1743", + "1.8.9-11.15.1.1744", + "1.8.9-11.15.1.1745", + "1.8.9-11.15.1.1746", + "1.8.9-11.15.1.1747", + "1.8.9-11.15.1.1748", + "1.8.9-11.15.1.1749", + "1.8.9-11.15.1.1750", + "1.8.9-11.15.1.1751", + "1.8.9-11.15.1.1752", + "1.8.9-11.15.1.1754", + "1.8.9-11.15.1.1755", + "1.8.9-11.15.1.1756", + "1.8.9-11.15.1.1757", + "1.8.9-11.15.1.1758", + "1.8.9-11.15.1.1759", + "1.8.9-11.15.1.1760", + "1.8.9-11.15.1.1761", + "1.8.9-11.15.1.1762", + "1.8.9-11.15.1.1763", + "1.8.9-11.15.1.1764", + "1.8.9-11.15.1.1765", + "1.8.9-11.15.1.1777", + "1.8.9-11.15.1.1783", + "1.8.9-11.15.1.1785", + "1.8.9-11.15.1.1791", + "1.8.9-11.15.1.1794", + "1.8.9-11.15.1.1808", + "1.8.9-11.15.1.1847", + "1.8.9-11.15.1.1855", + "1.8.9-11.15.1.1872", + "1.8.9-11.15.1.1873", + "1.8.9-11.15.1.1875", + "1.8.9-11.15.1.1890-1.8.9", + "1.8.9-11.15.1.1902-1.8.9", + "1.8.9-11.15.1.2318-1.8.9" + ], + "1.9": [ + "1.9-12.16.0.1766-1.9", + "1.9-12.16.0.1767-1.9", + "1.9-12.16.0.1768-1.9", + "1.9-12.16.0.1769-1.9", + "1.9-12.16.0.1770-1.9", + "1.9-12.16.0.1771-1.9", + "1.9-12.16.0.1772-1.9", + "1.9-12.16.0.1773-1.9", + "1.9-12.16.0.1774-1.9", + "1.9-12.16.0.1775-1.9", + "1.9-12.16.0.1776-1.9", + "1.9-12.16.0.1778-1.9", + "1.9-12.16.0.1779-1.9", + "1.9-12.16.0.1780-1.9", + "1.9-12.16.0.1781-1.9", + "1.9-12.16.0.1782-1.9", + "1.9-12.16.0.1784-1.9", + "1.9-12.16.0.1786-1.9", + "1.9-12.16.0.1787-1.9", + "1.9-12.16.0.1788-1.9", + "1.9-12.16.0.1789-1.9", + "1.9-12.16.0.1790-1.9", + "1.9-12.16.0.1792-1.9", + "1.9-12.16.0.1793-1.9", + "1.9-12.16.0.1795-1.9", + "1.9-12.16.0.1796-1.9", + "1.9-12.16.0.1797-1.9", + "1.9-12.16.0.1798-1.9", + "1.9-12.16.0.1799-1.9", + "1.9-12.16.0.1800-1.9", + "1.9-12.16.0.1801-1.9", + "1.9-12.16.0.1802-1.9", + "1.9-12.16.0.1803-1.9", + "1.9-12.16.0.1804-1.9", + "1.9-12.16.0.1805-1.9", + "1.9-12.16.0.1806-1.9", + "1.9-12.16.0.1807-1.9", + "1.9-12.16.0.1809-1.9", + "1.9-12.16.0.1810-1.9", + "1.9-12.16.0.1811-1.9", + "1.9-12.16.0.1812-1.9", + "1.9-12.16.0.1813-1.9", + "1.9-12.16.0.1814-1.9", + "1.9-12.16.0.1815-1.9", + "1.9-12.16.0.1816-1.9", + "1.9-12.16.0.1817-1.9", + "1.9-12.16.0.1819-1.9", + "1.9-12.16.0.1820-1.9", + "1.9-12.16.0.1821-1.9", + "1.9-12.16.0.1822-1.9", + "1.9-12.16.0.1823-1.9", + "1.9-12.16.0.1824-1.9", + "1.9-12.16.0.1825-1.9", + "1.9-12.16.0.1826-1.9", + "1.9-12.16.0.1827-1.9", + "1.9-12.16.0.1828-1.9", + "1.9-12.16.0.1829-1.9", + "1.9-12.16.0.1830-1.9", + "1.9-12.16.0.1831-1.9", + "1.9-12.16.0.1832-1.9", + "1.9-12.16.0.1833-1.9", + "1.9-12.16.0.1834-1.9", + "1.9-12.16.0.1835-1.9", + "1.9-12.16.0.1836-1.9", + "1.9-12.16.0.1837-1.9", + "1.9-12.16.0.1838-1.9", + "1.9-12.16.0.1839-1.9", + "1.9-12.16.0.1840-1.9", + "1.9-12.16.0.1841-1.9", + "1.9-12.16.0.1842-1.9", + "1.9-12.16.0.1843-1.9", + "1.9-12.16.0.1844-1.9", + "1.9-12.16.0.1845-1.9", + "1.9-12.16.0.1846-1.9", + "1.9-12.16.0.1848-1.9", + "1.9-12.16.0.1849-1.9", + "1.9-12.16.0.1850-1.9", + "1.9-12.16.0.1851-1.9", + "1.9-12.16.0.1852-1.9", + "1.9-12.16.0.1853-1.9", + "1.9-12.16.0.1854-1.9", + "1.9-12.16.0.1856-1.9", + "1.9-12.16.0.1857-1.9", + "1.9-12.16.0.1858-1.9", + "1.9-12.16.0.1859-1.9", + "1.9-12.16.0.1860-1.9", + "1.9-12.16.0.1861-1.9", + "1.9-12.16.0.1862-1.9", + "1.9-12.16.0.1863-1.9", + "1.9-12.16.0.1864-1.9", + "1.9-12.16.0.1865-1.9", + "1.9-12.16.0.1866-1.9", + "1.9-12.16.0.1867-1.9", + "1.9-12.16.0.1868-1.9", + "1.9-12.16.0.1869-1.9", + "1.9-12.16.0.1870-1.9", + "1.9-12.16.0.1871-1.9", + "1.9-12.16.0.1874-1.9", + "1.9-12.16.0.1877-1.9", + "1.9-12.16.0.1878-1.9", + "1.9-12.16.0.1879-1.9", + "1.9-12.16.0.1880-1.9", + "1.9-12.16.0.1881-1.9", + "1.9-12.16.0.1882-1.9", + "1.9-12.16.0.1883-1.9", + "1.9-12.16.0.1884-1.9", + "1.9-12.16.0.1885-1.9", + "1.9-12.16.0.1886", + "1.9-12.16.1.1887", + "1.9-12.16.1.1888", + "1.9-12.16.1.1889", + "1.9-12.16.1.1891", + "1.9-12.16.1.1892", + "1.9-12.16.1.1893", + "1.9-12.16.1.1894", + "1.9-12.16.1.1895", + "1.9-12.16.1.1896", + "1.9-12.16.1.1897", + "1.9-12.16.1.1898", + "1.9-12.16.1.1899", + "1.9-12.16.1.1900", + "1.9-12.16.1.1901", + "1.9-12.16.1.1904", + "1.9-12.16.1.1905", + "1.9-12.16.1.1906", + "1.9-12.16.1.1907", + "1.9-12.16.1.1923", + "1.9-12.16.1.1934", + "1.9-12.16.1.1938-1.9.0" + ], + "1.9.4": [ + "1.9.4-12.17.0.1908-1.9.4", + "1.9.4-12.17.0.1909-1.9.4", + "1.9.4-12.17.0.1910-1.9.4", + "1.9.4-12.17.0.1912-1.9.4", + "1.9.4-12.17.0.1913-1.9.4", + "1.9.4-12.17.0.1914-1.9.4", + "1.9.4-12.17.0.1915-1.9.4", + "1.9.4-12.17.0.1916-1.9.4", + "1.9.4-12.17.0.1917-1.9.4", + "1.9.4-12.17.0.1918-1.9.4", + "1.9.4-12.17.0.1919-EHUnit", + "1.9.4-12.17.0.1920-1.9.4", + "1.9.4-12.17.0.1921-1.9.4", + "1.9.4-12.17.0.1922-1.9.4", + "1.9.4-12.17.0.1924-1.9.4", + "1.9.4-12.17.0.1925-1.9.4", + "1.9.4-12.17.0.1926-1.9.4", + "1.9.4-12.17.0.1927-1.9.4", + "1.9.4-12.17.0.1928-1.9.4", + "1.9.4-12.17.0.1929-1.9.4", + "1.9.4-12.17.0.1930-1.9.4", + "1.9.4-12.17.0.1931-1.9.4", + "1.9.4-12.17.0.1932-1.9.4", + "1.9.4-12.17.0.1933-1.9.4", + "1.9.4-12.17.0.1935-1.9.4", + "1.9.4-12.17.0.1936-1.9.4", + "1.9.4-12.17.0.1937", + "1.9.4-12.17.0.1939", + "1.9.4-12.17.0.1940", + "1.9.4-12.17.0.1941", + "1.9.4-12.17.0.1943", + "1.9.4-12.17.0.1944", + "1.9.4-12.17.0.1945", + "1.9.4-12.17.0.1946", + "1.9.4-12.17.0.1947", + "1.9.4-12.17.0.1948", + "1.9.4-12.17.0.1949", + "1.9.4-12.17.0.1950", + "1.9.4-12.17.0.1951", + "1.9.4-12.17.0.1952", + "1.9.4-12.17.0.1953", + "1.9.4-12.17.0.1954", + "1.9.4-12.17.0.1955", + "1.9.4-12.17.0.1956", + "1.9.4-12.17.0.1957", + "1.9.4-12.17.0.1958", + "1.9.4-12.17.0.1959", + "1.9.4-12.17.0.1960", + "1.9.4-12.17.0.1961", + "1.9.4-12.17.0.1962", + "1.9.4-12.17.0.1963", + "1.9.4-12.17.0.1964", + "1.9.4-12.17.0.1965", + "1.9.4-12.17.0.1966", + "1.9.4-12.17.0.1967", + "1.9.4-12.17.0.1968", + "1.9.4-12.17.0.1969", + "1.9.4-12.17.0.1970", + "1.9.4-12.17.0.1972", + "1.9.4-12.17.0.1973", + "1.9.4-12.17.0.1974-1.9.4", + "1.9.4-12.17.0.1975", + "1.9.4-12.17.0.1976", + "1.9.4-12.17.0.1987", + "1.9.4-12.17.0.1990", + "1.9.4-12.17.0.2051", + "1.9.4-12.17.0.2317-1.9.4" + ], + "1.10": [ + "1.10-12.18.0.1981-1.10.0", + "1.10-12.18.0.1982-1.10.0", + "1.10-12.18.0.1983-1.10.0", + "1.10-12.18.0.1984-1.10.0", + "1.10-12.18.0.1985-1.10.0", + "1.10-12.18.0.1986-1.10.0", + "1.10-12.18.0.1988-1.10.0", + "1.10-12.18.0.1989-1.10.0", + "1.10-12.18.0.1991-1.10.0", + "1.10-12.18.0.1992-1.10.0", + "1.10-12.18.0.1993-1.10.0", + "1.10-12.18.0.1994-1.10.0", + "1.10-12.18.0.1995-1.10.0", + "1.10-12.18.0.1996-1.10.0", + "1.10-12.18.0.1997-1.10.0", + "1.10-12.18.0.1998-1.10.0", + "1.10-12.18.0.1999-1.10.0", + "1.10-12.18.0.2000-1.10.0" + ], + "1.10.2": [ + "1.10.2-12.18.0.2001-1.10.0", + "1.10.2-12.18.0.2002-1.10.0", + "1.10.2-12.18.0.2003-1.10.0", + "1.10.2-12.18.0.2004-1.10.0", + "1.10.2-12.18.0.2005-1.10.0", + "1.10.2-12.18.0.2006-1.10.0", + "1.10.2-12.18.0.2007-1.10.0", + "1.10.2-12.18.0.2008", + "1.10.2-12.18.0.2009", + "1.10.2-12.18.0.2010", + "1.10.2-12.18.1.2011", + "1.10.2-12.18.1.2012", + "1.10.2-12.18.1.2013", + "1.10.2-12.18.1.2014", + "1.10.2-12.18.1.2015-failtests", + "1.10.2-12.18.1.2016-failtests", + "1.10.2-12.18.1.2017", + "1.10.2-12.18.1.2018", + "1.10.2-12.18.1.2019", + "1.10.2-12.18.1.2020", + "1.10.2-12.18.1.2021", + "1.10.2-12.18.1.2022", + "1.10.2-12.18.1.2023", + "1.10.2-12.18.1.2024", + "1.10.2-12.18.1.2025", + "1.10.2-12.18.1.2026", + "1.10.2-12.18.1.2027", + "1.10.2-12.18.1.2028", + "1.10.2-12.18.1.2029", + "1.10.2-12.18.1.2030", + "1.10.2-12.18.1.2031", + "1.10.2-12.18.1.2032", + "1.10.2-12.18.1.2033", + "1.10.2-12.18.1.2034", + "1.10.2-12.18.1.2035", + "1.10.2-12.18.1.2036", + "1.10.2-12.18.1.2037", + "1.10.2-12.18.1.2038", + "1.10.2-12.18.1.2039", + "1.10.2-12.18.1.2040", + "1.10.2-12.18.1.2041", + "1.10.2-12.18.1.2042", + "1.10.2-12.18.1.2043", + "1.10.2-12.18.1.2044", + "1.10.2-12.18.1.2045", + "1.10.2-12.18.1.2046", + "1.10.2-12.18.1.2047", + "1.10.2-12.18.1.2048", + "1.10.2-12.18.1.2049", + "1.10.2-12.18.1.2050", + "1.10.2-12.18.1.2052", + "1.10.2-12.18.1.2053", + "1.10.2-12.18.1.2054", + "1.10.2-12.18.1.2055", + "1.10.2-12.18.1.2056", + "1.10.2-12.18.1.2057", + "1.10.2-12.18.1.2058", + "1.10.2-12.18.1.2059", + "1.10.2-12.18.1.2060", + "1.10.2-12.18.1.2061", + "1.10.2-12.18.1.2062", + "1.10.2-12.18.1.2063", + "1.10.2-12.18.1.2064", + "1.10.2-12.18.1.2065", + "1.10.2-12.18.1.2066", + "1.10.2-12.18.1.2067", + "1.10.2-12.18.1.2068", + "1.10.2-12.18.1.2069", + "1.10.2-12.18.1.2070", + "1.10.2-12.18.1.2071", + "1.10.2-12.18.1.2072", + "1.10.2-12.18.1.2073", + "1.10.2-12.18.1.2074", + "1.10.2-12.18.1.2075", + "1.10.2-12.18.1.2076", + "1.10.2-12.18.1.2077", + "1.10.2-12.18.1.2078", + "1.10.2-12.18.1.2079", + "1.10.2-12.18.1.2080", + "1.10.2-12.18.1.2081", + "1.10.2-12.18.1.2082", + "1.10.2-12.18.1.2083", + "1.10.2-12.18.1.2084", + "1.10.2-12.18.1.2085", + "1.10.2-12.18.1.2086", + "1.10.2-12.18.1.2087", + "1.10.2-12.18.1.2088", + "1.10.2-12.18.1.2089", + "1.10.2-12.18.1.2090", + "1.10.2-12.18.1.2091", + "1.10.2-12.18.1.2092", + "1.10.2-12.18.1.2093", + "1.10.2-12.18.1.2094", + "1.10.2-12.18.1.2095", + "1.10.2-12.18.1.2096", + "1.10.2-12.18.2.2097", + "1.10.2-12.18.2.2098", + "1.10.2-12.18.2.2099", + "1.10.2-12.18.2.2100", + "1.10.2-12.18.2.2101", + "1.10.2-12.18.2.2102", + "1.10.2-12.18.2.2103", + "1.10.2-12.18.2.2104", + "1.10.2-12.18.2.2105", + "1.10.2-12.18.2.2106", + "1.10.2-12.18.2.2107", + "1.10.2-12.18.2.2108", + "1.10.2-12.18.2.2109", + "1.10.2-12.18.2.2110", + "1.10.2-12.18.2.2111", + "1.10.2-12.18.2.2112", + "1.10.2-12.18.2.2113", + "1.10.2-12.18.2.2114", + "1.10.2-12.18.2.2115", + "1.10.2-12.18.2.2116", + "1.10.2-12.18.2.2117", + "1.10.2-12.18.2.2118", + "1.10.2-12.18.2.2119", + "1.10.2-12.18.2.2120", + "1.10.2-12.18.2.2121", + "1.10.2-12.18.2.2122", + "1.10.2-12.18.2.2123", + "1.10.2-12.18.2.2124", + "1.10.2-12.18.2.2125", + "1.10.2-12.18.2.2132", + "1.10.2-12.18.2.2134", + "1.10.2-12.18.2.2139", + "1.10.2-12.18.2.2140", + "1.10.2-12.18.2.2147", + "1.10.2-12.18.2.2151", + "1.10.2-12.18.2.2166", + "1.10.2-12.18.2.2170", + "1.10.2-12.18.2.2171", + "1.10.2-12.18.2.2179", + "1.10.2-12.18.2.2182", + "1.10.2-12.18.2.2183", + "1.10.2-12.18.3.2185", + "1.10.2-12.18.3.2202", + "1.10.2-12.18.3.2209", + "1.10.2-12.18.3.2215", + "1.10.2-12.18.3.2217", + "1.10.2-12.18.3.2219", + "1.10.2-12.18.3.2221", + "1.10.2-12.18.3.2234", + "1.10.2-12.18.3.2239", + "1.10.2-12.18.3.2254", + "1.10.2-12.18.3.2272", + "1.10.2-12.18.3.2281", + "1.10.2-12.18.3.2297", + "1.10.2-12.18.3.2316", + "1.10.2-12.18.3.2422", + "1.10.2-12.18.3.2477", + "1.10.2-12.18.3.2488", + "1.10.2-12.18.3.2511" + ], + "1.11": [ + "1.11-13.19.0.2126-1.11.x", + "1.11-13.19.0.2127-1.11.x", + "1.11-13.19.0.2128-1.11.x", + "1.11-13.19.0.2129-1.11.x", + "1.11-13.19.0.2130", + "1.11-13.19.0.2131", + "1.11-13.19.0.2133", + "1.11-13.19.0.2135", + "1.11-13.19.0.2136", + "1.11-13.19.0.2137", + "1.11-13.19.0.2138", + "1.11-13.19.0.2141", + "1.11-13.19.0.2142", + "1.11-13.19.0.2143", + "1.11-13.19.0.2144", + "1.11-13.19.0.2145", + "1.11-13.19.0.2146", + "1.11-13.19.0.2148", + "1.11-13.19.0.2149", + "1.11-13.19.0.2150", + "1.11-13.19.0.2152", + "1.11-13.19.0.2153", + "1.11-13.19.0.2154", + "1.11-13.19.0.2155", + "1.11-13.19.0.2156", + "1.11-13.19.0.2157", + "1.11-13.19.0.2159", + "1.11-13.19.0.2160", + "1.11-13.19.0.2161", + "1.11-13.19.0.2162", + "1.11-13.19.0.2163", + "1.11-13.19.0.2164", + "1.11-13.19.0.2165", + "1.11-13.19.0.2167", + "1.11-13.19.0.2168", + "1.11-13.19.0.2169", + "1.11-13.19.0.2172", + "1.11-13.19.0.2173", + "1.11-13.19.0.2174", + "1.11-13.19.0.2175", + "1.11-13.19.0.2176", + "1.11-13.19.0.2177", + "1.11-13.19.0.2178", + "1.11-13.19.0.2180", + "1.11-13.19.0.2181", + "1.11-13.19.0.2184", + "1.11-13.19.0.2186", + "1.11-13.19.0.2187", + "1.11-13.19.1.2188", + "1.11-13.19.1.2189", + "1.11-13.19.1.2190", + "1.11-13.19.1.2191", + "1.11-13.19.1.2192", + "1.11-13.19.1.2193", + "1.11-13.19.1.2194", + "1.11-13.19.1.2195", + "1.11-13.19.1.2196", + "1.11-13.19.1.2197", + "1.11-13.19.1.2198", + "1.11-13.19.1.2199" + ], + "1.11.2": [ + "1.11.2-13.20.0.2200", + "1.11.2-13.20.0.2201", + "1.11.2-13.20.0.2203", + "1.11.2-13.20.0.2204", + "1.11.2-13.20.0.2205", + "1.11.2-13.20.0.2206", + "1.11.2-13.20.0.2207", + "1.11.2-13.20.0.2208", + "1.11.2-13.20.0.2210", + "1.11.2-13.20.0.2211", + "1.11.2-13.20.0.2212", + "1.11.2-13.20.0.2213", + "1.11.2-13.20.0.2214", + "1.11.2-13.20.0.2216", + "1.11.2-13.20.0.2218", + "1.11.2-13.20.0.2220", + "1.11.2-13.20.0.2222", + "1.11.2-13.20.0.2223", + "1.11.2-13.20.0.2224", + "1.11.2-13.20.0.2225", + "1.11.2-13.20.0.2226", + "1.11.2-13.20.0.2227", + "1.11.2-13.20.0.2228", + "1.11.2-13.20.0.2229", + "1.11.2-13.20.0.2230", + "1.11.2-13.20.0.2231", + "1.11.2-13.20.0.2232", + "1.11.2-13.20.0.2233", + "1.11.2-13.20.0.2235", + "1.11.2-13.20.0.2236", + "1.11.2-13.20.0.2237", + "1.11.2-13.20.0.2238", + "1.11.2-13.20.0.2240", + "1.11.2-13.20.0.2241", + "1.11.2-13.20.0.2242", + "1.11.2-13.20.0.2243", + "1.11.2-13.20.0.2244", + "1.11.2-13.20.0.2245-3630", + "1.11.2-13.20.0.2246", + "1.11.2-13.20.0.2247", + "1.11.2-13.20.0.2248", + "1.11.2-13.20.0.2249", + "1.11.2-13.20.0.2250", + "1.11.2-13.20.0.2251", + "1.11.2-13.20.0.2252", + "1.11.2-13.20.0.2253", + "1.11.2-13.20.0.2255", + "1.11.2-13.20.0.2256", + "1.11.2-13.20.0.2257", + "1.11.2-13.20.0.2258", + "1.11.2-13.20.0.2259", + "1.11.2-13.20.0.2260", + "1.11.2-13.20.0.2261", + "1.11.2-13.20.0.2262", + "1.11.2-13.20.0.2263", + "1.11.2-13.20.0.2264", + "1.11.2-13.20.0.2265", + "1.11.2-13.20.0.2266", + "1.11.2-13.20.0.2267", + "1.11.2-13.20.0.2268", + "1.11.2-13.20.0.2269", + "1.11.2-13.20.0.2270", + "1.11.2-13.20.0.2271", + "1.11.2-13.20.0.2273", + "1.11.2-13.20.0.2274", + "1.11.2-13.20.0.2276", + "1.11.2-13.20.0.2277", + "1.11.2-13.20.0.2278", + "1.11.2-13.20.0.2279", + "1.11.2-13.20.0.2280", + "1.11.2-13.20.0.2282", + "1.11.2-13.20.0.2283", + "1.11.2-13.20.0.2284", + "1.11.2-13.20.0.2285", + "1.11.2-13.20.0.2286", + "1.11.2-13.20.0.2287", + "1.11.2-13.20.0.2288", + "1.11.2-13.20.0.2289", + "1.11.2-13.20.0.2290", + "1.11.2-13.20.0.2291", + "1.11.2-13.20.0.2292", + "1.11.2-13.20.0.2293", + "1.11.2-13.20.0.2294", + "1.11.2-13.20.0.2295", + "1.11.2-13.20.0.2296", + "1.11.2-13.20.0.2298", + "1.11.2-13.20.0.2299", + "1.11.2-13.20.0.2300", + "1.11.2-13.20.0.2301", + "1.11.2-13.20.0.2302", + "1.11.2-13.20.0.2303", + "1.11.2-13.20.0.2304", + "1.11.2-13.20.0.2305", + "1.11.2-13.20.0.2306", + "1.11.2-13.20.0.2307", + "1.11.2-13.20.0.2308", + "1.11.2-13.20.0.2309", + "1.11.2-13.20.0.2310", + "1.11.2-13.20.0.2311", + "1.11.2-13.20.0.2312", + "1.11.2-13.20.0.2313", + "1.11.2-13.20.0.2314", + "1.11.2-13.20.0.2315", + "1.11.2-13.20.0.2345", + "1.11.2-13.20.0.2356", + "1.11.2-13.20.0.2366", + "1.11.2-13.20.1.2386", + "1.11.2-13.20.1.2388", + "1.11.2-13.20.1.2391", + "1.11.2-13.20.1.2393", + "1.11.2-13.20.1.2414", + "1.11.2-13.20.1.2421", + "1.11.2-13.20.1.2425", + "1.11.2-13.20.1.2429", + "1.11.2-13.20.1.2454", + "1.11.2-13.20.1.2476", + "1.11.2-13.20.1.2504", + "1.11.2-13.20.1.2505", + "1.11.2-13.20.1.2506", + "1.11.2-13.20.1.2507", + "1.11.2-13.20.1.2510", + "1.11.2-13.20.1.2513", + "1.11.2-13.20.1.2516", + "1.11.2-13.20.1.2530", + "1.11.2-13.20.1.2563", + "1.11.2-13.20.1.2579", + "1.11.2-13.20.1.2588" + ], + "1.12": [ + "1.12-14.21.0.2320", + "1.12-14.21.0.2321", + "1.12-14.21.0.2322", + "1.12-14.21.0.2323", + "1.12-14.21.0.2324", + "1.12-14.21.0.2325", + "1.12-14.21.0.2326", + "1.12-14.21.0.2327", + "1.12-14.21.0.2328", + "1.12-14.21.0.2329", + "1.12-14.21.0.2330", + "1.12-14.21.0.2331", + "1.12-14.21.0.2332", + "1.12-14.21.0.2333", + "1.12-14.21.0.2334", + "1.12-14.21.0.2335", + "1.12-14.21.0.2336", + "1.12-14.21.0.2337", + "1.12-14.21.0.2338", + "1.12-14.21.0.2339", + "1.12-14.21.0.2340", + "1.12-14.21.0.2341", + "1.12-14.21.0.2342", + "1.12-14.21.0.2343", + "1.12-14.21.0.2344", + "1.12-14.21.0.2346", + "1.12-14.21.0.2347", + "1.12-14.21.0.2348", + "1.12-14.21.0.2349", + "1.12-14.21.0.2350", + "1.12-14.21.0.2351", + "1.12-14.21.0.2352", + "1.12-14.21.0.2353", + "1.12-14.21.0.2354", + "1.12-14.21.0.2355", + "1.12-14.21.0.2357", + "1.12-14.21.0.2358", + "1.12-14.21.0.2359", + "1.12-14.21.0.2360", + "1.12-14.21.0.2361", + "1.12-14.21.0.2362", + "1.12-14.21.0.2363", + "1.12-14.21.0.2364", + "1.12-14.21.0.2365", + "1.12-14.21.0.2367", + "1.12-14.21.0.2368", + "1.12-14.21.0.2369", + "1.12-14.21.0.2370", + "1.12-14.21.0.2371", + "1.12-14.21.0.2372", + "1.12-14.21.0.2373", + "1.12-14.21.0.2374", + "1.12-14.21.0.2375", + "1.12-14.21.0.2376", + "1.12-14.21.0.2377", + "1.12-14.21.0.2378", + "1.12-14.21.0.2379", + "1.12-14.21.0.2380", + "1.12-14.21.0.2381", + "1.12-14.21.0.2382", + "1.12-14.21.0.2383", + "1.12-14.21.0.2384", + "1.12-14.21.0.2385", + "1.12-14.21.1.2387", + "1.12-14.21.1.2389", + "1.12-14.21.1.2390", + "1.12-14.21.1.2392", + "1.12-14.21.1.2394", + "1.12-14.21.1.2395", + "1.12-14.21.1.2396", + "1.12-14.21.1.2397", + "1.12-14.21.1.2398", + "1.12-14.21.1.2399", + "1.12-14.21.1.2400", + "1.12-14.21.1.2401", + "1.12-14.21.1.2402", + "1.12-14.21.1.2403", + "1.12-14.21.1.2404", + "1.12-14.21.1.2405", + "1.12-14.21.1.2406", + "1.12-14.21.1.2407", + "1.12-14.21.1.2408", + "1.12-14.21.1.2409", + "1.12-14.21.1.2410", + "1.12-14.21.1.2411", + "1.12-14.21.1.2412", + "1.12-14.21.1.2413", + "1.12-14.21.1.2415", + "1.12-14.21.1.2416", + "1.12-14.21.1.2417", + "1.12-14.21.1.2418", + "1.12-14.21.1.2419", + "1.12-14.21.1.2420", + "1.12-14.21.1.2423", + "1.12-14.21.1.2424", + "1.12-14.21.1.2426", + "1.12-14.21.1.2427", + "1.12-14.21.1.2428", + "1.12-14.21.1.2430", + "1.12-14.21.1.2431", + "1.12-14.21.1.2432", + "1.12-14.21.1.2433", + "1.12-14.21.1.2434", + "1.12-14.21.1.2435", + "1.12-14.21.1.2436", + "1.12-14.21.1.2437", + "1.12-14.21.1.2438", + "1.12-14.21.1.2439", + "1.12-14.21.1.2440", + "1.12-14.21.1.2441", + "1.12-14.21.1.2442", + "1.12-14.21.1.2443" + ], + "1.12.1": [ + "1.12.1-14.22.0.2444", + "1.12.1-14.22.0.2445", + "1.12.1-14.22.0.2446", + "1.12.1-14.22.0.2447", + "1.12.1-14.22.0.2448", + "1.12.1-14.22.0.2449", + "1.12.1-14.22.0.2450", + "1.12.1-14.22.0.2451", + "1.12.1-14.22.0.2452", + "1.12.1-14.22.0.2453", + "1.12.1-14.22.0.2455", + "1.12.1-14.22.0.2456", + "1.12.1-14.22.0.2457", + "1.12.1-14.22.0.2458", + "1.12.1-14.22.0.2459", + "1.12.1-14.22.0.2460", + "1.12.1-14.22.0.2461", + "1.12.1-14.22.0.2462", + "1.12.1-14.22.0.2463", + "1.12.1-14.22.0.2464", + "1.12.1-14.22.0.2465", + "1.12.1-14.22.0.2466", + "1.12.1-14.22.0.2467", + "1.12.1-14.22.0.2468", + "1.12.1-14.22.0.2469", + "1.12.1-14.22.0.2470", + "1.12.1-14.22.0.2471", + "1.12.1-14.22.0.2472", + "1.12.1-14.22.0.2473", + "1.12.1-14.22.0.2474", + "1.12.1-14.22.0.2475", + "1.12.1-14.22.1.2478", + "1.12.1-14.22.1.2479", + "1.12.1-14.22.1.2480", + "1.12.1-14.22.1.2481", + "1.12.1-14.22.1.2482", + "1.12.1-14.22.1.2483", + "1.12.1-14.22.1.2484", + "1.12.1-14.22.1.2485" + ], + "1.12.2": [ + "1.12.2-14.23.0.2486", + "1.12.2-14.23.0.2487", + "1.12.2-14.23.0.2489", + "1.12.2-14.23.0.2490", + "1.12.2-14.23.0.2491", + "1.12.2-14.23.0.2492", + "1.12.2-14.23.0.2493", + "1.12.2-14.23.0.2494", + "1.12.2-14.23.0.2495", + "1.12.2-14.23.0.2496", + "1.12.2-14.23.0.2497", + "1.12.2-14.23.0.2498", + "1.12.2-14.23.0.2499", + "1.12.2-14.23.0.2500", + "1.12.2-14.23.0.2501", + "1.12.2-14.23.0.2502", + "1.12.2-14.23.0.2503", + "1.12.2-14.23.0.2508", + "1.12.2-14.23.0.2509", + "1.12.2-14.23.0.2512", + "1.12.2-14.23.0.2514", + "1.12.2-14.23.0.2515", + "1.12.2-14.23.0.2517", + "1.12.2-14.23.0.2518", + "1.12.2-14.23.0.2519", + "1.12.2-14.23.0.2520", + "1.12.2-14.23.0.2521", + "1.12.2-14.23.0.2522", + "1.12.2-14.23.0.2523", + "1.12.2-14.23.0.2524", + "1.12.2-14.23.0.2525", + "1.12.2-14.23.0.2526", + "1.12.2-14.23.0.2527", + "1.12.2-14.23.0.2528", + "1.12.2-14.23.0.2529", + "1.12.2-14.23.0.2531", + "1.12.2-14.23.0.2532", + "1.12.2-14.23.0.2533", + "1.12.2-14.23.0.2534", + "1.12.2-14.23.0.2535", + "1.12.2-14.23.0.2536", + "1.12.2-14.23.0.2537", + "1.12.2-14.23.0.2538", + "1.12.2-14.23.0.2539", + "1.12.2-14.23.0.2540", + "1.12.2-14.23.0.2541", + "1.12.2-14.23.0.2542", + "1.12.2-14.23.0.2543", + "1.12.2-14.23.0.2544", + "1.12.2-14.23.0.2545", + "1.12.2-14.23.0.2546", + "1.12.2-14.23.0.2547", + "1.12.2-14.23.0.2548", + "1.12.2-14.23.0.2549", + "1.12.2-14.23.0.2550", + "1.12.2-14.23.0.2551", + "1.12.2-14.23.0.2552", + "1.12.2-14.23.0.2553", + "1.12.2-14.23.1.2554", + "1.12.2-14.23.1.2555", + "1.12.2-14.23.1.2556", + "1.12.2-14.23.1.2557", + "1.12.2-14.23.1.2558", + "1.12.2-14.23.1.2559", + "1.12.2-14.23.1.2560", + "1.12.2-14.23.1.2561", + "1.12.2-14.23.1.2562", + "1.12.2-14.23.1.2564", + "1.12.2-14.23.1.2565", + "1.12.2-14.23.1.2566", + "1.12.2-14.23.1.2567", + "1.12.2-14.23.1.2568", + "1.12.2-14.23.1.2569", + "1.12.2-14.23.1.2570", + "1.12.2-14.23.1.2571", + "1.12.2-14.23.1.2572", + "1.12.2-14.23.1.2573", + "1.12.2-14.23.1.2574", + "1.12.2-14.23.1.2575", + "1.12.2-14.23.1.2576", + "1.12.2-14.23.1.2577", + "1.12.2-14.23.1.2578", + "1.12.2-14.23.1.2580", + "1.12.2-14.23.1.2581", + "1.12.2-14.23.1.2582", + "1.12.2-14.23.1.2583", + "1.12.2-14.23.1.2584", + "1.12.2-14.23.1.2585", + "1.12.2-14.23.1.2586", + "1.12.2-14.23.1.2587", + "1.12.2-14.23.1.2589", + "1.12.2-14.23.1.2590", + "1.12.2-14.23.1.2591", + "1.12.2-14.23.1.2592", + "1.12.2-14.23.1.2593", + "1.12.2-14.23.1.2594", + "1.12.2-14.23.1.2595", + "1.12.2-14.23.1.2596", + "1.12.2-14.23.1.2597", + "1.12.2-14.23.1.2598", + "1.12.2-14.23.1.2599", + "1.12.2-14.23.1.2600", + "1.12.2-14.23.1.2601", + "1.12.2-14.23.1.2602", + "1.12.2-14.23.1.2603", + "1.12.2-14.23.1.2604", + "1.12.2-14.23.1.2605", + "1.12.2-14.23.1.2606", + "1.12.2-14.23.1.2607", + "1.12.2-14.23.1.2608", + "1.12.2-14.23.1.2609", + "1.12.2-14.23.1.2610", + "1.12.2-14.23.2.2611", + "1.12.2-14.23.2.2612", + "1.12.2-14.23.2.2613", + "1.12.2-14.23.2.2614", + "1.12.2-14.23.2.2615", + "1.12.2-14.23.2.2616", + "1.12.2-14.23.2.2617", + "1.12.2-14.23.2.2618", + "1.12.2-14.23.2.2619", + "1.12.2-14.23.2.2620", + "1.12.2-14.23.2.2621", + "1.12.2-14.23.2.2622", + "1.12.2-14.23.2.2623", + "1.12.2-14.23.2.2624", + "1.12.2-14.23.2.2625", + "1.12.2-14.23.2.2626", + "1.12.2-14.23.2.2627", + "1.12.2-14.23.2.2628", + "1.12.2-14.23.2.2629", + "1.12.2-14.23.2.2630", + "1.12.2-14.23.2.2631", + "1.12.2-14.23.2.2632", + "1.12.2-14.23.2.2633", + "1.12.2-14.23.2.2634", + "1.12.2-14.23.2.2635", + "1.12.2-14.23.2.2636", + "1.12.2-14.23.2.2637", + "1.12.2-14.23.2.2638", + "1.12.2-14.23.2.2639", + "1.12.2-14.23.2.2640", + "1.12.2-14.23.2.2641", + "1.12.2-14.23.2.2642", + "1.12.2-14.23.2.2643", + "1.12.2-14.23.2.2644", + "1.12.2-14.23.2.2645", + "1.12.2-14.23.2.2646", + "1.12.2-14.23.2.2647", + "1.12.2-14.23.2.2648", + "1.12.2-14.23.2.2649", + "1.12.2-14.23.2.2650", + "1.12.2-14.23.2.2651", + "1.12.2-14.23.2.2652", + "1.12.2-14.23.2.2653", + "1.12.2-14.23.2.2654", + "1.12.2-14.23.3.2655", + "1.12.2-14.23.3.2658", + "1.12.2-14.23.3.2659", + "1.12.2-14.23.3.2660", + "1.12.2-14.23.3.2661", + "1.12.2-14.23.3.2662", + "1.12.2-14.23.3.2663", + "1.12.2-14.23.3.2664", + "1.12.2-14.23.3.2665", + "1.12.2-14.23.3.2666", + "1.12.2-14.23.3.2667", + "1.12.2-14.23.3.2668", + "1.12.2-14.23.3.2669", + "1.12.2-14.23.3.2670", + "1.12.2-14.23.3.2671", + "1.12.2-14.23.3.2672", + "1.12.2-14.23.3.2673", + "1.12.2-14.23.3.2674", + "1.12.2-14.23.3.2675", + "1.12.2-14.23.3.2676", + "1.12.2-14.23.3.2677", + "1.12.2-14.23.3.2678", + "1.12.2-14.23.3.2679", + "1.12.2-14.23.3.2680", + "1.12.2-14.23.3.2681", + "1.12.2-14.23.3.2682", + "1.12.2-14.23.3.2683", + "1.12.2-14.23.3.2684", + "1.12.2-14.23.3.2685", + "1.12.2-14.23.3.2686", + "1.12.2-14.23.3.2688", + "1.12.2-14.23.3.2689", + "1.12.2-14.23.3.2690", + "1.12.2-14.23.3.2691", + "1.12.2-14.23.3.2692", + "1.12.2-14.23.3.2693", + "1.12.2-14.23.3.2694", + "1.12.2-14.23.3.2695", + "1.12.2-14.23.3.2696", + "1.12.2-14.23.3.2697", + "1.12.2-14.23.3.2698", + "1.12.2-14.23.3.2699", + "1.12.2-14.23.3.2700", + "1.12.2-14.23.3.2701", + "1.12.2-14.23.3.2702", + "1.12.2-14.23.4.2703", + "1.12.2-14.23.4.2704", + "1.12.2-14.23.4.2705", + "1.12.2-14.23.4.2706", + "1.12.2-14.23.4.2707", + "1.12.2-14.23.4.2708", + "1.12.2-14.23.4.2709", + "1.12.2-14.23.4.2710", + "1.12.2-14.23.4.2711", + "1.12.2-14.23.4.2712", + "1.12.2-14.23.4.2713", + "1.12.2-14.23.4.2714", + "1.12.2-14.23.4.2715", + "1.12.2-14.23.4.2716", + "1.12.2-14.23.4.2717", + "1.12.2-14.23.4.2718", + "1.12.2-14.23.4.2719", + "1.12.2-14.23.4.2720-4627", + "1.12.2-14.23.4.2721", + "1.12.2-14.23.4.2722", + "1.12.2-14.23.4.2723", + "1.12.2-14.23.4.2724", + "1.12.2-14.23.4.2725", + "1.12.2-14.23.4.2726", + "1.12.2-14.23.4.2727", + "1.12.2-14.23.4.2728", + "1.12.2-14.23.4.2729", + "1.12.2-14.23.4.2730", + "1.12.2-14.23.4.2732", + "1.12.2-14.23.4.2733", + "1.12.2-14.23.4.2734", + "1.12.2-14.23.4.2735", + "1.12.2-14.23.4.2736", + "1.12.2-14.23.4.2737", + "1.12.2-14.23.4.2738", + "1.12.2-14.23.4.2739", + "1.12.2-14.23.4.2740", + "1.12.2-14.23.4.2741", + "1.12.2-14.23.4.2742", + "1.12.2-14.23.4.2743", + "1.12.2-14.23.4.2744", + "1.12.2-14.23.4.2745", + "1.12.2-14.23.4.2746", + "1.12.2-14.23.4.2747", + "1.12.2-14.23.4.2748", + "1.12.2-14.23.4.2749", + "1.12.2-14.23.4.2750", + "1.12.2-14.23.4.2751", + "1.12.2-14.23.4.2752", + "1.12.2-14.23.4.2753", + "1.12.2-14.23.4.2754", + "1.12.2-14.23.4.2755", + "1.12.2-14.23.4.2756", + "1.12.2-14.23.4.2757", + "1.12.2-14.23.4.2758", + "1.12.2-14.23.4.2759", + "1.12.2-14.23.4.2760", + "1.12.2-14.23.4.2761", + "1.12.2-14.23.4.2762", + "1.12.2-14.23.4.2763", + "1.12.2-14.23.4.2764", + "1.12.2-14.23.4.2765", + "1.12.2-14.23.4.2766", + "1.12.2-14.23.4.2767", + "1.12.2-14.23.5.2768", + "1.12.2-14.23.5.2769", + "1.12.2-14.23.5.2770", + "1.12.2-14.23.5.2771", + "1.12.2-14.23.5.2772", + "1.12.2-14.23.5.2773", + "1.12.2-14.23.5.2774", + "1.12.2-14.23.5.2775", + "1.12.2-14.23.5.2776", + "1.12.2-14.23.5.2777", + "1.12.2-14.23.5.2778", + "1.12.2-14.23.5.2779", + "1.12.2-14.23.5.2780", + "1.12.2-14.23.5.2781", + "1.12.2-14.23.5.2782", + "1.12.2-14.23.5.2783", + "1.12.2-14.23.5.2784", + "1.12.2-14.23.5.2785", + "1.12.2-14.23.5.2786", + "1.12.2-14.23.5.2787", + "1.12.2-14.23.5.2788", + "1.12.2-14.23.5.2789", + "1.12.2-14.23.5.2793", + "1.12.2-14.23.5.2794", + "1.12.2-14.23.5.2795", + "1.12.2-14.23.5.2796", + "1.12.2-14.23.5.2797", + "1.12.2-14.23.5.2798", + "1.12.2-14.23.5.2799", + "1.12.2-14.23.5.2800", + "1.12.2-14.23.5.2801", + "1.12.2-14.23.5.2802", + "1.12.2-14.23.5.2803", + "1.12.2-14.23.5.2804", + "1.12.2-14.23.5.2805", + "1.12.2-14.23.5.2806", + "1.12.2-14.23.5.2807", + "1.12.2-14.23.5.2808", + "1.12.2-14.23.5.2809", + "1.12.2-14.23.5.2810", + "1.12.2-14.23.5.2811", + "1.12.2-14.23.5.2812", + "1.12.2-14.23.5.2813", + "1.12.2-14.23.5.2814", + "1.12.2-14.23.5.2815", + "1.12.2-14.23.5.2816", + "1.12.2-14.23.5.2817", + "1.12.2-14.23.5.2818", + "1.12.2-14.23.5.2819", + "1.12.2-14.23.5.2820", + "1.12.2-14.23.5.2821", + "1.12.2-14.23.5.2822", + "1.12.2-14.23.5.2823", + "1.12.2-14.23.5.2824", + "1.12.2-14.23.5.2825", + "1.12.2-14.23.5.2826", + "1.12.2-14.23.5.2827", + "1.12.2-14.23.5.2828", + "1.12.2-14.23.5.2829", + "1.12.2-14.23.5.2830", + "1.12.2-14.23.5.2831", + "1.12.2-14.23.5.2832", + "1.12.2-14.23.5.2833", + "1.12.2-14.23.5.2834", + "1.12.2-14.23.5.2835", + "1.12.2-14.23.5.2836", + "1.12.2-14.23.5.2837", + "1.12.2-14.23.5.2838", + "1.12.2-14.23.5.2839", + "1.12.2-14.23.5.2840", + "1.12.2-14.23.5.2841", + "1.12.2-14.23.5.2842", + "1.12.2-14.23.5.2843", + "1.12.2-14.23.5.2844", + "1.12.2-14.23.5.2845", + "1.12.2-14.23.5.2846", + "1.12.2-14.23.5.2847", + "1.12.2-14.23.5.2851", + "1.12.2-14.23.5.2852", + "1.12.2-14.23.5.2854", + "1.12.2-14.23.5.2855", + "1.12.2-14.23.5.2856", + "1.12.2-14.23.5.2857", + "1.12.2-14.23.5.2858", + "1.12.2-14.23.5.2859", + "1.12.2-14.23.5.2860" + ], + "1.13.2": [ + "1.13.2-25.0.9", + "1.13.2-25.0.10", + "1.13.2-25.0.11", + "1.13.2-25.0.12", + "1.13.2-25.0.13", + "1.13.2-25.0.14", + "1.13.2-25.0.17", + "1.13.2-25.0.20", + "1.13.2-25.0.21", + "1.13.2-25.0.22", + "1.13.2-25.0.23", + "1.13.2-25.0.26", + "1.13.2-25.0.27", + "1.13.2-25.0.28", + "1.13.2-25.0.29", + "1.13.2-25.0.30", + "1.13.2-25.0.31", + "1.13.2-25.0.32", + "1.13.2-25.0.33", + "1.13.2-25.0.34", + "1.13.2-25.0.35", + "1.13.2-25.0.36", + "1.13.2-25.0.37", + "1.13.2-25.0.40", + "1.13.2-25.0.41", + "1.13.2-25.0.42", + "1.13.2-25.0.43", + "1.13.2-25.0.44", + "1.13.2-25.0.45", + "1.13.2-25.0.47", + "1.13.2-25.0.48", + "1.13.2-25.0.49", + "1.13.2-25.0.50", + "1.13.2-25.0.51", + "1.13.2-25.0.52", + "1.13.2-25.0.53", + "1.13.2-25.0.54", + "1.13.2-25.0.55", + "1.13.2-25.0.56", + "1.13.2-25.0.57", + "1.13.2-25.0.58", + "1.13.2-25.0.59", + "1.13.2-25.0.60", + "1.13.2-25.0.61", + "1.13.2-25.0.63", + "1.13.2-25.0.64", + "1.13.2-25.0.66", + "1.13.2-25.0.68", + "1.13.2-25.0.69", + "1.13.2-25.0.70", + "1.13.2-25.0.71", + "1.13.2-25.0.73", + "1.13.2-25.0.74", + "1.13.2-25.0.76", + "1.13.2-25.0.77", + "1.13.2-25.0.78", + "1.13.2-25.0.79", + "1.13.2-25.0.80", + "1.13.2-25.0.81", + "1.13.2-25.0.82", + "1.13.2-25.0.84", + "1.13.2-25.0.85", + "1.13.2-25.0.87", + "1.13.2-25.0.88", + "1.13.2-25.0.89", + "1.13.2-25.0.90", + "1.13.2-25.0.91", + "1.13.2-25.0.92", + "1.13.2-25.0.93", + "1.13.2-25.0.94", + "1.13.2-25.0.95", + "1.13.2-25.0.96", + "1.13.2-25.0.99", + "1.13.2-25.0.100", + "1.13.2-25.0.102", + "1.13.2-25.0.103", + "1.13.2-25.0.107", + "1.13.2-25.0.108", + "1.13.2-25.0.109", + "1.13.2-25.0.110", + "1.13.2-25.0.114", + "1.13.2-25.0.121", + "1.13.2-25.0.128", + "1.13.2-25.0.134", + "1.13.2-25.0.135", + "1.13.2-25.0.141", + "1.13.2-25.0.142", + "1.13.2-25.0.144", + "1.13.2-25.0.145", + "1.13.2-25.0.146", + "1.13.2-25.0.147", + "1.13.2-25.0.149", + "1.13.2-25.0.154", + "1.13.2-25.0.160", + "1.13.2-25.0.168", + "1.13.2-25.0.174", + "1.13.2-25.0.175", + "1.13.2-25.0.182", + "1.13.2-25.0.183", + "1.13.2-25.0.187", + "1.13.2-25.0.189", + "1.13.2-25.0.190", + "1.13.2-25.0.191", + "1.13.2-25.0.192", + "1.13.2-25.0.193", + "1.13.2-25.0.194", + "1.13.2-25.0.198", + "1.13.2-25.0.205", + "1.13.2-25.0.206", + "1.13.2-25.0.207", + "1.13.2-25.0.208", + "1.13.2-25.0.209", + "1.13.2-25.0.210", + "1.13.2-25.0.214", + "1.13.2-25.0.215", + "1.13.2-25.0.216", + "1.13.2-25.0.218", + "1.13.2-25.0.219", + "1.13.2-25.0.222", + "1.13.2-25.0.223" + ], + "1.14.2": [ + "1.14.2-26.0.0", + "1.14.2-26.0.2", + "1.14.2-26.0.3", + "1.14.2-26.0.4", + "1.14.2-26.0.5", + "1.14.2-26.0.6", + "1.14.2-26.0.7", + "1.14.2-26.0.8", + "1.14.2-26.0.10", + "1.14.2-26.0.12", + "1.14.2-26.0.13", + "1.14.2-26.0.14", + "1.14.2-26.0.15", + "1.14.2-26.0.16", + "1.14.2-26.0.17", + "1.14.2-26.0.18", + "1.14.2-26.0.19", + "1.14.2-26.0.21", + "1.14.2-26.0.22", + "1.14.2-26.0.23", + "1.14.2-26.0.25", + "1.14.2-26.0.28", + "1.14.2-26.0.29", + "1.14.2-26.0.30", + "1.14.2-26.0.32", + "1.14.2-26.0.33", + "1.14.2-26.0.35", + "1.14.2-26.0.37", + "1.14.2-26.0.39", + "1.14.2-26.0.40", + "1.14.2-26.0.41", + "1.14.2-26.0.42", + "1.14.2-26.0.43", + "1.14.2-26.0.47", + "1.14.2-26.0.48", + "1.14.2-26.0.49", + "1.14.2-26.0.50", + "1.14.2-26.0.51", + "1.14.2-26.0.52", + "1.14.2-26.0.54", + "1.14.2-26.0.55", + "1.14.2-26.0.56", + "1.14.2-26.0.57", + "1.14.2-26.0.60", + "1.14.2-26.0.61", + "1.14.2-26.0.62", + "1.14.2-26.0.63" + ], + "1.14.3": [ + "1.14.3-27.0.0", + "1.14.3-27.0.1", + "1.14.3-27.0.2", + "1.14.3-27.0.3", + "1.14.3-27.0.4", + "1.14.3-27.0.5", + "1.14.3-27.0.7", + "1.14.3-27.0.8", + "1.14.3-27.0.9", + "1.14.3-27.0.10", + "1.14.3-27.0.11", + "1.14.3-27.0.12", + "1.14.3-27.0.13", + "1.14.3-27.0.14", + "1.14.3-27.0.15", + "1.14.3-27.0.16", + "1.14.3-27.0.17", + "1.14.3-27.0.18", + "1.14.3-27.0.19", + "1.14.3-27.0.20", + "1.14.3-27.0.21", + "1.14.3-27.0.22", + "1.14.3-27.0.23", + "1.14.3-27.0.24", + "1.14.3-27.0.25", + "1.14.3-27.0.26", + "1.14.3-27.0.29", + "1.14.3-27.0.30", + "1.14.3-27.0.31", + "1.14.3-27.0.38", + "1.14.3-27.0.40", + "1.14.3-27.0.42", + "1.14.3-27.0.43", + "1.14.3-27.0.47", + "1.14.3-27.0.49", + "1.14.3-27.0.50", + "1.14.3-27.0.51", + "1.14.3-27.0.52", + "1.14.3-27.0.53", + "1.14.3-27.0.54", + "1.14.3-27.0.55", + "1.14.3-27.0.56", + "1.14.3-27.0.57", + "1.14.3-27.0.58", + "1.14.3-27.0.59", + "1.14.3-27.0.60" + ], + "1.14.4": [ + "1.14.4-28.0.1", + "1.14.4-28.0.2", + "1.14.4-28.0.3", + "1.14.4-28.0.4", + "1.14.4-28.0.5", + "1.14.4-28.0.9", + "1.14.4-28.0.11", + "1.14.4-28.0.12", + "1.14.4-28.0.13", + "1.14.4-28.0.14", + "1.14.4-28.0.16", + "1.14.4-28.0.17", + "1.14.4-28.0.18", + "1.14.4-28.0.19", + "1.14.4-28.0.20", + "1.14.4-28.0.21", + "1.14.4-28.0.22", + "1.14.4-28.0.23", + "1.14.4-28.0.24", + "1.14.4-28.0.25", + "1.14.4-28.0.26", + "1.14.4-28.0.27", + "1.14.4-28.0.28", + "1.14.4-28.0.29", + "1.14.4-28.0.30", + "1.14.4-28.0.32", + "1.14.4-28.0.34", + "1.14.4-28.0.35", + "1.14.4-28.0.37", + "1.14.4-28.0.38", + "1.14.4-28.0.39", + "1.14.4-28.0.40", + "1.14.4-28.0.41", + "1.14.4-28.0.45", + "1.14.4-28.0.46", + "1.14.4-28.0.47", + "1.14.4-28.0.48", + "1.14.4-28.0.49", + "1.14.4-28.0.51", + "1.14.4-28.0.55", + "1.14.4-28.0.56", + "1.14.4-28.0.58", + "1.14.4-28.0.62", + "1.14.4-28.0.63", + "1.14.4-28.0.65", + "1.14.4-28.0.67", + "1.14.4-28.0.68", + "1.14.4-28.0.69", + "1.14.4-28.0.70", + "1.14.4-28.0.73", + "1.14.4-28.0.74", + "1.14.4-28.0.75", + "1.14.4-28.0.76", + "1.14.4-28.0.81", + "1.14.4-28.0.82", + "1.14.4-28.0.83", + "1.14.4-28.0.84", + "1.14.4-28.0.85", + "1.14.4-28.0.86", + "1.14.4-28.0.87", + "1.14.4-28.0.88", + "1.14.4-28.0.90", + "1.14.4-28.0.91", + "1.14.4-28.0.92", + "1.14.4-28.0.93", + "1.14.4-28.0.94", + "1.14.4-28.0.95", + "1.14.4-28.0.100", + "1.14.4-28.0.101", + "1.14.4-28.0.102", + "1.14.4-28.0.104", + "1.14.4-28.0.105", + "1.14.4-28.0.106", + "1.14.4-28.0.107", + "1.14.4-28.1.0", + "1.14.4-28.1.1", + "1.14.4-28.1.2", + "1.14.4-28.1.3", + "1.14.4-28.1.4", + "1.14.4-28.1.5", + "1.14.4-28.1.6", + "1.14.4-28.1.7", + "1.14.4-28.1.8", + "1.14.4-28.1.10", + "1.14.4-28.1.11", + "1.14.4-28.1.14", + "1.14.4-28.1.15", + "1.14.4-28.1.16", + "1.14.4-28.1.17", + "1.14.4-28.1.18", + "1.14.4-28.1.19", + "1.14.4-28.1.20", + "1.14.4-28.1.22", + "1.14.4-28.1.23", + "1.14.4-28.1.24", + "1.14.4-28.1.25", + "1.14.4-28.1.26", + "1.14.4-28.1.28", + "1.14.4-28.1.30", + "1.14.4-28.1.31", + "1.14.4-28.1.32", + "1.14.4-28.1.33", + "1.14.4-28.1.34", + "1.14.4-28.1.35", + "1.14.4-28.1.36", + "1.14.4-28.1.37", + "1.14.4-28.1.38", + "1.14.4-28.1.39", + "1.14.4-28.1.40", + "1.14.4-28.1.41", + "1.14.4-28.1.42", + "1.14.4-28.1.44", + "1.14.4-28.1.45", + "1.14.4-28.1.46", + "1.14.4-28.1.47", + "1.14.4-28.1.48", + "1.14.4-28.1.49", + "1.14.4-28.1.50", + "1.14.4-28.1.56", + "1.14.4-28.1.58", + "1.14.4-28.1.59", + "1.14.4-28.1.60", + "1.14.4-28.1.61", + "1.14.4-28.1.62", + "1.14.4-28.1.64", + "1.14.4-28.1.65", + "1.14.4-28.1.66", + "1.14.4-28.1.67", + "1.14.4-28.1.68", + "1.14.4-28.1.69", + "1.14.4-28.1.70", + "1.14.4-28.1.71", + "1.14.4-28.1.72", + "1.14.4-28.1.73", + "1.14.4-28.1.74", + "1.14.4-28.1.75", + "1.14.4-28.1.76", + "1.14.4-28.1.77", + "1.14.4-28.1.78", + "1.14.4-28.1.79", + "1.14.4-28.1.80", + "1.14.4-28.1.81", + "1.14.4-28.1.85", + "1.14.4-28.1.86", + "1.14.4-28.1.87", + "1.14.4-28.1.88", + "1.14.4-28.1.90", + "1.14.4-28.1.91", + "1.14.4-28.1.92", + "1.14.4-28.1.93", + "1.14.4-28.1.94", + "1.14.4-28.1.95", + "1.14.4-28.1.96", + "1.14.4-28.1.97", + "1.14.4-28.1.98", + "1.14.4-28.1.99", + "1.14.4-28.1.102", + "1.14.4-28.1.103", + "1.14.4-28.1.104", + "1.14.4-28.1.105", + "1.14.4-28.1.106", + "1.14.4-28.1.107", + "1.14.4-28.1.108", + "1.14.4-28.1.109", + "1.14.4-28.1.110", + "1.14.4-28.1.111", + "1.14.4-28.1.113", + "1.14.4-28.1.114", + "1.14.4-28.1.115", + "1.14.4-28.1.116", + "1.14.4-28.1.117", + "1.14.4-28.1.118", + "1.14.4-28.2.0", + "1.14.4-28.2.1", + "1.14.4-28.2.2", + "1.14.4-28.2.3", + "1.14.4-28.2.4", + "1.14.4-28.2.5", + "1.14.4-28.2.6", + "1.14.4-28.2.10", + "1.14.4-28.2.11", + "1.14.4-28.2.12", + "1.14.4-28.2.13", + "1.14.4-28.2.14", + "1.14.4-28.2.15", + "1.14.4-28.2.16", + "1.14.4-28.2.17", + "1.14.4-28.2.18", + "1.14.4-28.2.19", + "1.14.4-28.2.20", + "1.14.4-28.2.21", + "1.14.4-28.2.23", + "1.14.4-28.2.25", + "1.14.4-28.2.26" + ], + "1.15": [ + "1.15-29.0.0", + "1.15-29.0.1", + "1.15-29.0.2", + "1.15-29.0.3", + "1.15-29.0.4" + ], + "1.15.1": [ + "1.15.1-30.0.0", + "1.15.1-30.0.2", + "1.15.1-30.0.4", + "1.15.1-30.0.5", + "1.15.1-30.0.7", + "1.15.1-30.0.8", + "1.15.1-30.0.10", + "1.15.1-30.0.11", + "1.15.1-30.0.12", + "1.15.1-30.0.13", + "1.15.1-30.0.14", + "1.15.1-30.0.15", + "1.15.1-30.0.16", + "1.15.1-30.0.17", + "1.15.1-30.0.18", + "1.15.1-30.0.19", + "1.15.1-30.0.20", + "1.15.1-30.0.21", + "1.15.1-30.0.22", + "1.15.1-30.0.24", + "1.15.1-30.0.25", + "1.15.1-30.0.26", + "1.15.1-30.0.27", + "1.15.1-30.0.28", + "1.15.1-30.0.29", + "1.15.1-30.0.30", + "1.15.1-30.0.31", + "1.15.1-30.0.32", + "1.15.1-30.0.33", + "1.15.1-30.0.35", + "1.15.1-30.0.36", + "1.15.1-30.0.38", + "1.15.1-30.0.39", + "1.15.1-30.0.40", + "1.15.1-30.0.41", + "1.15.1-30.0.42", + "1.15.1-30.0.43", + "1.15.1-30.0.48", + "1.15.1-30.0.49", + "1.15.1-30.0.50", + "1.15.1-30.0.51" + ], + "1.15.2": [ + "1.15.2-31.0.0", + "1.15.2-31.0.1", + "1.15.2-31.0.2", + "1.15.2-31.0.4", + "1.15.2-31.0.7", + "1.15.2-31.0.8", + "1.15.2-31.0.9", + "1.15.2-31.0.11", + "1.15.2-31.0.12", + "1.15.2-31.0.13", + "1.15.2-31.0.14", + "1.15.2-31.0.15", + "1.15.2-31.0.16", + "1.15.2-31.0.17", + "1.15.2-31.0.19", + "1.15.2-31.1.0", + "1.15.2-31.1.1", + "1.15.2-31.1.2", + "1.15.2-31.1.3", + "1.15.2-31.1.5", + "1.15.2-31.1.8", + "1.15.2-31.1.9", + "1.15.2-31.1.10", + "1.15.2-31.1.11", + "1.15.2-31.1.12", + "1.15.2-31.1.13", + "1.15.2-31.1.14", + "1.15.2-31.1.15", + "1.15.2-31.1.16", + "1.15.2-31.1.17", + "1.15.2-31.1.18", + "1.15.2-31.1.19", + "1.15.2-31.1.20", + "1.15.2-31.1.21", + "1.15.2-31.1.22", + "1.15.2-31.1.23", + "1.15.2-31.1.24", + "1.15.2-31.1.25", + "1.15.2-31.1.26", + "1.15.2-31.1.27", + "1.15.2-31.1.28", + "1.15.2-31.1.29", + "1.15.2-31.1.30", + "1.15.2-31.1.32", + "1.15.2-31.1.34", + "1.15.2-31.1.35", + "1.15.2-31.1.36", + "1.15.2-31.1.37", + "1.15.2-31.1.39", + "1.15.2-31.1.40", + "1.15.2-31.1.41", + "1.15.2-31.1.42", + "1.15.2-31.1.43", + "1.15.2-31.1.44", + "1.15.2-31.1.45", + "1.15.2-31.1.46", + "1.15.2-31.1.47", + "1.15.2-31.1.48", + "1.15.2-31.1.49", + "1.15.2-31.1.50", + "1.15.2-31.1.51", + "1.15.2-31.1.55", + "1.15.2-31.1.57", + "1.15.2-31.1.59", + "1.15.2-31.1.60", + "1.15.2-31.1.61", + "1.15.2-31.1.62", + "1.15.2-31.1.63", + "1.15.2-31.1.64", + "1.15.2-31.1.65", + "1.15.2-31.1.66", + "1.15.2-31.1.67", + "1.15.2-31.1.70", + "1.15.2-31.1.71", + "1.15.2-31.1.72", + "1.15.2-31.1.73", + "1.15.2-31.1.74", + "1.15.2-31.1.75", + "1.15.2-31.1.76", + "1.15.2-31.1.77", + "1.15.2-31.1.78", + "1.15.2-31.1.79", + "1.15.2-31.1.80", + "1.15.2-31.1.81", + "1.15.2-31.1.84", + "1.15.2-31.1.85", + "1.15.2-31.1.86", + "1.15.2-31.1.87", + "1.15.2-31.1.88", + "1.15.2-31.1.89", + "1.15.2-31.1.91", + "1.15.2-31.1.92", + "1.15.2-31.1.93", + "1.15.2-31.1.95", + "1.15.2-31.1.97", + "1.15.2-31.1.98", + "1.15.2-31.1.99", + "1.15.2-31.2.0", + "1.15.2-31.2.1", + "1.15.2-31.2.2", + "1.15.2-31.2.3", + "1.15.2-31.2.4", + "1.15.2-31.2.5", + "1.15.2-31.2.7", + "1.15.2-31.2.8", + "1.15.2-31.2.9", + "1.15.2-31.2.10", + "1.15.2-31.2.12", + "1.15.2-31.2.13", + "1.15.2-31.2.15", + "1.15.2-31.2.16", + "1.15.2-31.2.17", + "1.15.2-31.2.18", + "1.15.2-31.2.19", + "1.15.2-31.2.20", + "1.15.2-31.2.21", + "1.15.2-31.2.22", + "1.15.2-31.2.23", + "1.15.2-31.2.24", + "1.15.2-31.2.25", + "1.15.2-31.2.26", + "1.15.2-31.2.27", + "1.15.2-31.2.28", + "1.15.2-31.2.29", + "1.15.2-31.2.30", + "1.15.2-31.2.31", + "1.15.2-31.2.33", + "1.15.2-31.2.35", + "1.15.2-31.2.36", + "1.15.2-31.2.37", + "1.15.2-31.2.38", + "1.15.2-31.2.40", + "1.15.2-31.2.41", + "1.15.2-31.2.43", + "1.15.2-31.2.44", + "1.15.2-31.2.45", + "1.15.2-31.2.46", + "1.15.2-31.2.47", + "1.15.2-31.2.48", + "1.15.2-31.2.49", + "1.15.2-31.2.50", + "1.15.2-31.2.52", + "1.15.2-31.2.53", + "1.15.2-31.2.54", + "1.15.2-31.2.55", + "1.15.2-31.2.56", + "1.15.2-31.2.57", + "1.15.2-31.2.60" + ], + "1.16.1": [ + "1.16.1-32.0.1", + "1.16.1-32.0.2", + "1.16.1-32.0.6", + "1.16.1-32.0.7", + "1.16.1-32.0.8", + "1.16.1-32.0.9", + "1.16.1-32.0.10", + "1.16.1-32.0.12", + "1.16.1-32.0.13", + "1.16.1-32.0.14", + "1.16.1-32.0.15", + "1.16.1-32.0.16", + "1.16.1-32.0.17", + "1.16.1-32.0.18", + "1.16.1-32.0.19", + "1.16.1-32.0.20", + "1.16.1-32.0.21", + "1.16.1-32.0.23", + "1.16.1-32.0.24", + "1.16.1-32.0.25", + "1.16.1-32.0.26", + "1.16.1-32.0.27", + "1.16.1-32.0.28", + "1.16.1-32.0.29", + "1.16.1-32.0.30", + "1.16.1-32.0.31", + "1.16.1-32.0.32", + "1.16.1-32.0.33", + "1.16.1-32.0.34", + "1.16.1-32.0.35", + "1.16.1-32.0.36", + "1.16.1-32.0.38", + "1.16.1-32.0.39", + "1.16.1-32.0.40", + "1.16.1-32.0.41", + "1.16.1-32.0.43", + "1.16.1-32.0.44", + "1.16.1-32.0.46", + "1.16.1-32.0.47", + "1.16.1-32.0.48", + "1.16.1-32.0.49", + "1.16.1-32.0.50", + "1.16.1-32.0.51", + "1.16.1-32.0.52", + "1.16.1-32.0.53", + "1.16.1-32.0.54", + "1.16.1-32.0.55", + "1.16.1-32.0.56", + "1.16.1-32.0.57", + "1.16.1-32.0.59", + "1.16.1-32.0.60", + "1.16.1-32.0.61", + "1.16.1-32.0.62", + "1.16.1-32.0.63", + "1.16.1-32.0.64", + "1.16.1-32.0.65", + "1.16.1-32.0.66", + "1.16.1-32.0.67", + "1.16.1-32.0.68", + "1.16.1-32.0.69", + "1.16.1-32.0.70", + "1.16.1-32.0.71", + "1.16.1-32.0.72", + "1.16.1-32.0.73", + "1.16.1-32.0.74", + "1.16.1-32.0.75", + "1.16.1-32.0.76", + "1.16.1-32.0.77", + "1.16.1-32.0.79", + "1.16.1-32.0.80", + "1.16.1-32.0.81", + "1.16.1-32.0.82", + "1.16.1-32.0.83", + "1.16.1-32.0.84", + "1.16.1-32.0.85", + "1.16.1-32.0.86", + "1.16.1-32.0.88", + "1.16.1-32.0.90", + "1.16.1-32.0.91", + "1.16.1-32.0.92", + "1.16.1-32.0.93", + "1.16.1-32.0.95", + "1.16.1-32.0.96", + "1.16.1-32.0.97", + "1.16.1-32.0.98", + "1.16.1-32.0.99", + "1.16.1-32.0.101", + "1.16.1-32.0.104", + "1.16.1-32.0.105", + "1.16.1-32.0.106", + "1.16.1-32.0.107", + "1.16.1-32.0.108" + ], + "1.16.2": [ + "1.16.2-33.0.0", + "1.16.2-33.0.2", + "1.16.2-33.0.3", + "1.16.2-33.0.5", + "1.16.2-33.0.6", + "1.16.2-33.0.7", + "1.16.2-33.0.8", + "1.16.2-33.0.9", + "1.16.2-33.0.10", + "1.16.2-33.0.11", + "1.16.2-33.0.12", + "1.16.2-33.0.13", + "1.16.2-33.0.14", + "1.16.2-33.0.15", + "1.16.2-33.0.16", + "1.16.2-33.0.17", + "1.16.2-33.0.18", + "1.16.2-33.0.19", + "1.16.2-33.0.20", + "1.16.2-33.0.21", + "1.16.2-33.0.22", + "1.16.2-33.0.23", + "1.16.2-33.0.30", + "1.16.2-33.0.31", + "1.16.2-33.0.32", + "1.16.2-33.0.34", + "1.16.2-33.0.35", + "1.16.2-33.0.36", + "1.16.2-33.0.37", + "1.16.2-33.0.40", + "1.16.2-33.0.41", + "1.16.2-33.0.42", + "1.16.2-33.0.43", + "1.16.2-33.0.44", + "1.16.2-33.0.45", + "1.16.2-33.0.46", + "1.16.2-33.0.49", + "1.16.2-33.0.50", + "1.16.2-33.0.54", + "1.16.2-33.0.55", + "1.16.2-33.0.56", + "1.16.2-33.0.57", + "1.16.2-33.0.58", + "1.16.2-33.0.59", + "1.16.2-33.0.60", + "1.16.2-33.0.61" + ], + "1.16.3": [ + "1.16.3-34.0.0", + "1.16.3-34.0.1", + "1.16.3-34.0.2", + "1.16.3-34.0.3", + "1.16.3-34.0.4", + "1.16.3-34.0.5", + "1.16.3-34.0.6", + "1.16.3-34.0.7", + "1.16.3-34.0.8", + "1.16.3-34.0.9", + "1.16.3-34.0.10", + "1.16.3-34.0.11", + "1.16.3-34.0.12", + "1.16.3-34.0.13", + "1.16.3-34.0.14", + "1.16.3-34.0.16", + "1.16.3-34.0.17", + "1.16.3-34.0.18", + "1.16.3-34.0.19", + "1.16.3-34.0.20", + "1.16.3-34.0.21", + "1.16.3-34.1.0", + "1.16.3-34.1.1", + "1.16.3-34.1.2", + "1.16.3-34.1.3", + "1.16.3-34.1.4", + "1.16.3-34.1.5", + "1.16.3-34.1.7", + "1.16.3-34.1.9", + "1.16.3-34.1.10", + "1.16.3-34.1.11", + "1.16.3-34.1.12", + "1.16.3-34.1.13", + "1.16.3-34.1.14", + "1.16.3-34.1.15", + "1.16.3-34.1.16", + "1.16.3-34.1.17", + "1.16.3-34.1.18", + "1.16.3-34.1.19", + "1.16.3-34.1.20", + "1.16.3-34.1.21", + "1.16.3-34.1.22", + "1.16.3-34.1.23", + "1.16.3-34.1.24", + "1.16.3-34.1.25", + "1.16.3-34.1.27", + "1.16.3-34.1.28", + "1.16.3-34.1.29", + "1.16.3-34.1.30", + "1.16.3-34.1.31", + "1.16.3-34.1.32", + "1.16.3-34.1.33", + "1.16.3-34.1.34", + "1.16.3-34.1.35", + "1.16.3-34.1.39", + "1.16.3-34.1.40", + "1.16.3-34.1.41", + "1.16.3-34.1.42" + ], + "1.16.4": [ + "1.16.4-35.0.0", + "1.16.4-35.0.1", + "1.16.4-35.0.2", + "1.16.4-35.0.3", + "1.16.4-35.0.4", + "1.16.4-35.0.5", + "1.16.4-35.0.6", + "1.16.4-35.0.7", + "1.16.4-35.0.9", + "1.16.4-35.0.10", + "1.16.4-35.0.11", + "1.16.4-35.0.12", + "1.16.4-35.0.13", + "1.16.4-35.0.14", + "1.16.4-35.0.15", + "1.16.4-35.0.16", + "1.16.4-35.0.17", + "1.16.4-35.0.18", + "1.16.4-35.0.19", + "1.16.4-35.0.20", + "1.16.4-35.0.22", + "1.16.4-35.1.0", + "1.16.4-35.1.1", + "1.16.4-35.1.2", + "1.16.4-35.1.3", + "1.16.4-35.1.4", + "1.16.4-35.1.5", + "1.16.4-35.1.6", + "1.16.4-35.1.7", + "1.16.4-35.1.8", + "1.16.4-35.1.9", + "1.16.4-35.1.10", + "1.16.4-35.1.11", + "1.16.4-35.1.12", + "1.16.4-35.1.13", + "1.16.4-35.1.15", + "1.16.4-35.1.16", + "1.16.4-35.1.17", + "1.16.4-35.1.18", + "1.16.4-35.1.20", + "1.16.4-35.1.21", + "1.16.4-35.1.22", + "1.16.4-35.1.23", + "1.16.4-35.1.24", + "1.16.4-35.1.26", + "1.16.4-35.1.27", + "1.16.4-35.1.28", + "1.16.4-35.1.29", + "1.16.4-35.1.31", + "1.16.4-35.1.32", + "1.16.4-35.1.33", + "1.16.4-35.1.34", + "1.16.4-35.1.35", + "1.16.4-35.1.36", + "1.16.4-35.1.37" + ], + "1.16.5": [ + "1.16.5-36.0.0", + "1.16.5-36.0.1", + "1.16.5-36.0.2", + "1.16.5-36.0.4", + "1.16.5-36.0.7", + "1.16.5-36.0.8", + "1.16.5-36.0.9", + "1.16.5-36.0.10", + "1.16.5-36.0.11", + "1.16.5-36.0.12", + "1.16.5-36.0.13", + "1.16.5-36.0.14", + "1.16.5-36.0.15", + "1.16.5-36.0.16", + "1.16.5-36.0.17", + "1.16.5-36.0.18", + "1.16.5-36.0.19", + "1.16.5-36.0.20", + "1.16.5-36.0.21", + "1.16.5-36.0.22", + "1.16.5-36.0.23", + "1.16.5-36.0.24", + "1.16.5-36.0.25", + "1.16.5-36.0.26", + "1.16.5-36.0.27", + "1.16.5-36.0.28", + "1.16.5-36.0.29", + "1.16.5-36.0.30", + "1.16.5-36.0.31", + "1.16.5-36.0.32", + "1.16.5-36.0.33", + "1.16.5-36.0.34", + "1.16.5-36.0.35", + "1.16.5-36.0.36", + "1.16.5-36.0.37", + "1.16.5-36.0.39", + "1.16.5-36.0.40", + "1.16.5-36.0.41", + "1.16.5-36.0.42", + "1.16.5-36.0.43", + "1.16.5-36.0.44", + "1.16.5-36.0.45", + "1.16.5-36.0.46", + "1.16.5-36.0.48", + "1.16.5-36.0.52", + "1.16.5-36.0.53", + "1.16.5-36.0.54", + "1.16.5-36.0.55", + "1.16.5-36.0.58", + "1.16.5-36.0.59", + "1.16.5-36.0.60", + "1.16.5-36.0.61", + "1.16.5-36.0.62", + "1.16.5-36.0.63", + "1.16.5-36.1.0", + "1.16.5-36.1.1", + "1.16.5-36.1.2", + "1.16.5-36.1.3", + "1.16.5-36.1.4", + "1.16.5-36.1.6", + "1.16.5-36.1.7", + "1.16.5-36.1.8", + "1.16.5-36.1.9", + "1.16.5-36.1.10", + "1.16.5-36.1.12", + "1.16.5-36.1.13", + "1.16.5-36.1.14", + "1.16.5-36.1.15", + "1.16.5-36.1.16", + "1.16.5-36.1.17", + "1.16.5-36.1.18", + "1.16.5-36.1.19", + "1.16.5-36.1.20", + "1.16.5-36.1.21", + "1.16.5-36.1.22", + "1.16.5-36.1.23", + "1.16.5-36.1.24", + "1.16.5-36.1.25", + "1.16.5-36.1.26", + "1.16.5-36.1.27", + "1.16.5-36.1.28", + "1.16.5-36.1.29", + "1.16.5-36.1.30", + "1.16.5-36.1.31", + "1.16.5-36.1.32", + "1.16.5-36.1.33", + "1.16.5-36.1.34", + "1.16.5-36.1.35", + "1.16.5-36.1.36", + "1.16.5-36.1.51", + "1.16.5-36.1.52", + "1.16.5-36.1.53", + "1.16.5-36.1.58", + "1.16.5-36.1.61", + "1.16.5-36.1.62", + "1.16.5-36.1.63", + "1.16.5-36.1.65", + "1.16.5-36.1.66", + "1.16.5-36.2.0", + "1.16.5-36.2.1", + "1.16.5-36.2.2", + "1.16.5-36.2.3", + "1.16.5-36.2.4", + "1.16.5-36.2.5", + "1.16.5-36.2.6", + "1.16.5-36.2.8", + "1.16.5-36.2.9", + "1.16.5-36.2.10", + "1.16.5-36.2.11", + "1.16.5-36.2.12", + "1.16.5-36.2.13", + "1.16.5-36.2.14", + "1.16.5-36.2.15", + "1.16.5-36.2.16", + "1.16.5-36.2.17", + "1.16.5-36.2.18", + "1.16.5-36.2.19", + "1.16.5-36.2.20", + "1.16.5-36.2.21", + "1.16.5-36.2.22", + "1.16.5-36.2.23", + "1.16.5-36.2.25", + "1.16.5-36.2.26", + "1.16.5-36.2.27", + "1.16.5-36.2.28", + "1.16.5-36.2.29", + "1.16.5-36.2.30", + "1.16.5-36.2.31", + "1.16.5-36.2.32", + "1.16.5-36.2.33", + "1.16.5-36.2.34", + "1.16.5-36.2.35", + "1.16.5-36.2.39", + "1.16.5-36.2.40", + "1.16.5-36.2.41", + "1.16.5-36.2.42" + ], + "1.17.1": [ + "1.17.1-37.0.0", + "1.17.1-37.0.1", + "1.17.1-37.0.2", + "1.17.1-37.0.3", + "1.17.1-37.0.5", + "1.17.1-37.0.7", + "1.17.1-37.0.8", + "1.17.1-37.0.9", + "1.17.1-37.0.10", + "1.17.1-37.0.11", + "1.17.1-37.0.12", + "1.17.1-37.0.13", + "1.17.1-37.0.15", + "1.17.1-37.0.16", + "1.17.1-37.0.17", + "1.17.1-37.0.18", + "1.17.1-37.0.19", + "1.17.1-37.0.20", + "1.17.1-37.0.21", + "1.17.1-37.0.22", + "1.17.1-37.0.24", + "1.17.1-37.0.25", + "1.17.1-37.0.26", + "1.17.1-37.0.27", + "1.17.1-37.0.28", + "1.17.1-37.0.29", + "1.17.1-37.0.30", + "1.17.1-37.0.31", + "1.17.1-37.0.32", + "1.17.1-37.0.33", + "1.17.1-37.0.34", + "1.17.1-37.0.35", + "1.17.1-37.0.36", + "1.17.1-37.0.37", + "1.17.1-37.0.38", + "1.17.1-37.0.39", + "1.17.1-37.0.40", + "1.17.1-37.0.41", + "1.17.1-37.0.42", + "1.17.1-37.0.43", + "1.17.1-37.0.44", + "1.17.1-37.0.45", + "1.17.1-37.0.46", + "1.17.1-37.0.47", + "1.17.1-37.0.48", + "1.17.1-37.0.49", + "1.17.1-37.0.50", + "1.17.1-37.0.51", + "1.17.1-37.0.52", + "1.17.1-37.0.53", + "1.17.1-37.0.54", + "1.17.1-37.0.55", + "1.17.1-37.0.56", + "1.17.1-37.0.57", + "1.17.1-37.0.58", + "1.17.1-37.0.59", + "1.17.1-37.0.60", + "1.17.1-37.0.61", + "1.17.1-37.0.62", + "1.17.1-37.0.64", + "1.17.1-37.0.65", + "1.17.1-37.0.66", + "1.17.1-37.0.67", + "1.17.1-37.0.68", + "1.17.1-37.0.69", + "1.17.1-37.0.70", + "1.17.1-37.0.71", + "1.17.1-37.0.72", + "1.17.1-37.0.73", + "1.17.1-37.0.74", + "1.17.1-37.0.75", + "1.17.1-37.0.76", + "1.17.1-37.0.78", + "1.17.1-37.0.80", + "1.17.1-37.0.81", + "1.17.1-37.0.82", + "1.17.1-37.0.83", + "1.17.1-37.0.84", + "1.17.1-37.0.85", + "1.17.1-37.0.86", + "1.17.1-37.0.87", + "1.17.1-37.0.88", + "1.17.1-37.0.89", + "1.17.1-37.0.90", + "1.17.1-37.0.91", + "1.17.1-37.0.94", + "1.17.1-37.0.95", + "1.17.1-37.0.97", + "1.17.1-37.0.98", + "1.17.1-37.0.102", + "1.17.1-37.0.103", + "1.17.1-37.0.104", + "1.17.1-37.0.105", + "1.17.1-37.0.107", + "1.17.1-37.0.108", + "1.17.1-37.0.109", + "1.17.1-37.0.110", + "1.17.1-37.0.111", + "1.17.1-37.0.112", + "1.17.1-37.0.113", + "1.17.1-37.0.114", + "1.17.1-37.0.116", + "1.17.1-37.0.117", + "1.17.1-37.0.118", + "1.17.1-37.0.119", + "1.17.1-37.0.120", + "1.17.1-37.0.121", + "1.17.1-37.0.122", + "1.17.1-37.0.123", + "1.17.1-37.0.125", + "1.17.1-37.0.126", + "1.17.1-37.0.127", + "1.17.1-37.1.0", + "1.17.1-37.1.1" + ], + "1.18": [ + "1.18-38.0.0", + "1.18-38.0.1", + "1.18-38.0.2", + "1.18-38.0.3", + "1.18-38.0.4", + "1.18-38.0.5", + "1.18-38.0.6", + "1.18-38.0.8", + "1.18-38.0.10", + "1.18-38.0.11", + "1.18-38.0.12", + "1.18-38.0.13", + "1.18-38.0.14", + "1.18-38.0.15", + "1.18-38.0.16", + "1.18-38.0.17" + ], + "1.18.1": [ + "1.18.1-39.0.0", + "1.18.1-39.0.1", + "1.18.1-39.0.2", + "1.18.1-39.0.3", + "1.18.1-39.0.4", + "1.18.1-39.0.5", + "1.18.1-39.0.6", + "1.18.1-39.0.7", + "1.18.1-39.0.8", + "1.18.1-39.0.9", + "1.18.1-39.0.10", + "1.18.1-39.0.11", + "1.18.1-39.0.12", + "1.18.1-39.0.13", + "1.18.1-39.0.14", + "1.18.1-39.0.15", + "1.18.1-39.0.16", + "1.18.1-39.0.17", + "1.18.1-39.0.18", + "1.18.1-39.0.19", + "1.18.1-39.0.20", + "1.18.1-39.0.22", + "1.18.1-39.0.36", + "1.18.1-39.0.37", + "1.18.1-39.0.38", + "1.18.1-39.0.40", + "1.18.1-39.0.43", + "1.18.1-39.0.44", + "1.18.1-39.0.45", + "1.18.1-39.0.46", + "1.18.1-39.0.47", + "1.18.1-39.0.48", + "1.18.1-39.0.49", + "1.18.1-39.0.50", + "1.18.1-39.0.51", + "1.18.1-39.0.52", + "1.18.1-39.0.53", + "1.18.1-39.0.54", + "1.18.1-39.0.55", + "1.18.1-39.0.56", + "1.18.1-39.0.57", + "1.18.1-39.0.58", + "1.18.1-39.0.59", + "1.18.1-39.0.60", + "1.18.1-39.0.61", + "1.18.1-39.0.62", + "1.18.1-39.0.63", + "1.18.1-39.0.64", + "1.18.1-39.0.65", + "1.18.1-39.0.66", + "1.18.1-39.0.67", + "1.18.1-39.0.68", + "1.18.1-39.0.69", + "1.18.1-39.0.70", + "1.18.1-39.0.71", + "1.18.1-39.0.72", + "1.18.1-39.0.73", + "1.18.1-39.0.74", + "1.18.1-39.0.75", + "1.18.1-39.0.76", + "1.18.1-39.0.77", + "1.18.1-39.0.78", + "1.18.1-39.0.79", + "1.18.1-39.0.80", + "1.18.1-39.0.81", + "1.18.1-39.0.82", + "1.18.1-39.0.83", + "1.18.1-39.0.84", + "1.18.1-39.0.85", + "1.18.1-39.0.86", + "1.18.1-39.0.87", + "1.18.1-39.0.88", + "1.18.1-39.0.89", + "1.18.1-39.0.90", + "1.18.1-39.1.0", + "1.18.1-39.1.1", + "1.18.1-39.1.2" + ], + "1.18.2": [ + "1.18.2-40.0.0", + "1.18.2-40.0.1", + "1.18.2-40.0.2", + "1.18.2-40.0.3", + "1.18.2-40.0.4", + "1.18.2-40.0.5", + "1.18.2-40.0.6", + "1.18.2-40.0.7", + "1.18.2-40.0.8", + "1.18.2-40.0.9", + "1.18.2-40.0.10", + "1.18.2-40.0.11", + "1.18.2-40.0.12", + "1.18.2-40.0.13", + "1.18.2-40.0.14", + "1.18.2-40.0.15", + "1.18.2-40.0.16", + "1.18.2-40.0.17", + "1.18.2-40.0.18", + "1.18.2-40.0.19", + "1.18.2-40.0.20", + "1.18.2-40.0.21", + "1.18.2-40.0.22", + "1.18.2-40.0.23", + "1.18.2-40.0.24", + "1.18.2-40.0.25", + "1.18.2-40.0.26", + "1.18.2-40.0.27", + "1.18.2-40.0.28", + "1.18.2-40.0.29", + "1.18.2-40.0.30", + "1.18.2-40.0.31", + "1.18.2-40.0.32", + "1.18.2-40.0.33", + "1.18.2-40.0.34", + "1.18.2-40.0.35", + "1.18.2-40.0.36", + "1.18.2-40.0.38", + "1.18.2-40.0.39", + "1.18.2-40.0.40", + "1.18.2-40.0.41", + "1.18.2-40.0.42", + "1.18.2-40.0.43", + "1.18.2-40.0.44", + "1.18.2-40.0.45", + "1.18.2-40.0.46", + "1.18.2-40.0.47", + "1.18.2-40.0.48", + "1.18.2-40.0.49", + "1.18.2-40.0.51", + "1.18.2-40.0.52", + "1.18.2-40.0.53", + "1.18.2-40.0.54", + "1.18.2-40.1.0", + "1.18.2-40.1.1", + "1.18.2-40.1.2", + "1.18.2-40.1.3", + "1.18.2-40.1.4", + "1.18.2-40.1.5", + "1.18.2-40.1.6", + "1.18.2-40.1.8", + "1.18.2-40.1.10", + "1.18.2-40.1.11", + "1.18.2-40.1.12", + "1.18.2-40.1.13", + "1.18.2-40.1.14", + "1.18.2-40.1.15", + "1.18.2-40.1.16", + "1.18.2-40.1.17", + "1.18.2-40.1.18", + "1.18.2-40.1.19", + "1.18.2-40.1.20", + "1.18.2-40.1.22", + "1.18.2-40.1.21", + "1.18.2-40.1.23", + "1.18.2-40.1.24", + "1.18.2-40.1.25", + "1.18.2-40.1.26", + "1.18.2-40.1.27", + "1.18.2-40.1.28", + "1.18.2-40.1.29", + "1.18.2-40.1.30", + "1.18.2-40.1.31", + "1.18.2-40.1.34", + "1.18.2-40.1.35", + "1.18.2-40.1.36", + "1.18.2-40.1.41", + "1.18.2-40.1.44", + "1.18.2-40.1.45", + "1.18.2-40.1.46", + "1.18.2-40.1.47", + "1.18.2-40.1.48", + "1.18.2-40.1.51", + "1.18.2-40.1.52", + "1.18.2-40.1.53", + "1.18.2-40.1.54", + "1.18.2-40.1.55", + "1.18.2-40.1.56", + "1.18.2-40.1.57", + "1.18.2-40.1.59", + "1.18.2-40.1.60", + "1.18.2-40.1.61", + "1.18.2-40.1.62", + "1.18.2-40.1.67", + "1.18.2-40.1.68", + "1.18.2-40.1.69", + "1.18.2-40.1.70", + "1.18.2-40.1.71", + "1.18.2-40.1.73", + "1.18.2-40.1.74", + "1.18.2-40.1.75", + "1.18.2-40.1.76", + "1.18.2-40.1.78", + "1.18.2-40.1.79", + "1.18.2-40.1.80", + "1.18.2-40.1.81", + "1.18.2-40.1.82", + "1.18.2-40.1.84", + "1.18.2-40.1.85", + "1.18.2-40.1.86", + "1.18.2-40.1.87", + "1.18.2-40.1.92", + "1.18.2-40.1.93", + "1.18.2-40.2.0", + "1.18.2-40.2.1", + "1.18.2-40.2.2", + "1.18.2-40.2.4", + "1.18.2-40.2.5", + "1.18.2-40.2.6", + "1.18.2-40.2.7", + "1.18.2-40.2.8", + "1.18.2-40.2.9", + "1.18.2-40.2.10", + "1.18.2-40.2.11", + "1.18.2-40.2.13", + "1.18.2-40.2.14", + "1.18.2-40.2.15", + "1.18.2-40.2.17", + "1.18.2-40.2.18", + "1.18.2-40.2.19", + "1.18.2-40.2.21", + "1.18.2-40.2.24", + "1.18.2-40.2.25", + "1.18.2-40.2.26", + "1.18.2-40.2.27", + "1.18.2-40.2.29", + "1.18.2-40.2.31", + "1.18.2-40.2.33", + "1.18.2-40.2.34", + "1.18.2-40.3.0", + "1.18.2-40.3.1", + "1.18.2-40.3.3", + "1.18.2-40.3.6", + "1.18.2-40.3.7", + "1.18.2-40.3.9", + "1.18.2-40.3.10", + "1.18.2-40.3.11" + ], + "1.19": [ + "1.19-41.0.1", + "1.19-41.0.2", + "1.19-41.0.3", + "1.19-41.0.4", + "1.19-41.0.5", + "1.19-41.0.7", + "1.19-41.0.9", + "1.19-41.0.11", + "1.19-41.0.12", + "1.19-41.0.13", + "1.19-41.0.14", + "1.19-41.0.15", + "1.19-41.0.16", + "1.19-41.0.17", + "1.19-41.0.18", + "1.19-41.0.19", + "1.19-41.0.20", + "1.19-41.0.21", + "1.19-41.0.22", + "1.19-41.0.24", + "1.19-41.0.26", + "1.19-41.0.25", + "1.19-41.0.27", + "1.19-41.0.28", + "1.19-41.0.29", + "1.19-41.0.30", + "1.19-41.0.31", + "1.19-41.0.32", + "1.19-41.0.33", + "1.19-41.0.34", + "1.19-41.0.35", + "1.19-41.0.36", + "1.19-41.0.37", + "1.19-41.0.38", + "1.19-41.0.42", + "1.19-41.0.43", + "1.19-41.0.44", + "1.19-41.0.45", + "1.19-41.0.46", + "1.19-41.0.47", + "1.19-41.0.48", + "1.19-41.0.49", + "1.19-41.0.50", + "1.19-41.0.51", + "1.19-41.0.54", + "1.19-41.0.56", + "1.19-41.0.57", + "1.19-41.0.60", + "1.19-41.0.61", + "1.19-41.0.62", + "1.19-41.0.63", + "1.19-41.0.64", + "1.19-41.0.67", + "1.19-41.0.68", + "1.19-41.0.69", + "1.19-41.0.70", + "1.19-41.0.71", + "1.19-41.0.76", + "1.19-41.0.77", + "1.19-41.0.78", + "1.19-41.0.79", + "1.19-41.0.80", + "1.19-41.0.81", + "1.19-41.0.82", + "1.19-41.0.83", + "1.19-41.0.84", + "1.19-41.0.85", + "1.19-41.0.86", + "1.19-41.0.87", + "1.19-41.0.88", + "1.19-41.0.91", + "1.19-41.0.92", + "1.19-41.0.93", + "1.19-41.0.94", + "1.19-41.0.96", + "1.19-41.0.98", + "1.19-41.0.99", + "1.19-41.0.100", + "1.19-41.0.103", + "1.19-41.0.104", + "1.19-41.0.105", + "1.19-41.0.106", + "1.19-41.0.107", + "1.19-41.0.108", + "1.19-41.0.109", + "1.19-41.0.110", + "1.19-41.0.111", + "1.19-41.0.112", + "1.19-41.0.113", + "1.19-41.1.0" + ], + "1.19.1": [ + "1.19.1-42.0.0", + "1.19.1-42.0.1", + "1.19.1-42.0.2", + "1.19.1-42.0.3", + "1.19.1-42.0.4", + "1.19.1-42.0.5", + "1.19.1-42.0.8", + "1.19.1-42.0.9" + ], + "1.19.2": [ + "1.19.2-43.0.0", + "1.19.2-43.0.1", + "1.19.2-43.0.2", + "1.19.2-43.0.3", + "1.19.2-43.0.4", + "1.19.2-43.0.5", + "1.19.2-43.0.7", + "1.19.2-43.0.8", + "1.19.2-43.0.10", + "1.19.2-43.0.11", + "1.19.2-43.0.12", + "1.19.2-43.0.13", + "1.19.2-43.0.15", + "1.19.2-43.0.16", + "1.19.2-43.0.17", + "1.19.2-43.0.18", + "1.19.2-43.0.19", + "1.19.2-43.0.20", + "1.19.2-43.0.21", + "1.19.2-43.0.22", + "1.19.2-43.1.0", + "1.19.2-43.1.1", + "1.19.2-43.1.2", + "1.19.2-43.1.3", + "1.19.2-43.1.4", + "1.19.2-43.1.7", + "1.19.2-43.1.10", + "1.19.2-43.1.12", + "1.19.2-43.1.13", + "1.19.2-43.1.14", + "1.19.2-43.1.15", + "1.19.2-43.1.16", + "1.19.2-43.1.17", + "1.19.2-43.1.23", + "1.19.2-43.1.24", + "1.19.2-43.1.25", + "1.19.2-43.1.26", + "1.19.2-43.1.27", + "1.19.2-43.1.28", + "1.19.2-43.1.29", + "1.19.2-43.1.30", + "1.19.2-43.1.32", + "1.19.2-43.1.33", + "1.19.2-43.1.34", + "1.19.2-43.1.35", + "1.19.2-43.1.36", + "1.19.2-43.1.37", + "1.19.2-43.1.38", + "1.19.2-43.1.39", + "1.19.2-43.1.40", + "1.19.2-43.1.41", + "1.19.2-43.1.42", + "1.19.2-43.1.43", + "1.19.2-43.1.47", + "1.19.2-43.1.52", + "1.19.2-43.1.53", + "1.19.2-43.1.55", + "1.19.2-43.1.57", + "1.19.2-43.1.58", + "1.19.2-43.1.59", + "1.19.2-43.1.60", + "1.19.2-43.1.64", + "1.19.2-43.1.65", + "1.19.2-43.2.0", + "1.19.2-43.1.63", + "1.19.2-43.2.1", + "1.19.2-43.2.2", + "1.19.2-43.2.3", + "1.19.2-43.2.4", + "1.19.2-43.2.5", + "1.19.2-43.2.6", + "1.19.2-43.2.7", + "1.19.2-43.2.8", + "1.19.2-43.2.9", + "1.19.2-43.2.10", + "1.19.2-43.2.11", + "1.19.2-43.2.12", + "1.19.2-43.2.13", + "1.19.2-43.2.14", + "1.19.2-43.2.17", + "1.19.2-43.2.18", + "1.19.2-43.2.19", + "1.19.2-43.2.20", + "1.19.2-43.2.21", + "1.19.2-43.2.22", + "1.19.2-43.2.23", + "1.19.2-43.3.0", + "1.19.2-43.3.2", + "1.19.2-43.3.5", + "1.19.2-43.3.6", + "1.19.2-43.3.7", + "1.19.2-43.3.8", + "1.19.2-43.3.9", + "1.19.2-43.3.10", + "1.19.2-43.3.12", + "1.19.2-43.3.13", + "1.19.2-43.3.14", + "1.19.2-43.4.0", + "1.19.2-43.4.1", + "1.19.2-43.4.2", + "1.19.2-43.4.4", + "1.19.2-43.4.5", + "1.19.2-43.4.6", + "1.19.2-43.4.7", + "1.19.2-43.4.9", + "1.19.2-43.4.10", + "1.19.2-43.4.12", + "1.19.2-43.4.13", + "1.19.2-43.4.16", + "1.19.2-43.4.19", + "1.19.2-43.4.20", + "1.19.2-43.4.22", + "1.19.2-43.4.23", + "1.19.2-43.5.0", + "1.19.2-43.5.1" + ], + "1.19.3": [ + "1.19.3-44.0.0", + "1.19.3-44.0.1", + "1.19.3-44.0.4", + "1.19.3-44.0.5", + "1.19.3-44.0.6", + "1.19.3-44.0.7", + "1.19.3-44.0.10", + "1.19.3-44.0.11", + "1.19.3-44.0.17", + "1.19.3-44.0.18", + "1.19.3-44.0.22", + "1.19.3-44.0.23", + "1.19.3-44.0.29", + "1.19.3-44.0.30", + "1.19.3-44.0.35", + "1.19.3-44.0.36", + "1.19.3-44.0.37", + "1.19.3-44.0.40", + "1.19.3-44.0.41", + "1.19.3-44.0.42", + "1.19.3-44.0.48", + "1.19.3-44.0.49", + "1.19.3-44.1.0", + "1.19.3-44.1.1", + "1.19.3-44.1.2", + "1.19.3-44.1.3", + "1.19.3-44.1.4", + "1.19.3-44.1.5", + "1.19.3-44.1.6", + "1.19.3-44.1.7", + "1.19.3-44.1.8", + "1.19.3-44.1.9", + "1.19.3-44.1.10", + "1.19.3-44.1.12", + "1.19.3-44.1.16", + "1.19.3-44.1.17", + "1.19.3-44.1.18", + "1.19.3-44.1.20", + "1.19.3-44.1.21", + "1.19.3-44.1.22", + "1.19.3-44.1.23" + ], + "1.19.4": [ + "1.19.4-45.0.0", + "1.19.4-45.0.1", + "1.19.4-45.0.4", + "1.19.4-45.0.6", + "1.19.4-45.0.8", + "1.19.4-45.0.9", + "1.19.4-45.0.10", + "1.19.4-45.0.11", + "1.19.4-45.0.12", + "1.19.4-45.0.13", + "1.19.4-45.0.18", + "1.19.4-45.0.19", + "1.19.4-45.0.20", + "1.19.4-45.0.22", + "1.19.4-45.0.23", + "1.19.4-45.0.24", + "1.19.4-45.0.25", + "1.19.4-45.0.26", + "1.19.4-45.0.27", + "1.19.4-45.0.29", + "1.19.4-45.0.30", + "1.19.4-45.0.31", + "1.19.4-45.0.35", + "1.19.4-45.0.36", + "1.19.4-45.0.37", + "1.19.4-45.0.38", + "1.19.4-45.0.39", + "1.19.4-45.0.40", + "1.19.4-45.0.41", + "1.19.4-45.0.42", + "1.19.4-45.0.43", + "1.19.4-45.0.44", + "1.19.4-45.0.45", + "1.19.4-45.0.46", + "1.19.4-45.0.47", + "1.19.4-45.0.49", + "1.19.4-45.0.50", + "1.19.4-45.0.52", + "1.19.4-45.0.53", + "1.19.4-45.0.55", + "1.19.4-45.0.57", + "1.19.4-45.0.58", + "1.19.4-45.0.59", + "1.19.4-45.0.63", + "1.19.4-45.0.64", + "1.19.4-45.0.66", + "1.19.4-45.1.0", + "1.19.4-45.1.2", + "1.19.4-45.1.4", + "1.19.4-45.1.5", + "1.19.4-45.1.6", + "1.19.4-45.1.7", + "1.19.4-45.1.8", + "1.19.4-45.1.9", + "1.19.4-45.1.10", + "1.19.4-45.1.11", + "1.19.4-45.1.12", + "1.19.4-45.1.13", + "1.19.4-45.1.14", + "1.19.4-45.1.15", + "1.19.4-45.1.16", + "1.19.4-45.1.17", + "1.19.4-45.1.18", + "1.19.4-45.1.19", + "1.19.4-45.2.0", + "1.19.4-45.2.2", + "1.19.4-45.2.3", + "1.19.4-45.2.4", + "1.19.4-45.2.6", + "1.19.4-45.2.7", + "1.19.4-45.2.8", + "1.19.4-45.2.9", + "1.19.4-45.2.10", + "1.19.4-45.2.12", + "1.19.4-45.2.14", + "1.19.4-45.2.15", + "1.19.4-45.2.16", + "1.19.4-45.3.0", + "1.19.4-45.3.1", + "1.19.4-45.3.2", + "1.19.4-45.3.3", + "1.19.4-45.3.6", + "1.19.4-45.3.7", + "1.19.4-45.3.8", + "1.19.4-45.3.9", + "1.19.4-45.3.10", + "1.19.4-45.3.12", + "1.19.4-45.3.13", + "1.19.4-45.3.15", + "1.19.4-45.3.17", + "1.19.4-45.3.20", + "1.19.4-45.3.23", + "1.19.4-45.3.24", + "1.19.4-45.3.27", + "1.19.4-45.4.0", + "1.19.4-45.4.1", + "1.19.4-45.4.2" + ], + "1.20": [ + "1.20-46.0.1", + "1.20-46.0.2", + "1.20-46.0.10", + "1.20-46.0.11", + "1.20-46.0.12", + "1.20-46.0.13", + "1.20-46.0.14" + ], + "1.20.1": [ + "1.20.1-47.0.0", + "1.20.1-47.0.1", + "1.20.1-47.0.2", + "1.20.1-47.0.3", + "1.20.1-47.0.4", + "1.20.1-47.0.5", + "1.20.1-47.0.6", + "1.20.1-47.0.14", + "1.20.1-47.0.15", + "1.20.1-47.0.16", + "1.20.1-47.0.17", + "1.20.1-47.0.18", + "1.20.1-47.0.19", + "1.20.1-47.0.34", + "1.20.1-47.0.35", + "1.20.1-47.0.39", + "1.20.1-47.0.42", + "1.20.1-47.0.43", + "1.20.1-47.0.44", + "1.20.1-47.0.45", + "1.20.1-47.0.46", + "1.20.1-47.0.49", + "1.20.1-47.0.50", + "1.20.1-47.1.0", + "1.20.1-47.1.1", + "1.20.1-47.1.3", + "1.20.1-47.1.4", + "1.20.1-47.1.5", + "1.20.1-47.1.6", + "1.20.1-47.1.7", + "1.20.1-47.1.8", + "1.20.1-47.1.10", + "1.20.1-47.1.12", + "1.20.1-47.1.13", + "1.20.1-47.1.14", + "1.20.1-47.1.15", + "1.20.1-47.1.16", + "1.20.1-47.1.17", + "1.20.1-47.1.18", + "1.20.1-47.1.19", + "1.20.1-47.1.21", + "1.20.1-47.1.22", + "1.20.1-47.1.23", + "1.20.1-47.1.25", + "1.20.1-47.1.26", + "1.20.1-47.1.27", + "1.20.1-47.1.28", + "1.20.1-47.1.29", + "1.20.1-47.1.30", + "1.20.1-47.1.32", + "1.20.1-47.1.33", + "1.20.1-47.1.36", + "1.20.1-47.1.37", + "1.20.1-47.1.39", + "1.20.1-47.1.40", + "1.20.1-47.1.41", + "1.20.1-47.1.42", + "1.20.1-47.1.43", + "1.20.1-47.1.44", + "1.20.1-47.1.46", + "1.20.1-47.1.47", + "1.20.1-47.2.0", + "1.20.1-47.2.1", + "1.20.1-47.2.4", + "1.20.1-47.2.5", + "1.20.1-47.2.6", + "1.20.1-47.2.7", + "1.20.1-47.2.8", + "1.20.1-47.2.14", + "1.20.1-47.2.16", + "1.20.1-47.2.17", + "1.20.1-47.2.18", + "1.20.1-47.2.19", + "1.20.1-47.2.20", + "1.20.1-47.2.21", + "1.20.1-47.2.23", + "1.20.1-47.2.29", + "1.20.1-47.2.30", + "1.20.1-47.2.31", + "1.20.1-47.2.32", + "1.20.1-47.2.36", + "1.20.1-47.3.0", + "1.20.1-47.3.1", + "1.20.1-47.3.2", + "1.20.1-47.3.3", + "1.20.1-47.3.4", + "1.20.1-47.3.5", + "1.20.1-47.3.6", + "1.20.1-47.3.7", + "1.20.1-47.3.10", + "1.20.1-47.3.11", + "1.20.1-47.3.12", + "1.20.1-47.3.13", + "1.20.1-47.3.14", + "1.20.1-47.3.15", + "1.20.1-47.3.16", + "1.20.1-47.3.19", + "1.20.1-47.3.20", + "1.20.1-47.3.22", + "1.20.1-47.3.24", + "1.20.1-47.3.25", + "1.20.1-47.3.27", + "1.20.1-47.3.28", + "1.20.1-47.3.29", + "1.20.1-47.3.31", + "1.20.1-47.3.32", + "1.20.1-47.3.33", + "1.20.1-47.3.34", + "1.20.1-47.3.37", + "1.20.1-47.3.38", + "1.20.1-47.3.39", + "1.20.1-47.4.0", + "1.20.1-47.4.1", + "1.20.1-47.4.2", + "1.20.1-47.4.3", + "1.20.1-47.4.4", + "1.20.1-47.4.5", + "1.20.1-47.4.6", + "1.20.1-47.4.8" + ], + "1.20.2": [ + "1.20.2-48.0.0", + "1.20.2-48.0.1", + "1.20.2-48.0.4", + "1.20.2-48.0.6", + "1.20.2-48.0.7", + "1.20.2-48.0.8", + "1.20.2-48.0.10", + "1.20.2-48.0.11", + "1.20.2-48.0.12", + "1.20.2-48.0.13", + "1.20.2-48.0.17", + "1.20.2-48.0.18", + "1.20.2-48.0.19", + "1.20.2-48.0.20", + "1.20.2-48.0.22", + "1.20.2-48.0.23", + "1.20.2-48.0.24", + "1.20.2-48.0.26", + "1.20.2-48.0.29", + "1.20.2-48.0.30", + "1.20.2-48.0.31", + "1.20.2-48.0.32", + "1.20.2-48.0.33", + "1.20.2-48.0.34", + "1.20.2-48.0.35", + "1.20.2-48.0.36", + "1.20.2-48.0.37", + "1.20.2-48.0.38", + "1.20.2-48.0.39", + "1.20.2-48.0.40", + "1.20.2-48.0.41", + "1.20.2-48.0.42", + "1.20.2-48.0.43", + "1.20.2-48.0.44", + "1.20.2-48.0.45", + "1.20.2-48.0.47", + "1.20.2-48.0.48", + "1.20.2-48.0.49", + "1.20.2-48.1.0" + ], + "1.20.3": [ + "1.20.3-49.0.1", + "1.20.3-49.0.2" + ], + "1.20.4": [ + "1.20.4-49.0.3", + "1.20.4-49.0.4", + "1.20.4-49.0.7", + "1.20.4-49.0.8", + "1.20.4-49.0.9", + "1.20.4-49.0.10", + "1.20.4-49.0.11", + "1.20.4-49.0.12", + "1.20.4-49.0.13", + "1.20.4-49.0.14", + "1.20.4-49.0.16", + "1.20.4-49.0.19", + "1.20.4-49.0.21", + "1.20.4-49.0.22", + "1.20.4-49.0.24", + "1.20.4-49.0.26", + "1.20.4-49.0.27", + "1.20.4-49.0.28", + "1.20.4-49.0.30", + "1.20.4-49.0.31", + "1.20.4-49.0.32", + "1.20.4-49.0.33", + "1.20.4-49.0.34", + "1.20.4-49.0.38", + "1.20.4-49.0.39", + "1.20.4-49.0.40", + "1.20.4-49.0.41", + "1.20.4-49.0.42", + "1.20.4-49.0.43", + "1.20.4-49.0.45", + "1.20.4-49.0.46", + "1.20.4-49.0.48", + "1.20.4-49.0.49", + "1.20.4-49.0.50", + "1.20.4-49.0.51", + "1.20.4-49.0.52", + "1.20.4-49.0.53", + "1.20.4-49.1.0", + "1.20.4-49.1.1", + "1.20.4-49.1.2", + "1.20.4-49.1.3", + "1.20.4-49.1.4", + "1.20.4-49.1.5", + "1.20.4-49.1.8", + "1.20.4-49.1.10", + "1.20.4-49.1.12", + "1.20.4-49.1.13", + "1.20.4-49.1.14", + "1.20.4-49.1.15", + "1.20.4-49.1.16", + "1.20.4-49.1.17", + "1.20.4-49.1.19", + "1.20.4-49.1.21", + "1.20.4-49.1.22", + "1.20.4-49.1.23", + "1.20.4-49.1.25", + "1.20.4-49.1.26", + "1.20.4-49.1.27", + "1.20.4-49.1.29", + "1.20.4-49.1.32", + "1.20.4-49.1.33", + "1.20.4-49.1.34", + "1.20.4-49.1.35", + "1.20.4-49.1.36", + "1.20.4-49.1.37", + "1.20.4-49.1.40", + "1.20.4-49.1.41", + "1.20.4-49.2.0", + "1.20.4-49.2.1" + ], + "1.20.6": [ + "1.20.6-50.0.0", + "1.20.6-50.0.1", + "1.20.6-50.0.2", + "1.20.6-50.0.4", + "1.20.6-50.0.5", + "1.20.6-50.0.6", + "1.20.6-50.0.7", + "1.20.6-50.0.8", + "1.20.6-50.0.9", + "1.20.6-50.0.10", + "1.20.6-50.0.11", + "1.20.6-50.0.12", + "1.20.6-50.0.13", + "1.20.6-50.0.14", + "1.20.6-50.0.15", + "1.20.6-50.0.16", + "1.20.6-50.0.17", + "1.20.6-50.0.18", + "1.20.6-50.0.19", + "1.20.6-50.0.20", + "1.20.6-50.0.21", + "1.20.6-50.0.22", + "1.20.6-50.0.24", + "1.20.6-50.0.25", + "1.20.6-50.0.26", + "1.20.6-50.0.28", + "1.20.6-50.0.29", + "1.20.6-50.0.30", + "1.20.6-50.0.31", + "1.20.6-50.0.34", + "1.20.6-50.0.36", + "1.20.6-50.0.37", + "1.20.6-50.1.0", + "1.20.6-50.1.1", + "1.20.6-50.1.2", + "1.20.6-50.1.3", + "1.20.6-50.1.4", + "1.20.6-50.1.5", + "1.20.6-50.1.6", + "1.20.6-50.1.7", + "1.20.6-50.1.8", + "1.20.6-50.1.9", + "1.20.6-50.1.10", + "1.20.6-50.1.12", + "1.20.6-50.1.13", + "1.20.6-50.1.14", + "1.20.6-50.1.15", + "1.20.6-50.1.16", + "1.20.6-50.1.17", + "1.20.6-50.1.18", + "1.20.6-50.1.19", + "1.20.6-50.1.20", + "1.20.6-50.1.21", + "1.20.6-50.1.22", + "1.20.6-50.1.23", + "1.20.6-50.1.24", + "1.20.6-50.1.25", + "1.20.6-50.1.26", + "1.20.6-50.1.27", + "1.20.6-50.1.29", + "1.20.6-50.1.30", + "1.20.6-50.1.31", + "1.20.6-50.1.32", + "1.20.6-50.1.34", + "1.20.6-50.1.35", + "1.20.6-50.1.37", + "1.20.6-50.1.39", + "1.20.6-50.1.42", + "1.20.6-50.1.43", + "1.20.6-50.1.45", + "1.20.6-50.1.46", + "1.20.6-50.1.47", + "1.20.6-50.1.48", + "1.20.6-50.1.51", + "1.20.6-50.2.0", + "1.20.6-50.2.1" + ], + "1.21": [ + "1.21-51.0.0", + "1.21-51.0.1", + "1.21-51.0.3", + "1.21-51.0.4", + "1.21-51.0.5", + "1.21-51.0.6", + "1.21-51.0.7", + "1.21-51.0.8", + "1.21-51.0.13", + "1.21-51.0.15", + "1.21-51.0.16", + "1.21-51.0.17", + "1.21-51.0.18", + "1.21-51.0.21", + "1.21-51.0.22", + "1.21-51.0.23", + "1.21-51.0.24", + "1.21-51.0.25", + "1.21-51.0.26", + "1.21-51.0.28", + "1.21-51.0.29", + "1.21-51.0.30", + "1.21-51.0.31", + "1.21-51.0.32", + "1.21-51.0.33" + ], + "1.21.1": [ + "1.21.1-52.0.0", + "1.21.1-52.0.1", + "1.21.1-52.0.2", + "1.21.1-52.0.3", + "1.21.1-52.0.4", + "1.21.1-52.0.5", + "1.21.1-52.0.6", + "1.21.1-52.0.7", + "1.21.1-52.0.8", + "1.21.1-52.0.9", + "1.21.1-52.0.10", + "1.21.1-52.0.11", + "1.21.1-52.0.12", + "1.21.1-52.0.13", + "1.21.1-52.0.15", + "1.21.1-52.0.16", + "1.21.1-52.0.17", + "1.21.1-52.0.18", + "1.21.1-52.0.19", + "1.21.1-52.0.20", + "1.21.1-52.0.21", + "1.21.1-52.0.22", + "1.21.1-52.0.23", + "1.21.1-52.0.24", + "1.21.1-52.0.25", + "1.21.1-52.0.26", + "1.21.1-52.0.27", + "1.21.1-52.0.28", + "1.21.1-52.0.29", + "1.21.1-52.0.30", + "1.21.1-52.0.32", + "1.21.1-52.0.33", + "1.21.1-52.0.34", + "1.21.1-52.0.35", + "1.21.1-52.0.36", + "1.21.1-52.0.37", + "1.21.1-52.0.38", + "1.21.1-52.0.39", + "1.21.1-52.0.40", + "1.21.1-52.0.42", + "1.21.1-52.0.45", + "1.21.1-52.0.47", + "1.21.1-52.0.48", + "1.21.1-52.0.49", + "1.21.1-52.0.50", + "1.21.1-52.0.51", + "1.21.1-52.0.52", + "1.21.1-52.0.53", + "1.21.1-52.0.56", + "1.21.1-52.0.57", + "1.21.1-52.1.0", + "1.21.1-52.1.1", + "1.21.1-52.1.2", + "1.21.1-52.1.3" + ], + "1.21.3": [ + "1.21.3-53.0.0", + "1.21.3-53.0.1", + "1.21.3-53.0.2", + "1.21.3-53.0.3", + "1.21.3-53.0.4", + "1.21.3-53.0.5", + "1.21.3-53.0.7", + "1.21.3-53.0.8", + "1.21.3-53.0.9", + "1.21.3-53.0.10", + "1.21.3-53.0.11", + "1.21.3-53.0.12", + "1.21.3-53.0.13", + "1.21.3-53.0.14", + "1.21.3-53.0.17", + "1.21.3-53.0.19", + "1.21.3-53.0.21", + "1.21.3-53.0.23", + "1.21.3-53.0.24", + "1.21.3-53.0.25", + "1.21.3-53.0.26", + "1.21.3-53.0.27", + "1.21.3-53.0.28", + "1.21.3-53.0.29", + "1.21.3-53.0.30", + "1.21.3-53.0.32", + "1.21.3-53.0.33", + "1.21.3-53.0.34", + "1.21.3-53.0.35", + "1.21.3-53.0.36", + "1.21.3-53.0.37", + "1.21.3-53.0.39", + "1.21.3-53.0.42", + "1.21.3-53.0.44", + "1.21.3-53.0.45", + "1.21.3-53.0.47", + "1.21.3-53.0.48", + "1.21.3-53.0.49", + "1.21.3-53.0.51", + "1.21.3-53.0.52", + "1.21.3-53.0.53", + "1.21.3-53.0.55", + "1.21.3-53.1.0", + "1.21.3-53.1.1", + "1.21.3-53.1.2" + ], + "1.21.4": [ + "1.21.4-54.0.0", + "1.21.4-54.0.1", + "1.21.4-54.0.3", + "1.21.4-54.0.5", + "1.21.4-54.0.6", + "1.21.4-54.0.7", + "1.21.4-54.0.9", + "1.21.4-54.0.10", + "1.21.4-54.0.11", + "1.21.4-54.0.12", + "1.21.4-54.0.13", + "1.21.4-54.0.14", + "1.21.4-54.0.15", + "1.21.4-54.0.16", + "1.21.4-54.0.17", + "1.21.4-54.0.18", + "1.21.4-54.0.20", + "1.21.4-54.0.21", + "1.21.4-54.0.24", + "1.21.4-54.0.25", + "1.21.4-54.0.26", + "1.21.4-54.0.27", + "1.21.4-54.0.29", + "1.21.4-54.0.30", + "1.21.4-54.0.31", + "1.21.4-54.0.32", + "1.21.4-54.0.33", + "1.21.4-54.0.34", + "1.21.4-54.0.35", + "1.21.4-54.0.36", + "1.21.4-54.0.37", + "1.21.4-54.0.38", + "1.21.4-54.1.0", + "1.21.4-54.1.1", + "1.21.4-54.1.2", + "1.21.4-54.1.3", + "1.21.4-54.1.4", + "1.21.4-54.1.5", + "1.21.4-54.1.6" + ], + "1.21.5": [ + "1.21.5-55.0.0", + "1.21.5-55.0.1", + "1.21.5-55.0.2", + "1.21.5-55.0.3", + "1.21.5-55.0.4", + "1.21.5-55.0.5", + "1.21.5-55.0.6", + "1.21.5-55.0.7", + "1.21.5-55.0.8", + "1.21.5-55.0.9", + "1.21.5-55.0.10", + "1.21.5-55.0.11", + "1.21.5-55.0.12", + "1.21.5-55.0.14", + "1.21.5-55.0.15", + "1.21.5-55.0.16", + "1.21.5-55.0.17", + "1.21.5-55.0.19", + "1.21.5-55.0.20", + "1.21.5-55.0.21", + "1.21.5-55.0.22", + "1.21.5-55.0.23", + "1.21.5-55.0.24", + "1.21.5-55.0.25", + "1.21.5-55.1.0" + ], + "1.21.6": [ + "1.21.6-56.0.0", + "1.21.6-56.0.1", + "1.21.6-56.0.2", + "1.21.6-56.0.3", + "1.21.6-56.0.4", + "1.21.6-56.0.5", + "1.21.6-56.0.6", + "1.21.6-56.0.7", + "1.21.6-56.0.8", + "1.21.6-56.0.9" + ], + "1.21.7": [ + "1.21.7-57.0.0", + "1.21.7-57.0.1", + "1.21.7-57.0.2", + "1.21.7-57.0.3" + ], + "1.21.8": [ + "1.21.8-58.0.0", + "1.21.8-58.0.1", + "1.21.8-58.0.2", + "1.21.8-58.0.3", + "1.21.8-58.0.4", + "1.21.8-58.0.5", + "1.21.8-58.0.6", + "1.21.8-58.0.7", + "1.21.8-58.0.8", + "1.21.8-58.0.9", + "1.21.8-58.0.10", + "1.21.8-58.1.0", + "1.21.8-58.1.1", + "1.21.8-58.1.2" + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a7062999..3b567e24 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,112 +1,165 @@ { "name": "minecraft-java-core", - "version": "3.5.7", + "version": "4.2.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "minecraft-java-core", - "version": "3.5.7", + "version": "4.2.7", "license": "CCANC", "dependencies": { - "adm-zip": "^0.5.9", - "minecraft-loader": "^1.3.0", - "node-fetch": "^2.6.9", - "prompt": "^1.2.1", - "tslib": "^2.4.1" + "prompt": "^1.3.0", + "semver": "^7.7.2", + "tslib": "^2.8.1" }, "devDependencies": { - "@types/node": "^18.11.13", - "@types/node-fetch": "^2.6.2", - "rimraf": "^3.0.2", - "typescript": "^4.9.4" + "@types/node": "^22.10.2", + "@types/semver": "^7.7.0", + "rimraf": "^6.0.1", + "typescript": "^5.7.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "license": "MIT", "engines": { "node": ">=0.1.90" } }, - "node_modules/@types/node": { - "version": "18.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", - "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==", - "dev": true + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } }, - "node_modules/@types/node-fetch": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", - "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", + "node_modules/@types/node": { + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*", - "form-data": "^3.0.0" + "undici-types": "~6.20.0" } }, - "node_modules/adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", + "node_modules/@types/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=6.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/async": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", - "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "license": "MIT" }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/colors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", + "license": "MIT", "engines": { "node": ">=0.1.90" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 8" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, "node_modules/cycle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", @@ -115,14 +168,19 @@ "node": ">=0.4.0" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, - "engines": { - "node": ">=0.4.0" - } + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" }, "node_modules/eyes": { "version": "0.1.8", @@ -132,160 +190,173 @@ "node": "> 0.1.90" } }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, + "license": "ISC", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">= 6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, + "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "license": "MIT" + }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", "dev": true, + "license": "ISC", "engines": { - "node": ">= 0.6" + "node": "20 || >=22" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, + "license": "ISC", "dependencies": { - "mime-db": "1.52.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minecraft-loader": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/minecraft-loader/-/minecraft-loader-1.3.0.tgz", - "integrity": "sha512-r+tXeGBb8CyIVfpLTL1fdG7GQv0yfzygktYk3BVWMYNFLhd3nGBxN/HCR5Lo53iir8m9P/2X9S2jGJ74BDqbHQ==", - "dependencies": { - "adm-zip": "^0.5.10", - "node-fetch": "^2.6.9" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "license": "ISC", "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" } }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" }, - "node_modules/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "dependencies": { - "wrappy": "1" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, "engines": { - "node": ">=0.10.0" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/prompt": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.3.0.tgz", "integrity": "sha512-ZkaRWtaLBZl7KKAKndKYUL8WqNT+cQHKRZnT4RYYms48jQkFw3rrBL+/N5K/KtdEveHkxs982MX2BkDKub2ZMg==", + "license": "MIT", "dependencies": { "@colors/colors": "1.5.0", "async": "3.2.3", @@ -301,6 +372,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "license": "ISC", "dependencies": { "mute-stream": "~0.0.4" }, @@ -312,20 +384,74 @@ "version": "0.1.8", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", "integrity": "sha512-xcBILK2pA9oh4SiinPEZfhP8HfrB/ha+a2fTMyl7Om2WjlDVrOQy99N2MXXlUHqGJz4qEu2duXxHJjDWuK/0xg==", + "license": "Apache 2.0", "engines": { "node": ">= 0.4.0" } }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dev": true, + "license": "ISC", "dependencies": { - "glob": "^7.1.3" + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", "bin": { - "rimraf": "bin.js" + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -335,51 +461,163 @@ "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", "engines": { "node": "*" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, "node_modules/winston": { "version": "2.4.7", "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.7.tgz", "integrity": "sha512-vLB4BqzCKDnnZH9PHGoS2ycawueX4HLqENXQitvFHczhgW2vFpSOn31LZtVr1KU8YTw7DS4tM+cqyovxo8taVg==", + "license": "MIT", "dependencies": { "async": "^2.6.4", "colors": "1.0.x", @@ -396,15 +634,108 @@ "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "license": "MIT", "dependencies": { "lodash": "^4.17.14" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } } } } diff --git a/package.json b/package.json index a68e30da..b6969c7a 100755 --- a/package.json +++ b/package.json @@ -1,17 +1,22 @@ { "name": "minecraft-java-core", - "version": "3.5.7", + "version": "4.2.7", "types": "./build/Index.d.ts", "exports": { ".": { - "import": "./build/Index.js", - "require": "./build/Index.js" + "import": "./build/esm/Index.js", + "require": "./build/Index.js", + "types": "./build/Index.d.ts" } }, "description": "A library starting minecraft game NW.js and Electron.js", "scripts": { "dev": "rimraf ./build && tsc -w", - "build": "rimraf ./build && tsc" + "build": "rimraf ./build && tsc && tsc -p tsconfig.esm.json", + "prepublishOnly": "npm i && npm run build" + }, + "engines": { + "node": ">=18.0.0" }, "files": [ "assets/**", @@ -31,17 +36,15 @@ "author": "Luuxis", "license": "CCANC", "dependencies": { - "adm-zip": "^0.5.9", - "minecraft-loader": "^1.3.0", - "node-fetch": "^2.6.9", - "prompt": "^1.2.1", - "tslib": "^2.4.1" + "prompt": "^1.3.0", + "semver": "^7.7.2", + "tslib": "^2.8.1" }, "devDependencies": { - "@types/node": "^18.11.13", - "@types/node-fetch": "^2.6.2", - "rimraf": "^3.0.2", - "typescript": "^4.9.4" + "@types/node": "^22.10.2", + "@types/semver": "^7.7.0", + "rimraf": "^6.0.1", + "typescript": "^5.7.2" }, "bugs": { "url": "https://github.com/luuxis/minecraft-java-core/issues" diff --git a/src/Authenticator/AZauth.ts b/src/Authenticator/AZauth.ts index 819e4c2d..10f73a1c 100755 --- a/src/Authenticator/AZauth.ts +++ b/src/Authenticator/AZauth.ts @@ -1,105 +1,182 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ -import nodeFetch from 'node-fetch'; +import { Buffer } from 'node:buffer'; +import type { AZauthUser, AZauthUserInfo } from '../types.js'; + +export type { AZauthUser, AZauthUserInfo }; export default class AZauth { - url: string; - constructor(url: string) { - this.url = `${url}/api/auth`; - } - - async login(username: string, password: string, A2F: any = null) { - let response = await nodeFetch(`${this.url}/authenticate`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - email: username, - password: password, - code: A2F - }), - }).then((res: any) => res.json()) - - if (response.status == 'pending' && response.reason == '2fa') { - return { A2F: true }; - } - - if (response.status == 'error') { - return { - error: true, - reason: response.reason, - message: response.message - }; - } - - return { - access_token: response.access_token, - client_token: response.uuid, - uuid: response.uuid, - name: response.username, - user_properties: '{}', - user_info: { - banned: response.banned, - money: response.money, - role: response.role - }, - meta: { - online: false, - type: 'AZauth', - } - } - } - - async verify(user: any) { - let response = await nodeFetch(`${this.url}/verify`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - access_token: user.access_token - }), - }).then((res: any) => res.json()) - - if (response.status == 'error') { - return { - error: true, - reason: response.reason, - message: response.message - }; - } - - return { - access_token: response.access_token, - client_token: response.uuid, - uuid: response.uuid, - name: response.username, - user_properties: '{}', - user_info: { - banned: response.banned, - money: response.money, - role: response.role - }, - meta: { - online: false, - type: 'AZauth', - } - } - } - - async signout(user: any) { - let auth = await nodeFetch(`${this.url}/logout`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - access_token: user.access_token - }), - }).then((res: any) => res.json()) - if (auth.error) return false; - return true - } -} \ No newline at end of file + private url: string; + private skinAPI: string; + + /** + * The constructor prepares the authentication and skin URLs from the base URL. + * @param url The base URL of the AZauth server + */ + constructor(url: string) { + // '/api/auth' for authentication, '/api/skin-api/skins' for skin data + this.url = new URL('/api/auth', url).toString(); + this.skinAPI = new URL('/api/skin-api/skins', url).toString(); + } + + /** + * Authenticates a user using their username/email and password. + * Optionally, a 2FA code can be provided. + * + * @param username The email or username for authentication + * @param password The password + * @param A2F Optional 2FA code + * @returns A Promise that resolves to an AZauthUser object + */ + public async login(username: string, password: string, A2F: string | null = null): Promise { + const response = await fetch(`${this.url}/authenticate`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + email: username, + password, + code: A2F + }) + }); + + const data = await response.json(); + + // If the server indicates that 2FA is required + if (data.status === 'pending' && data.reason === '2fa') { + return { A2F: true }; + } + + // If the server returns an error status + if (data.status === 'error') { + return { + error: true, + reason: data.reason, + message: data.message + }; + } + + // If authentication is successful, return the complete user object + return { + access_token: data.access_token, + client_token: data.uuid, + uuid: data.uuid, + name: data.username, + user_properties: '{}', + user_info: { + id: data.id, + banned: data.banned, + money: data.money, + role: data.role, + verified: data.email_verified + }, + meta: { + online: false, + type: 'AZauth' + }, + profile: { + skins: [await this.skin(data.id)] + } + }; + } + + /** + * Verifies an existing session (e.g., for refreshing tokens). + * @param user An AZauthUser object containing at least the access token + * @returns A Promise that resolves to an updated AZauthUser object or an error object + */ + public async verify(user: AZauthUser): Promise { + const response = await fetch(`${this.url}/verify`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + access_token: user.access_token + }) + }); + + const data = await response.json(); + + // If the server indicates an error + if (data.status === 'error') { + return { + error: true, + reason: data.reason, + message: data.message + }; + } + + // Return the updated user session object + return { + access_token: data.access_token, + client_token: data.uuid, + uuid: data.uuid, + name: data.username, + user_properties: '{}', + user_info: { + id: data.id, + banned: data.banned, + money: data.money, + role: data.role, + verified: data.email_verified + }, + meta: { + online: false, + type: 'AZauth' + }, + profile: { + skins: [await this.skin(data.id)] + } + }; + } + + /** + * Logs out a user from the AZauth service (invalidates the token). + * @param user The AZauthUser object with a valid access token + * @returns A Promise that resolves to true if logout is successful, otherwise false + */ + public async signout(user: AZauthUser): Promise { + const response = await fetch(`${this.url}/logout`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + access_token: user.access_token + }) + }); + + const data = await response.json(); + if (data.error) return false; + return true; + } + + /** + * Retrieves the skin of a user by their ID (UUID). + * If the skin exists, returns both the direct URL and a base64-encoded PNG. + * If the skin doesn't exist, only the URL is returned. + * + * @param uuid The UUID or ID of the user + * @returns A Promise resolving to an object with the skin URL (and optional base64 data) + */ + private async skin(uuid: string): Promise<{ url: string; base64?: string }> { + let response = await fetch(`${this.skinAPI}/${uuid}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' } + }); + + // If the skin is not found (404), return only the URL + if (response.status === 404) { + return { + url: `${this.skinAPI}/${uuid}` + }; + } + + // Otherwise, convert the skin image to a base64-encoded string + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + return { + url: `${this.skinAPI}/${uuid}`, + base64: `data:image/png;base64,${buffer.toString('base64')}` + }; + } +} diff --git a/src/Authenticator/GUI/Electron.ts b/src/Authenticator/GUI/Electron.ts index db0d6f70..7196a921 100755 --- a/src/Authenticator/GUI/Electron.ts +++ b/src/Authenticator/GUI/Electron.ts @@ -1,6 +1,6 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ const path = require('path') @@ -14,7 +14,7 @@ const defaultProperties = { icon: path.join(__dirname, '../../../assets/icons', `Microsoft.${(process.platform === 'win32') ? 'ico' : 'png'}`), } -module.exports = async function (url: string) { +module.exports = async function (url: string, redirect_uri: string = "https://login.live.com/oauth20_desktop.srf") { await new Promise((resolve: any) => { app.whenReady().then(() => { session.defaultSession.cookies.get({ domain: 'live.com' }).then((cookies: any) => { @@ -40,7 +40,7 @@ module.exports = async function (url: string) { mainWindow.webContents.on("did-finish-load", () => { const loc = mainWindow.webContents.getURL(); - if (loc.startsWith("https://login.live.com/oauth20_desktop.srf")) { + if (loc.startsWith(redirect_uri)) { const urlParams = new URLSearchParams(loc.substr(loc.indexOf("?") + 1)).get("code"); if (urlParams) { resolve(urlParams); diff --git a/src/Authenticator/GUI/NW.ts b/src/Authenticator/GUI/NW.ts index 0985f61a..1fe3f8d7 100755 --- a/src/Authenticator/GUI/NW.ts +++ b/src/Authenticator/GUI/NW.ts @@ -1,9 +1,8 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ -'use strict'; import path from 'path'; const defaultProperties = { @@ -15,7 +14,7 @@ const defaultProperties = { icon: path.join(__dirname, '../../../assets/icons/Microsoft.png') } -module.exports = async function (url: string) { +module.exports = async function (url: string, redirect_uri: string = "https://login.live.com/oauth20_desktop.srf") { await new Promise((resolve: any) => { //@ts-ignore nw.Window.get().cookies.getAll({ domain: "live.com" }, async (cookies) => { @@ -34,7 +33,7 @@ module.exports = async function (url: string) { let interval = null; let code; interval = setInterval(() => { - if (Window.window.document.location.href.startsWith("https://login.live.com/oauth20_desktop.srf")) { + if (Window.window.document.location.href.startsWith(redirect_uri)) { clearInterval(interval); try { code = Window.window.document.location.href.split("code=")[1].split("&")[0]; diff --git a/src/Authenticator/GUI/Terminal.ts b/src/Authenticator/GUI/Terminal.ts index 0349fb4c..9eddb839 100755 --- a/src/Authenticator/GUI/Terminal.ts +++ b/src/Authenticator/GUI/Terminal.ts @@ -1,6 +1,6 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ import prompt from 'prompt'; diff --git a/src/Authenticator/Microsoft.ts b/src/Authenticator/Microsoft.ts index d5a14e31..ce1090b2 100755 --- a/src/Authenticator/Microsoft.ts +++ b/src/Authenticator/Microsoft.ts @@ -1,212 +1,306 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ -import nodeFetch from 'node-fetch'; +import { Buffer } from 'node:buffer'; import crypto from 'crypto'; +import type { + MicrosoftClientType, + MinecraftSkin, + MinecraftProfile, + AuthError, + MicrosoftAuthResponse as AuthResponse +} from '../types.js'; +export type { MicrosoftClientType, MinecraftSkin, MinecraftProfile, AuthError, AuthResponse }; -export default class Microsoft { - client_id: string; - type: 'electron' | 'nwjs' | 'terminal'; - - constructor(client_id: string) { - if (client_id === '' || !client_id) client_id = '00000000402b5328'; - this.client_id = client_id; - - if (!!process && !!process.versions && !!process.versions.electron) { - this.type = 'electron'; - } else if (!!process && !!process.versions && !!process.versions.nw) { - this.type = 'nwjs'; - } else { - this.type = 'terminal'; - } - } - - async getAuth(type: string, url: string) { - if (!url) url = `https://login.live.com/oauth20_authorize.srf?client_id=${this.client_id}&response_type=code&redirect_uri=https://login.live.com/oauth20_desktop.srf&scope=XboxLive.signin%20offline_access&cobrandid=8058f65d-ce06-4c30-9559-473c9275a65d&prompt=select_account`; - if (!type) type = this.type; - - if (type == "electron") { - let usercode = await (require('./GUI/Electron.js'))(url) - if (usercode === "cancel") return false; - else return await this.url(usercode); - } else if (type == "nwjs") { - let usercode = await (require('./GUI/NW.js'))(url) - if (usercode === "cancel") return false; - else return await this.url(usercode); - } else if (type == "terminal") { - let usercode = await (require('./GUI/Terminal.js'))(url) - if (usercode === "cancel") return false; - else return await this.url(usercode); - } - } - - async url(code: string) { - let oauth2 = await nodeFetch("https://login.live.com/oauth20_token.srf", { - method: "POST", - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - }, - body: `client_id=${this.client_id}&code=${code}&grant_type=authorization_code&redirect_uri=https://login.live.com/oauth20_desktop.srf` - }).then(res => res.json()).catch(err => { return { error: err } });; - if (oauth2.error) return oauth2; - return await this.getAccount(oauth2) - } - - async refresh(acc: any) { - let timeStamp = Math.floor(Date.now() / 1000) - let oauth2 = await nodeFetch("https://login.live.com/oauth20_token.srf", { - method: "POST", - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - }, - body: `grant_type=refresh_token&client_id=${this.client_id}&refresh_token=${acc.refresh_token}` - }).then(res => res.json()).catch(err => { return { error: err } });; - if (oauth2.error) { - oauth2.errorType = "oauth2"; - return oauth2 - }; - - if (timeStamp < (acc?.meta?.access_token_expires_in - 7200)) { - let profile = await this.getProfile(acc) - acc.refresh_token = oauth2.refresh_token - acc.profile = { - skins: profile.skins, - capes: profile.capes - } - return acc - } else { - return await this.getAccount(oauth2) - } - } - - async getAccount(oauth2: any) { - let xbl = await nodeFetch("https://user.auth.xboxlive.com/user/authenticate", { - method: "post", - body: JSON.stringify({ - Properties: { - AuthMethod: "RPS", - SiteName: "user.auth.xboxlive.com", - RpsTicket: "d=" + oauth2.access_token - }, - RelyingParty: "http://auth.xboxlive.com", - TokenType: "JWT" - }), - headers: { "Content-Type": "application/json", Accept: "application/json" }, - }).then(res => res.json()).catch(err => { return { error: err } });; - if (xbl.error) { - xbl.errorType = "xbl"; - return xbl - } - - let xsts = await nodeFetch("https://xsts.auth.xboxlive.com/xsts/authorize", { - method: "POST", - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - Properties: { - SandboxId: "RETAIL", - UserTokens: [xbl.Token] - }, - RelyingParty: "rp://api.minecraftservices.com/", - TokenType: "JWT" - }) - }).then(res => res.json()); - if (xsts.error) { - xsts.errorType = "xsts"; - return xsts - } - - let xboxAccount = await nodeFetch("https://xsts.auth.xboxlive.com/xsts/authorize", { - method: "POST", - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - Properties: { - SandboxId: "RETAIL", - UserTokens: [xbl.Token] - }, - RelyingParty: "http://xboxlive.com", - TokenType: "JWT" - }) - }).then(res => res.json()).catch(err => { return { error: err } }); - if (xsts.error) { - xsts.errorType = "xboxAccount"; - return xsts - } - - let mcLogin = await nodeFetch("https://api.minecraftservices.com/authentication/login_with_xbox", { - method: "POST", - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ "identityToken": `XBL3.0 x=${xbl.DisplayClaims.xui[0].uhs};${xsts.Token}` }) - }).then(res => res.json()).catch(err => { return { error: err } }); - if (mcLogin.error) { - mcLogin.errorType = "mcLogin"; - return mcLogin - } - - let profile = await this.getProfile(mcLogin); - if (profile.error) { - profile.errorType = "profile"; - return profile - } - - return { - access_token: mcLogin.access_token, - client_token: crypto.randomBytes(16).toString('hex'), - uuid: profile.id, - name: profile.name, - refresh_token: oauth2.refresh_token, - user_properties: '{}', - meta: { - xuid: xboxAccount.DisplayClaims.xui[0].xid, - type: "Xbox", - access_token_expires_in: mcLogin.expires_in + Math.floor(Date.now() / 1000), - demo: profile.error ? true : false - }, - profile: { - skins: profile.skins, - capes: profile.capes - } - } - } - - async getProfile(mcLogin: any) { - let profile = await nodeFetch("https://api.minecraftservices.com/minecraft/profile", { - method: "GET", - headers: { - 'Authorization': `Bearer ${mcLogin.access_token}` - } - }).then(res => res.json()).catch(err => { return { error: err } });; - if (profile.error) return profile - - let skins = profile.skins; - let capes = profile.capes; - - for (let skin of skins) { - skin.base64 = `data:image/png;base64,${await getBass64(skin.url)}` - } - for (let cape of capes) { - cape.base64 = `data:image/png;base64,${await getBass64(cape.url)}` - } - - return { - id: profile.id, - name: profile.name, - skins: profile.skins || [], - capes: profile.capes || [] - } - } +// Utility function to fetch and convert an image to base64 +async function getBase64(url: string): Promise { + const response = await fetch(url); + if (response.ok) { + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + return buffer.toString('base64'); + } else { + return ''; + } } -async function getBass64(url: string) { - let response = await nodeFetch(url); - let buffer = await response.buffer(); - return buffer.toString('base64'); +export default class Microsoft { + public client_id: string; + public type: MicrosoftClientType; + public redirect_uri: string; + + /** + * Creates a Microsoft auth instance. + * @param client_id Your Microsoft OAuth client ID (default: '00000000402b5328' if none provided). + */ + constructor(client_id: string, redirect_uri?: string) { + this.client_id = client_id || '00000000402b5328'; + this.redirect_uri = redirect_uri || 'https://login.live.com/oauth20_desktop.srf'; + + // Determine if we're running under Electron, NW.js, or just in a terminal + if (typeof process !== 'undefined' && process.versions && process.versions.electron) { + this.type = 'electron'; + } else if (typeof process !== 'undefined' && process.versions && process.versions.nw) { + this.type = 'nwjs'; + } else { + this.type = 'terminal'; + } + } + + /** + * Opens a GUI (Electron or NW.js) or uses terminal approach to fetch an OAuth2 code, + * and then retrieves user information from Microsoft if successful. + * + * @param type The environment to open the OAuth window. Defaults to the auto-detected type. + * @param url The full OAuth2 authorization URL. If not provided, a default is used. + * @returns An object with user data on success, or false if canceled. + */ + public async getAuth(type?: MicrosoftClientType, url?: string): Promise { + const finalType = type || this.type; + const finalUrl = url || `https://login.live.com/oauth20_authorize.srf?client_id=${this.client_id}&response_type=code&redirect_uri=${this.redirect_uri}&scope=XboxLive.signin%20offline_access&cobrandid=8058f65d-ce06-4c30-9559-473c9275a65d&prompt=select_account`; + + let userCode: string | 'cancel'; + switch (finalType) { + case 'electron': + userCode = await (require('./GUI/Electron.js'))(finalUrl, this.redirect_uri); + break; + case 'nwjs': + userCode = await (require('./GUI/NW.js'))(finalUrl, this.redirect_uri); + break; + case 'terminal': + userCode = await (require('./GUI/Terminal.js'))(finalUrl, this.redirect_uri); + break; + default: + return false; + } + + // Exchange the code for an OAuth2 token, then retrieve account data + if (userCode === 'cancel') return false; + return this.exchangeCodeForToken(userCode); + } + + /** + * Exchanges an OAuth2 authorization code for an access token, then retrieves account information. + * @param code The OAuth2 authorization code returned by Microsoft. + * @returns The authenticated user data or an error object. + */ + private async exchangeCodeForToken(code: string): Promise { + try { + const response = await fetch('https://login.live.com/oauth20_token.srf', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: `client_id=${this.client_id}&code=${code}&grant_type=authorization_code&redirect_uri=${this.redirect_uri}` + }); + const oauth2 = await response.json(); + + if (oauth2.error) { + return { error: oauth2.error, errorType: 'oauth2', ...oauth2 }; + } + return this.getAccount(oauth2); + } catch (err: any) { + return { error: err.message, errorType: 'network' }; + } + } + + /** + * Refreshes the user's session if the token has expired or is about to expire. + * Otherwise, simply fetches the user's profile. + * + * @param acc A previously obtained AuthResponse object. + * @returns Updated AuthResponse (with new token if needed) or an error object. + */ + public async refresh(acc: AuthResponse): Promise { + const timeStamp = Math.floor(Date.now()); + + // If the token is still valid for at least 2 more hours, just re-fetch the profile + if (timeStamp < (acc?.meta?.access_token_expires_in - 7200)) { + const updatedProfile = await this.getProfile({ access_token: acc.access_token }); + if ('error' in updatedProfile) { + // If there's an error, return it directly + return updatedProfile; + } + acc.profile = { + skins: updatedProfile.skins, + capes: updatedProfile.capes + }; + return acc; + } + + // Otherwise, refresh the token + try { + const response = await fetch('https://login.live.com/oauth20_token.srf', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: `grant_type=refresh_token&client_id=${this.client_id}&refresh_token=${acc.refresh_token}` + }); + const oauth2 = await response.json(); + + if (oauth2.error) { + return { error: oauth2.error, errorType: 'oauth2', ...oauth2 }; + } + // Retrieve account data with the new tokens + return this.getAccount(oauth2); + } catch (err: any) { + return { error: err.message, errorType: 'network' }; + } + } + + /** + * Retrieves and assembles the full account details (Xbox Live, XSTS, Minecraft). + * @param oauth2 The token object returned by the Microsoft OAuth endpoint. + * @returns A fully populated AuthResponse object or an error. + */ + private async getAccount(oauth2: { access_token: string; refresh_token: string }): Promise { + const authenticateResponse = await fetch('https://user.auth.xboxlive.com/user/authenticate', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, + body: JSON.stringify({ + Properties: { + AuthMethod: 'RPS', + SiteName: 'user.auth.xboxlive.com', + RpsTicket: `d=${oauth2.access_token}`, + }, + RelyingParty: 'http://auth.xboxlive.com', + TokenType: 'JWT', + }), + }); + const xbl = await authenticateResponse.json(); + if (xbl.error) { + return { error: xbl.error, errorType: 'xbl', ...xbl, refresh_token: oauth2.refresh_token }; + } + + const authorizeResponse = await fetch('https://xsts.auth.xboxlive.com/xsts/authorize', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, + body: JSON.stringify({ + Properties: { + SandboxId: 'RETAIL', + UserTokens: [xbl.Token], + }, + RelyingParty: 'rp://api.minecraftservices.com/', + TokenType: 'JWT', + }), + }); + const xsts = await authorizeResponse.json(); + if (xsts.error) { + return { error: xsts.error, errorType: 'xsts', ...xsts, refresh_token: oauth2.refresh_token }; + } + + const mcLoginResponse = await fetch('https://api.minecraftservices.com/authentication/login_with_xbox', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, + body: JSON.stringify({ + identityToken: `XBL3.0 x=${xbl.DisplayClaims.xui[0].uhs};${xsts.Token}` + }), + }); + const mcLogin = await mcLoginResponse.json(); + if (mcLogin.error) { + return { error: mcLogin.error, errorType: 'mcLogin', ...mcLogin, refresh_token: oauth2.refresh_token }; + } + if (!mcLogin.username) { + return { error: 'NO_MINECRAFT_ACCOUNT', errorType: 'mcLogin', ...mcLogin, refresh_token: oauth2.refresh_token }; + } + + const mcstoreResponse = await fetch('https://api.minecraftservices.com/entitlements/mcstore', { + method: 'GET', + headers: { 'Authorization': `Bearer ${mcLogin.access_token}` }, + }); + const mcstore = await mcstoreResponse.json(); + if (mcstore.error) { + return { error: mcstore.error, errorType: 'mcStore', ...mcstore, refresh_token: oauth2.refresh_token }; + } + + if (!mcstore.items.some((item: { name: string }) => item.name === "game_minecraft" || item.name === "product_minecraft")) { + return { error: 'NO_MINECRAFT_ENTITLEMENTS', errorType: 'mcStore', ...mcstore, refresh_token: oauth2.refresh_token }; + } + + + const profile = await this.getProfile(mcLogin); + if ('error' in profile) { + return { error: profile.error, errorType: 'mcProfile', ...profile, refresh_token: oauth2.refresh_token }; + } + + const xboxAccountResponse = await fetch('https://xsts.auth.xboxlive.com/xsts/authorize', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + Properties: { + SandboxId: 'RETAIL', + UserTokens: [xbl.Token] + }, + RelyingParty: 'http://xboxlive.com', + TokenType: 'JWT' + }) + }); + const xboxAccount = await xboxAccountResponse.json(); + if (xboxAccount.error) { + return { error: xboxAccount.error, errorType: 'xboxAccount', ...xboxAccount, refresh_token: oauth2.refresh_token }; + } + + return { + access_token: mcLogin.access_token, + client_token: crypto.randomUUID(), + uuid: profile.id, + name: profile.name, + refresh_token: oauth2.refresh_token, + user_properties: "{}", + meta: { + type: 'Xbox', + access_token_expires_in: Date.now() + (mcLogin.expires_in * 1000), + demo: false + }, + xboxAccount: { + xuid: xboxAccount.DisplayClaims.xui[0].xid, + gamertag: xboxAccount.DisplayClaims.xui[0].gtg, + ageGroup: xboxAccount.DisplayClaims.xui[0].agg + }, + profile: { + skins: [...profile.skins], + capes: [...profile.capes] + } + }; + } + + public async getProfile(mcLogin: { access_token: string }): Promise { + try { + const response = await fetch('https://api.minecraftservices.com/minecraft/profile', { + method: 'GET', + headers: { + Authorization: `Bearer ${mcLogin.access_token}` + } + }); + const profile = await response.json(); + + if (profile.error) { + return { error: profile.error }; + } + + if (Array.isArray(profile.skins)) { + for (const skin of profile.skins) { + if (skin.url) { + skin.base64 = `data:image/png;base64,${await getBase64(skin.url)}`; + } + } + } + if (Array.isArray(profile.capes)) { + for (const cape of profile.capes) { + if (cape.url) { + cape.base64 = `data:image/png;base64,${await getBase64(cape.url)}`; + } + } + } + + return { + id: profile.id, + name: profile.name, + skins: profile.skins || [], + capes: profile.capes || [] + }; + } catch (err: any) { + return { error: err.message }; + } + } } \ No newline at end of file diff --git a/src/Authenticator/Mojang.ts b/src/Authenticator/Mojang.ts index 57d84cd2..018ebf46 100755 --- a/src/Authenticator/Mojang.ts +++ b/src/Authenticator/Mojang.ts @@ -1,141 +1,160 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ import crypto from 'crypto'; -import nodeFetch from 'node-fetch'; +import type { MojangAuthResponse } from '../types.js'; let api_url = 'https://authserver.mojang.com'; +interface MojangApiResponse { + accessToken?: string; + clientToken?: string; + selectedProfile?: { + id: string; + name: string; + }; + error?: string; + errorMessage?: string; +} + +async function login(username: string, password?: string): Promise { + const UUID = crypto.randomBytes(16).toString('hex'); + if (!password) { + return { + access_token: UUID, + client_token: UUID, + uuid: UUID, + name: username, + user_properties: '{}', + meta: { + online: false, + type: 'Mojang' + } + }; + } + + const message: MojangApiResponse = await fetch(`${api_url}/authenticate`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + agent: { + name: "Minecraft", + version: 1 + }, + username, + password, + clientToken: UUID, + requestUser: true + }) + }).then(res => res.json()); + + if (message.error) { + return { + access_token: '', + client_token: '', + uuid: '', + name: '', + user_properties: '{}', + meta: { online: false, type: 'Mojang' }, + error: message.error, + errorMessage: message.errorMessage + }; + } -async function login(username: string, password?: string) { - let UUID = crypto.randomBytes(16).toString('hex'); - if (!password) { - return { - access_token: UUID, - client_token: UUID, - uuid: UUID, - name: username, - user_properties: '{}', - meta: { - online: false, - type: 'Mojang' - } - } - } - - let message = await nodeFetch(`${api_url}/authenticate`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - agent: { - name: "Minecraft", - version: 1 - }, - username, - password, - clientToken: UUID, - requestUser: true - }) - }).then(res => res.json()); - - if (message.error) { - return message; - }; - let user = { - access_token: message.accessToken, - client_token: message.clientToken, - uuid: message.selectedProfile.id, - name: message.selectedProfile.name, - user_properties: '{}', - meta: { - online: true, - type: 'Mojang' - } - } - return user; + return { + access_token: message.accessToken!, + client_token: message.clientToken!, + uuid: message.selectedProfile!.id, + name: message.selectedProfile!.name, + user_properties: '{}', + meta: { + online: true, + type: 'Mojang' + } + }; } -async function refresh(acc: any) { - let message = await nodeFetch(`${api_url}/refresh`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - accessToken: acc.access_token, - clientToken: acc.client_token, - requestUser: true - }) - }).then(res => res.json()); - - if (message.error) { - return message; - }; - - let user = { - access_token: message.accessToken, - client_token: message.clientToken, - uuid: message.selectedProfile.id, - name: message.selectedProfile.name, - user_properties: '{}', - meta: { - online: true, - type: 'Mojang' - } - } - return user; +async function refresh(acc: MojangAuthResponse): Promise { + const message: MojangApiResponse = await fetch(`${api_url}/refresh`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + accessToken: acc.access_token, + clientToken: acc.client_token, + requestUser: true + }) + }).then(res => res.json()); + + if (message.error) { + return { + access_token: '', + client_token: '', + uuid: '', + name: '', + user_properties: '{}', + meta: { online: false, type: 'Mojang' }, + error: message.error, + errorMessage: message.errorMessage + }; + } + + return { + access_token: message.accessToken!, + client_token: message.clientToken!, + uuid: message.selectedProfile!.id, + name: message.selectedProfile!.name, + user_properties: '{}', + meta: { + online: true, + type: 'Mojang' + } + }; } -async function validate(acc: any) { - let message = await nodeFetch(`${api_url}/validate`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - accessToken: acc.access_token, - clientToken: acc.client_token, - }) - }); - - if (message.status == 204) { - return true; - } else { - return false; - } +async function validate(acc: MojangAuthResponse): Promise { + const message = await fetch(`${api_url}/validate`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + accessToken: acc.access_token, + clientToken: acc.client_token, + }) + }); + + return message.status === 204; } -async function signout(acc: any) { - let message = await nodeFetch(`${api_url}/invalidate`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - accessToken: acc.access_token, - clientToken: acc.client_token, - }) - }).then(res => res.text()); - - if (message == "") { - return true; - } else { - return false; - } +async function signout(acc: MojangAuthResponse): Promise { + const message = await fetch(`${api_url}/invalidate`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + accessToken: acc.access_token, + clientToken: acc.client_token, + }) + }).then(res => res.text()); + + return message === ""; } -function ChangeAuthApi(url: string) { - api_url = url +function ChangeAuthApi(url: string): void { + api_url = url; } export { - login as login, - refresh as refresh, - validate as validate, - signout as signout, - ChangeAuthApi as ChangeAuthApi -} \ No newline at end of file + login, + refresh, + validate, + signout, + ChangeAuthApi +}; \ No newline at end of file diff --git a/src/Index.ts b/src/Index.ts index cb838cf8..d7d0c12f 100755 --- a/src/Index.ts +++ b/src/Index.ts @@ -1,18 +1,40 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ + import AZauth from './Authenticator/AZauth.js'; import Launch from './Launch.js'; import Microsoft from './Authenticator/Microsoft.js'; import * as Mojang from './Authenticator/Mojang.js'; import Status from './StatusServer/status.js'; +import Downloader from './utils/Downloader.js'; +// Re-export types for consumers +export type { + LaunchOptions, + LaunchArguments, + LoaderArguments, + LoaderConfig, + LoaderJSON, + LoaderType, + Authenticator, + MinecraftVersionJSON, + JavaDownloadResult, + DownloadFile, + ScreenOptions, + MemoryOptions, + JavaOptions, + MicrosoftAuthResponse, + AZauthUser, + MojangAuthResponse, +} from './types.js'; export { AZauth as AZauth, Launch as Launch, Microsoft as Microsoft, Mojang as Mojang, - Status as Status + Status as Status, + Downloader as Downloader }; \ No newline at end of file diff --git a/src/Launch.ts b/src/Launch.ts index 5c4d8848..a28b1b3a 100755 --- a/src/Launch.ts +++ b/src/Launch.ts @@ -1,6 +1,6 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ import { EventEmitter } from 'events'; @@ -9,182 +9,234 @@ import fs from 'fs'; import { spawn } from 'child_process'; import jsonMinecraft from './Minecraft/Minecraft-Json.js'; - import librariesMinecraft from './Minecraft/Minecraft-Libraries.js'; import assetsMinecraft from './Minecraft/Minecraft-Assets.js'; import loaderMinecraft from './Minecraft/Minecraft-Loader.js'; import javaMinecraft from './Minecraft/Minecraft-Java.js'; - import bundleMinecraft from './Minecraft/Minecraft-Bundle.js'; import argumentsMinecraft from './Minecraft/Minecraft-Arguments.js'; import { isold } from './utils/Index.js'; - import Downloader from './utils/Downloader.js'; +import type { + LaunchOptions, + LaunchArguments, + LoaderArguments, + LoaderJSON, + MinecraftVersionJSON, + JavaDownloadResult, + DownloadFile, +} from './types.js'; + +export type LaunchOPTS = LaunchOptions; + +export default class Launch extends EventEmitter { + options: LaunchOptions; + + async Launch(opt: LaunchOptions) { + const defaultOptions: LaunchOptions = { + url: null, + authenticator: null, + timeout: 10000, + path: '.Minecraft', + version: 'latest_release', + instance: null, + detached: false, + intelEnabledMac: false, + ignore_log4j: false, + downloadFileMultiple: 5, + bypassOffline: false, + + loader: { + path: './loader', + type: null, + build: 'latest', + enable: false, + }, + + mcp: null, + + verify: false, + ignored: [], + JVM_ARGS: [], + GAME_ARGS: [], + + java: { + path: null, + version: null, + type: 'jre', + }, + + screen: { + width: null, + height: null, + fullscreen: false, + }, + + memory: { + min: '1G', + max: '2G' + }, + ...opt, + } as LaunchOptions; + + this.options = defaultOptions; + this.options.path = path.resolve(this.options.path).replace(/\\/g, '/'); + + if (this.options.mcp) { + if (this.options.instance) this.options.mcp = `${this.options.path}/instances/${this.options.instance}/${this.options.mcp}` + else this.options.mcp = path.resolve(`${this.options.path}/${this.options.mcp}`).replace(/\\/g, '/') + } + + if (this.options.loader.type) { + this.options.loader.type = this.options.loader.type.toLowerCase() + this.options.loader.build = this.options.loader.build.toLowerCase() + } + + if (!this.options.authenticator) return this.emit("error", { error: "Authenticator not found" }); + if (this.options.downloadFileMultiple < 1) this.options.downloadFileMultiple = 1 + if (this.options.downloadFileMultiple > 30) this.options.downloadFileMultiple = 30 + if (typeof this.options.loader.path !== 'string') this.options.loader.path = `./loader/${this.options.loader.type}`; + if (this.options.java.version && typeof this.options.java.type !== 'string') this.options.java.type = 'jre'; + this.start(); + } + + + async start() { + let data = await this.DownloadGame(); + if (!data || 'error' in data) return this.emit('error', data); + let { minecraftJson, minecraftLoader, minecraftVersion, minecraftJava } = data; + + let minecraftArguments: LaunchArguments | { error: string } = await new argumentsMinecraft(this.options).GetArguments(minecraftJson, minecraftLoader); + if ('error' in minecraftArguments) return this.emit('error', minecraftArguments); + + let loaderArguments: LoaderArguments | { error: string } = await new loaderMinecraft(this.options).GetArguments(minecraftLoader, minecraftVersion); + if ('error' in loaderArguments) return this.emit('error', loaderArguments); + + let Arguments: string[] = [ + ...minecraftArguments.jvm, + ...minecraftArguments.classpath, + ...loaderArguments.jvm, + minecraftArguments.mainClass, + ...minecraftArguments.game, + ...loaderArguments.game + ] + + let java: string = this.options.java.path ? this.options.java.path : minecraftJava.path; + let logs = this.options.instance ? `${this.options.path}/instances/${this.options.instance}` : this.options.path; + if (!fs.existsSync(logs)) fs.mkdirSync(logs, { recursive: true }); + + let argumentsLogs: string = Arguments.join(' ') + argumentsLogs = argumentsLogs.replaceAll(this.options.authenticator?.access_token, '????????') + argumentsLogs = argumentsLogs.replaceAll(this.options.authenticator?.client_token, '????????') + argumentsLogs = argumentsLogs.replaceAll(this.options.authenticator?.uuid, '????????') + argumentsLogs = argumentsLogs.replaceAll(this.options.authenticator?.xboxAccount?.xuid, '????????') + argumentsLogs = argumentsLogs.replaceAll(`${this.options.path}/`, '') + this.emit('data', `Launching with arguments ${argumentsLogs}`); + + let minecraftDebug = spawn(java, Arguments, { cwd: logs, detached: this.options.detached }) + minecraftDebug.stdout.on('data', (data) => this.emit('data', data.toString('utf-8'))) + minecraftDebug.stderr.on('data', (data) => this.emit('data', data.toString('utf-8'))) + minecraftDebug.on('close', (code) => this.emit('close', 'Minecraft closed')) + } + + async DownloadGame(): Promise<{ minecraftJson: MinecraftVersionJSON; minecraftLoader: LoaderJSON | null; minecraftVersion: string; minecraftJava: JavaDownloadResult } | void> { + const InfoVersion = await new jsonMinecraft(this.options).GetInfoVersion(); + let loaderJson: LoaderJSON | null = null; + if ('error' in InfoVersion) { this.emit('error', InfoVersion); return; } + + const { json, version } = InfoVersion; + + const libraries = new librariesMinecraft(this.options) + + const bundle = new bundleMinecraft(this.options) + const java = new javaMinecraft(this.options) + + bundle.on('check', (progress: number, size: number, element: string) => { + this.emit('check', progress, size, element); + }); + + java.on('progress', (progress: number, size: number, element: string) => { + this.emit('progress', progress, size, element) + }); + + java.on('extract', (progress: string) => { + this.emit('extract', progress) + }); + + const gameLibraries: DownloadFile[] = await libraries.Getlibraries(json); + const gameLogging: DownloadFile[] = await libraries.GetLogging(); + const gameAssetsOther: DownloadFile[] = await libraries.GetAssetsOthers(this.options.url); + const gameAssets: DownloadFile[] = await new assetsMinecraft(this.options).getAssets(json); + const gameJava: JavaDownloadResult = this.options.java.path ? { files: [], path: this.options.java.path } : await java.getJavaFiles(json); + + + if ('error' in gameJava) { this.emit('error', gameJava); return; } + + const filesList: DownloadFile[] = await bundle.checkBundle([...gameLibraries, ...gameLogging, ...gameAssetsOther, ...gameAssets, ...gameJava.files]); + + if (filesList.length > 0) { + let downloader = new Downloader(); + let totsize = await bundle.getTotalSize(filesList); + + downloader.on("progress", (DL: number, totDL: number, element: string) => { + this.emit("progress", DL, totDL, element); + }); + + downloader.on("speed", (speed: number) => { + this.emit("speed", speed); + }); + + downloader.on("estimated", (time: number) => { + this.emit("estimated", time); + }); + + downloader.on("error", (e: Error) => { + this.emit("error", e); + }); + + await downloader.downloadFileMultiple(filesList, totsize, this.options.downloadFileMultiple, this.options.timeout); + } + + if (this.options.loader.enable === true) { + const loaderInstall = new loaderMinecraft(this.options) + + loaderInstall.on('extract', (extract: string) => { + this.emit('extract', extract); + }); + + loaderInstall.on('progress', (progress: number, size: number, element: string) => { + this.emit('progress', progress, size, element); + }); + + loaderInstall.on('check', (progress: number, size: number, element: string) => { + this.emit('check', progress, size, element); + }); + + loaderInstall.on('patch', (patch: string) => { + this.emit('patch', patch); + }); + + const jsonLoader = await loaderInstall.GetLoader(version, this.options.java.path ? this.options.java.path : gameJava.path) + .then((data: LoaderJSON) => data) + .catch((err: Error) => ({ error: err.message })); + if ('error' in jsonLoader) { this.emit('error', jsonLoader); return; } + loaderJson = jsonLoader; + } + + if (this.options.verify) await bundle.checkFiles([...gameLibraries, ...gameAssetsOther, ...gameAssets, ...gameJava.files]); + + const natives = await libraries.natives(gameLibraries); + if (natives.length === 0) json.nativesList = false; + else json.nativesList = true; + if (isold(json)) new assetsMinecraft(this.options).copyAssets(json); -export default class Launch { - options: any; - on: any; - emit: any; - - constructor() { - this.on = EventEmitter.prototype.on; - this.emit = EventEmitter.prototype.emit; - } - - async Launch(opt: any) { - this.options = { - url: opt?.url || null, - authenticator: opt?.authenticator || null, - timeout: opt?.timeout || 10000, - path: path.resolve(opt?.path || '.Minecraft').replace(/\\/g, '/'), - version: opt?.version || 'latest_release', - instance: opt?.instance || null, - detached: opt?.detached || false, - downloadFileMultiple: opt?.downloadFileMultiple || 3, - - loader: { - type: opt?.loader?.type || null, - build: opt?.loader?.build || 'latest', - enable: opt?.loader?.enable || false, - }, - - verify: opt?.verify || false, - ignored: opt?.ignored || [], - JVM_ARGS: opt?.JVM_ARGS || [], - GAME_ARGS: opt?.GAME_ARGS || [], - - javaPath: opt?.javaPath || null, - - screen: { - width: opt?.screen?.width || null, - height: opt?.screen?.height || null, - fullscreen: opt?.screen?.fullscreen || false, - }, - - memory: { - min: opt?.memory?.min || '1G', - max: opt?.memory?.max || '2G' - } - } - - if (!this.options.authenticator) return this.emit("error", { error: "Authenticator not found" }); - if (this.options.downloadFileMultiple < 1) this.options.downloadFileMultiple = 1 - if (this.options.downloadFileMultiple > 20) this.options.downloadFileMultiple = 20 - if (!this.options.loader.enable) this.options.loader = false; - this.start(); - } - - async start() { - let data: any = await this.DownloadGame(); - if (data.error) return this.emit('error', data); - let { minecraftJson, minecraftLoader, minecraftVersion, minecraftJava } = data; - - let minecraftArguments: any = await new argumentsMinecraft(this.options).GetArguments(minecraftJson, minecraftLoader); - if (minecraftArguments.error) return this.emit('error', minecraftArguments); - - let loaderArguments: any = await new loaderMinecraft(this.options).GetArguments(minecraftLoader, minecraftVersion); - if (loaderArguments.error) return this.emit('error', loaderArguments); - - let Arguments: any = [ - ...minecraftArguments.jvm, - ...loaderArguments.jvm, - ...minecraftArguments.classpath, - ...loaderArguments.game, - ...minecraftArguments.game - ] - - let java: any = this.options.javaPath ? this.options.javaPath : minecraftJava.path; - let logs = this.options.instance ? `${this.options.path}/instances/${this.options.instance}` : this.options.path; - if (!fs.existsSync(logs)) fs.mkdirSync(logs, { recursive: true }); - - let minecraftDebug = spawn(java, Arguments, { cwd: logs, detached: this.options.detached }) - - this.emit('data', `Launching with arguments ${Arguments.join(' ')}`) - minecraftDebug.stdout.on('data', (data) => this.emit('data', data.toString('utf-8'))) - minecraftDebug.stderr.on('data', (data) => this.emit('data', data.toString('utf-8'))) - minecraftDebug.on('close', (code) => this.emit('close', 'Minecraft closed')) - } - - async DownloadGame() { - let InfoVersion = await new jsonMinecraft(this.options).GetInfoVersion(); - let loaderJson: any = null; - if (InfoVersion.error) return InfoVersion - let { json, version } = InfoVersion; - - let libraries = new librariesMinecraft(this.options) - - let gameLibraries: any = await libraries.Getlibraries(json); - let gameAssetsOther: any = await libraries.GetAssetsOthers(this.options.url); - let gameAssets: any = await new assetsMinecraft(this.options).GetAssets(json); - let gameJava: any = this.options.javaPath ? { files: [] } : await new javaMinecraft(this.options).GetJsonJava(json); - - let bundle: any = [...gameLibraries, ...gameAssetsOther, ...gameAssets, ...gameJava.files] - let filesList: any = await new bundleMinecraft(this.options).checkBundle(bundle); - - if (filesList.length > 0) { - let downloader = new Downloader(); - let totsize = await new bundleMinecraft(this.options).getTotalSize(filesList); - - downloader.on("progress", (DL: any, totDL: any, element: any) => { - this.emit("progress", DL, totDL, element); - }); - - downloader.on("speed", (speed: any) => { - this.emit("speed", speed); - }); - - downloader.on("estimated", (time: any) => { - this.emit("estimated", time); - }); - - downloader.on("error", (e: any) => { - this.emit("error", e); - }); - - await downloader.downloadFileMultiple(filesList, totsize, this.options.downloadFileMultiple, this.options.timeout); - } - - if (this.options.loader) { - let loaderInstall = new loaderMinecraft(this.options) - - loaderInstall.on('extract', (extract: any) => { - this.emit('extract', extract); - }); - - loaderInstall.on('progress', (progress: any, size: any, element: any) => { - this.emit('progress', progress, size, element); - }); - - loaderInstall.on('check', (progress: any, size: any, element: any) => { - this.emit('check', progress, size, element); - }); - - loaderInstall.on('patch', (patch: any) => { - this.emit('patch', patch); - }); - - let jsonLoader = await loaderInstall.GetLoader(version, this.options.javaPath ? this.options.javaPath : gameJava.path) - .then((data: any) => data) - .catch((err: any) => err); - if (jsonLoader.error) return jsonLoader; - loaderJson = jsonLoader; - } - - if (this.options.verify) await libraries.checkFiles(bundle); - - let natives = await libraries.natives(bundle); - if (natives.length === 0) json.nativesList = false; - else json.nativesList = true; - - if (isold(json)) new assetsMinecraft(this.options).copyAssets(json); - - return { - minecraftJson: json, - minecraftLoader: loaderJson, - minecraftVersion: version, - minecraftJava: gameJava - } - } + return { + minecraftJson: json, + minecraftLoader: loaderJson, + minecraftVersion: version, + minecraftJava: gameJava + } + } } \ No newline at end of file diff --git a/src/Minecraft-Loader/index.ts b/src/Minecraft-Loader/index.ts new file mode 100644 index 00000000..74489b0f --- /dev/null +++ b/src/Minecraft-Loader/index.ts @@ -0,0 +1,299 @@ +/** + * @author Luuxis + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + */ + +import { EventEmitter } from 'events'; +import fs from 'fs'; +import path from 'path'; +import { loader as loaderFunction } from '../utils/Index.js'; +import type { + LoaderType, + LoaderResult, + LoaderDownloaderOptions, + LoaderDownloaderConfig +} from '../types.js'; + +// Loader sub-classes +import Forge from './loader/forge/forge.js'; +import NeoForge from './loader/neoForge/neoForge.js'; +import Fabric from './loader/fabric/fabric.js'; +import LegacyFabric from './loader/legacyfabric/legacyFabric.js'; +import Quilt from './loader/quilt/quilt.js'; + +export type { LoaderType }; + +/** + * The main Loader class that orchestrates installation of different + * Minecraft mod loaders (Forge, Fabric, LegacyFabric, Quilt, etc.). + */ +export default class Loader extends EventEmitter { + private readonly options: LoaderDownloaderOptions; + + constructor(options: LoaderDownloaderOptions) { + super(); + this.options = options; + } + + /** + * Main entry point for installing the selected loader. + * Checks the loader type from `this.options.loader.type` and delegates to the appropriate method. + * Emits: + * - "error" if the loader is not found or if an installation step fails + * - "json" upon successful completion, returning the version JSON or loader info + */ + public async install(): Promise { + // Retrieve a loader definition from your `loaderFunction` + // (Presumably a function that returns metadata URLs, etc. based on the type.) + const LoaderData = loaderFunction(this.options.loader.type); + if (!LoaderData) { + this.emit('error', { error: `Loader ${this.options.loader.type} not found` }); + return; + } + + const loaderType = this.options.loader.type; + let result: LoaderResult | undefined; + + switch (loaderType) { + case 'forge': { + result = await this.forge(LoaderData); + break; + } + case 'neoforge': { + result = await this.neoForge(LoaderData); + break; + } + case 'fabric': { + result = await this.fabric(LoaderData); + break; + } + case 'legacyfabric': { + result = await this.legacyFabric(LoaderData); + break; + } + case 'quilt': { + result = await this.quilt(LoaderData); + break; + } + default: { + this.emit('error', { error: `Loader ${loaderType} not found` }); + return; + } + } + + // If there's an error property, emit it. Otherwise, emit the final JSON. + if (result && result.error) { + this.emit('error', result); + } else if (result) { + this.emit('json', result); + } + } + + /** + * Handles Forge installation by: + * 1. Downloading the installer + * 2. Depending on installer type, extracting an install profile or creating a merged Jar + * 3. Downloading required libraries + * 4. Patching Forge if necessary + * 5. Returns the final version JSON object or an error + */ + private async forge(LoaderData: any): Promise { + const forge = new Forge(this.options); + + // Forward Forge events + forge.on('check', (progress: number, size: number, element: string) => { + this.emit('check', progress, size, element); + }); + forge.on('progress', (progress: number, size: number, element: string) => { + this.emit('progress', progress, size, element); + }); + forge.on('extract', (element: string) => { + this.emit('extract', element); + }); + forge.on('patch', (patch: any) => { + this.emit('patch', patch); + }); + + // 1. Download installer + const installer: any = await forge.downloadInstaller(LoaderData as any); + if (installer.error) return installer; + + const profile: any = await forge.extractProfile(installer.filePath); + if (profile.error) return profile; + + // Write the version JSON to disk + if ("version" in profile && "id" in profile.version) { + const destination = path.resolve(this.options.path, 'versions', profile.version.id); + if (!fs.existsSync(destination)) fs.mkdirSync(destination, { recursive: true }); + fs.writeFileSync(path.resolve(destination, `${profile.version.id}.json`), JSON.stringify(profile.version, null, 4)); + fs.cpSync(path.resolve(this.options.loader.config.minecraftJar), path.resolve(destination, `${profile.version.id}.jar`)); + } + + // 3. Extract universal jar if needed + const universal: any = await forge.extractUniversalJar(profile.install, installer.filePath); + if (universal.error) return universal; + + // 4. Download libraries + const libraries: any = await forge.downloadLibraries(profile, universal); + if (libraries.error) return libraries; + + // 5. Patch Forge if necessary + const patch: any = await forge.patchForge(profile.install); + if (patch.error) return patch; + + return profile.version; + } + + /** + * Manages installation flow for NeoForge: + * 1. Download the installer + * 2. Extract the install profile + * 3. Extract the universal jar + * 4. Download libraries + * 5. Patch if needed + */ + private async neoForge(LoaderData: any): Promise { + const neoForge = new NeoForge(this.options); + + // Forward events + neoForge.on('check', (progress: number, size: number, element: string) => { + this.emit('check', progress, size, element); + }); + neoForge.on('progress', (progress: number, size: number, element: string) => { + this.emit('progress', progress, size, element); + }); + neoForge.on('extract', (element: string) => { + this.emit('extract', element); + }); + neoForge.on('patch', (patch: any) => { + this.emit('patch', patch); + }); + + const installer = await neoForge.downloadInstaller(LoaderData as any); + if (installer.error) return installer as LoaderResult; + + // Extract the main profile + const profile: any = await neoForge.extractProfile(installer.filePath); + if (profile.error) return profile; + + // Write version JSON + if ("version" in profile && "id" in profile.version) { + const destination = path.resolve(this.options.path, 'versions', profile.version.id); + if (!fs.existsSync(destination)) fs.mkdirSync(destination, { recursive: true }); + fs.writeFileSync(path.resolve(destination, `${profile.version.id}.json`), JSON.stringify(profile.version, null, 4)); + fs.cpSync(path.resolve(this.options.loader.config.minecraftJar), path.resolve(destination, `${profile.version.id}.jar`)); + } + // Extract universal jar + const universal: any = await neoForge.extractUniversalJar(profile.install, installer.filePath, installer.oldAPI); + if (universal.error) return universal; + + // Download libraries + const libraries: any = await neoForge.downloadLibraries(profile, universal); + if (libraries.error) return libraries; + + // Patch if needed + const patch: any = await neoForge.patchneoForge(profile.install, installer.oldAPI); + if (patch.error) return patch; + + if ("version" in profile) return profile.version; + } + + /** + * Installs Fabric: + * 1. Download the loader JSON + * 2. Save it as a version .json + * 3. Download required libraries + */ + private async fabric(LoaderData: any): Promise { + const fabric = new Fabric(this.options); + + // Forward events + fabric.on('check', (progress: number, size: number, element: string) => { + this.emit('check', progress, size, element); + }); + fabric.on('progress', (progress: number, size: number, element: string) => { + this.emit('progress', progress, size, element); + }); + + const json = await fabric.downloadJson(LoaderData); + if (json.error) return json; + + if ("id" in json) { + const destination = path.resolve(this.options.path, 'versions', json.id); + if (!fs.existsSync(destination)) fs.mkdirSync(destination, { recursive: true }); + fs.writeFileSync(path.resolve(destination, `${json.id}.json`), JSON.stringify(json, null, 4)); + fs.cpSync(path.resolve(this.options.loader.config.minecraftJar), path.resolve(destination, `${json.id}.jar`)); + } + + if ("libraries" in json) { + await fabric.downloadLibraries(json); + } + + return json; + } + + /** + * Installs Legacy Fabric: + * 1. Download JSON + * 2. Save version .json + * 3. Download libraries + */ + private async legacyFabric(LoaderData: any): Promise { + const legacyFabric = new LegacyFabric(this.options); + + // Forward events + legacyFabric.on('check', (progress: number, size: number, element: string) => { + this.emit('check', progress, size, element); + }); + legacyFabric.on('progress', (progress: number, size: number, element: string) => { + this.emit('progress', progress, size, element); + }); + + const json = await legacyFabric.downloadJson(LoaderData); + if (json.error) return json; + + if ("id" in json) { + const destination = path.resolve(this.options.path, 'versions', json.id); + if (!fs.existsSync(destination)) fs.mkdirSync(destination, { recursive: true }); + fs.writeFileSync(path.resolve(destination, `${json.id}.json`), JSON.stringify(json, null, 4)); + fs.cpSync(path.resolve(this.options.loader.config.minecraftJar), path.resolve(destination, `${json.id}.jar`)); + } + if ("libraries" in json) { + await legacyFabric.downloadLibraries(json); + } + return json; + } + + /** + * Installs Quilt: + * 1. Download the loader JSON + * 2. Write to a version file + * 3. Download required libraries + */ + private async quilt(LoaderData: any): Promise { + const quilt = new Quilt(this.options); + + // Forward events + quilt.on('check', (progress: number, size: number, element: string) => { + this.emit('check', progress, size, element); + }); + quilt.on('progress', (progress: number, size: number, element: string) => { + this.emit('progress', progress, size, element); + }); + + const json = await quilt.downloadJson(LoaderData); + if ('error' in json && json.error) return json as LoaderResult; + + if ("id" in json) { + const destination = path.resolve(this.options.path, 'versions', json.id as string); + if (!fs.existsSync(destination)) fs.mkdirSync(destination, { recursive: true }); + fs.writeFileSync(path.resolve(destination, `${json.id as string}.json`), JSON.stringify(json, null, 4)); + fs.cpSync(path.resolve(this.options.loader.config.minecraftJar), path.resolve(destination, `${json.id as string}.jar`)); + } + if ("libraries" in json) { + await quilt.downloadLibraries(json); + } + + return json; + } +} diff --git a/src/Minecraft-Loader/loader/fabric/fabric.ts b/src/Minecraft-Loader/loader/fabric/fabric.ts new file mode 100644 index 00000000..5e0193c2 --- /dev/null +++ b/src/Minecraft-Loader/loader/fabric/fabric.ts @@ -0,0 +1,160 @@ +/** + * @author Luuxis + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + */ + +import { EventEmitter } from 'events'; +import fs from 'fs'; +import path from 'path'; + +import { getPathLibraries } from '../../../utils/Index.js'; +import Downloader from '../../../utils/Downloader.js'; +import type { FabricLoaderData, FabricJSON } from '../../../types.js'; + +interface FabricOptions { + path: string; + downloadFileMultiple?: number; + loader: { + version: string; + build: string; + }; +} + +interface FabricLibrary { + name: string; + url: string; + rules?: Array>; +} + +/** + * This class handles downloading Fabric loader JSON metadata, + * resolving the correct build, and downloading the required libraries. + */ +export default class FabricMC extends EventEmitter { + private readonly options: FabricOptions; + + constructor(options: FabricOptions) { + super(); + this.options = options; + } + + /** + * Fetches the Fabric loader metadata to find the correct build for the given + * Minecraft version. If the specified build is "latest" or "recommended", + * it uses the first (most recent) entry. Otherwise, it looks up a specific build. + * + * @param Loader A LoaderObject describing metadata and json URL templates. + * @returns A JSON object representing the Fabric loader profile, or an error object. + */ + public async downloadJson(Loader: FabricLoaderData): Promise { + let buildInfo: { version: string; stable: boolean } | undefined; + + let response = await fetch(Loader.metaData); + let metaData: { game: Array<{ version: string; stable: boolean }>; loader: Array<{ version: string; stable: boolean }> } = await response.json(); + + // Check if the Minecraft version is supported + const version = metaData.game.find(v => v.version === this.options.loader.version); + if (!version) { + return { error: `FabricMC doesn't support Minecraft ${this.options.loader.version}` }; + } + + // Determine the loader build + const availableBuilds = metaData.loader.map(b => b.version); + if (this.options.loader.build === 'latest' || this.options.loader.build === 'recommended') { + buildInfo = metaData.loader[0]; // The first entry is presumably the latest + } else { + buildInfo = metaData.loader.find(l => l.version === this.options.loader.build); + } + + if (!buildInfo) { + return { + error: `Fabric Loader ${this.options.loader.build} not found, Available builds: ${availableBuilds.join(', ')}` + }; + } + + // Build the URL for the Fabric JSON using placeholders + const url = Loader.json + .replace('${build}', buildInfo.version) + .replace('${version}', this.options.loader.version); + + // Fetch the Fabric loader JSON + try { + const result = await fetch(url); + const fabricJson: FabricJSON = await result.json(); + return fabricJson; + } catch (err: unknown) { + return { error: err instanceof Error ? err.message : 'An error occurred while fetching Fabric JSON' }; + } + } + + /** + * Downloads any missing libraries defined in the Fabric JSON manifest, + * skipping those that already exist locally (or that have rules preventing download). + * + * @param fabricJson The Fabric JSON object with a `libraries` array. + * @returns The same `libraries` array after downloading as needed. + */ + public async downloadLibraries(fabricJson: FabricJSON): Promise { + const { libraries } = fabricJson; + const downloader = new Downloader(); + const downloadQueue: Array<{ + url: string; + folder: string; + path: string; + name: string; + size: number; + }> = []; + + let checkedLibraries = 0; + let totalSize = 0; + + // Identify which libraries need downloading + for (const lib of libraries) { + // Skip if there are any rules that prevent downloading + if (lib.rules) { + this.emit('check', checkedLibraries++, libraries.length, 'libraries'); + continue; + } + + // Parse out the library path + const libInfo = getPathLibraries(lib.name); + const libFolderPath = path.resolve(this.options.path, 'libraries', libInfo.path); + const libFilePath = path.resolve(libFolderPath, libInfo.name); + + // If the file doesn't exist locally, we prepare a download item + if (!fs.existsSync(libFilePath)) { + const libUrl = `${lib.url}${libInfo.path}/${libInfo.name}`; + + let sizeFile = 0; + // Check if the file is accessible and retrieve its size + const res = await downloader.checkURL(libUrl); + if (res && typeof res === 'object' && 'status' in res && res.status === 200) { + sizeFile = res.size; + totalSize += res.size; + } + + downloadQueue.push({ + url: libUrl, + folder: libFolderPath, + path: libFilePath, + name: libInfo.name, + size: sizeFile + }); + } + + // Emit a "check" event for progress tracking + this.emit('check', checkedLibraries++, libraries.length, 'libraries'); + } + + // If there are files to download, do so now + if (downloadQueue.length > 0) { + downloader.on('progress', (downloaded: number, total: number) => { + this.emit('progress', downloaded, total, 'libraries'); + }); + + await downloader.downloadFileMultiple(downloadQueue, totalSize, this.options.downloadFileMultiple); + } + + return libraries; + } +} diff --git a/src/Minecraft-Loader/loader/forge/forge.ts b/src/Minecraft-Loader/loader/forge/forge.ts new file mode 100644 index 00000000..b8a642ba --- /dev/null +++ b/src/Minecraft-Loader/loader/forge/forge.ts @@ -0,0 +1,465 @@ +/** + * @author Luuxis + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + */ + + +import fs from 'fs'; +import path from 'path'; +import { EventEmitter } from 'events'; + +import { + getPathLibraries, + getFileHash, + mirrors, + getFileFromArchive, + skipLibrary +} from '../../../utils/Index.js'; + +import Downloader from '../../../utils/Downloader.js'; +import ForgePatcher, { Profile } from '../../patcher.js'; +import type { + ForgeInstallProfile, + MinecraftLibrary, + PatcherProfile, + PatcherProcessor, +} from '../../../types.js'; + +/** + * Maps Node.js process.platform values to Mojang library naming conventions. + * Used for choosing the right native library. + */ +const Lib: Record = { + win32: 'windows', + darwin: 'osx', + linux: 'linux' +}; + +/** + * Represents the loader configuration. You may need to expand or adjust + * this interface if your real code has more properties. + */ +interface LoaderConfig { + version: string; // Minecraft version for Forge (e.g., "1.19.2") + build: string; // Forge build (e.g., "latest", "recommended", or a numeric version) + config: { + javaPath: string; // Path to Java for patching + minecraftJar: string; // Path to the vanilla Minecraft JAR + minecraftJson: string; // Path to the corresponding .json version file + }; +} + +/** + * Options passed to ForgeMC. Adjust as needed. + */ +interface ForgeOptions { + path: string; // Base path where files will be placed or read from + loader: { + version: string; // Minecraft version (e.g. "1.19.2") + build: string; // Build type ("latest", "recommended", or a numeric version) + config: { + javaPath: string; // Path to the Java executable for patching + minecraftJar: string; // Path to the vanilla Minecraft .jar + minecraftJson: string; // Path to the corresponding .json version file + }; + type: string; // Type of loader + }; + downloadFileMultiple?: number; // Number of concurrent downloads + [key: string]: any; // Allow extra fields as necessary +} + +/** + * Represents information about the Forge installer file after download: + * - If successful, contains filePath, metaData, ext, and an id (e.g. "forge-") + * - If an error occurs, returns an object with `error` describing the issue. + */ +type DownloadInstallerResult = + | { + filePath: string; + metaData: string; + ext: string; + id: string; + } + | { + error: string; + }; + +/** + * Describes the structure of an install_profile.json (Forge Installer) after extraction. + */ +interface ForgeProfile { + install?: ForgeInstallProfile; + version?: Record & { id?: string; libraries?: MinecraftLibrary[] }; + data: Record; + filePath?: string; + path?: string; + processors?: PatcherProcessor[]; + libraries?: MinecraftLibrary[]; + error?: { message: string }; +} + +/** + * The main class for handling Forge installations, including: + * - Downloading the appropriate Forge installer + * - Extracting relevant files from the installer + * - Patching Forge when necessary + * - Creating a merged jar for older Forge versions + */ +export default class ForgeMC extends EventEmitter { + private readonly options: ForgeOptions; + + constructor(options: ForgeOptions) { + super(); + this.options = options; + } + + /** + * Downloads the Forge installer (or client/universal) for the specified version/build. + * Verifies the downloaded file's MD5 hash. Returns file details or an error. + * + * @param Loader An object containing URLs for metadata and Forge files. + */ + public async downloadInstaller(Loader: any): Promise { + // Fetch metadata for the given Forge version + let metaDataList: any = await fetch(Loader.metaData); + + if (!metaDataList.ok) { + metaDataList = fs.readFileSync(path.resolve(__dirname, '../../../../assets/forge', 'forge-metadata.json'), 'utf-8'); + metaDataList = JSON.parse(metaDataList); + } else { + metaDataList = await metaDataList.json(); + } + + metaDataList = metaDataList[this.options.loader.version]; + + if (!metaDataList) { + return { error: `Forge ${this.options.loader.version} not supported` }; + } + + const allBuilds = metaDataList; + let build: string | undefined; + + // Handle "latest" or "recommended" builds by checking promotions + if (this.options.loader.build === 'latest') { + let promotions = await fetch(Loader.promotions).then(res => res.json()); + const promoKey = `${this.options.loader.version}-latest`; + const promoBuild = promotions.promos[promoKey]; + build = metaDataList.find(b => b.includes(promoBuild)); + } else if (this.options.loader.build === 'recommended') { + let promotions = await fetch(Loader.promotions).then(res => res.json()); + let promoKey = `${this.options.loader.version}-recommended`; + let promoBuild = promotions.promos[promoKey] || promotions.promos[`${this.options.loader.version}-latest`]; + build = metaDataList.find(b => b.includes(promoBuild)); + } else { + // Else, look for a specific numeric build if provided + build = this.options.loader.build; + } + + const chosenBuild = metaDataList.find(b => b === build); + if (!chosenBuild) { + return { + error: `Build ${build} not found, Available builds: ${allBuilds.join(', ')}` + }; + } + + // Fetch info about the chosen build from the meta URL + const meta = await fetch(Loader.meta.replace(/\${build}/g, chosenBuild)).then(res => res.json()); + + // Determine which classifier to use (installer, client, or universal) + const hasInstaller = meta.classifiers.installer; + const hasClient = meta.classifiers.client; + const hasUniversal = meta.classifiers.universal; + + let forgeURL: string = ''; + let ext: string = ''; + let hashFileOrigin: string = ''; + + if (hasInstaller) { + forgeURL = Loader.install.replace(/\${version}/g, chosenBuild); + ext = Object.keys(meta.classifiers.installer)[0]; + hashFileOrigin = meta.classifiers.installer[ext]; + } else if (hasClient) { + forgeURL = Loader.client.replace(/\${version}/g, chosenBuild); + ext = Object.keys(meta.classifiers.client)[0]; + hashFileOrigin = meta.classifiers.client[ext]; + } else if (hasUniversal) { + forgeURL = Loader.universal.replace(/\${version}/g, chosenBuild); + ext = Object.keys(meta.classifiers.universal)[0]; + hashFileOrigin = meta.classifiers.universal[ext]; + } else { + return { error: 'Invalid forge installer' }; + } + + const forgeFolder = path.resolve(this.options.path, 'libraries/net/minecraftforge/installer'); + const fileName = `${forgeURL}.${ext}`.split('/').pop()!; + const installerPath = path.resolve(forgeFolder, fileName); + + // Download if not already present + if (!fs.existsSync(installerPath)) { + if (!fs.existsSync(forgeFolder)) { + fs.mkdirSync(forgeFolder, { recursive: true }); + } + const dl = new Downloader(); + dl.on('progress', (downloaded: number, size: number) => { + this.emit('progress', downloaded, size, fileName); + }); + + await dl.downloadFile(`${forgeURL}.${ext}`, forgeFolder, fileName); + } + + // Verify the MD5 hash + const hashFileDownload = await getFileHash(installerPath, 'md5'); + if (hashFileDownload !== hashFileOrigin) { + fs.rmSync(installerPath); + return { error: 'Invalid hash' }; + } + + return { + filePath: installerPath, + metaData: chosenBuild, + ext, + id: `forge-${build}` + }; + } + + /** + * Extracts the main Forge profile from the installer's archive (install_profile.json), + * plus an additional JSON if specified in that profile. Returns an object containing + * both "install" and "version" data for further processing. + * + * @param pathInstaller Path to the downloaded Forge installer file. + */ + public async extractProfile(pathInstaller: string): Promise { + const fileContent = await getFileFromArchive(pathInstaller, 'install_profile.json'); + if (!fileContent) { + return { error: { message: 'Invalid forge installer' } }; + } + + const forgeJsonOrigin = JSON.parse(fileContent.toString()); + if (!forgeJsonOrigin) { + return { error: { message: 'Invalid forge installer' } }; + } + + const result: ForgeProfile = { data: {} }; + + // Distinguish between older and newer Forge installers + if (forgeJsonOrigin.install) { + result.install = forgeJsonOrigin.install; + result.version = forgeJsonOrigin.versionInfo; + } else { + result.install = forgeJsonOrigin; + const extraFile = await getFileFromArchive(pathInstaller, path.basename((result.install as ForgeInstallProfile).json)); + if (!extraFile) { + return { error: { message: 'Invalid additional JSON in forge installer' } }; + } + result.version = JSON.parse(extraFile.toString()); + } + + return result; + } + + /** + * Extracts the "universal" Forge jar (or other relevant data) from the installer, + * placing it in your local "libraries" folder. Also extracts client data if required. + * + * @param profile The Forge profile object containing file paths to extract. + * @param pathInstaller The path to the Forge installer file. + * @returns A boolean (skipForgeFilter) that indicates whether to filter out certain Forge libs + */ + public async extractUniversalJar(profile: ForgeInstallProfile, pathInstaller: string): Promise { + let skipForgeFilter = true; + + // If there's a direct file path, extract just that file + if (profile.filePath) { + const fileInfo = getPathLibraries(profile.path); + this.emit('extract', `Extracting ${fileInfo.name}...`); + + const destFolder = path.resolve(this.options.path, 'libraries', fileInfo.path); + if (!fs.existsSync(destFolder)) { + fs.mkdirSync(destFolder, { recursive: true }); + } + + const archiveContent = await getFileFromArchive(pathInstaller, profile.filePath!) as Buffer | undefined; + if (archiveContent) { + fs.writeFileSync(path.join(destFolder, fileInfo.name), archiveContent, { mode: 0o777 }); + } + } + // Otherwise, if there's a path referencing "maven/" + else if (profile.path) { + const fileInfo = getPathLibraries(profile.path); + const filesInArchive = await getFileFromArchive(pathInstaller, null, `maven/${fileInfo.path}`) as string[]; + for (const file of filesInArchive) { + const fileName = path.basename(file); + this.emit('extract', `Extracting ${fileName}...`); + const fileContent = await getFileFromArchive(pathInstaller, file) as Buffer | undefined; + if (!fileContent) { + continue; + } + + const destFolder = path.resolve(this.options.path, 'libraries', fileInfo.path); + if (!fs.existsSync(destFolder)) { + fs.mkdirSync(destFolder, { recursive: true }); + } + + fs.writeFileSync(path.join(destFolder, fileName), fileContent, { mode: 0o777 }); + } + } else { + // If we do not find filePath or path in profile, skip the Forge filter + skipForgeFilter = false; + } + + // If there are processors, we likely have a "client.lzma" to store + if (profile.processors?.length) { + const universalPath = profile.libraries?.find((v) => (v.name || '').startsWith('net.minecraftforge:forge')); + const clientData = await getFileFromArchive(pathInstaller, 'data/client.lzma') as Buffer | undefined; + if (clientData) { + const fileInfo = getPathLibraries(profile.path || universalPath?.name || '', '-clientdata', '.lzma'); + const destFolder = path.resolve(this.options.path, 'libraries', fileInfo.path); + if (!fs.existsSync(destFolder)) { + fs.mkdirSync(destFolder, { recursive: true }); + } + fs.writeFileSync(path.join(destFolder, fileInfo.name), clientData, { mode: 0o777 }); + this.emit('extract', `Extracting ${fileInfo.name}...`); + } + } + + return skipForgeFilter; + } + + /** + * Downloads all the libraries needed by the Forge profile, skipping duplicates + * and any library that is already present. Also applies optional skip logic + * for certain Forge libraries if skipForgeFilter is true. + * + * @param profile The parsed Forge profile. + * @param skipForgeFilter Whether to filter out "net.minecraftforge:forge" or "minecraftforge" + * @returns An array of the final libraries (including newly downloaded ones). + */ + public async downloadLibraries(profile: ForgeProfile, skipForgeFilter: boolean): Promise { + let libraries = profile.version?.libraries || []; + const dl = new Downloader(); + let checkCount = 0; + const downloadList: Array<{ + url: string; + folder: string; + path: string; + name: string; + size: number; + }> = []; + let totalSize = 0; + + // Combine with any "install.libraries" + if (profile.install?.libraries) { + libraries = libraries.concat(profile.install.libraries); + } + + // Remove duplicates by name + libraries = libraries.filter( + (library: any, index: number, self: any[]) => index === self.findIndex(t => t.name === library.name) + ); + + // Certain Forge libs may be skipped if skipForgeFilter is true + const skipForge = ['net.minecraftforge:forge:', 'net.minecraftforge:minecraftforge:']; + + for (const lib of libraries) { + // If skipForgeFilter is true, skip the core Forge libs + if (skipForgeFilter && skipForge.some(forgePrefix => lib.name.includes(forgePrefix))) { + // If the artifact URL is empty, we skip it + if (!lib.downloads?.artifact?.url) { + this.emit('check', checkCount++, libraries.length, 'libraries'); + continue; + } + } + + // Some libraries might need skipping altogether (e.g., OS-specific constraints) + if (skipLibrary(lib)) { + this.emit('check', checkCount++, libraries.length, 'libraries'); + continue; + } + + // Check if the library includes "natives" for the current OS + let nativesSuffix: string | undefined; + if (lib.natives) { + nativesSuffix = lib.natives[Lib[process.platform]]; + } + + const libInfo = getPathLibraries(lib.name, nativesSuffix ? `-${nativesSuffix}` : ''); + const libFolder = path.resolve(this.options.path, 'libraries', libInfo.path); + const libFilePath = path.resolve(libFolder, libInfo.name); + + // If not present locally, schedule it for download + if (!fs.existsSync(libFilePath)) { + let url: string | null = null; + let fileSize = 0; + + // First, try checking a mirror + const baseURL = nativesSuffix ? `${libInfo.path}/` : `${libInfo.path}/${libInfo.name}`; + const mirrorResp: any = await dl.checkMirror(baseURL, mirrors); + + if (mirrorResp?.status === 200) { + fileSize = mirrorResp.size; + totalSize += fileSize; + url = mirrorResp.url; + } else if (lib.downloads?.artifact) { + url = lib.downloads.artifact.url; + fileSize = lib.downloads.artifact.size; + totalSize += fileSize; + } + + if (!url) { + this.emit('check', checkCount++, libraries.length, 'libraries'); + this.emit('error', `Library ${libInfo.name} not found`); + continue; + } + + downloadList.push({ + url, + folder: libFolder, + path: libFilePath, + name: libInfo.name, + size: fileSize + }); + } + + this.emit('check', checkCount++, libraries.length, 'libraries'); + } + + // Perform the downloads if any are needed + if (downloadList.length > 0) { + dl.on('progress', (DL: number, totDL: number) => { + this.emit('progress', DL, totDL, 'libraries'); + }); + await dl.downloadFileMultiple(downloadList, totalSize, this.options.downloadFileMultiple); + } + + return libraries; + } + + /** + * Applies any necessary patches to Forge using the `forgePatcher` class. + * If the patcher determines it's already patched, it skips. + * + * @param profile The Forge profile containing processor information + * @returns True if successful or if no patching was required + */ + public async patchForge(profile: ForgeInstallProfile): Promise { + if (profile?.processors?.length) { + const patcher = new ForgePatcher(this.options); + + // Forward patcher events + patcher.on('patch', (data: string) => this.emit('patch', data)); + patcher.on('error', (data: string) => this.emit('error', data)); + + // If the patch is not valid yet, run the patch process + if (!patcher.check(profile as PatcherProfile)) { + const config = { + java: this.options.loader.config.javaPath, + minecraft: this.options.loader.config.minecraftJar, + minecraftJson: this.options.loader.config.minecraftJson + }; + await patcher.patcher(profile, config); + } + } + return true; + } +} diff --git a/src/Minecraft-Loader/loader/legacyfabric/legacyFabric.ts b/src/Minecraft-Loader/loader/legacyfabric/legacyFabric.ts new file mode 100644 index 00000000..a33e6895 --- /dev/null +++ b/src/Minecraft-Loader/loader/legacyfabric/legacyFabric.ts @@ -0,0 +1,159 @@ +/** + * @author Luuxis + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + */ + +import fs from 'fs'; +import path from 'path'; +import { EventEmitter } from 'events'; + +import { getPathLibraries } from '../../../utils/Index.js'; +import Downloader from '../../../utils/Downloader.js'; +import type { FabricLoaderData, FabricJSON } from '../../../types.js'; + +interface FabricOptions { + path: string; + loader: { + version: string; + build: string; + }; + downloadFileMultiple?: number; +} + +interface FabricLibrary { + name: string; + url: string; + rules?: Array>; +} + +/** + * A class that handles downloading the Fabric loader JSON metadata + * and the libraries needed to launch Fabric. + */ +export default class FabricMC extends EventEmitter { + private readonly options: FabricOptions; + + constructor(options: FabricOptions = { path: '', loader: { version: '', build: '' } }) { + super(); + this.options = options; + } + + /** + * Fetches metadata from the Fabric API to identify the correct build for the given version. + * If the build is "latest" or "recommended", it picks the first entry from the loader array. + * Otherwise, it tries to match the specific build requested by the user. + * + * @param Loader A LoaderObject with metaData and json URLs for Fabric. + * @returns A FabricJSON object on success, or an error object. + */ + public async downloadJson(Loader: FabricLoaderData): Promise { + let selectedBuild: { version: string } | undefined; + + const metaResponse = await fetch(Loader.metaData); + const metaData: { game: Array<{ version: string }>; loader: Array<{ version: string }> } = await metaResponse.json(); + + const versionExists = metaData.game.find((ver) => ver.version === this.options.loader.version); + if (!versionExists) { + return { error: `FabricMC doesn't support Minecraft ${this.options.loader.version}` }; + } + + // Extract all possible loader builds + const availableBuilds = metaData.loader.map((b) => b.version); + + if (this.options.loader.build === 'latest' || this.options.loader.build === 'recommended') { + selectedBuild = metaData.loader[0]; + } else { + selectedBuild = metaData.loader.find((loaderBuild) => loaderBuild.version === this.options.loader.build); + } + + if (!selectedBuild) { + return { + error: `Fabric Loader ${this.options.loader.build} not found, Available builds: ${availableBuilds.join(', ')}` + }; + } + + // Construct the final URL for fetching the Fabric JSON + const url = Loader.json + .replace('${build}', selectedBuild.version) + .replace('${version}', this.options.loader.version); + + // Fetch and parse the JSON + try { + const response = await fetch(url); + const fabricJson: FabricJSON = await response.json(); + return fabricJson; + } catch (err: unknown) { + return { error: err instanceof Error ? err.message : 'Failed to fetch or parse Fabric loader JSON' }; + } + } + + /** + * Iterates over the libraries in the Fabric JSON, checks if they exist locally, + * and if not, downloads them. Skips libraries that have "rules" (usually platform-specific). + * + * @param json The Fabric loader JSON object with a "libraries" array. + * @returns The same libraries array after downloads, or an error object if something fails. + */ + public async downloadLibraries(json: FabricJSON): Promise { + const { libraries } = json; + const downloader = new Downloader(); + let pendingDownloads: Array<{ + url: string; + folder: string; + path: string; + name: string; + size: number; + }> = []; + + let checkedCount = 0; + let totalSize = 0; + + // Evaluate each library for possible download + for (const lib of libraries) { + // Skip if library has rules that might disqualify it for this platform + if (lib.rules) { + this.emit('check', checkedCount++, libraries.length, 'libraries'); + continue; + } + + // Build the local file path + const libInfo = getPathLibraries(lib.name); + const libFolder = path.resolve(this.options.path, 'libraries', libInfo.path); + const libFilePath = path.resolve(libFolder, libInfo.name); + + // If it doesn't exist, prepare to download + if (!fs.existsSync(libFilePath)) { + const libUrl = `${lib.url}${libInfo.path}/${libInfo.name}`; + + let fileSize = 0; + // Check if the file is available and get its size + const checkRes = await downloader.checkURL(libUrl); + if (checkRes && typeof checkRes === 'object' && 'status' in checkRes && checkRes.status === 200) { + fileSize = checkRes.size; + totalSize += fileSize; + } + + pendingDownloads.push({ + url: libUrl, + folder: libFolder, + path: libFilePath, + name: libInfo.name, + size: fileSize + }); + } + + this.emit('check', checkedCount++, libraries.length, 'libraries'); + } + + // Download all missing libraries in bulk + if (pendingDownloads.length > 0) { + downloader.on('progress', (downloaded: number, total: number) => { + this.emit('progress', downloaded, total, 'libraries'); + }); + + await downloader.downloadFileMultiple(pendingDownloads, totalSize, this.options.downloadFileMultiple); + } + + return libraries; + } +} diff --git a/src/Minecraft-Loader/loader/neoForge/neoForge.ts b/src/Minecraft-Loader/loader/neoForge/neoForge.ts new file mode 100644 index 00000000..a279ff71 --- /dev/null +++ b/src/Minecraft-Loader/loader/neoForge/neoForge.ts @@ -0,0 +1,432 @@ +/** + * @author Luuxis + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + */ + +import fs from 'fs'; +import path from 'path'; +import { EventEmitter } from 'events'; + +import { getPathLibraries, mirrors, getFileFromArchive } from '../../../utils/Index.js'; +import Downloader from '../../../utils/Downloader.js'; +import NeoForgePatcher from '../../patcher.js'; +import type { + PatcherProfile, + ForgeInstallProfile, + MinecraftLibrary, + NeoForgeLoaderData, + PatcherProcessor, +} from '../../../types.js'; + +interface NeoForgeOptions { + path: string; + loader: { + version: string; + build: string; + config: { + javaPath: string; + minecraftJar: string; + minecraftJson: string; + }; + type: string; + }; + downloadFileMultiple?: number; +} + +interface DownloadInstallerResult { + filePath?: string; + oldAPI?: boolean; + error?: string; +} + +interface NeoForgeProfile { + install?: ForgeInstallProfile; + version?: Record & { id?: string; libraries?: MinecraftLibrary[] }; + data?: Record; + filePath?: string; + path?: string; + processors?: PatcherProcessor[]; + libraries?: MinecraftLibrary[]; + error?: { message: string }; +} + +/** + * This class handles downloading and installing NeoForge (formerly Forge) for Minecraft, + * including picking the correct build, extracting libraries, and running patchers if needed. + */ +export default class NeoForgeMC extends EventEmitter { + private readonly options: NeoForgeOptions; + + constructor(options: NeoForgeOptions) { + super(); + this.options = options; + } + + /** + * Converts a NeoForge version string to its corresponding Minecraft version. + * Handles all NeoForge versioning formats: + * + * - Snapshot special: "0.25w14craftmine.3-beta" → "25w14craftmine" + * - 4-parts + suffix: "26.1.0.0-alpha.1+snapshot-1" → "26.1-snapshot-1" + * - 4-parts + suffix: "26.1.0.0-alpha.15+pre-3" → "26.1-pre-3" + * - 4-parts no suffix: "26.1.0.1-beta" → "26.1" + * - 3-parts classic: "21.4.121" → "1.21.4" + * - 3-parts minor=0: "21.0.143" → "1.21" + * + * @param version The NeoForge version string + * @returns The corresponding Minecraft version, or null if unrecognized + */ + private neoforgeVersionToMinecraft(version: string): string | null { + // 1. Snapshot special: starts with "0." → e.g., "0.25w14craftmine.3-beta" + const snapshotMatch = version.match(/^0\.([^.]+)\./); + if (snapshotMatch) { + return snapshotMatch[1]; + } + + // 2. Strip suffix (everything after first "-" or "+") and split numeric part + const numericPart = version.split(/[-+]/)[0]; + const parts = numericPart.split('.'); + + // 3. 4+ parts → 2026+ format (e.g., "26.1.0.1" → MC "26.1") + if (parts.length >= 4) { + let mc = `${parts[0]}.${parts[1]}`; + // Check for "+snapshot-N" or "+pre-N" suffix + const suffixMatch = version.match(/\+(snapshot-\d+|pre-\d+)/); + if (suffixMatch) { + mc += `-${suffixMatch[1]}`; + } + return mc; + } + + // 4. 3 parts → classic format (e.g., "21.4.121" → MC "1.21.4") + if (parts.length === 3) { + if (parts[1] === '0') { + return `1.${parts[0]}`; + } + return `1.${parts[0]}.${parts[1]}`; + } + + return null; + } + + /** + * Downloads the NeoForge installer jar for the specified version and build, + * either using a legacy API or the newer metaData approach. If "latest" or "recommended" + * is specified, it picks the newest build from the filtered list. + * + * Uses neoforgeVersionToMinecraft() to map each NeoForge version to its + * Minecraft version, supporting all formats (releases, snapshots, pre-releases, + * 4-part 2026+ versions, etc.). + * + * @param Loader An object containing URLs and patterns for legacy and new metadata/installers. + * @returns An object with filePath and oldAPI fields, or an error. + */ + public async downloadInstaller(Loader: NeoForgeLoaderData): Promise { + let build: string | undefined; + let neoForgeURL: string; + let oldAPI = true; + + const minecraftVersion = this.options.loader.version; + + // Fetch versions from both APIs + const legacyMetaData = await fetch(Loader.legacyMetaData).then(res => res.json()); + const metaData = await fetch(Loader.metaData).then(res => res.json()); + + // Try legacy API first (old Forge-era versions like "1.20.1-47.1.0") + let versions: string[] = legacyMetaData.versions.filter((v: string) => + v.includes(`${minecraftVersion}-`) + ); + + // If none found in legacy, use modern API with neoforgeVersionToMinecraft + if (!versions.length) { + versions = metaData.versions.filter((v: string) => { + const mc = this.neoforgeVersionToMinecraft(v); + return mc === minecraftVersion; + }); + oldAPI = false; + } + + // If still no versions found, return an error + if (!versions.length) { + return { error: `NeoForge doesn't support Minecraft ${minecraftVersion}` }; + } + + // Determine which build to use + if (this.options.loader.build === 'latest' || this.options.loader.build === 'recommended') { + build = versions[versions.length - 1]; // The most recent build + } else { + build = versions.find(v => v === this.options.loader.build); + } + + if (!build) { + return { + error: `NeoForge Loader ${this.options.loader.build} not found, Available builds: ${versions.join(', ')}` + }; + } + + // Build the installer URL, depending on whether we use the legacy or new API + if (oldAPI) { + neoForgeURL = Loader.legacyInstall.replaceAll(/\${version}/g, build); + } else { + neoForgeURL = Loader.install.replaceAll(/\${version}/g, build); + } + + // Create a local folder for "neoForge" if it doesn't exist + const neoForgeFolder = path.resolve(this.options.path, 'libraries/net/neoforged/installer'); + const installerFilePath = path.resolve(neoForgeFolder, `neoForge-${build}-installer.jar`); + + if (!fs.existsSync(installerFilePath)) { + if (!fs.existsSync(neoForgeFolder)) { + fs.mkdirSync(neoForgeFolder, { recursive: true }); + } + const downloader = new Downloader(); + downloader.on('progress', (downloaded: number, size: number) => { + this.emit('progress', downloaded, size, `neoForge-${build}-installer.jar`); + }); + + await downloader.downloadFile(neoForgeURL, neoForgeFolder, `neoForge-${build}-installer.jar`); + } + + return { filePath: installerFilePath, oldAPI }; + } + + /** + * Extracts the main JSON profile (install_profile.json) from the NeoForge installer. + * If the JSON references an additional file, it also extracts and parses that, returning + * a unified object with `install` and `version` keys. + * + * @param pathInstaller Full path to the downloaded NeoForge installer jar. + * @returns A NeoForgeProfile object, or an error if invalid. + */ + public async extractProfile(pathInstaller: string): Promise { + const fileContent = await getFileFromArchive(pathInstaller, 'install_profile.json'); + if (!fileContent) { + return { error: { message: 'Invalid neoForge installer' } }; + } + + const neoForgeJsonOrigin = JSON.parse(fileContent.toString()); + if (!neoForgeJsonOrigin) { + return { error: { message: 'Invalid neoForge installer' } }; + } + + const result: NeoForgeProfile = { data: {} }; + if (neoForgeJsonOrigin.install) { + result.install = neoForgeJsonOrigin.install; + result.version = neoForgeJsonOrigin.versionInfo; + } else { + result.install = neoForgeJsonOrigin; + const extraFile = await getFileFromArchive(pathInstaller, path.basename(result.install.json)); + if (extraFile) { + result.version = JSON.parse(extraFile.toString()); + } else { + return { error: { message: 'Unable to read additional JSON from neoForge installer' } }; + } + } + + return result; + } + + /** + * Extracts the universal jar or associated files for NeoForge into the local "libraries" directory. + * Also handles client.lzma if processors are present. Returns a boolean indicating whether we skip + * certain neoforge libraries in subsequent steps. + * + * @param profile The extracted NeoForge profile with file path references + * @param pathInstaller Path to the NeoForge installer + * @param oldAPI Whether we are dealing with the old or new NeoForge API (affects library naming) + * @returns A boolean indicating if we should filter out certain libraries afterwards + */ + public async extractUniversalJar(profile: NeoForgeProfile, pathInstaller: string, oldAPI: boolean): Promise { + let skipNeoForgeFilter = true; + + if (profile.filePath) { + const fileInfo = getPathLibraries(profile.path!); + this.emit('extract', `Extracting ${fileInfo.name}...`); + + const destFolder = path.resolve(this.options.path, 'libraries', fileInfo.path); + if (!fs.existsSync(destFolder)) { + fs.mkdirSync(destFolder, { recursive: true }); + } + + const archiveContent = await getFileFromArchive(pathInstaller, profile.filePath) as Buffer | undefined; + if (archiveContent) { + fs.writeFileSync(path.join(destFolder, fileInfo.name), archiveContent, { mode: 0o777 }); + } + } else if (profile.path) { + const fileInfo = getPathLibraries(profile.path); + const filesInArchive = await getFileFromArchive(pathInstaller, null, `maven/${fileInfo.path}`) as string[]; + if (filesInArchive && Array.isArray(filesInArchive)) { + for (const file of filesInArchive) { + const fileName = path.basename(file); + this.emit('extract', `Extracting ${fileName}...`); + + const content = await getFileFromArchive(pathInstaller, file) as Buffer | undefined; + if (!content) continue; + + const destFolder = path.resolve(this.options.path, 'libraries', fileInfo.path); + if (!fs.existsSync(destFolder)) { + fs.mkdirSync(destFolder, { recursive: true }); + } + fs.writeFileSync(path.join(destFolder, fileName), content, { mode: 0o777 }); + } + } + } else { + skipNeoForgeFilter = false; + } + + if (profile.processors?.length) { + const universalPath = profile.libraries?.find(lib => + (lib.name || '').startsWith(oldAPI ? 'net.neoforged:forge' : 'net.neoforged:neoforge') + ); + + const clientData = await getFileFromArchive(pathInstaller, 'data/client.lzma') as Buffer | undefined; + if (clientData) { + const fileInfo = getPathLibraries(profile.path || universalPath?.name || '', '-clientdata', '.lzma'); + const destFolder = path.resolve(this.options.path, 'libraries', fileInfo.path); + + if (!fs.existsSync(destFolder)) { + fs.mkdirSync(destFolder, { recursive: true }); + } + fs.writeFileSync(path.join(destFolder, fileInfo.name), clientData, { mode: 0o777 }); + this.emit('extract', `Extracting ${fileInfo.name}...`); + } + } + + return skipNeoForgeFilter; + } + + /** + * Downloads all libraries referenced in the NeoForge profile. If skipNeoForgeFilter is true, + * certain core libraries are excluded. Checks for duplicates and local existence before downloading. + * + * @param profile The NeoForge profile containing version/install libraries + * @param skipNeoForgeFilter Whether we skip specific "net.minecraftforge:neoforged" libs + * @returns An array of library objects after download, or an error object if something fails + */ + public async downloadLibraries(profile: NeoForgeProfile, skipNeoForgeFilter: boolean): Promise { + let libraries = profile.version?.libraries || []; + const dl = new Downloader(); + let checkCount = 0; + const pendingFiles: Array<{ + url: string; + folder: string; + path: string; + name: string; + size: number; + }> = []; + let totalSize = 0; + + // Combine install.libraries with version.libraries + if (profile.install?.libraries) { + libraries = libraries.concat(profile.install.libraries); + } + + // Remove duplicates by 'name' + libraries = libraries.filter( + (lib, index, self) => index === self.findIndex(item => item.name === lib.name) + ); + + // If skipping certain neoforge libs + const skipNeoForge = ['net.minecraftforge:neoforged:', 'net.minecraftforge:minecraftforge:']; + + // Evaluate each library + for (const lib of libraries) { + if (skipNeoForgeFilter && skipNeoForge.some(str => lib.name.includes(str))) { + // If there's no valid artifact URL, skip it + if (!lib.downloads?.artifact?.url) { + this.emit('check', checkCount++, libraries.length, 'libraries'); + continue; + } + } + + // If the library has rules, skip automatically + if (lib.rules) { + this.emit('check', checkCount++, libraries.length, 'libraries'); + continue; + } + + // Construct the local path to the library + const libInfo = getPathLibraries(lib.name); + const libFolder = path.resolve(this.options.path, 'libraries', libInfo.path); + const libFilePath = path.resolve(libFolder, libInfo.name); + + // If it doesn't exist locally, schedule for download + if (!fs.existsSync(libFilePath)) { + let finalURL: string | null = null; + let fileSize = 0; + + // Attempt to resolve via mirror first + const baseURL = `${libInfo.path}/${libInfo.name}`; + const mirrorCheck = await dl.checkMirror(baseURL, mirrors); + if (mirrorCheck && typeof mirrorCheck === 'object' && 'status' in mirrorCheck && mirrorCheck.status === 200) { + finalURL = mirrorCheck.url; + fileSize = mirrorCheck.size; + totalSize += fileSize; + } else if (lib.downloads?.artifact) { + finalURL = lib.downloads.artifact.url; + fileSize = lib.downloads.artifact.size; + totalSize += fileSize; + } + + if (!finalURL) { + return { error: `Impossible to download ${libInfo.name}` }; + } + + pendingFiles.push({ + url: finalURL, + folder: libFolder, + path: libFilePath, + name: libInfo.name, + size: fileSize + }); + } + + this.emit('check', checkCount++, libraries.length, 'libraries'); + } + + // Download all pending files + if (pendingFiles.length > 0) { + dl.on('progress', (downloaded: number, totDL: number) => { + this.emit('progress', downloaded, totDL, 'libraries'); + }); + + await dl.downloadFileMultiple(pendingFiles, totalSize, this.options.downloadFileMultiple); + } + + return libraries; + } + + /** + * Runs the NeoForge patch process, if any processors exist. Checks if patching is needed, + * then uses the `NeoForgePatcher` class. If the patch is already applied, it skips. + * + * @param profile The NeoForge profile, which may include processors. + * @param oldAPI Whether we are dealing with the old or new API (passed to the patcher). + * @returns True on success or if no patch was needed. + */ + public async patchneoForge(profile: NeoForgeProfile, oldAPI: boolean): Promise { + if (profile?.processors?.length) { + const patcher = new NeoForgePatcher(this.options); + + // Relay events + patcher.on('patch', (data: string) => { + this.emit('patch', data); + }); + patcher.on('error', (error: string) => { + this.emit('error', error); + }); + + // If not already patched, run the patcher + if (!patcher.check(profile)) { + const config = { + java: this.options.loader.config.javaPath, + minecraft: this.options.loader.config.minecraftJar, + minecraftJson: this.options.loader.config.minecraftJson + }; + + await patcher.patcher(profile, config, oldAPI); + } + } + return true; + } +} diff --git a/src/Minecraft-Loader/loader/quilt/quilt.ts b/src/Minecraft-Loader/loader/quilt/quilt.ts new file mode 100644 index 00000000..9fa83924 --- /dev/null +++ b/src/Minecraft-Loader/loader/quilt/quilt.ts @@ -0,0 +1,173 @@ +/** + * @author Luuxis + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + */ + +import fs from 'fs'; +import path from 'path'; +import { EventEmitter } from 'events'; + +import { getPathLibraries } from '../../../utils/Index.js'; +import Downloader from '../../../utils/Downloader.js'; +import type { FabricLoaderData, FabricJSON } from '../../../types.js'; + +interface QuiltOptions { + path: string; + loader: { + version: string; + build: string; + }; + downloadFileMultiple?: number; +} + +interface QuiltLibrary { + name: string; + url: string; + rules?: Array; +} + +interface QuiltJSON { + libraries: QuiltLibrary[]; + [key: string]: unknown; +} + +/** + * This class handles fetching the Quilt loader metadata, + * identifying the appropriate build for a given Minecraft version, + * and downloading required libraries. + */ +export default class Quilt extends EventEmitter { + private readonly options: QuiltOptions; + private versionMinecraft: string | undefined; + + constructor(options: QuiltOptions = { path: '', loader: { version: '', build: '' } }) { + super(); + this.options = options; + } + + /** + * Fetches the Quilt loader metadata to identify the correct build for the specified + * Minecraft version. If "latest" or "recommended" is requested, picks the most + * recent or stable build accordingly. + * + * @param Loader An object describing where to fetch Quilt metadata and JSON. + * @returns A QuiltJSON object on success, or an error object if something fails. + */ + public async downloadJson(Loader: FabricLoaderData): Promise { + let selectedBuild: { version: string } | undefined; + + const metaResponse = await fetch(Loader.metaData); + const metaData: { game: Array<{ version: string }>; loader: Array<{ version: string }> } = await metaResponse.json(); + + const mcVersionExists = metaData.game.find((ver) => ver.version === this.options.loader.version); + if (!mcVersionExists) { + return { error: `QuiltMC doesn't support Minecraft ${this.options.loader.version}` }; + } + + // Gather all available builds for this version + const availableBuilds = metaData.loader.map((b) => b.version); + + if (this.options.loader.build === 'latest') { + selectedBuild = metaData.loader[0]; + } else if (this.options.loader.build === 'recommended') { + selectedBuild = metaData.loader.find((b) => !b.version.includes('beta')); + } else { + selectedBuild = metaData.loader.find( + (loaderItem) => loaderItem.version === this.options.loader.build + ); + } + + if (!selectedBuild) { + return { + error: `QuiltMC Loader ${this.options.loader.build} not found, Available builds: ${availableBuilds.join(', ')}` + }; + } + + // Build the URL for the Quilt loader profile JSON + const url = Loader.json + .replace('${build}', selectedBuild.version) + .replace('${version}', this.options.loader.version); + + // Fetch the JSON profile + try { + const response = await fetch(url); + const quiltJson: QuiltJSON = await response.json(); + return quiltJson; + } catch (err: unknown) { + return { error: err instanceof Error ? err.message : 'Failed to fetch or parse Quilt loader JSON' }; + } + } + + /** + * Parses the Quilt JSON to determine which libraries need downloading, skipping + * any that already exist or that are disqualified by "rules". Downloads them + * in bulk using the Downloader utility. + * + * @param quiltJson A QuiltJSON object containing a list of libraries. + * @returns The final list of libraries, or an error if something fails. + */ + public async downloadLibraries(quiltJson: QuiltJSON): Promise { + const { libraries } = quiltJson; + const downloader = new Downloader(); + + let filesToDownload: Array<{ + url: string; + folder: string; + path: string; + name: string; + size: number; + }> = []; + + let checkedLibraries = 0; + let totalSize = 0; + + for (const lib of libraries) { + // If rules exist, skip it (likely platform-specific logic) + if (lib.rules) { + this.emit('check', checkedLibraries++, libraries.length, 'libraries'); + continue; + } + + // Construct the local path where this library should reside + const libInfo = getPathLibraries(lib.name); + const libFolder = path.resolve(this.options.path, 'libraries', libInfo.path); + const libFilePath = path.resolve(libFolder, libInfo.name); + + // If the library doesn't exist locally, prepare to download + if (!fs.existsSync(libFilePath)) { + const libUrl = `${lib.url}${libInfo.path}/${libInfo.name}`; + + let fileSize = 0; + const checkResult = await downloader.checkURL(libUrl); + + if (checkResult && checkResult.status === 200) { + fileSize = checkResult.size; + totalSize += fileSize; + } + + filesToDownload.push({ + url: libUrl, + folder: libFolder, + path: libFilePath, + name: libInfo.name, + size: fileSize + }); + } + + + // Emit a "check" event for each library + this.emit('check', checkedLibraries++, libraries.length, 'libraries'); + } + + // If there are libraries to download, proceed with the bulk download + if (filesToDownload.length > 0) { + downloader.on('progress', (downloaded: number, total: number) => { + this.emit('progress', downloaded, total, 'libraries'); + }); + + await downloader.downloadFileMultiple(filesToDownload, totalSize, this.options.downloadFileMultiple); + } + + return libraries; + } +} diff --git a/src/Minecraft-Loader/patcher.ts b/src/Minecraft-Loader/patcher.ts new file mode 100644 index 00000000..401824ce --- /dev/null +++ b/src/Minecraft-Loader/patcher.ts @@ -0,0 +1,157 @@ +/** + * @author Luuxis + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + */ + +import { spawn } from 'child_process'; +import fs from 'fs'; +import path from 'path'; +import { EventEmitter } from 'events'; +import { getPathLibraries, getFileFromArchive } from '../utils/Index.js'; +import type { + PatcherProfile, + PatcherConfig, + PatcherProcessor, + MinecraftLibrary +} from '../types.js'; + +export type { PatcherProfile as Profile }; + +interface ForgePatcherOptions { + path: string; + loader: { + type: string; + }; +} + +export default class ForgePatcher extends EventEmitter { + private readonly options: ForgePatcherOptions; + + constructor(options: ForgePatcherOptions) { + super(); + this.options = options; + } + + public async patcher(profile: PatcherProfile, config: PatcherConfig, neoForgeOld: boolean = true): Promise { + const { processors } = profile; + + for (const [_, processor] of Object.entries(processors)) { + if (processor.sides && !processor.sides.includes('client')) continue; + + const jarInfo = getPathLibraries(processor.jar); + const jarPath = path.resolve(this.options.path, 'libraries', jarInfo.path, jarInfo.name); + + const args = processor.args + .map(arg => this.setArgument(arg, profile, config, neoForgeOld)) + .map(arg => this.computePath(arg)); + + const classPaths = processor.classpath.map(cp => { + const cpInfo = getPathLibraries(cp); + return `"${path.join(this.options.path, 'libraries', cpInfo.path, cpInfo.name)}"`; + }); + + const mainClass = await this.readJarManifest(jarPath); + if (!mainClass) { + this.emit('error', `Impossible de déterminer la classe principale dans le JAR: ${jarPath}`); + continue; + } + + await new Promise((resolve) => { + const spawned = spawn( + `"${path.resolve(config.java)}"`, + [ + '-classpath', + [`"${jarPath}"`, ...classPaths].join(path.delimiter), + mainClass, + ...args + ], + { shell: true } + ); + + spawned.stdout.on('data', data => { + this.emit('patch', data.toString('utf-8')); + }); + + spawned.stderr.on('data', data => { + this.emit('patch', data.toString('utf-8')); + }); + + spawned.on('close', code => { + if (code !== 0) { + this.emit('error', `Le patcher Forge s'est terminé avec le code ${code}`); + } + resolve(); + }); + }); + } + } + + public check(profile: PatcherProfile): boolean { + const { processors } = profile; + let files: string[] = []; + + for (const processor of Object.values(processors!)) { + if (processor.sides && !processor.sides.includes('client')) continue; + + processor.args.forEach(arg => { + const finalArg = arg.replace('{', '').replace('}', ''); + if (profile.data[finalArg]) { + if (finalArg === 'BINPATCH') return; + files.push(profile.data[finalArg].client); + } + }); + } + + files = Array.from(new Set(files)); + + for (const file of files) { + const lib = getPathLibraries(file.replace('[', '').replace(']', '')); + const filePath = path.resolve(this.options.path, 'libraries', lib.path, lib.name); + if (!fs.existsSync(filePath)) return false; + } + return true; + } + + private setArgument(arg: string, profile: PatcherProfile, config: PatcherConfig, neoForgeOld: boolean): string { + const finalArg = arg.replace('{', '').replace('}', ''); + + const universalLib = profile.libraries?.find(lib => { + if (this.options.loader.type === 'forge') return lib.name.startsWith('net.minecraftforge:forge'); + else return lib.name.startsWith(neoForgeOld ? 'net.neoforged:forge' : 'net.neoforged:neoforge'); + }); + + if (profile.data[finalArg]) { + if (finalArg === 'BINPATCH') { + const jarInfo = getPathLibraries(profile.path || (universalLib?.name ?? '')); + return `"${path.join(this.options.path, 'libraries', jarInfo.path, jarInfo.name).replace('.jar', '-clientdata.lzma')}"`; + } + return profile.data[finalArg].client; + } + + return arg + .replace('{SIDE}', 'client') + .replace('{ROOT}', `"${path.dirname(path.resolve(this.options.path, 'forge'))}"`) + .replace('{MINECRAFT_JAR}', `"${config.minecraft}"`) + .replace('{MINECRAFT_VERSION}', `"${config.minecraftJson}"`) + .replace('{INSTALLER}', `"${path.join(this.options.path, 'libraries')}"`) + .replace('{LIBRARY_DIR}', `"${path.join(this.options.path, 'libraries')}"`); + } + + private computePath(arg: string): string { + if (arg.startsWith('[')) { + const libInfo = getPathLibraries(arg.replace('[', '').replace(']', '')); + return `"${path.join(this.options.path, 'libraries', libInfo.path, libInfo.name)}"`; + } + return arg; + } + + private async readJarManifest(jarPath: string): Promise { + const manifestContent = await getFileFromArchive(jarPath, 'META-INF/MANIFEST.MF'); + if (!manifestContent) return null; + + const content = manifestContent.toString(); + const mainClassLine = content.split('Main-Class: ')[1]; + if (!mainClassLine) return null; + return mainClassLine.split('\r\n')[0]; + } +} diff --git a/src/Minecraft/Minecraft-Arguments.ts b/src/Minecraft/Minecraft-Arguments.ts index 36a41e67..b68f6a3d 100755 --- a/src/Minecraft/Minecraft-Arguments.ts +++ b/src/Minecraft/Minecraft-Arguments.ts @@ -1,140 +1,417 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ +import fs from 'fs'; +import os from 'os'; +import semver from 'semver'; import { getPathLibraries, isold } from '../utils/Index.js'; +import type { + LaunchOptions, + MinecraftVersionJSON, + LoaderJSON, + LaunchArguments, + MinecraftLibrary, + Authenticator, + LibraryRule +} from '../types.js'; -let MojangLib = { win32: "windows", darwin: "osx", linux: "linux" }; +/** Maps Node.js platforms to Mojang's library folders */ +const MOJANG_LIBRARY_MAP: Record = { + win32: 'windows', + darwin: 'osx', + linux: 'linux' +}; +/** + * Builds and organizes JVM and game arguments required to launch Minecraft. + */ export default class MinecraftArguments { - options: any; - authenticator: any; - constructor(options: any) { - this.options = options; - this.authenticator = options.authenticator; - } - - async GetArguments(json: any, loaderJson: any) { - let game = await this.GetGameArguments(json, loaderJson); - let jvm = await this.GetJVMArguments(json); - let classpath = await this.GetClassPath(json, loaderJson); - - return { - game: game, - jvm: jvm, - classpath: classpath - } - } - - async GetGameArguments(json: any, loaderJson: any) { - let game = json.minecraftArguments ? json.minecraftArguments.split(' ') : json.arguments.game; - if (loaderJson) { - let gameLoader = loaderJson.minecraftArguments ? loaderJson.minecraftArguments.split(' ') : []; - game = game.concat(gameLoader); - game = game.filter((item: any, index: any, self: any) => index === self.findIndex((res: any) => res == item)) - } - - let table = { - '${auth_access_token}': this.authenticator.access_token, - '${auth_session}': this.authenticator.access_token, - '${auth_player_name}': this.authenticator.name, - '${auth_uuid}': this.authenticator.uuid, - '${auth_xuid}': this.authenticator.meta.xuid || this.authenticator.access_token, - '${user_properties}': this.authenticator.user_properties, - '${user_type}': this.authenticator.meta.type, - '${version_name}': json.id, - '${assets_index_name}': json.assetIndex.id, - '${game_directory}': this.options.instance ? `${this.options.path}/instances/${this.options.instance}` : this.options.path, - '${assets_root}': isold(json) ? `${this.options.path}/resources` : `${this.options.path}/assets`, - '${game_assets}': isold(json) ? `${this.options.path}/resources` : `${this.options.path}/assets`, - '${version_type}': json.type, - '${clientid}': this.authenticator.clientId || (this.authenticator.client_token || this.authenticator.access_token) - } - - for (let i in game) { - if (typeof game[i] == 'object') game.splice(i, 2) - if (Object.keys(table).includes(game[i])) game[i] = table[game[i]] - } - - if (this.options.screen) { - if (this.options.screen.width !== null && this.options.screen.height !== null) { - game.push('--width') - game.push(this.options.screen.width) - game.push('--height') - game.push(this.options.screen.height) - } - } - - game.push(...this.options.GAME_ARGS) - - return game.filter((item: any) => typeof item !== 'object') - } - - async GetJVMArguments(json: any) { - let opts = { - win32: '-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump', - darwin: '-XstartOnFirstThread', - linux: '-Xss1M' - } - let jvm = [ - `-Xms${this.options.memory.min}`, - `-Xmx${this.options.memory.max}`, - '-XX:+UnlockExperimentalVMOptions', - '-XX:G1NewSizePercent=20', - '-XX:G1ReservePercent=20', - '-XX:MaxGCPauseMillis=50', - '-XX:G1HeapRegionSize=32M', - '-Dfml.ignoreInvalidMinecraftCertificates=true' - ] - - if (process.platform == 'darwin') { - if (!json.minecraftArguments) { - jvm.push(opts[process.platform]) - } - } - - if (json.nativesList) { - jvm.push(`-Djava.library.path=${this.options.path}/versions/${json.id}/natives`) - } - - jvm.push(...this.options.JVM_ARGS) - - return jvm; - } - - async GetClassPath(json: any, loaderJson: any) { - let classPath: any = [] - let libraries: any = json.libraries; - - if (loaderJson?.libraries) libraries = loaderJson.libraries.concat(libraries); - libraries = libraries.filter((library: any, index: any, self: any) => index === self.findIndex((res: any) => res.name === library.name)) - - for (let lib of libraries) { - if (lib.natives) { - let native = lib.natives[MojangLib[process.platform]]; - if (!native) native = lib.natives[process.platform]; - if (!native) continue; - } else { - if (lib.rules && lib.rules[0].os) { - if (lib.rules[0].os.name !== MojangLib[process.platform]) continue; - } - } - - - let path = getPathLibraries(lib.name) - if (lib.loader) { - classPath.push(`${lib.loader}/libraries/${path.path}/${path.name}`) - } else { - classPath.push(`${this.options.path}/libraries/${path.path}/${path.name}`) - } - } - classPath.push(`${this.options.path}/versions/${json.id}/${json.id}.jar`) - - return [ - `-cp`, - classPath.join(process.platform === 'win32' ? ';' : ':'), - loaderJson ? loaderJson.mainClass : json.mainClass - ] - } + private options: LaunchOptions; + private authenticator: Authenticator; + + constructor(options: LaunchOptions) { + this.options = options; + this.authenticator = options.authenticator; + } + + /** + * Gathers all arguments (game, JVM, classpath) and returns them for launching. + */ + public async GetArguments(versionJson: MinecraftVersionJSON, loaderJson?: LoaderJSON): Promise { + const gameArguments = await this.GetGameArguments(versionJson, loaderJson); + const jvmArguments = await this.GetJVMArguments(versionJson); + const classpathData = await this.GetClassPath(versionJson, loaderJson); + + return { + game: gameArguments, + jvm: jvmArguments, + classpath: classpathData.classpath, + mainClass: classpathData.mainClass + }; + } + + /** + * Builds the Minecraft game arguments, injecting authentication tokens, + * user info, and any loader arguments if present. + * @param versionJson The Minecraft version JSON. + * @param loaderJson The loader JSON (e.g., Forge) if applicable. + */ + public async GetGameArguments(versionJson: MinecraftVersionJSON, loaderJson?: LoaderJSON): Promise> { + // For older MC versions, arguments may be in `minecraftArguments` instead of `arguments.game` + let gameArgs = versionJson.minecraftArguments + ? versionJson.minecraftArguments.split(' ') + : versionJson.arguments?.game ?? []; + + // Merge loader's Minecraft arguments if provided + if (loaderJson) { + const loaderGameArgs = loaderJson.minecraftArguments ? loaderJson.minecraftArguments.split(' ') : []; + gameArgs = gameArgs.concat(loaderGameArgs); + // Remove duplicate arguments + gameArgs = gameArgs.filter( + (item, index, self) => index === self.findIndex(arg => arg === item) + ); + } + + // Determine user type (e.g. 'msa' or 'Xbox') depending on version and authenticator + let userType = 'msa'; + if (versionJson.id.startsWith('1.16')) { + userType = 'Xbox'; + } else { + userType = this.authenticator.meta.type === 'Xbox' ? 'msa' : this.authenticator.meta.type; + } + + // Map of placeholders to actual values + const placeholderMap: Record = { + '${auth_access_token}': this.authenticator.access_token, + '${auth_session}': this.authenticator.access_token, + '${auth_player_name}': this.authenticator.name, + '${auth_uuid}': this.authenticator.uuid, + '${auth_xuid}': this.authenticator?.xboxAccount?.xuid || this.authenticator.access_token, + '${user_properties}': this.authenticator.user_properties, + '${user_type}': userType, + '${version_name}': loaderJson ? loaderJson.id || versionJson.id : versionJson.id, + '${assets_index_name}': versionJson.assetIndex.id, + '${game_directory}': this.options.instance + ? `${this.options.path}/instances/${this.options.instance}` + : this.options.path, + '${assets_root}': isold(versionJson) + ? `${this.options.path}/resources` + : `${this.options.path}/assets`, + '${game_assets}': isold(versionJson) + ? `${this.options.path}/resources` + : `${this.options.path}/assets`, + '${version_type}': versionJson.type, + '${clientid}': this.authenticator.clientId + || this.authenticator.client_token + || this.authenticator.access_token + }; + + // Replace placeholders in the game arguments + for (let i = 0; i < gameArgs.length; i++) { + if (typeof gameArgs[i] === 'object') { + gameArgs.splice(i, 1); + i--; + continue; + } + const arg = gameArgs[i] as string; + if (placeholderMap[arg]) { + gameArgs[i] = placeholderMap[arg]; + } + } + + // If screen options are provided, add them + if (this.options.screen) { + const { width, height } = this.options.screen; + if (width && height) { + gameArgs.push('--width', String(width), '--height', String(height)); + } + } + + // Add any extra game arguments from user config + gameArgs.push(...this.options.GAME_ARGS); + + // Filter out any remaining unexpected objects + return gameArgs.filter(item => typeof item === 'string'); + } + + /** + * Evaluates rules for arguments (OS, version, features). + * @param rules Array of rules to evaluate. + * @returns true if all rules pass, false otherwise. + */ + private EvaluateRules(rules?: Array): boolean { + if (!rules || rules.length === 0) return true; + + for (const rule of rules) { + const action = rule.action || 'allow'; + let matches = true; + + // Check OS rules + if (rule.os) { + const osName = MOJANG_LIBRARY_MAP[process.platform]; + + if (rule.os.name && rule.os.name !== osName) { + matches = false; + } + + // Check OS version range (for Windows) + if (matches && rule.os.versionRange) { + const osRelease = os.release(); + + // Simple version comparison for Windows + if (rule.os.versionRange.min) { + // For simplicity, we'll allow the rule if on Windows + // A more complete implementation would parse and compare versions + matches = process.platform === 'win32'; + } + if (rule.os.versionRange.max) { + // Similar simple check + matches = process.platform === 'win32'; + } + } + } + + // If action is 'allow' and matches, or action is 'disallow' and doesn't match + if ((action === 'allow' && !matches) || (action === 'disallow' && matches)) { + return false; + } + } + + return true; + } + + /** + * Extracts the key from a JVM argument for comparison. + * @param arg The JVM argument to extract the key from. + */ + private getArgKey(arg: string): string { + if (arg.includes('=')) return arg.split('=')[0]; + if (arg.match(/^-XX:[+-]/)) return arg; + const match = arg.match(/^(.+?)(\d+[GMKgmkBb]?)?$/); + return match ? match[1] : arg; + } + + /** + * Processes default-user-jvm arguments from the version JSON. + * @param versionJson The Minecraft version JSON. + * @param existingArgs Existing JVM arguments to check for duplicates. + * @returns Array of default JVM arguments to add. + */ + private ProcessDefaultUserJVMArgs(versionJson: MinecraftVersionJSON, existingArgs: Array): Array { + const defaultUserJVM = versionJson.arguments?.['default-user-jvm']; + if (!defaultUserJVM || !Array.isArray(defaultUserJVM)) { + return []; + } + + // Créer un Set des clés existantes (jvmArgs + JVM_ARGS utilisateur) + const allExistingArgs = [...existingArgs, ...this.options.JVM_ARGS]; + const existingKeys = new Set(allExistingArgs.map(arg => this.getArgKey(arg))); + + const defaultArgs: Array = []; + + for (const argEntry of defaultUserJVM) { + // Check if rules exist and evaluate them + if (argEntry.rules && !this.EvaluateRules(argEntry.rules)) continue; + + // Extract values + let values: Array = []; + if (typeof argEntry.value === 'string') values = [argEntry.value]; + else if (Array.isArray(argEntry.value)) values = argEntry.value; + + // Add values if they don't already exist + for (const value of values) { + if (typeof value === 'string') { + const key = this.getArgKey(value); + // Ajouter seulement si la clé n'existe pas déjà + if (!existingKeys.has(key)) { + defaultArgs.push(value); + existingKeys.add(key); // Éviter les doublons dans defaultUserJVM lui-même + } + } + } + } + + return defaultArgs; + } + + /** + * Builds the JVM arguments needed by Minecraft. This includes memory settings, + * OS-specific options, and any additional arguments supplied by the user. + * @param versionJson The Minecraft version JSON. + */ + public async GetJVMArguments(versionJson: MinecraftVersionJSON): Promise> { + // Some OS-specific defaults + const osSpecificOpts: Record = { + win32: '-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump', + darwin: '-XstartOnFirstThread', + linux: '-Xss1M' + }; + + // Core JVM arguments + const jvmArgs: Array = [ + `-Xms${this.options.memory.min}`, + `-Xmx${this.options.memory.max}`, + '-XX:+UnlockExperimentalVMOptions', + '-XX:G1NewSizePercent=20', + '-XX:G1ReservePercent=20', + '-XX:MaxGCPauseMillis=50', + '-XX:G1HeapRegionSize=32M', + '-Dfml.ignoreInvalidMinecraftCertificates=true', + `-Djna.tmpdir=${this.options.path}/versions/${versionJson.id}/natives`, + `-Dorg.lwjgl.system.SharedLibraryExtractPath=${this.options.path}/versions/${versionJson.id}/natives`, + `-Dio.netty.native.workdir=${this.options.path}/versions/${versionJson.id}/natives` + ]; + + // For newer MC versions that use "arguments.game" instead of "minecraftArguments", + // we add OS-specific arguments (e.g., Mac uses -XstartOnFirstThread). + if (!versionJson.minecraftArguments) { + const opt = osSpecificOpts[process.platform]; + if (opt) { + jvmArgs.push(opt); + } + } + + // bypass offline mode multiplayer + if (this.options?.bypassOffline) { + jvmArgs.push('-Dminecraft.api.auth.host=https://nope.invalid/'); + jvmArgs.push('-Dminecraft.api.account.host=https://nope.invalid/'); + jvmArgs.push('-Dminecraft.api.session.host=https://nope.invalid/'); + jvmArgs.push('-Dminecraft.api.services.host=https://nope.invalid/'); + } + + // If natives are specified, add the native library path + if (versionJson.nativesList) { + jvmArgs.push(`-Djava.library.path=${this.options.path}/versions/${versionJson.id}/natives`); + } + + // Special handling for macOS (setting dock icon) + if (os.platform() === 'darwin') { + const assetsPath = `${this.options.path}/assets/indexes/${versionJson.assets}.json`; + const assetsContent = fs.readFileSync(assetsPath, 'utf-8'); + const assetsJson = JSON.parse(assetsContent); + + // Retrieve the hash of the minecraft.icns file + const iconHash = assetsJson.objects['icons/minecraft.icns']?.hash; + if (iconHash) { + jvmArgs.push('-Xdock:name=Minecraft'); + jvmArgs.push(`-Xdock:icon=${this.options.path}/assets/objects/${iconHash.substring(0, 2)}/${iconHash}`); + } + } + + if (versionJson.logging && versionJson.logging.client && !this.options.ignore_log4j) { + const logConfig = versionJson.logging.client; + const logConfigPath = `${this.options.path}/assets/log_configs/${logConfig.file.id}`; + jvmArgs.push(logConfig.argument.replace('${path}', logConfigPath)); + } + + // Process and add default-user-jvm arguments from version JSON + // These are Mojang's recommended JVM arguments for this version + // Pass existing jvmArgs to avoid duplicates with hardcoded arguments + const defaultUserJVMArgs = this.ProcessDefaultUserJVMArgs(versionJson, jvmArgs); + jvmArgs.push(...defaultUserJVMArgs); + + // Append any user-supplied JVM arguments + // User arguments come last so they can override defaults + jvmArgs.push(...this.options.JVM_ARGS); + + return jvmArgs; + } + + /** + * Constructs the classpath (including libraries) that Minecraft requires + * to launch, and identifies the main class. Optionally merges loader libraries. + * @param versionJson The Minecraft version JSON. + * @param loaderJson The loader JSON (e.g., Forge, Fabric) if applicable. + */ + public async GetClassPath(versionJson: MinecraftVersionJSON, loaderJson?: LoaderJSON): Promise<{ + classpath: Array; + mainClass: string | undefined; + }> { + let combinedLibraries: MinecraftLibrary[] = versionJson.libraries ?? []; + + // If a loader JSON is provided, merge its libraries with the base MC version + if (loaderJson?.libraries) { + combinedLibraries = loaderJson.libraries.concat(combinedLibraries); + } + + const map = new Map(); + + for (const dep of combinedLibraries) { + const parts = getPathLibraries(dep.name); + const version = semver.valid(semver.coerce(parts.version)); + if (!version) continue; + + const pathParts = parts.path.split('/'); + const basePath = pathParts.slice(0, -1).join('/'); + + const key = `${basePath}/${parts.name.replace(`-${parts.version}`, '')}`; + const current = map.get(key); + + const isSupportedVersion = semver.satisfies(semver.valid(semver.coerce(this.options.version)), '1.14.4 - 1.18.2'); + const isWindows = process.platform === 'win32'; + + if (!current || semver.gt(version, current.version) && (isSupportedVersion && isWindows)) { + map.set(key, { ...dep, version }); + } + } + + const latest: Record = Object.fromEntries( + Array.from(map.entries()).map(([key, value]) => [key, value as MinecraftLibrary & { version: string }]) + ); + + const librariesList: string[] = []; + for (const lib of Object.values(latest)) { + // Skip certain logging libraries if flagged (e.g., in Forge's "loader" property) + if (lib.loader && lib.name.startsWith('org.apache.logging.log4j:log4j-slf4j2-impl')) continue; + + + // Check if the library has native bindings + if (lib.natives) { + const nativeName = lib.natives[MOJANG_LIBRARY_MAP[process.platform]] || lib.natives[process.platform]; + if (!nativeName) continue; + } else if (lib.rules && lib.rules[0].os) { + // Some libraries only apply to specific OS platforms + if (lib.rules[0].os.name !== MOJANG_LIBRARY_MAP[process.platform]) continue; + } + + // Build the path for this library + const libPath = getPathLibraries(lib.name); + if (lib.loader) { + // If the loader uses a specific library path + librariesList.push(`${lib.loader}/libraries/${libPath.path}/${libPath.name}`); + } else { + librariesList.push(`${this.options.path}/libraries/${libPath.path}/${libPath.name}`); + } + } + + // Add the main Minecraft JAR (or special jar if using old Forge or MCP) + if (loaderJson?.isOldForge && loaderJson.jarPath) { + librariesList.push(loaderJson.jarPath); + } else if (this.options.mcp) { + librariesList.push(this.options.mcp); + } else { + librariesList.push(`${this.options.path}/versions/${versionJson.id}/${versionJson.id}.jar`); + } + + // Filter out duplicates in the final library paths + const uniquePaths: string[] = []; + for (const libPath of librariesList) { + // We only check if we've already used the exact file name + const fileName = libPath.split('/').pop(); + if (fileName && !uniquePaths.includes(fileName)) { + uniquePaths.push(libPath); + } + } + + // The final classpath argument is OS-dependent (':' on Unix, ';' on Windows) + const cpSeparator = process.platform === 'win32' ? ';' : ':'; + const cpArgument = uniquePaths.length > 0 ? uniquePaths.join(cpSeparator) : ''; + + return { + classpath: ['-cp', cpArgument], + mainClass: loaderJson ? loaderJson.mainClass : versionJson.mainClass + }; + } } diff --git a/src/Minecraft/Minecraft-Assets.ts b/src/Minecraft/Minecraft-Assets.ts index 7d47d8ed..11bf783b 100755 --- a/src/Minecraft/Minecraft-Assets.ts +++ b/src/Minecraft/Minecraft-Assets.ts @@ -1,65 +1,121 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ - -import nodeFetch from 'node-fetch'; import fs from 'fs'; +import type { AssetItem, LaunchOptions, MinecraftVersionJSON } from '../types.js'; +/** + * Class responsible for handling Minecraft asset index fetching + * and optionally copying legacy assets to the correct directory. + */ export default class MinecraftAssets { - assetIndex: any; - options: any; - constructor(options: any) { - this.options = options; - } + private assetIndex: { id: string; url: string } | undefined; + private readonly options: LaunchOptions; + + constructor(options: LaunchOptions) { + this.options = options; + } + + /** + * Fetches the asset index from the provided JSON object, then constructs + * and returns an array of asset download objects. These can be processed + * by a downloader to ensure all assets are present locally. + * + * @param versionJson A JSON object containing an "assetIndex" field. + * @returns An array of AssetItem objects with download info. + */ + public async getAssets(versionJson: MinecraftVersionJSON): Promise { + this.assetIndex = versionJson.assetIndex; + if (!this.assetIndex) { + // If there's no assetIndex, there's nothing to download. + return []; + } + + // Fetch the asset index JSON from the remote URL + let data; + try { + const response = await fetch(this.assetIndex.url); + data = await response.json(); + } catch (err: any) { + throw new Error(`Failed to fetch asset index: ${err.message}`); + } + + // First item is the index file itself, which we'll store locally + const assetsArray: AssetItem[] = [ + { + type: 'CFILE', + path: `assets/indexes/${this.assetIndex.id}.json`, + content: JSON.stringify(data) + } + ]; - async GetAssets(json: any) { - this.assetIndex = json.assetIndex; + // Convert the "objects" property into a list of individual assets + const objects = Object.values(data.objects || {}); + for (const obj of objects as Array<{ hash: string; size: number }>) { + assetsArray.push({ + type: 'Assets', + sha1: obj.hash, + size: obj.size, + path: `assets/objects/${obj.hash.substring(0, 2)}/${obj.hash}`, + url: `https://resources.download.minecraft.net/${obj.hash.substring(0, 2)}/${obj.hash}` + }); + } - let assets = []; - let data = await nodeFetch(this.assetIndex.url).then(res => res.json()); + return assetsArray; + } - assets.push({ - type: "CFILE", - path: `assets/indexes/${this.assetIndex.id}.json`, - content: JSON.stringify(data) - }); + /** + * Copies legacy assets (when using older versions of Minecraft) from + * the main "objects" folder to a "resources" folder, preserving the + * directory structure. + * + * @param versionJson A JSON object that has an "assets" property for the index name. + */ + public copyAssets(versionJson: MinecraftVersionJSON): void { + // Determine the legacy directory where resources should go + let legacyDirectory = `${this.options.path}/resources`; + if (this.options.instance) { + legacyDirectory = `${this.options.path}/instances/${this.options.instance}/resources`; + } - data = Object.values(data.objects); + // The path to the local asset index JSON + const pathAssets = `${this.options.path}/assets/indexes/${versionJson.assets}.json`; + if (!fs.existsSync(pathAssets)) { + return; // Nothing to copy if the file doesn't exist + } - for (let asset of data) { - assets.push({ - sha1: asset.hash, - size: asset.size, - type: "Assets", - path: `assets/objects/${asset.hash.substring(0, 2)}/${asset.hash}`, - url: `https://resources.download.minecraft.net/${asset.hash.substring(0, 2)}/${asset.hash}` - }); - } - return assets - } + // Parse the asset index JSON + let assetsData; + try { + assetsData = JSON.parse(fs.readFileSync(pathAssets, 'utf-8')); + } catch (err: any) { + throw new Error(`Failed to read assets index file: ${err.message}`); + } - copyAssets(json: any) { - let legacyDirectory = `${this.options.path}/resources`; - let pathAssets = `${this.options.path}/assets/indexes/${json.assets}.json`; - if (!fs.existsSync(pathAssets)) return; - let assets = JSON.parse(fs.readFileSync(pathAssets, 'utf-8')); - assets = Object.entries(assets.objects); + // Each entry is [filePath, { hash, size }] + const assetsEntries = Object.entries(assetsData.objects || {}); + for (const [filePath, hashData] of assetsEntries) { + const hashObj = hashData as { hash: string; size: number }; + const fullHash = hashObj.hash; + const subHash = fullHash.substring(0, 2); - for (let [file, hash] of assets) { - let Hash = hash.hash; - let Subhash = Hash.substring(0, 2) - let SubAsset = `${this.options.path}/assets/objects/${Subhash}` - let legacyAsset = file.split('/') - legacyAsset.pop() + // Directory where the hashed file is stored + const subAssetDir = `${this.options.path}/assets/objects/${subHash}`; - if (!fs.existsSync(`${legacyDirectory}/${legacyAsset.join('/')}`)) { - fs.mkdirSync(`${legacyDirectory}/${legacyAsset.join('/')}`, { recursive: true }) - } + // If needed, create the corresponding directories in the legacy folder + const pathSegments = filePath.split('/'); + pathSegments.pop(); // Remove the last segment (the filename itself) + if (!fs.existsSync(`${legacyDirectory}/${pathSegments.join('/')}`)) { + fs.mkdirSync(`${legacyDirectory}/${pathSegments.join('/')}`, { recursive: true }); + } - if (!fs.existsSync(`${legacyDirectory}/${file}`)) { - fs.copyFileSync(`${SubAsset}/${Hash}`, `${legacyDirectory}/${file}`) - } - } - } -} \ No newline at end of file + // Copy the file if it doesn't already exist in the legacy location + const sourceFile = `${subAssetDir}/${fullHash}`; + const targetFile = `${legacyDirectory}/${filePath}`; + if (!fs.existsSync(targetFile)) { + fs.copyFileSync(sourceFile, targetFile); + } + } + } +} diff --git a/src/Minecraft/Minecraft-Bundle.ts b/src/Minecraft/Minecraft-Bundle.ts index ad047eb3..33ec25e1 100755 --- a/src/Minecraft/Minecraft-Bundle.ts +++ b/src/Minecraft/Minecraft-Bundle.ts @@ -1,51 +1,242 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ import fs from 'fs'; import path from 'path'; -import crypto from 'crypto'; - -export default class MinecraftBundle { - options: any; - constructor(options: any) { - this.options = options; - } - - async checkBundle(bundle: any) { - let todownload = []; - - for (let file of bundle) { - if (!file.path) continue; - file.path = path.resolve(this.options.path, file.path).replace(/\\/g, "/"); - file.folder = file.path.split("/").slice(0, -1).join("/"); - - if (file.type == "CFILE") { - if (!fs.existsSync(file.folder)) fs.mkdirSync(file.folder, { recursive: true, mode: 0o777 }); - fs.writeFileSync(file.path, file.content, { encoding: "utf8", mode: 0o755 }); - continue; - } - - if (fs.existsSync(file.path)) { - if (this.options.ignored.find(ignored => ignored == file.path.split("/").slice(-1)[0])) continue - if (file.sha1) if (!(await this.checkSHA1(file.path, file.sha1))) todownload.push(file); - } else todownload.push(file); - } - return todownload; - } - - async checkSHA1(file: string, sha1: string) { - const hex = crypto.createHash('sha1').update(fs.readFileSync(file)).digest('hex') - if (hex == sha1) return true; - return false; - } - - async getTotalSize(bundle: any) { - let todownload = 0; - for (let file of bundle) { - todownload += file.size; - } - return todownload; - } -} \ No newline at end of file +import { EventEmitter } from 'events'; +import { getFileHash } from '../utils/Index.js'; +import type { BundleItem, LaunchOptions } from '../types.js'; + +export type { BundleItem }; + +/** Number of files to hash in parallel during bundle checking */ +const CHECK_CONCURRENCY = 64; + +/** + * This class manages checking, downloading, and cleaning up Minecraft files. + */ +export default class MinecraftBundle extends EventEmitter { + private options: LaunchOptions; + + constructor(options: LaunchOptions) { + super(); + this.options = options; + } + + /** + * Checks each item in the provided bundle to see if it needs to be + * downloaded or updated (e.g., if hashes don't match). + * + * Phase 1 (sync, fast): resolve paths, write CFILE files, quick existence + * and size checks to immediately classify files as "missing" or "need hash". + * + * Phase 2 (parallel): hash files that passed the size check in batches + * of CHECK_CONCURRENCY to saturate disk I/O without exhausting memory. + * + * @param bundle Array of file items describing what needs to be on disk. + * @returns Array of BundleItem objects that require downloading. + */ + public async checkBundle(bundle: BundleItem[]): Promise { + const toDownload: BundleItem[] = []; + const toHash: BundleItem[] = []; // files that exist & need hash verification + + let replaceName = `${this.options.path}/`; + if (this.options.instance) { + replaceName = `${this.options.path}/instances/${this.options.instance}/`; + } + const ignoredSet = new Set(this.options.ignored); + + // ── Phase 1: synchronous fast-pass ───────────────────────────── + for (const file of bundle) { + if (!file.path) continue; + + file.path = path.resolve(this.options.path, file.path).replace(/\\/g, '/'); + file.folder = file.path.split('/').slice(0, -1).join('/'); + + if (file.type === 'CFILE') { + if (!fs.existsSync(file.folder)) { + fs.mkdirSync(file.folder, { recursive: true, mode: 0o777 }); + } + fs.writeFileSync(file.path, file.content ?? '', { encoding: 'utf8', mode: 0o755 }); + continue; + } + + let stat: fs.Stats | null = null; + try { stat = fs.statSync(file.path); } catch { /* does not exist */ } + + if (!stat) { + toDownload.push(file); + continue; + } + + // Skip ignored files + const relativePath = file.path.replace(replaceName, ''); + if (ignoredSet.has(relativePath)) continue; + + if (file.sha1) { + // Quick size check: if size is known and doesn't match → skip hash, redownload + if (file.size && stat.size !== file.size) { + toDownload.push(file); + } else { + toHash.push(file); + } + } + } + + // ── Phase 2: parallel hash verification ──────────────────────── + if (toHash.length > 0) { + let checked = 0; + const total = toHash.length; + let idx = 0; + + const worker = async () => { + while (idx < total) { + const file = toHash[idx++]; + try { + const localHash = await getFileHash(file.path); + if (localHash !== file.sha1) { + toDownload.push(file); + } + } catch { + toDownload.push(file); + } + checked++; + this.emit('check', checked, total, 'Checking files'); + } + }; + + const workers: Promise[] = []; + const concurrency = Math.min(CHECK_CONCURRENCY, toHash.length); + for (let i = 0; i < concurrency; i++) { + workers.push(worker()); + } + await Promise.all(workers); + } + + return toDownload; + } + + /** + * Calculates the total download size of all files in the bundle. + * + * @param bundle Array of items in the bundle (with a 'size' field). + * @returns Sum of all file sizes in bytes. + */ + public async getTotalSize(bundle: BundleItem[]): Promise { + let totalSize = 0; + for (const file of bundle) { + if (file.size) { + totalSize += file.size; + } + } + return totalSize; + } + + /** + * Removes files or directories that should not be present, i.e., those + * not listed in the bundle and not in the "ignored" list. + * If the file is a directory, it's removed recursively. + * + * @param bundle Array of BundleItems representing valid files. + */ + public async checkFiles(bundle: BundleItem[]): Promise { + // If using instances, ensure the 'instances' directory exists + let instancePath = ''; + if (this.options.instance) { + if (!fs.existsSync(`${this.options.path}/instances`)) { + fs.mkdirSync(`${this.options.path}/instances`, { recursive: true }); + } + instancePath = `/instances/${this.options.instance}`; + } + + // Gather all existing files in the relevant directory + const allFiles = this.options.instance + ? this.getFiles(`${this.options.path}${instancePath}`) + : this.getFiles(this.options.path); + + // Also gather files from "loader" and "runtime" directories to ignore + const ignoredFiles = [ + ...this.getFiles(`${this.options.path}/loader`), + ...this.getFiles(`${this.options.path}/runtime`) + ]; + + // Convert custom ignored paths to actual file paths + for (let ignoredPath of this.options.ignored) { + ignoredPath = `${this.options.path}${instancePath}/${ignoredPath}`; + if (fs.existsSync(ignoredPath)) { + if (fs.statSync(ignoredPath).isDirectory()) { + // If it's a directory, add all files within it + ignoredFiles.push(...this.getFiles(ignoredPath)); + } else { + // If it's a single file, just add that file + ignoredFiles.push(ignoredPath); + } + } + } + + // Mark bundle paths as ignored (so we don't delete them) + bundle.forEach(file => { + ignoredFiles.push(file.path); + }); + + // Filter out all ignored files from the main file list + const filesToDelete = allFiles.filter(file => !ignoredFiles.includes(file)); + + // Remove each file or directory + for (const filePath of filesToDelete) { + try { + const stats = fs.statSync(filePath); + if (stats.isDirectory()) { + fs.rmSync(filePath, { recursive: true }); + } else { + fs.unlinkSync(filePath); + + // Clean up empty folders going upward until we hit the main path + let currentDir = path.dirname(filePath); + while (true) { + if (currentDir === this.options.path) break; + const dirContents = fs.readdirSync(currentDir); + if (dirContents.length === 0) { + fs.rmSync(currentDir); + } + currentDir = path.dirname(currentDir); + } + } + } catch { + // If an error occurs (e.g. file locked or non-existent), skip it + continue; + } + } + } + + /** + * Recursively gathers all files in a given directory path. + * If a directory is empty, it is also added to the returned array. + * + * @param dirPath The starting directory path to walk. + * @param collectedFiles Used internally to store file paths. + * @returns The array of all file paths (and empty directories) under dirPath. + */ + private getFiles(dirPath: string, collectedFiles: string[] = []): string[] { + if (fs.existsSync(dirPath)) { + const entries = fs.readdirSync(dirPath); + // If the directory is empty, store it as a "file" so it can be processed + if (entries.length === 0) { + collectedFiles.push(dirPath); + } + // Explore each child entry + for (const entry of entries) { + const fullPath = `${dirPath}/${entry}`; + const stats = fs.statSync(fullPath); + if (stats.isDirectory()) { + this.getFiles(fullPath, collectedFiles); + } else { + collectedFiles.push(fullPath); + } + } + } + return collectedFiles; + } +} diff --git a/src/Minecraft/Minecraft-Java.ts b/src/Minecraft/Minecraft-Java.ts index 8af5e865..f68ee313 100755 --- a/src/Minecraft/Minecraft-Java.ts +++ b/src/Minecraft/Minecraft-Java.ts @@ -1,58 +1,254 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ import os from 'os'; -import nodeFetch from 'node-fetch'; import path from 'path'; +import fs from 'fs'; +import EventEmitter from 'events'; -export default class java { - options: any; - constructor(options: any) { - this.options = options; - } - - async GetJsonJava(jsonversion: any) { - let version: any; - let files: any = []; - let javaVersionsJson = await nodeFetch("https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json").then(res => res.json()) - - if (!jsonversion.javaVersion) jsonversion = "jre-legacy" - else jsonversion = jsonversion.javaVersion.component - - if (os.platform() == "win32") { - let arch = { x64: "windows-x64", ia32: "windows-x86" } - version = `jre-${javaVersionsJson[`${arch[os.arch()]}`][jsonversion][0].version.name}` - javaVersionsJson = Object.entries((await nodeFetch(javaVersionsJson[`${arch[os.arch()]}`][jsonversion][0].manifest.url).then(res => res.json())).files) - } else if (os.platform() == "darwin") { - let arch = { x64: "mac-os", arm64: "mac-os-arm64" } - version = `jre-${javaVersionsJson[`${arch[os.arch()]}`][jsonversion][0].version.name}` - javaVersionsJson = Object.entries((await nodeFetch(javaVersionsJson[`${arch[os.arch()]}`][jsonversion][0].manifest.url).then(res => res.json())).files) - } else if (os.platform() == "linux") { - let arch = { x64: "linux", ia32: "linux-i386" } - version = `jre-${javaVersionsJson[`${arch[os.arch()]}`][jsonversion][0].version.name}` - javaVersionsJson = Object.entries((await nodeFetch(javaVersionsJson[`${arch[os.arch()]}`][jsonversion][0].manifest.url).then(res => res.json())).files) - } else return console.log("OS not supported"); - - let java = javaVersionsJson.find(file => file[0].endsWith(process.platform == "win32" ? "bin/javaw.exe" : "bin/java"))[0]; - let toDelete = java.replace(process.platform == "win32" ? "bin/javaw.exe" : "bin/java", ""); - - for (let [path, info] of javaVersionsJson) { - if (info.type == "directory") continue; - if (!info.downloads) continue; - let file: any = {}; - file.path = `runtime/${version}/${path.replace(toDelete, "")}`; - file.executable = info.executable; - file.sha1 = info.downloads.raw.sha1; - file.size = info.downloads.raw.size; - file.url = info.downloads.raw.url; - file.type = "Java"; - files.push(file); - } - return { - files: files, - path: path.resolve(this.options.path, `runtime/${version}/bin/java${process.platform == "win32" ? ".exe" : ""}`).replace(/\\/g, "/"), - }; - } -} \ No newline at end of file +import { getFileFromArchive } from '../utils/Index.js'; +import Downloader from '../utils/Downloader.js'; +import type { + LaunchOptions, + MinecraftVersionJSON, + JavaDownloadResult, + JavaFileItem, + ArchiveEntry +} from '../types.js'; + +export type { JavaDownloadResult, JavaFileItem }; + +/** + * Manages the download and extraction of the correct Java runtime for Minecraft. + * It supports both Mojang's curated list and Azul fallback. + */ +export default class JavaDownloader extends EventEmitter { + private options: LaunchOptions; + + constructor(options: LaunchOptions) { + super(); + this.options = options; + } + + /** + * Retrieves Java files from Mojang's runtime metadata if possible, + * otherwise falls back to getJavaOther(). + * + * @param jsonversion A JSON object describing the Minecraft version (with optional javaVersion). + * @returns An object containing a list of JavaFileItems and the final path to "java". + */ + public async getJavaFiles(jsonversion: MinecraftVersionJSON): Promise { + // If a specific version is forced, delegate to getJavaOther() immediately + if (this.options.java.version) { + return this.getJavaOther(jsonversion, this.options.java.version); + } + + // OS-to-architecture mapping for Mojang's curated Java. + const archMapping: Record> = { + win32: { x64: 'windows-x64', ia32: 'windows-x86', arm64: 'windows-arm64' }, + darwin: { x64: 'mac-os', arm64: this.options.intelEnabledMac ? 'mac-os' : 'mac-os-arm64' }, + linux: { x64: 'linux', ia32: 'linux-i386' } + }; + + const osPlatform = os.platform(); // "win32", "darwin", "linux", ... + const arch = os.arch(); // "x64", "arm64", "ia32", ... + + const javaVersionName = jsonversion.javaVersion?.component || 'jre-legacy'; + const osArchMapping = archMapping[osPlatform]; + const files: JavaFileItem[] = []; + + // If we don't have a valid mapping for the current OS, fallback to Adoptium + if (!osArchMapping) { + return this.getJavaOther(jsonversion); + } + + // Determine the OS-specific identifier + const archOs = osArchMapping[arch]; + if (!archOs) { + // If we can't match the arch in the sub-object, fallback + return this.getJavaOther(jsonversion); + } + + // Fetch Mojang's Java runtime metadata + const url = 'https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json'; + const javaVersionsJson = await fetch(url).then(res => res.json()); + + const versionName = javaVersionsJson[archOs]?.[javaVersionName]?.[0]?.version?.name; + if (!versionName) { + return this.getJavaOther(jsonversion); + } + + // Fetch the runtime manifest which lists individual files + const manifestUrl = javaVersionsJson[archOs][javaVersionName][0]?.manifest?.url; + const manifest = await fetch(manifestUrl).then(res => res.json()); + const manifestEntries: Array<[string, any]> = Object.entries(manifest.files); + + // Identify the Java executable in the manifest + const javaExeKey = process.platform === 'win32' ? 'bin/javaw.exe' : 'bin/java'; + const javaEntry = manifestEntries.find(([relPath]) => relPath.endsWith(javaExeKey)); + if (!javaEntry) { + // If we can't find the executable, fallback + return this.getJavaOther(jsonversion); + } + + const toDelete = javaEntry[0].replace(javaExeKey, ''); + for (const [relPath, info] of manifestEntries) { + if (info.type === 'directory') continue; + if (!info.downloads) continue; + + files.push({ + path: `runtime/${javaVersionName}/${relPath.replace(toDelete, '')}`, + executable: info.executable, + sha1: info.downloads.raw.sha1, + size: info.downloads.raw.size, + url: info.downloads.raw.url, + type: 'Java' + }); + } + + return { + files, + path: path.resolve( + this.options.path, + `runtime/${javaVersionName}`, + 'bin', + process.platform === 'win32' ? 'javaw.exe' : 'java' + ) + }; + } + + /** + * Fallback method to download Java from Adoptium if Mojang's metadata is unavailable + * or doesn't have the appropriate runtime for the user's platform/arch. + * + * @param jsonversion A Minecraft version JSON (with optional javaVersion). + * @param versionDownload A forced Java version (string) if provided by the user. + */ + public async getJavaOther(jsonversion: MinecraftVersionJSON, versionDownload?: string): Promise { + const { platform, arch } = this.getPlatformArch(); + const majorVersion = versionDownload || jsonversion.javaVersion?.majorVersion || 8; + const pathFolder = path.resolve(this.options.path, `runtime/jre-${majorVersion}`); + + // Build the API query to fetch the Java version + const queryParams = new URLSearchParams({ + java_version: majorVersion.toString(), + os: platform, + arch: arch, + archive_type: 'zip', + java_package_type: this.options.java.type + }); + const javaVersionURL = `https://api.azul.com/metadata/v1/zulu/packages/?${queryParams.toString()}`; + let javaVersions = await fetch(javaVersionURL).then(res => res.json()); + if (!Array.isArray(javaVersions) || javaVersions.length === 0) { + return { files: [], path: '', error: true, message: 'No Java versions found for the specified parameters.' }; + } + javaVersions = javaVersions[0]; + + let javaExePath = path.join(pathFolder, javaVersions.name.replace('.zip', ''), 'bin', 'java'); + if (platform === 'macos') { + try { + const pathBin = fs.readFileSync(path.join(pathFolder, javaVersions.name.replace('.zip', ''), "bin"), 'utf8').toString(); + javaExePath = path.join(pathFolder, javaVersions.name.replace('.zip', ''), pathBin, 'java'); + } catch (_) { + } + } + + if (!fs.existsSync(javaExePath)) { + await this.verifyAndDownloadFile({ + filePath: path.join(pathFolder, javaVersions.name), + pathFolder: pathFolder, + fileName: javaVersions.name, + url: javaVersions.download_url + }) + + const entries = await getFileFromArchive(path.join(pathFolder, javaVersions.name), null, null, true) as ArchiveEntry[]; + + for (const entry of entries) { + if (entry.name.startsWith('META-INF')) continue; + + if (entry.isDirectory) { + fs.mkdirSync(`${pathFolder}/${entry.name}`, { recursive: true, mode: 0o777 }); + continue; + } + fs.writeFileSync(`${pathFolder}/${entry.name}`, entry.data, { mode: 0o777 }); + } + + if (platform === 'macos') { + try { + const pathBin = fs.readFileSync(path.join(pathFolder, javaVersions.name.replace('.zip', ''), "bin"), 'utf8').toString(); + javaExePath = path.join(pathFolder, javaVersions.name.replace('.zip', ''), pathBin, 'java'); + } catch (_) { + } + } + } + + return { files: [], path: javaExePath }; + } + + /** + * Maps the Node `os.platform()` and `os.arch()` to Adoptium's expected format. + * Apple Silicon can optionally download x64 if `intelEnabledMac` is true. + */ + private getPlatformArch(): { platform: string; arch: string } { + const platformMap: Record = { + win32: 'windows', + darwin: 'macos', + linux: 'linux' + }; + const archMap: Record = { + x64: 'x64', + ia32: 'x32', + arm64: 'aarch64', + arm: 'arm' + }; + + const mappedPlatform = platformMap[os.platform()] || os.platform(); + let mappedArch = archMap[os.arch()] || os.arch(); + + // Force x64 if Apple Silicon but user wants to use Intel-based Java + if (os.platform() === 'darwin' && os.arch() === 'arm64' && this.options.intelEnabledMac) { + mappedArch = 'x64'; + } + + return { platform: mappedPlatform, arch: mappedArch }; + } + + /** + * Verifies if the Java archive already exists and matches the expected checksum. + * If it doesn't exist or fails the hash check, it downloads from the given URL. + * + * @param params.filePath The local file path + * @param params.pathFolder The folder to place the file in + * @param params.fileName The name of the file + * @param params.url The remote download URL + * @param params.checksum Expected SHA-256 hash + */ + private async verifyAndDownloadFile({ + filePath, + pathFolder, + fileName, + url + }: { + filePath: string; + pathFolder: string; + fileName: string; + url: string; + }): Promise { + + // If not found or failed checksum, download anew + if (!fs.existsSync(filePath)) { + fs.mkdirSync(pathFolder, { recursive: true }); + const download = new Downloader(); + + // Relay progress events + download.on('progress', (downloaded: number, size: number) => { + this.emit('progress', downloaded, size, fileName); + }); + + // Start download + await download.downloadFile(url, pathFolder, fileName); + } + } +} diff --git a/src/Minecraft/Minecraft-Json.ts b/src/Minecraft/Minecraft-Json.ts index 4a72d381..1609d157 100755 --- a/src/Minecraft/Minecraft-Json.ts +++ b/src/Minecraft/Minecraft-Json.ts @@ -1,40 +1,77 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ -import nodeFetch from 'node-fetch'; +import os from 'os'; +import MinecraftNativeLinuxARM from './Minecraft-Lwjgl-Native.js'; +import type { + MinecraftVersionJSON, + VersionEntry, + MojangVersionManifest, + GetInfoVersionResult, + GetInfoVersionError, + LaunchOptions +} from '../types.js'; +export type { GetInfoVersionResult, GetInfoVersionError }; + +/** + * This class retrieves Minecraft version information from Mojang's + * version manifest, and optionally processes the JSON for ARM-based Linux. + */ export default class Json { - options: any; - - constructor(options: any) { - this.options = options; - } - - async GetInfoVersion() { - let version: string = this.options.version; - let data: any = await nodeFetch(`https://launchermeta.mojang.com/mc/game/version_manifest_v2.json?_t=${new Date().toISOString()}`); - data = await data.json(); - - if (version == 'latest_release' || version == 'r' || version == 'lr') { - version = data.latest.release; - } - else if (version == 'latest_snapshot' || version == 's' || version == 'ls') { - version = data.latest.snapshot; - } - - data = data.versions.find(v => v.id === version); - - if (!data) return { - error: true, - message: `Minecraft ${version} is not found.` - }; - - return { - InfoVersion: data, - json: await nodeFetch(data.url).then(res => res.json()), - version: version - }; - } -} \ No newline at end of file + private readonly options: LaunchOptions; + + constructor(options: LaunchOptions) { + this.options = options; + } + + /** + * Fetches the Mojang version manifest, resolves the intended version (release, snapshot, etc.), + * and returns the associated JSON object for that version. + * If the system is Linux ARM, it will run additional processing on the JSON. + * + * @returns An object containing { InfoVersion, json, version }, or an error object. + */ + public async GetInfoVersion(): Promise { + let { version } = this.options; + + // Fetch the version manifest + const response = await fetch( + `https://launchermeta.mojang.com/mc/game/version_manifest_v2.json?_t=${new Date().toISOString()}` + ); + const manifest: MojangVersionManifest = await response.json(); + + // Resolve "latest_release"/"latest_snapshot" shorthands + if (version === 'latest_release' || version === 'r' || version === 'lr') { + version = manifest.latest.release; + } else if (version === 'latest_snapshot' || version === 's' || version === 'ls') { + version = manifest.latest.snapshot; + } + + // Find the matching version info from the manifest + const matchedVersion = manifest.versions.find((v) => v.id === version); + if (!matchedVersion) { + return { + error: true, + message: `Minecraft ${version} is not found.` + }; + } + + // Fetch the detailed version JSON from Mojang + const jsonResponse = await fetch(matchedVersion.url); + let versionJson = await jsonResponse.json(); + + // If on Linux ARM, run additional processing + if (os.platform() === 'linux' && os.arch().startsWith('arm')) { + versionJson = await new MinecraftNativeLinuxARM(this.options).ProcessJson(versionJson); + } + + return { + InfoVersion: matchedVersion, + json: versionJson, + version + }; + } +} diff --git a/src/Minecraft/Minecraft-Libraries.ts b/src/Minecraft/Minecraft-Libraries.ts index 5ca38b0f..c0cb5e45 100755 --- a/src/Minecraft/Minecraft-Libraries.ts +++ b/src/Minecraft/Minecraft-Libraries.ts @@ -1,171 +1,200 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ import os from 'os'; import fs from 'fs'; -import AdmZip from 'adm-zip'; -import nodeFetch from 'node-fetch'; - -let MojangLib = { win32: "windows", darwin: "osx", linux: "linux" }; -let Arch = { x32: "32", x64: "64", arm: "32", arm64: "64" }; +import { getFileFromArchive } from '../utils/Index.js'; +import type { + MinecraftVersionJSON, + MinecraftLibrary, + DownloadFile, + CustomAssetItem, + LaunchOptions, + MOJANG_OS_MAP, + MOJANG_ARCH_MAP, + ArchiveEntry +} from '../types.js'; + +/** Maps Node.js platforms to Mojang's naming scheme */ +const MojangLib: Record = { + win32: 'windows', + darwin: 'osx', + linux: 'linux' +}; + +/** Maps Node.js architecture to Mojang's arch replacements */ +const Arch: Record = { + x32: '32', + x64: '64', + arm: '32', + arm64: '64' +}; +/** + * This class is responsible for: + * - Gathering library download info from the version JSON + * - Handling custom asset entries if provided + * - Extracting native libraries for the current OS + */ export default class Libraries { - json: any; - options: any; - constructor(options: any) { - this.options = options; - } - - async Getlibraries(json: any) { - this.json = json; - let libraries = []; - - for (let lib of this.json.libraries) { - let artifact: any; - let type = "Libraries"; - - if (lib.natives) { - let classifiers = lib.downloads.classifiers; - let native = lib.natives[MojangLib[process.platform]]; - if (!native) native = lib.natives[process.platform]; - type = "Native"; - if (native) artifact = classifiers[native.replace("${arch}", Arch[os.arch()])]; - else continue; - } else { - if (lib.rules && lib.rules[0].os) { - if (lib.rules[0].os.name !== MojangLib[process.platform]) continue; - } - artifact = lib.downloads.artifact; - } - if (!artifact) continue; - libraries.push({ - sha1: artifact.sha1, - size: artifact.size, - path: `libraries/${artifact.path}`, - type: type, - url: artifact.url - }); - } - - let clientjar = this.json.downloads.client; - libraries.push({ - sha1: clientjar.sha1, - size: clientjar.size, - path: `versions/${this.json.id}/${this.json.id}.jar`, - type: "Libraries", - url: clientjar.url - }); - - libraries.push({ - path: `versions/${this.json.id}/${this.json.id}.json`, - type: "CFILE", - content: JSON.stringify(this.json) - }); - return libraries; - } - - async GetAssetsOthers(url: any) { - if (!url) return []; - let data = await nodeFetch(url).then(res => res.json()); - - let assets = []; - for (let asset of data) { - if (!asset.path) continue - let path = asset.path; - assets.push({ - sha1: asset.hash, - size: asset.size, - type: path.split("/")[0], - path: this.options.instance ? `${this.options.path}/instances/${this.options.instance}/${path}` : path, - url: asset.url - }); - } - return assets - } - - async natives(bundle: any) { - let natives = bundle.filter(mod => mod.type === "Native").map(mod => `${mod.path}`); - if (natives.length === 0) return natives; - let nativeFolder = (`${this.options.path}/versions/${this.json.id}/natives`).replace(/\\/g, "/"); - if (!fs.existsSync(nativeFolder)) fs.mkdirSync(nativeFolder, { recursive: true, mode: 0o777 }); - - for (let native of natives) { - let zip = new AdmZip(native); - let entries = zip.getEntries(); - for (let entry of entries) { - if (entry.entryName.startsWith("META-INF")) continue; - if (entry.isDirectory) { - fs.mkdirSync(`${nativeFolder}/${entry.entryName}`, { recursive: true, mode: 0o777 }); - continue - } - fs.writeFile(`${nativeFolder}/${entry.entryName}`, zip.readFile(entry), { encoding: "utf8", mode: 0o777 }, () => { }); - } - } - return natives; - } - - async checkFiles(bundle: any) { - let instancePath = '' - let instanceFolder = [] - if (this.options.instance) { - if (!fs.existsSync(`${this.options.path}/instances`)) fs.mkdirSync(`${this.options.path}/instances`, { recursive: true }); - instancePath = `/instances/${this.options.instance}` - instanceFolder = fs.readdirSync(`${this.options.path}/instances`).filter(dir => dir != this.options.instance) - } - let files = this.getFiles(this.options.path); - let ignoredfiles = [...this.getFiles(`${this.options.path}/loader`)] - - for (let instances of instanceFolder) { - ignoredfiles.push(...this.getFiles(`${this.options.path}/instances/${instances}`)); - } - - for (let file of this.options.ignored) { - file = (`${this.options.path}${instancePath}/${file}`) - if (fs.existsSync(file)) { - if (fs.statSync(file).isDirectory()) { - ignoredfiles.push(...this.getFiles(file)); - } else if (fs.statSync(file).isFile()) { - ignoredfiles.push(file); - } - } - } - - ignoredfiles.forEach(file => this.options.ignored.push((file))); - bundle.forEach(file => ignoredfiles.push((file.path))); - files = files.filter(file => ignoredfiles.indexOf(file) < 0); - - for (let file of files) { - try { - if (fs.statSync(file).isDirectory()) { - fs.rmdirSync(file); - } else { - fs.unlinkSync(file); - let folder = file.split("/").slice(0, -1).join("/"); - while (true) { - if (folder == this.options.path) break; - let content = fs.readdirSync(folder); - if (content.length == 0) fs.rmdirSync(folder); - folder = folder.split("/").slice(0, -1).join("/"); - } - } - } catch (e) { - continue; - } - } - } - - getFiles(path: any, file = []) { - if (fs.existsSync(path)) { - let files = fs.readdirSync(path); - if (files.length == 0) file.push(path); - for (let i in files) { - let name = `${path}/${files[i]}`; - if (fs.statSync(name).isDirectory()) this.getFiles(name, file); - else file.push(name); - } - } - return file; - } -} \ No newline at end of file + private json!: MinecraftVersionJSON; + private readonly options: LaunchOptions; + + constructor(options: LaunchOptions) { + this.options = options; + } + + public async Getlibraries(json: MinecraftVersionJSON): Promise { + this.json = json; + const libraries: DownloadFile[] = []; + + for (const lib of this.json.libraries) { + let artifact: { sha1: string; size: number; path: string; url: string } | undefined; + let type = 'Libraries'; + + if (lib.natives) { + const classifiers = lib.downloads?.classifiers; + let native = lib.natives[MojangLib[os.platform()]] || lib.natives[os.platform()]; + type = 'Native'; + if (native) { + // Replace "${arch}" if present, e.g. "natives-windows-${arch}" + const archReplaced = native.replace('${arch}', Arch[os.arch()] || ''); + artifact = classifiers ? classifiers[archReplaced] : undefined; + } else { + // No valid native for the current platform + continue; + } + } else { + if (lib.rules && lib.rules[0]?.os?.name) { + if (lib.rules[0].os.name !== MojangLib[os.platform()]) { + continue; + } + } + artifact = lib.downloads?.artifact; + } + + if (!artifact) continue; + + libraries.push({ + sha1: artifact.sha1, + size: artifact.size, + path: `libraries/${artifact.path}`, + type: type, + url: artifact.url + }); + } + + // Add the main Minecraft client JAR to the list + libraries.push({ + sha1: this.json.downloads.client.sha1, + size: this.json.downloads.client.size, + path: `versions/${this.json.id}/${this.json.id}.jar`, + type: 'Libraries', + url: this.json.downloads.client.url + }); + + // Add the JSON file for this version as a "CFILE" + libraries.push({ + path: `versions/${this.json.id}/${this.json.id}.json`, + type: 'CFILE', + content: JSON.stringify(this.json) + }); + + return libraries; + } + + public async GetLogging(): Promise { + let libraries: DownloadFile[] = []; + if (this.json.logging) { + const logConfig = this.json.logging; + const artifact = logConfig.client.file; + + libraries.push({ + sha1: artifact.sha1, + size: artifact.size, + path: `assets/log_configs/${artifact.id}`, + type: 'Log_configs', + url: artifact.url + }); + } + return libraries; + } + + /** + * Fetches custom assets or libraries from a remote URL if provided. + * This method expects the response to be an array of objects with + * "path", "hash", "size", and "url". + * + * @param url The remote URL that returns a JSON array of CustomAssetItem + * @returns An array of LibraryDownload entries describing each item + */ + public async GetAssetsOthers(url: string | null): Promise { + if (!url) return []; + + const response = await fetch(url); + const data: CustomAssetItem[] = await response.json(); + + const assets: DownloadFile[] = []; + for (const asset of data) { + if (!asset.path) continue; + + // The 'type' is deduced from the first part of the path + const fileType = asset.path.split('/')[0]; + assets.push({ + sha1: asset.hash, + size: asset.size, + type: fileType, + path: this.options.instance + ? `instances/${this.options.instance}/${asset.path}` + : asset.path, + url: asset.url + }); + } + return assets; + } + + /** + * Extracts native libraries from the downloaded jars (those marked type="Native") + * and places them into the "natives" folder under "versions//natives". + * + * @param bundle An array of library entries (some of which may be natives) + * @returns The paths of the native files that were extracted + */ + public async natives(bundle: DownloadFile[]): Promise { + // Gather only the native library files + const natives = bundle + .filter((item) => item.type === 'Native') + .map((item) => `${item.path}`); + + if (natives.length === 0) { + return []; + } + + // Create the natives folder if it doesn't already exist + const nativesFolder = `${this.options.path}/versions/${this.json.id}/natives`.replace(/\\/g, '/'); + if (!fs.existsSync(nativesFolder)) { + fs.mkdirSync(nativesFolder, { recursive: true, mode: 0o777 }); + } + + // For each native jar, extract its contents (excluding META-INF) + for (const native of natives) { + const entries = await getFileFromArchive(native, null, null, true) as ArchiveEntry[]; + + + for (const entry of entries) { + if (entry.name.startsWith('META-INF')) continue; + + if (entry.isDirectory) { + fs.mkdirSync(`${nativesFolder}/${entry.name}`, { recursive: true, mode: 0o777 }); + continue; + } + + // Write the file to the natives folder + fs.writeFileSync(`${nativesFolder}/${entry.name}`, entry.data, { mode: 0o777 }); + } + } + return natives; + } +} diff --git a/src/Minecraft/Minecraft-Loader.ts b/src/Minecraft/Minecraft-Loader.ts index 4141872c..3cfcc397 100755 --- a/src/Minecraft/Minecraft-Loader.ts +++ b/src/Minecraft/Minecraft-Loader.ts @@ -1,97 +1,119 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ import { EventEmitter } from 'events'; -const loaderDownloader = require('minecraft-loader'); - - -export default class MinecraftLoader { - options: any; - on: any; - emit: any; - constructor(options: any) { - this.options = options; - this.on = EventEmitter.prototype.on; - this.emit = EventEmitter.prototype.emit; - } - - async GetLoader(version: any, javaPath: any) { - let loader = new loaderDownloader({ - path: `${this.options.path}/loader/${this.options.loader.type}`, - timeout: this.options.timeout, - downloadFileMultiple: this.options.downloadFileMultiple, - autoClean: true, - loader: { - type: this.options.loader.type, - version: version, - build: this.options.loader.build, - config: { - javaPath: javaPath, - minecraftJar: `${this.options.path}/versions/${version}/${version}.jar`, - minecraftJson: `${this.options.path}/versions/${version}/${version}.json` - } - } - }); - return await new Promise((resolve, reject) => { - loader.install(); - - loader.on('json', (json: any) => { - let loaderJson = json; - loaderJson.libraries = loaderJson.libraries.map((lib: any) => { - lib.loader = `${this.options.path}/loader/${this.options.loader.type}`; - return lib; - }); - - resolve(loaderJson); - }); - - loader.on('extract', (extract: any) => { - this.emit('extract', extract); - }); - - loader.on('progress', (progress: any, size: any, element: any) => { - this.emit('progress', progress, size, element); - }); - - loader.on('check', (progress: any, size: any, element: any) => { - this.emit('check', progress, size, element); - }); - - loader.on('patch', (patch: any) => { - this.emit('patch', patch); - }); - - loader.on('error', (err: any) => { - reject(err); - }); - }) - } - - async GetArguments(json: any, version: any) { - if (json === null) { - return { - game: [], - jvm: [] - } - } - - let moddeArguments = json.arguments; - if (!moddeArguments) return { game: [], jvm: [] }; - let Arguments: any = {} - if (moddeArguments.game) Arguments.game = moddeArguments.game; - if (moddeArguments.jvm) Arguments.jvm = moddeArguments.jvm.map(jvm => { - return jvm - .replace(/\${version_name}/g, version) - .replace(/\${library_directory}/g, `${this.options.path}/loader/${this.options.loader.type}/libraries`) - .replace(/\${classpath_separator}/g, process.platform === 'win32' ? ';' : ':'); - }) - - return { - game: Arguments.game || [], - jvm: Arguments.jvm || [], - mainClass: json.mainClass - }; - } -} \ No newline at end of file +import path from 'path'; +import LoaderDownloader from '../Minecraft-Loader/index.js'; +import type { + LaunchOptions, + LoaderJSON, + LoaderArguments, + LoaderType +} from '../types.js'; + +/** + * This class manages the installation and argument-building for a Minecraft + * mod loader (e.g. Forge, Fabric). It wraps a `LoaderDownloader` and emits + * the same events for progress, extraction, patching, etc. + */ +export default class MinecraftLoader extends EventEmitter { + private options: LaunchOptions; + private loaderPath: string; + + constructor(options: LaunchOptions) { + super(); + this.options = options; + this.loaderPath = path.join(this.options.path, this.options.loader.path!); + } + + /** + * Installs the loader for a given Minecraft version using a LoaderDownloader, + * returning the loader's JSON on completion. This function emits several events + * for progress reporting and patch notifications. + * + * @param version The Minecraft version (e.g. "1.19.2") + * @param javaPath Path to the Java executable used by the loader for patching + * @returns A Promise that resolves to the loader's JSON configuration + */ + public async GetLoader(version: string, javaPath: string): Promise { + const loader = new LoaderDownloader({ + path: this.loaderPath, + downloadFileMultiple: this.options.downloadFileMultiple, + loader: { + type: this.options.loader.type! as LoaderType, + version: version, + build: this.options.loader.build!, + config: { + javaPath, + minecraftJar: `${this.options.path}/versions/${version}/${version}.jar`, + minecraftJson: `${this.options.path}/versions/${version}/${version}.json` + } + } + }); + + return new Promise((resolve, reject) => { + loader.install(); + + loader.on('json', (json: LoaderJSON) => { + const modifiedJson = json; + if (modifiedJson.libraries) { + modifiedJson.libraries = modifiedJson.libraries.map(lib => { + lib.loader = this.loaderPath; + return lib; + }); + } + resolve(modifiedJson); + }); + + loader.on('extract', (extract: string) => { + this.emit('extract', extract); + }); + + loader.on('progress', (progress: number, size: number, element: string) => { + this.emit('progress', progress, size, element); + }); + + loader.on('check', (progress: number, size: number, element: string) => { + this.emit('check', progress, size, element); + }); + + loader.on('patch', (patch: string) => { + this.emit('patch', patch); + }); + + loader.on('error', (err: Error) => { + reject(err); + }); + }); + } + + public async GetArguments(json: LoaderJSON | null, version: string): Promise { + // If no loader JSON is provided, return empty arrays + if (json === null) { + return { game: [], jvm: [] }; + } + + const moddedArgs = json.arguments; + if (!moddedArgs) return { game: [], jvm: [] }; + + const args: LoaderArguments = { game: [], jvm: [] }; + + if (moddedArgs.game) { + args.game = moddedArgs.game; + } + + if (moddedArgs.jvm) { + args.jvm = moddedArgs.jvm.map((jvmArg) => + jvmArg + .replace(/\${version_name}/g, version) + .replace(/\${library_directory}/g, `${this.loaderPath}/libraries`) + .replace(/\${classpath_separator}/g, process.platform === 'win32' ? ';' : ':') + ); + } + + args.mainClass = json.mainClass; + return args; + } +} diff --git a/src/Minecraft/Minecraft-Lwjgl-Native.ts b/src/Minecraft/Minecraft-Lwjgl-Native.ts new file mode 100644 index 00000000..577aa92a --- /dev/null +++ b/src/Minecraft/Minecraft-Lwjgl-Native.ts @@ -0,0 +1,83 @@ +/** + * @author Luuxis + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + */ + +import path from 'path'; +import fs from 'fs'; +import os from 'os'; +import type { MinecraftVersionJSON, MinecraftLibrary, LaunchOptions } from '../types.js'; + +/** + * This class modifies the version JSON for ARM-based Linux systems, + * specifically handling LWJGL library replacements. + */ +export default class MinecraftLoader { + private options: LaunchOptions; + + constructor(options: LaunchOptions) { + this.options = options; + } + + /** + * Processes a Minecraft version JSON, removing default JInput and LWJGL entries + * if needed, then injecting ARM-compatible LWJGL libraries from local JSON files. + * + * @param version A MinecraftVersion object containing a list of libraries + * @returns The same version object, but with updated libraries for ARM-based Linux + */ + public async ProcessJson(version: MinecraftVersionJSON): Promise { + // Maps Node's arm architecture to the expected LWJGL naming + const archMapping: Record = { + arm64: 'aarch64', + arm: 'aarch' + }; + const currentArch = os.arch(); + const mappedArch = archMapping[currentArch]; + + // If running on a non-ARM environment, or if the mapping doesn't exist, no changes are needed + if (!mappedArch) { + return version; + } + + // Path to the directory containing LWJGL JSON files for ARM + const pathLWJGL = path.join(__dirname, '../../assets/LWJGL', mappedArch); + + // Identify the version strings for JInput and LWJGL from the existing libraries + const versionJinput = version.libraries.find(lib => + lib.name.startsWith('net.java.jinput:jinput-platform:') || + lib.name.startsWith('net.java.jinput:jinput:') + )?.name.split(':').pop(); + + const versionLWJGL = version.libraries.find(lib => + lib.name.startsWith('org.lwjgl:lwjgl:') || + lib.name.startsWith('org.lwjgl.lwjgl:lwjgl:') + )?.name.split(':').pop(); + + // Remove all JInput-related libraries if a JInput version is found + if (versionJinput) { + version.libraries = version.libraries.filter(lib => !lib.name.includes('jinput')); + } + + // Remove all LWJGL-related libraries if an LWJGL version is found + if (versionLWJGL) { + version.libraries = version.libraries.filter(lib => !lib.name.includes('lwjgl')); + + // Inject ARM-compatible LWJGL libraries + let lwjglJsonFile = versionLWJGL.includes('2.9') + ? '2.9.4.json' + : `${versionLWJGL}.json`; + + const lwjglPath = path.join(pathLWJGL, lwjglJsonFile); + + // Read the appropriate LWJGL JSON (e.g., "2.9.4.json" or ".json") + const lwjglNativesContent = fs.readFileSync(lwjglPath, 'utf-8'); + const lwjglNatives = JSON.parse(lwjglNativesContent) as { libraries: MinecraftLibrary[] }; + + // Append the ARM-compatible libraries + version.libraries.push(...lwjglNatives.libraries); + } + + return version; + } +} diff --git a/src/StatusServer/buffer.ts b/src/StatusServer/buffer.ts index c99aaa21..dda9b85d 100644 --- a/src/StatusServer/buffer.ts +++ b/src/StatusServer/buffer.ts @@ -1,8 +1,13 @@ -function CustomBuffer(existingBuffer: any = Buffer.alloc(48)) { +/** + * @author Luuxis + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + */ + +function CustomBuffer(this: any, existingBuffer: Buffer = Buffer.alloc(48)) { let buffer = existingBuffer; let offset = 0; - this.writeletInt = (val: any) => { + this.writeletInt = (val: number) => { while (true) { if ((val & 0xFFFFFF80) == 0) { return this.writeUByte(val); @@ -12,21 +17,21 @@ function CustomBuffer(existingBuffer: any = Buffer.alloc(48)) { } }; - this.writeString = (string: any) => { + this.writeString = (string: string) => { this.writeletInt(string.length); - if (offset + string.length >= buffer.length) Buffer.concat([buffer, new Buffer(string.length)]); - buffer.write(string, offset, string.length, "UTF-8"); + if (offset + string.length >= buffer.length) Buffer.concat([buffer, Buffer.alloc(string.length)]); + buffer.write(string, offset, string.length, "utf-8"); offset += string.length; }; - this.writeUShort = (val: any) => { + this.writeUShort = (val: number) => { this.writeUByte(val >> 8); this.writeUByte(val & 0xFF); }; - this.writeUByte = (val: any) => { + this.writeUByte = (val: number) => { if (offset >= buffer.length) { - buffer = Buffer.concat([buffer, new Buffer(50)]); + buffer = Buffer.concat([buffer, Buffer.alloc(50)]); } buffer.writeUInt8(val, offset++); @@ -46,7 +51,7 @@ function CustomBuffer(existingBuffer: any = Buffer.alloc(48)) { this.readString = () => { let length = this.readletInt(); - let str = buffer.toString("UTF-8", offset, offset + length); + let str = buffer.toString("utf-8", offset, offset + length); offset += length; return str; }; diff --git a/src/StatusServer/status.ts b/src/StatusServer/status.ts index d7dadf3b..9e902ce9 100755 --- a/src/StatusServer/status.ts +++ b/src/StatusServer/status.ts @@ -1,18 +1,26 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ import net from 'net' import createBuffer from './buffer.js'; -function ping(server: any, port: any, callback: any, timeout: any, protocol: any = '') { - let start: any = new Date(); +interface ServerStatus { + error: boolean; + ms: number; + version: string; + playersConnect: number; + playersMax: number; +} + +function ping(server: string, port: number, callback: (err: Error | null, result: ServerStatus | null) => void, timeout: number, protocol: number | string = '') { + let start = Date.now(); let socket = net.connect({ port: port, host: server }, () => { - let handshakeBuffer = new createBuffer(); + let handshakeBuffer = new (createBuffer as any)(); handshakeBuffer.writeletInt(0); handshakeBuffer.writeletInt(protocol); @@ -22,7 +30,7 @@ function ping(server: any, port: any, callback: any, timeout: any, protocol: any writePCBuffer(socket, handshakeBuffer); - let setModeBuffer = new createBuffer(); + let setModeBuffer = new (createBuffer as any)(); setModeBuffer.writeletInt(0); @@ -39,8 +47,8 @@ function ping(server: any, port: any, callback: any, timeout: any, protocol: any socket.on('data', data => { readingBuffer = Buffer.concat([readingBuffer, data]); - let buffer = new createBuffer(readingBuffer); - let length: any; + let buffer = new (createBuffer as any)(readingBuffer); + let length: number; try { length = buffer.readletInt(); @@ -53,7 +61,7 @@ function ping(server: any, port: any, callback: any, timeout: any, protocol: any buffer.readletInt(); try { - let end: any = new Date() + let end = Date.now() let json = JSON.parse(buffer.readString()); callback(null, { error: false, @@ -75,8 +83,8 @@ function ping(server: any, port: any, callback: any, timeout: any, protocol: any }); }; -function writePCBuffer(client: any, buffer: any) { - let length = new createBuffer(); +function writePCBuffer(client: net.Socket, buffer: { buffer: () => Buffer }) { + let length = new (createBuffer as any)(); length.writeletInt(buffer.buffer().length); client.write(Buffer.concat([length.buffer(), buffer.buffer()])); } @@ -89,11 +97,11 @@ export default class status { this.port = port } - async getStatus() { + async getStatus(): Promise { return await new Promise((resolve, reject) => { - ping(this.ip, this.port, (err: any, res: any) => { + ping(this.ip, this.port, (err: Error | null, res: ServerStatus | null) => { if (err) return reject({ error: err }); - return resolve(res); + return resolve(res!); }, 3000); }) } diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 00000000..1537c4c8 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,748 @@ +/** + * @author Luuxis + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + * + * Centralized type definitions for minecraft-java-core. + * All shared types are defined here for easier maintenance. + */ + +// ======================== +// Authenticator Types +// ======================== + +/** Microsoft OAuth client environment type */ +export type MicrosoftClientType = 'electron' | 'nwjs' | 'terminal'; + +/** A Minecraft skin/cape entry */ +export interface MinecraftSkin { + id?: string; + state?: string; + url?: string; + variant?: string; + alias?: string; + base64?: string; +} + +/** Minecraft profile returned by Mojang API */ +export interface MinecraftProfile { + id: string; + name: string; + skins?: MinecraftSkin[]; + capes?: MinecraftSkin[]; +} + +/** Authentication error structure */ +export interface AuthError { + error: string; + errorType?: string; + [key: string]: unknown; +} + +/** Microsoft authentication successful response */ +export interface MicrosoftAuthResponse { + access_token: string; + client_token: string; + uuid: string; + name: string; + refresh_token: string; + user_properties: string; + meta: { + type: 'Xbox'; + access_token_expires_in: number; + demo: boolean; + }; + xboxAccount: { + xuid: string; + gamertag: string; + ageGroup: string; + }; + profile: { + skins?: MinecraftSkin[]; + capes?: MinecraftSkin[]; + }; +} + +/** AZauth user info */ +export interface AZauthUserInfo { + id?: string; + banned?: boolean; + money?: number; + role?: string; + verified?: boolean; +} + +/** AZauth user returned by the AZauth API */ +export interface AZauthUser { + access_token?: string; + client_token?: string; + uuid?: string; + name?: string; + user_properties?: string; + user_info?: AZauthUserInfo; + meta?: { + online: boolean; + type: string; + }; + profile?: { + skins: Array<{ + url: string; + base64?: string; + }>; + }; + error?: boolean; + reason?: string; + message?: string; + A2F?: boolean; +} + +/** Mojang authentication response */ +export interface MojangAuthResponse { + access_token: string; + client_token: string; + uuid: string; + name: string; + user_properties: string; + meta: { + online: boolean; + type: 'Mojang'; + }; + error?: string; + errorMessage?: string; +} + +/** Generic authenticator result — union of all auth providers */ +export type AuthenticatorResult = MicrosoftAuthResponse | AZauthUser | MojangAuthResponse; + +/** Authenticator object used at runtime (common fields across all auth methods) */ +export interface Authenticator { + access_token: string; + client_token: string; + uuid: string; + name: string; + user_properties: string; + meta: { + online?: boolean; + type: string; + access_token_expires_in?: number; + demo?: boolean; + }; + xboxAccount?: { + xuid: string; + gamertag: string; + ageGroup: string; + }; + clientId?: string; + profile?: { + skins?: MinecraftSkin[]; + capes?: MinecraftSkin[]; + }; +} + +// ======================== +// Loader Types +// ======================== + +/** Supported loader types */ +export type LoaderType = 'forge' | 'neoforge' | 'fabric' | 'legacyfabric' | 'quilt'; + +/** Loader configuration in LaunchOptions */ +export interface LoaderConfig { + /** Path to loader directory, relative to the Minecraft root. Defaults to `./loader/`. */ + path?: string; + /** Loader type */ + type?: LoaderType | string | null; + /** Loader build version. `'latest'`, `'recommended'`, or a specific version. */ + build?: string; + /** Should the launcher use a loader? */ + enable?: boolean; +} + +/** Loader JSON structure (returned after installation) */ +export interface LoaderJSON { + id?: string; + mainClass?: string; + libraries?: LoaderLibrary[]; + minecraftArguments?: string; + isOldForge?: boolean; + jarPath?: string; + arguments?: { + game?: string[]; + jvm?: string[]; + }; + [key: string]: unknown; +} + +/** A library entry from a loader JSON */ +export interface LoaderLibrary { + name: string; + loader?: string; + url?: string; + rules?: LibraryRule[]; + natives?: Record; + downloads?: LibraryDownloads; + [key: string]: unknown; +} + +/** Loader installation result */ +export interface LoaderResult { + id?: string; + error?: string; + [key: string]: unknown; +} + +/** Type alias for method returns that may be a result or error */ +export type LoaderMethodResult = Record & { error?: string }; + +// ======================== +// Loader Downloader Types +// ======================== + +/** Configuration passed to the main Loader downloader */ +export interface LoaderDownloaderConfig { + type: LoaderType; + version: string; + build: string; + config: { + javaPath: string; + minecraftJar: string; + minecraftJson: string; + }; +} + +/** Options for the Loader downloader */ +export interface LoaderDownloaderOptions { + path: string; + loader: LoaderDownloaderConfig; + downloadFileMultiple?: number; +} + +// ======================== +// Minecraft Version JSON Types +// ======================== + +/** Java version info within a Minecraft version JSON */ +export interface JavaVersionInfo { + component?: string; + majorVersion?: number; +} + +/** Asset index reference */ +export interface AssetIndex { + id: string; + url: string; + sha1?: string; + size?: number; + totalSize?: number; +} + +/** Library rule for allow/disallow */ +export interface LibraryRule { + action: 'allow' | 'disallow'; + os?: { + name?: string; + version?: string; + }; + features?: Record; +} + +/** Library download artifact */ +export interface LibraryArtifact { + sha1: string; + size: number; + path: string; + url: string; +} + +/** Library downloads section */ +export interface LibraryDownloads { + artifact?: LibraryArtifact; + classifiers?: Record; +} + +/** A library entry in a Minecraft version JSON */ +export interface MinecraftLibrary { + name: string; + rules?: LibraryRule[]; + natives?: Record; + downloads?: LibraryDownloads; + loader?: string; + [key: string]: unknown; +} + +/** Client download info */ +export interface ClientDownload { + sha1: string; + size: number; + url: string; +} + +/** Logging configuration file */ +export interface LoggingFile { + id: string; + sha1: string; + size: number; + url: string; +} + +/** Logging configuration */ +export interface LoggingConfig { + client?: { + argument: string; + file: LoggingFile; + type: string; + }; +} + +/** The Minecraft version JSON structure */ +export interface MinecraftVersionJSON { + id: string; + type: string; + assets?: string; + assetIndex?: AssetIndex; + mainClass?: string; + minecraftArguments?: string; + javaVersion?: JavaVersionInfo; + nativesList?: boolean; + logging?: LoggingConfig; + arguments?: { + game?: Array; + jvm?: Array; + 'default-user-jvm'?: Array; + }; + libraries: MinecraftLibrary[]; + downloads: { + client: ClientDownload; + [key: string]: ClientDownload; + }; +} + +/** Argument rule (complex argument entry) */ +export interface ArgumentRule { + rules?: LibraryRule[]; + value?: string | string[]; +} + +/** Default user JVM argument entry */ +export interface DefaultUserJVMArg { + rules?: LibraryRule[]; + value?: string | string[]; +} + +// ======================== +// Mojang Version Manifest +// ======================== + +/** A single version entry from the Mojang version manifest */ +export interface VersionEntry { + id: string; + type: string; + url: string; + time: string; + releaseTime: string; +} + +/** The Mojang version manifest structure */ +export interface MojangVersionManifest { + latest: { + release: string; + snapshot: string; + }; + versions: VersionEntry[]; +} + +/** Result of GetInfoVersion */ +export interface GetInfoVersionResult { + InfoVersion: VersionEntry; + json: MinecraftVersionJSON; + version: string; +} + +/** Error from GetInfoVersion */ +export interface GetInfoVersionError { + error: true; + message: string; +} + +// ======================== +// Download Types +// ======================== + +/** Represents a file to download */ +export interface DownloadFile { + sha1?: string; + size?: number; + path: string; + type?: string; + url?: string; + content?: string; + folder?: string; + name?: string; +} + +/** Options for downloading a single file */ +export interface DownloadOptions { + url: string; + path: string; + length?: number; + folder: string; + type?: string; +} + +/** Asset item from the asset index */ +export interface AssetItem { + type: 'CFILE' | 'Assets'; + path: string; + content?: string; + sha1?: string; + size?: number; + url?: string; +} + +/** Bundle item for checking/downloading */ +export interface BundleItem { + type?: string; + path: string; + folder?: string; + content?: string; + sha1?: string; + size?: number; + url?: string; +} + +// ======================== +// Java Types +// ======================== + +/** Java download options */ +export interface JavaOptions { + /** + * Absolute path to Java binaries directory. + * If set, expects Java to be already downloaded. + */ + path?: string; + /** + * Java version number (e.g., 21). + * If set, fetched from Azul API. + * If undefined, fetched from Mojang. + */ + version?: string; + /** + * Java image type: 'jdk', 'jre', 'testimage', 'debugimage', 'staticlibs', 'sources', 'sbom'. + */ + type: string; +} + +/** Result from Java download */ +export interface JavaDownloadResult { + files: JavaFileItem[]; + path: string; + error?: boolean; + message?: string; +} + +/** A single Java file entry */ +export interface JavaFileItem { + path: string; + executable?: boolean; + sha1?: string; + size?: number; + url?: string; + type?: string; +} + +// ======================== +// Screen / Memory Types +// ======================== + +/** Screen options */ +export interface ScreenOptions { + width?: number; + height?: number; + /** Should Minecraft be started in fullscreen mode? */ + fullscreen?: boolean; +} + +/** Memory limits */ +export interface MemoryOptions { + /** Sets the `-Xms` JVM argument (initial memory). */ + min?: string; + /** Sets the `-Xmx` JVM argument (max memory). */ + max?: string; +} + +// ======================== +// Launch Options +// ======================== + +/** Main launch options passed to the Launch class */ +export interface LaunchOptions { + /** + * URL to the launcher backend. + */ + url?: string | null; + /** + * Authenticator object from Mojang, Microsoft or AZauth. + */ + authenticator: Authenticator; + /** Connection timeout in milliseconds. */ + timeout?: number; + /** Absolute path to Minecraft's root directory. */ + path: string; + /** Minecraft version. */ + version: string; + /** + * Path to instance directory, relative to `path`. + * Separates game files from game data. + */ + instance?: string | null; + /** Should Minecraft process be independent of launcher? */ + detached?: boolean; + /** How many concurrent downloads can be in progress at once. */ + downloadFileMultiple?: number; + /** Bypass offline mode for multiplayer. */ + bypassOffline?: boolean; + /** Intel Macs: use dedicated GPU instead of integrated. */ + intelEnabledMac?: boolean; + /** Ignore log4j configuration. */ + ignore_log4j?: boolean; + /** Loader configuration. */ + loader: LoaderConfig; + /** MCPatcher directory. Relative to `instance` or `path`. */ + mcp?: string | null; + /** Should game files be verified each launch? */ + verify: boolean; + /** Files to ignore from instance. */ + ignored: string[]; + /** Custom JVM arguments. */ + JVM_ARGS: string[]; + /** Custom game arguments. */ + GAME_ARGS: string[]; + /** Java options. */ + java: JavaOptions; + /** Screen options. */ + screen: ScreenOptions; + /** Memory limit options. */ + memory: MemoryOptions; +} + +// ======================== +// Launch Arguments Return Types +// ======================== + +/** Data returned by MinecraftArguments.GetArguments() */ +export interface LaunchArguments { + game: string[]; + jvm: string[]; + classpath: string[]; + mainClass?: string; +} + +/** Data returned by MinecraftLoader.GetArguments() */ +export interface LoaderArguments { + game: string[]; + jvm: string[]; + mainClass?: string; +} + +// ======================== +// Status Server Types +// ======================== + +/** Server status response */ +export interface ServerStatus { + error: false; + ms: number; + version: string; + playersConnect: number; + playersMax: number; +} + +/** Server status error */ +export interface ServerStatusError { + error: Error; +} + +// ======================== +// Forge Patcher Types +// ======================== + +/** Forge/NeoForge patcher processor */ +export interface PatcherProcessor { + jar: string; + args: string[]; + classpath: string[]; + sides?: string[]; +} + +/** Forge/NeoForge patcher profile data entry */ +export interface PatcherProfileData { + client: string; + [key: string]: string; +} + +/** Forge/NeoForge patcher profile */ +export interface PatcherProfile { + data?: Record; + processors?: PatcherProcessor[]; + libraries?: MinecraftLibrary[]; + path?: string; +} + +/** Patcher config */ +export interface PatcherConfig { + java: string; + minecraft: string; + minecraftJson: string; +} + +// ======================== +// Forge/NeoForge Install Profile +// ======================== + +/** Forge install profile */ +export interface ForgeInstallProfile { + libraries?: MinecraftLibrary[]; + processors?: PatcherProcessor[]; + data?: Record; + path?: string; + filePath?: string; + json?: string; + [key: string]: unknown; +} + +/** Forge extracted profile result */ +export interface ForgeExtractedProfile { + install?: ForgeInstallProfile; + version?: LoaderJSON; + error?: { message: string }; +} + +// ======================== +// Fabric/Quilt Types +// ======================== + +/** Fabric/Quilt/LegacyFabric metadata */ +export interface FabricMetaData { + game: Array<{ version: string; stable: boolean }>; + loader: Array<{ version: string; stable: boolean }>; +} + +/** Fabric/Quilt library */ +export interface FabricLibrary { + name: string; + url: string; + rules?: Array>; +} + +/** Fabric/Quilt JSON */ +export interface FabricJSON { + id?: string; + libraries: FabricLibrary[]; + mainClass?: string; + arguments?: { + game?: string[]; + jvm?: string[]; + }; + error?: string; + [key: string]: unknown; +} + +/** Fabric/Quilt loader data endpoints */ +export interface FabricLoaderData { + metaData: string; + json: string; +} + +// ======================== +// Forge loader data endpoints +// ======================== + +export interface ForgeLoaderData { + metaData: string; + meta: string; + promotions: string; + install: string; + universal: string; + client: string; +} + +/** NeoForge loader data endpoints */ +export interface NeoForgeLoaderData { + legacyMetaData: string; + metaData: string; + legacyInstall: string; + install: string; +} + +// ======================== +// Custom Asset (from backend URL) +// ======================== + +/** Custom asset item from a backend URL */ +export interface CustomAssetItem { + path: string; + hash: string; + size: number; + url: string; +} + +// ======================== +// Utility Types +// ======================== + +/** Library path info returned by getPathLibraries() */ +export interface LibraryPathInfo { + path: string; + name: string; + version: string; +} + +/** Zip entry */ +export interface ZipEntry { + entryName: string; + isDirectory: boolean; + getData: () => Buffer; +} + +/** Archive extracted file */ +export interface ArchiveEntry { + name: string; + data: Buffer; + isDirectory: boolean; +} + +// ======================== +// Event Payload Types +// ======================== + +/** Progress event data */ +export interface ProgressEvent { + progress: number; + size: number; + element: string; +} + +/** Speed event data (bytes per second) */ +export type SpeedEvent = number; + +/** Estimated time event data (seconds remaining) */ +export type EstimatedEvent = number; + +// ======================== +// OS Mappings +// ======================== + +/** Maps Node.js platforms to Mojang's OS naming */ +export const MOJANG_OS_MAP: Record = { + win32: 'windows', + darwin: 'osx', + linux: 'linux' +}; + +/** Maps Node.js arch to Mojang's arch replacements */ +export const MOJANG_ARCH_MAP: Record = { + x32: '32', + x64: '64', + arm: '32', + arm64: '64' +}; diff --git a/src/utils/Downloader.ts b/src/utils/Downloader.ts index 0f352fe1..452cedd4 100755 --- a/src/utils/Downloader.ts +++ b/src/utils/Downloader.ts @@ -1,95 +1,261 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) */ import fs from 'fs'; -import nodeFetch from 'node-fetch'; import { EventEmitter } from 'events'; +import { fromAnyReadable } from './Index.js'; +import type { DownloadFile } from '../types.js'; -interface downloadOptions { - url: string, - path: string, - length: number, - folder: string -} +export type { DownloadFile as DownloadOptions }; + +/** + * Files smaller than this threshold are downloaded as a single buffer + * instead of streaming, avoiding stream/event overhead for tiny files. + */ +const SMALL_FILE_THRESHOLD = 1 * 1024 * 1024; // 1 MB + +/** + * Minimum interval (ms) between progress event emissions. + * Prevents flooding the event loop when downloading thousands of files. + */ +const PROGRESS_THROTTLE_MS = 50; + +/** + * A class responsible for downloading single or multiple files, + * emitting events for progress, speed, estimated time, and errors. + */ +export default class Downloader extends EventEmitter { + /** + * Downloads a single file from the given URL to the specified local path. + * Emits "progress" events with the number of bytes downloaded and total size. + * + * @param url - The remote URL to download from + * @param dirPath - Local folder path where the file is saved + * @param fileName - Name of the file (e.g., "mod.jar") + */ + public async downloadFile(url: string, dirPath: string, fileName: string): Promise { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } + + const filePath = `${dirPath}/${fileName}`; + const writer = fs.createWriteStream(filePath); + const response = await fetch(url); + + const contentLength = response.headers.get('content-length'); + const totalSize = contentLength ? parseInt(contentLength, 10) : 0; + + let downloaded = 0; + + return new Promise((resolve, reject) => { + const body = fromAnyReadable(response.body as ReadableStream); + + body.on('data', (chunk: Buffer) => { + downloaded += chunk.length; + this.emit('progress', downloaded, totalSize); + writer.write(chunk); + }); + + body.on('end', () => { + writer.end(() => resolve()); + }); + + body.on('error', (err: Error) => { + writer.destroy(); + try { fs.unlinkSync(filePath); } catch { /* ignore */ } + this.emit('error', err); + reject(err); + }); + }); + } + + /** + * Downloads multiple files concurrently using a worker-pool pattern. + * Small files (< 1 MB) are fetched as a single buffer and written at once, + * avoiding per-file stream/event overhead. Large files are streamed to disk. + * + * Progress events are throttled to avoid flooding the event loop. + * Directories are pre-created in a single pass before downloading begins. + * + * @param files - Array of DownloadFile describing each file + * @param size - Total size (bytes) of all files to download + * @param limit - Maximum number of simultaneous downloads + * @param timeout - Timeout in ms for each fetch request + */ + public async downloadFileMultiple( + files: DownloadFile[], + size: number, + limit: number = 1, + timeout: number = 10000 + ): Promise { + if (files.length === 0) return; + if (limit > files.length) limit = files.length; + + let downloaded = 0; + let queued = 0; + + // ── Pre-create all unique directories in one pass ────────────── + const dirs = new Set(); + for (const f of files) { + if (f.folder) dirs.add(f.folder); + } + for (const dir of dirs) { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true, mode: 0o777 }); + } + } + + // ── Speed & ETA tracking ────────────────────────────────────── + let speedStart = Date.now(); + let speedBefore = 0; + const speeds: number[] = []; + + const speedInterval = setInterval(() => { + const elapsed = (Date.now() - speedStart) / 1000; + const chunk = downloaded - speedBefore; + if (speeds.length >= 5) speeds.shift(); + speeds.push(chunk / elapsed); -export default class download { - on: any; - emit: any; - - constructor() { - this.on = EventEmitter.prototype.on; - this.emit = EventEmitter.prototype.emit; - } - - async downloadFileMultiple(files: downloadOptions, size: number, limit: number = 1, timeout: number = 10000) { - if (limit > files.length) limit = files.length; - let completed = 0; - let downloaded = 0; - let queued = 0; - - let start = new Date().getTime(); - let before = 0; - let speeds = []; - - let estimated = setInterval(() => { - let duration = (new Date().getTime() - start) / 1000; - let loaded = (downloaded - before) * 8; - if (speeds.length >= 5) speeds = speeds.slice(1); - speeds.push((loaded / duration) / 8); - let speed = 0; - for (let s of speeds) speed += s; - speed /= speeds.length; - this.emit("speed", speed); - let time = (size - downloaded) / (speed); - this.emit("estimated", time); - start = new Date().getTime(); - before = downloaded; - }, 500); - - const downloadNext = async () => { - if (queued < files.length) { - let file = files[queued]; - queued++; - - if (!fs.existsSync(file.foler)) fs.mkdirSync(file.folder, { recursive: true, mode: 0o777 }); - const writer: any = fs.createWriteStream(file.path, { flags: 'w', mode: 0o777 }); - - try { - const response = await nodeFetch(file.url, { timeout: timeout }); - - response.body.on('data', (chunk: any) => { - downloaded += chunk.length; - this.emit('progress', downloaded, size, file.type); - writer.write(chunk); - }); - - response.body.on('end', () => { - writer.end(); - completed++; - downloadNext(); - }); - - } catch (e) { - writer.end(); - completed++; - downloadNext(); - this.emit('error', e); - } - } - }; - - while (queued < limit) downloadNext(); - - return new Promise((resolve: any) => { - const interval = setInterval(() => { - if (completed === files.length) { - clearInterval(estimated); - clearInterval(interval); - resolve(); - } - }, 100); - }); - } -} \ No newline at end of file + const avg = speeds.reduce((a, b) => a + b, 0) / speeds.length; + this.emit('speed', avg); + this.emit('estimated', (size - downloaded) / avg); + + speedStart = Date.now(); + speedBefore = downloaded; + }, 500); + + // ── Throttled progress emission ─────────────────────────────── + let lastEmit = 0; + const emitProgress = (type?: string, force = false) => { + const now = Date.now(); + if (force || now - lastEmit >= PROGRESS_THROTTLE_MS) { + lastEmit = now; + this.emit('progress', downloaded, size, type); + } + }; + + // ── Worker: loops picking files from queue until exhausted ──── + const worker = async (): Promise => { + while (queued < files.length) { + const file = files[queued++]; + + const controller = new AbortController(); + const tid = setTimeout(() => controller.abort(), timeout); + + try { + const response = await fetch(file.url!, { signal: controller.signal }); + clearTimeout(tid); + + if (!file.size || file.size < SMALL_FILE_THRESHOLD) { + // ── Small file: single buffer write (no stream overhead) ── + const buffer = Buffer.from(await response.arrayBuffer()); + fs.writeFileSync(file.path, buffer, { mode: 0o777 }); + downloaded += buffer.length; + emitProgress(file.type); + } else { + // ── Large file: stream to disk ──────────────────────────── + await new Promise((resolve, reject) => { + const writer = fs.createWriteStream(file.path, { flags: 'w', mode: 0o777 }); + const stream = fromAnyReadable(response.body as ReadableStream); + + stream.on('data', (chunk: Buffer) => { + downloaded += chunk.length; + emitProgress(file.type); + writer.write(chunk); + }); + + stream.on('end', () => writer.end(() => resolve())); + + stream.on('error', (err) => { + writer.destroy(); + try { fs.unlinkSync(file.path); } catch { /* ignore */ } + reject(err); + }); + }); + } + } catch (e) { + clearTimeout(tid); + try { fs.unlinkSync(file.path); } catch { /* ignore */ } + this.emit('error', e); + } + } + }; + + // ── Launch worker pool & wait for all to finish ─────────────── + const workers: Promise[] = []; + for (let i = 0; i < limit; i++) { + workers.push(worker()); + } + await Promise.all(workers); + + clearInterval(speedInterval); + emitProgress(undefined, true); // final progress update + } + + /** + * Performs a HEAD request on the given URL to check if it is valid (status=200) + * and retrieves the "content-length" if available. + * + * @param url The URL to check + * @param timeout Time in ms before the request times out + * @returns An object containing { size, status } or rejects with false + */ + public async checkURL( + url: string, + timeout: number = 10000 + ): Promise<{ size: number; status: number } | false> { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeout); + + try { + const res = await fetch(url, { + method: 'HEAD', + signal: controller.signal + }); + + clearTimeout(timeoutId); + + if (res.status === 200) { + const contentLength = res.headers.get('content-length'); + const size = contentLength ? parseInt(contentLength, 10) : 0; + return { size, status: 200 }; + } + return false; + } catch (e: unknown) { + clearTimeout(timeoutId); + return false; + } + } + + + + /** + * Tries each mirror in turn, constructing an URL (mirror + baseURL). If a valid + * response is found (status=200), it returns the final URL and size. Otherwise, returns false. + * + * @param baseURL The relative path (e.g. "group/id/artifact.jar") + * @param mirrors An array of possible mirror base URLs + * @returns An object { url, size, status } if found, or false if all mirrors fail + */ + public async checkMirror( + baseURL: string, + mirrors: string[] + ): Promise<{ url: string; size: number; status: number } | false> { + + for (const mirror of mirrors) { + const testURL = `${mirror}/${baseURL}`; + const res = await this.checkURL(testURL); + + if (res !== false && res.status === 200) { + return { + url: testURL, + size: res.size, + status: 200 + }; + } + } + return false; + } +} diff --git a/src/utils/Index.ts b/src/utils/Index.ts index aa299dc2..31f869af 100755 --- a/src/utils/Index.ts +++ b/src/utils/Index.ts @@ -1,21 +1,265 @@ /** * @author Luuxis - * @license CC-BY-NC 4.0 - https://creativecommons.org/licenses/by-nc/4.0/ - */ - -function getPathLibraries(main: any, nativeString?: any, forceExt?: any) { - let libSplit = main.split(':') - let fileName = libSplit[3] ? `${libSplit[2]}-${libSplit[3]}` : libSplit[2]; - let finalFileName = fileName.includes('@') ? fileName.replace('@', '.') : `${fileName}${nativeString || ''}${forceExt || '.jar'}`; - let pathLib = `${libSplit[0].replace(/\./g, '/')}/${libSplit[1]}/${libSplit[2].split('@')[0]}` - return { - path: pathLib, - name: `${libSplit[1]}-${finalFileName}` - }; + * Luuxis License v1.0 (voir fichier LICENSE pour les détails en FR/EN) + */ + +import crypto from 'crypto'; +import fs from 'fs'; +import { Readable } from 'node:stream'; +import Unzipper from './unzipper.js'; +import type { + MinecraftLibrary, + MinecraftVersionJSON, + LibraryRule, + ForgeLoaderData, + NeoForgeLoaderData, + FabricLoaderData, + ArchiveEntry, +} from '../types.js'; + +/** + * Parses a Gradle/Maven identifier string (like "net.minecraftforge:forge:1.19-41.0.63") + * into a local file path (group/artifact/version) and final filename (artifact-version.jar). + * Optionally allows specifying a native string suffix or forcing an extension. + * + * @param main A Gradle-style coordinate (group:artifact:version[:classifier]) + * @param nativeString A suffix for native libraries (e.g., "-natives-linux") + * @param forceExt A forced file extension (default is ".jar") + * @returns An object with `path` and `name`, where `path` is the directory path and `name` is the filename + */ +function getPathLibraries(main: string, nativeString?: string, forceExt?: string) { + // Example "net.minecraftforge:forge:1.19-41.0.63" + const libSplit = main.split(':'); + + // If there's a fourth element, it's typically a classifier appended to version + const fileName = libSplit[3] ? `${libSplit[2]}-${libSplit[3]}` : libSplit[2]; + + // Replace '@' in versions if present (e.g., "1.0@beta" => "1.0.beta") + let finalFileName = fileName.includes('@') + ? fileName.replace('@', '.') + : `${fileName}${nativeString || ''}${forceExt || '.jar'}`; + + // Construct the path: "net.minecraftforge" => "net/minecraftforge" + // artifact => "forge" + // version => "1.19-41.0.63" + const pathLib = `${libSplit[0].replace(/\./g, '/')}/${libSplit[1]}/${libSplit[2].split('@')[0]}`; + + return { + path: pathLib, + name: `${libSplit[1]}-${finalFileName}`, + version: libSplit[2], + }; +} + +/** + * Computes a hash (default SHA-1) of the given file by streaming its contents. + * + * @param filePath Full path to the file on disk + * @param algorithm Hashing algorithm (default: "sha1") + * @returns A Promise resolving to the hex string of the file's hash + */ +async function getFileHash(filePath: string, algorithm: string = 'sha1'): Promise { + const shasum = crypto.createHash(algorithm); + + // For small files, avoid the stream overhead entirely + const stat = fs.statSync(filePath); + if (stat.size <= 512 * 1024) { // ≤ 512 KB + shasum.update(fs.readFileSync(filePath)); + return shasum.digest('hex'); + } + + // For larger files, stream to avoid loading everything into memory + const fileStream = fs.createReadStream(filePath); + return new Promise((resolve, reject) => { + fileStream.on('data', (data) => shasum.update(data)); + fileStream.on('end', () => resolve(shasum.digest('hex'))); + fileStream.on('error', reject); + }); +} + +/** + * Determines if a given Minecraft version JSON is considered "old" + * by checking its assets field (e.g., "legacy" or "pre-1.6"). + * + * @param json The Minecraft version JSON + * @returns true if it's an older version, false otherwise + */ +function isold(json: MinecraftVersionJSON): boolean { + return json.assets === 'legacy' || json.assets === 'pre-1.6'; +} + +/** + * Returns metadata necessary to download specific loaders (Forge, Fabric, etc.) + * based on a loader type string (e.g., "forge", "fabric"). + * If the loader type is unrecognized, returns undefined. + * + * @param type A string representing the loader type + */ +function loader(type: string): ForgeLoaderData | NeoForgeLoaderData | FabricLoaderData | undefined { + if (type === 'forge') { + return { + metaData: 'https://files.minecraftforge.net/net/minecraftforge/forge/maven-metadata.json', + meta: 'https://files.minecraftforge.net/net/minecraftforge/forge/${build}/meta.json', + promotions: 'https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json', + install: 'https://maven.minecraftforge.net/net/minecraftforge/forge/${version}/forge-${version}-installer', + universal: 'https://maven.minecraftforge.net/net/minecraftforge/forge/${version}/forge-${version}-universal', + client: 'https://maven.minecraftforge.net/net/minecraftforge/forge/${version}/forge-${version}-client' + }; + } else if (type === 'neoforge') { + return { + legacyMetaData: 'https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/forge', + metaData: 'https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/neoforge', + legacyInstall: 'https://maven.neoforged.net/releases/net/neoforged/forge/${version}/forge-${version}-installer.jar', + install: 'https://maven.neoforged.net/releases/net/neoforged/neoforge/${version}/neoforge-${version}-installer.jar' + }; + } else if (type === 'fabric') { + return { + metaData: 'https://meta.fabricmc.net/v2/versions', + json: 'https://meta.fabricmc.net/v2/versions/loader/${version}/${build}/profile/json' + }; + } else if (type === 'legacyfabric') { + return { + metaData: 'https://meta.legacyfabric.net/v2/versions', + json: 'https://meta.legacyfabric.net/v2/versions/loader/${version}/${build}/profile/json' + }; + } else if (type === 'quilt') { + return { + metaData: 'https://meta.quiltmc.org/v3/versions', + json: 'https://meta.quiltmc.org/v3/versions/loader/${version}/${build}/profile/json' + }; + } + // If none match, return undefined } -function isold(json: any) { - return json.assets === 'legacy' || json.assets === 'pre-1.6' +/** + * A list of potential Maven mirrors for downloading libraries. + */ +const mirrors = [ + 'https://maven.minecraftforge.net', + 'https://maven.neoforged.net/releases', + 'https://maven.creeperhost.net', + 'https://libraries.minecraft.net', + 'https://repo1.maven.org/maven2' +]; + +/** + * Reads a .jar or .zip file, returning specific entries or listing file entries in the archive. + * + * @param jar Full path to the jar/zip file + * @param file The file entry to extract data from (e.g., "install_profile.json"). If null, returns all entries or partial lists. + * @param prefix A path prefix filter (e.g., "maven/org/lwjgl/") if you want a list of matching files instead of direct extraction + * @returns A buffer or an array of { name, data }, or a list of filenames if prefix is given + */ +async function getFileFromArchive(jar: string, file: string | null = null, prefix: string | null = null, includeDirs: boolean = false): Promise { + const result: Array = []; + const zip = new Unzipper(jar); + const entries = zip.getEntries(); + + return new Promise((resolve) => { + for (const entry of entries) { + if (includeDirs ? !prefix : (!entry.isDirectory && !prefix)) { + if (entry.entryName === file) { + return resolve(entry.getData()); + } else if (!file) { + result.push({ name: entry.entryName, data: entry.getData(), isDirectory: entry.isDirectory }); + } + } + + if (!entry.isDirectory && entry.entryName.includes(prefix as string)) { + result.push(entry.entryName); + } + } + + if (file && !prefix) { + return resolve(undefined); + } + + // If prefix was used, result contains only strings; otherwise only ArchiveEntries + resolve(result as string[] | ArchiveEntry[]); + }); +} + +/** + * Determines if a library should be skipped based on its 'rules' property. + * For example, it might skip libraries if action='disallow' for the current OS, + * or if there are specific conditions not met. + * + * @param lib A library object (with optional 'rules' array) + * @returns true if the library should be skipped, false otherwise + */ +function skipLibrary(lib: MinecraftLibrary): boolean { + // Map Node.js platform strings to Mojang's naming + const LibMap: Record = { + win32: 'windows', + darwin: 'osx', + linux: 'linux' + }; + + // If no rules, it's not skipped + if (!lib.rules) { + return false; + } + + let shouldSkip = true; + + for (const rule of lib.rules) { + // If features exist, your logic can handle them here + if (rule.features) { + // Implementation is up to your usage + continue; + } + + // "allow" means it can be used if OS matches (or no OS specified) + // "disallow" means skip if OS matches (or no OS specified) + if ( + rule.action === 'allow' && + ((rule.os && rule.os.name === LibMap[process.platform]) || !rule.os) + ) { + shouldSkip = false; + } else if ( + rule.action === 'disallow' && + ((rule.os && rule.os.name === LibMap[process.platform]) || !rule.os) + ) { + shouldSkip = true; + } + } + + return shouldSkip; +} + +function fromAnyReadable(webStream: ReadableStream): import('node:stream').Readable { + // Try Readable.fromWeb() first (Node.js 18+), works for both Node.js and Electron + if (typeof (Readable as unknown as { fromWeb: Function }).fromWeb === 'function') { + try { + return (Readable as unknown as { fromWeb: (stream: ReadableStream) => Readable }).fromWeb(webStream); + } catch { + // If fromWeb fails (e.g. wrong stream type in Electron), fall through to manual pump + } + } + + // Manual pump fallback for environments where Readable.fromWeb() is unavailable or fails + const nodeStream = new Readable({ read() { } }); + const reader = webStream.getReader(); + + (function pump() { + reader.read().then(({ done, value }) => { + if (done) return nodeStream.push(null); + nodeStream.push(Buffer.from(value)); + pump(); + }).catch(err => nodeStream.destroy(err)); + })(); + + return nodeStream; } -export { getPathLibraries, isold }; \ No newline at end of file +// Export all utility functions and constants +export { + getPathLibraries, + getFileHash, + isold, + loader, + mirrors, + getFileFromArchive, + skipLibrary, + fromAnyReadable +}; diff --git a/src/utils/unzipper.ts b/src/utils/unzipper.ts new file mode 100644 index 00000000..6626e3d9 --- /dev/null +++ b/src/utils/unzipper.ts @@ -0,0 +1,154 @@ +import fs from 'fs'; +import zlib from 'zlib'; +import type { ZipEntry } from '../types.js'; + +export default class Unzipper { + private entries: ZipEntry[] = []; + + constructor(zipFilePath: string) { + const fileBuffer = fs.readFileSync(zipFilePath); + + const eocdSig = Buffer.from([0x50, 0x4B, 0x05, 0x06]); + const maxCommentLength = 0xFFFF; + const scanStart = Math.max(0, fileBuffer.length - (maxCommentLength + 22)); + + let eocdPos = -1; + for (let i = fileBuffer.length - 22; i >= scanStart; i--) { + if ( + fileBuffer[i] === 0x50 && + fileBuffer[i + 1] === 0x4B && + fileBuffer[i + 2] === 0x05 && + fileBuffer[i + 3] === 0x06 + ) { + eocdPos = i; + break; + } + } + + if (eocdPos !== -1) { + const cdOffset = fileBuffer.readUInt32LE(eocdPos + 16); + const cdSize = fileBuffer.readUInt32LE(eocdPos + 12); + const cdEnd = cdOffset + cdSize; + + let cdCursor = cdOffset; + + while (cdCursor < cdEnd) { + if (cdCursor + 46 > fileBuffer.length) break; // sécurité + + if (fileBuffer.readUInt32LE(cdCursor) !== 0x02014b50) break; + + const compressionMethod = fileBuffer.readUInt16LE(cdCursor + 10); + const compressedSize = fileBuffer.readUInt32LE(cdCursor + 20); + const uncompressedSize = fileBuffer.readUInt32LE(cdCursor + 24); + const fileNameLength = fileBuffer.readUInt16LE(cdCursor + 28); + const extraFieldLength = fileBuffer.readUInt16LE(cdCursor + 30); + const fileCommentLength = fileBuffer.readUInt16LE(cdCursor + 32); + const relativeOffset = fileBuffer.readUInt32LE(cdCursor + 42); + + const fileName = fileBuffer.toString( + 'utf-8', + cdCursor + 46, + cdCursor + 46 + fileNameLength + ); + + const headerOffset = relativeOffset; + // Sécurité sur l'offset du header + if (headerOffset + 30 > fileBuffer.length) { + cdCursor += 46 + fileNameLength + extraFieldLength + fileCommentLength; + continue; + } + if (fileBuffer.readUInt32LE(headerOffset) !== 0x04034b50) { + cdCursor += 46 + fileNameLength + extraFieldLength + fileCommentLength; + continue; + } + + const lfFileNameLength = fileBuffer.readUInt16LE(headerOffset + 26); + const lfExtraFieldLength = fileBuffer.readUInt16LE(headerOffset + 28); + const dataStart = headerOffset + 30 + lfFileNameLength + lfExtraFieldLength; + const dataEnd = dataStart + compressedSize; + + // Sécurité: ne pas dépasser la taille du buffer + if (dataEnd > fileBuffer.length) { + cdCursor += 46 + fileNameLength + extraFieldLength + fileCommentLength; + continue; + } + + const compressedData = fileBuffer.slice(dataStart, dataEnd); + + this.entries.push({ + entryName: fileName, + isDirectory: fileName.endsWith('/'), + getData: () => { + if (compressionMethod === 8) { + return zlib.inflateRawSync(compressedData); + } else if (compressionMethod === 0) { + return compressedData; + } else { + throw new Error(`Unsupported compression method: ${compressionMethod}`); + } + } + }); + + cdCursor += 46 + fileNameLength + extraFieldLength + fileCommentLength; + } + } else { + let currentOffset = 0; + while (currentOffset < fileBuffer.length - 4) { + const signaturePos = fileBuffer.indexOf( + Buffer.from([0x50, 0x4B, 0x03, 0x04]), + currentOffset + ); + if (signaturePos === -1) break; + + const headerOffset = signaturePos; + if (headerOffset + 30 > fileBuffer.length) break; + + const compressionMethod = fileBuffer.readUInt16LE(headerOffset + 8); + const compressedSize = fileBuffer.readUInt32LE(headerOffset + 18); + const uncompressedSize = fileBuffer.readUInt32LE(headerOffset + 22); + const fileNameLength = fileBuffer.readUInt16LE(headerOffset + 26); + const extraFieldLength = fileBuffer.readUInt16LE(headerOffset + 28); + + const fileNameStart = headerOffset + 30; + const fileNameEnd = fileNameStart + fileNameLength; + if (fileNameEnd > fileBuffer.length) break; + + const fileName = fileBuffer.toString( + 'utf-8', + fileNameStart, + fileNameEnd + ); + + const dataStart = fileNameEnd + extraFieldLength; + const dataEnd = dataStart + compressedSize; + + if (dataEnd > fileBuffer.length) { + currentOffset = dataEnd; + continue; + } + + const compressedData = fileBuffer.slice(dataStart, dataEnd); + + this.entries.push({ + entryName: fileName, + isDirectory: fileName.endsWith('/'), + getData: () => { + if (compressionMethod === 8) { + return zlib.inflateRawSync(compressedData); + } else if (compressionMethod === 0) { + return compressedData; + } else { + throw new Error(`Unsupported compression method: ${compressionMethod}`); + } + } + }); + + currentOffset = dataEnd; + } + } + } + + getEntries(): ZipEntry[] { + return this.entries; + } +} diff --git a/test/3D/index.html b/test/3D/index.html deleted file mode 100755 index ad267ef5..00000000 --- a/test/3D/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - My first three.js app - - - - - - - \ No newline at end of file diff --git a/test/3D/index.js b/test/3D/index.js deleted file mode 100755 index 724de854..00000000 --- a/test/3D/index.js +++ /dev/null @@ -1,24 +0,0 @@ -let skinUrl = 'http://textures.minecraft.net/texture/ac3de5b50fb6174da51032677ead046295486bf682b3ea73e0617a210c4b4f46' - -const scene = new THREE.Scene(); -const camera = new THREE.PerspectiveCamera(75, 1000 / 500, 0.1, 100); - -const renderer = new THREE.WebGLRenderer(); -renderer.setSize(1000, 500); -document.body.appendChild(renderer.domElement); - -const geometry = new THREE.BoxGeometry(1, 1, 1); -const material = new THREE.MeshBasicMaterial({ color: '#F1F1F1'}); -const cube = new THREE.Mesh(geometry, material); -scene.add(cube); - -camera.position.z = 2; - -function animate() { - requestAnimationFrame(animate); - cube.rotation.x += 0.01; - cube.rotation.y += 0.05; - renderer.render(scene, camera); -}; - -animate(); \ No newline at end of file diff --git a/test/AZauth.js b/test/AZauth.js index cd6445dc..4f3207b9 100755 --- a/test/AZauth.js +++ b/test/AZauth.js @@ -1,7 +1,7 @@ const prompt = require('prompt') const { AZauth, Launch } = require('../build/Index'); const launch = new Launch(); -const auth = new AZauth('http://craftdium.ml/test'); +const auth = new AZauth('https://nincraft.fr'); const fs = require('fs'); let mc @@ -47,9 +47,10 @@ async function main() { // url: 'https://luuxis.fr/api/user/893bbc-a0bc41-da8568-ef56dd-7f2df8/files', authenticator: mc, timeout: 10000, - path: './.Minecraft', - version: '1.19.3', + path: './Minecraft', + version: '1.16.5', detached: false, + intelEnabledMac: true, downloadFileMultiple: 10, loader: { diff --git a/test/index.js b/test/index.js index 84e123f6..a38134d4 100755 --- a/test/index.js +++ b/test/index.js @@ -1,109 +1,113 @@ -const { Microsoft, Launch, Mojang } = require('../build/Index'); -const launch = new Launch(); +const { Launch, Microsoft } = require('minecraft-java-core'); const fs = require('fs'); -let client_id = '13f589e1-e2fc-443e-a68a-63b0092b8eeb' -let mc - -(async () => { - if (!fs.existsSync('./account.json')) { - mc = await new Microsoft(client_id).getAuth(); - fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); - } else { - mc = JSON.parse(fs.readFileSync('./account.json')); - if (!mc.refresh_token) { - mc = await new Microsoft(client_id).getAuth(); - fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); - } else { - mc = await new Microsoft(client_id).refresh(mc); - if (mc.error) mc = await new Microsoft(client_id).getAuth(); - fs.writeFileSync('./account.json', JSON.stringify(mc, null, 4)); - } + +const ACCOUNT_FILE = './account.json'; +const API_URL = 'http://luuxcraft.fr/api/user/bb8f5247-1d38-41bb-ab6d-3200471a06b2/instances'; +const INSTANCE_NAME = 'dev'; +const DOWNLOAD_SIMULTANEOUS = 30; +const MINECRAFT_PATH = './minecraft'; +const MEMORY_CONFIG = { min: '14G', max: '16G' }; + + +async function loadOrAuthenticateAccount() { + const microsoft = new Microsoft(); + + if (!fs.existsSync(ACCOUNT_FILE)) { + const account = await microsoft.getAuth(); + fs.writeFileSync(ACCOUNT_FILE, JSON.stringify(account, null, 4)); + return account; } - let opt = { - // url: 'http://craftdium.ml/launcherSelvania/files?instance=hypixel', - authenticator: mc, - timeout: 10000, - path: './.Minecraft', - instance: 'Hypixel', - version: '1.20.1', - detached: false, - downloadFileMultiple: 30, + const account = JSON.parse(fs.readFileSync(ACCOUNT_FILE, 'utf8')); - loader: { - type: 'forge', - build: 'latest', - enable: true - }, + if (!account.refresh_token) { + const newAccount = await microsoft.getAuth(); + fs.writeFileSync(ACCOUNT_FILE, JSON.stringify(newAccount, null, 4)); + return newAccount; + } - verify: false, - ignored: [ - 'config', - 'essential', - 'logs', - 'resourcepacks', - 'saves', - 'screenshots', - 'shaderpacks', - 'W-OVERFLOW', - 'options.txt', - 'optionsof.txt' - ], - JVM_ARGS: [], - GAME_ARGS: [], - - javaPath: null, - - screen: { - width: 1600, - height: 900 - }, + const refreshedAccount = await microsoft.refresh(account); + if (refreshedAccount.error) { + throw new Error(`Erreur d'authentification: ${refreshedAccount.error}`); + } + + fs.writeFileSync(ACCOUNT_FILE, JSON.stringify(refreshedAccount, null, 4)); + return refreshedAccount; +} - memory: { - min: '4G', - max: '6G' - } +async function fetchInstanceData() { + const response = await fetch(API_URL); + + if (!response.ok) { + throw new Error(`Erreur API: ${response.status} ${response.statusText}`); } - await launch.Launch(opt); + const instances = await response.json(); + const instanceData = Object.values(instances).find( + i => i.name.toLowerCase() === INSTANCE_NAME.toLowerCase() + ); - launch.on('extract', extract => { - console.log(extract); - }); + if (!instanceData) { + throw new Error(`Instance "${INSTANCE_NAME}" introuvable`); + } - launch.on('progress', (progress, size, element) => { - console.log(`Downloading ${element} ${Math.round((progress / size) * 100)}%`); + return instanceData; +} + +function buildLaunchOptions(instanceData, account) { + const loaderType = instanceData.loader?.loader_type.toLowerCase() || instanceData.loadder.loadder_type.toLowerCase(); + + return { + url: instanceData.url, + path: MINECRAFT_PATH, + authenticator: account, + version: instanceData.loader?.minecraft_version || instanceData.loadder.minecraft_version, + intelEnabledMac: true, + instance: instanceData.name, + ignore_log4j: true, + ignored: instanceData.ignored, + downloadFileMultiple: DOWNLOAD_SIMULTANEOUS, + loader: { + type: loaderType, + build: instanceData.loader?.loader_version || instanceData.loadder.loadder_version, + enable: loaderType !== 'none', + path: './' + }, + memory: MEMORY_CONFIG + }; +} + +function setupLauncherListeners(launcher) { + launcher.on('progress', (progress, size) => { + const percentage = ((progress / size) * 100).toFixed(2); + console.log(`[DL] ${percentage} %`); }); - launch.on('check', (progress, size, element) => { - console.log(`Checking ${element} ${Math.round((progress / size) * 100)}%`); - }); + launcher.on('patch', patch => process.stdout.write(patch)); + launcher.on('data', line => process.stdout.write(line)); + launcher.on('error', err => console.error('[ERROR]', err)); +} - launch.on('estimated', (time) => { - let hours = Math.floor(time / 3600); - let minutes = Math.floor((time - hours * 3600) / 60); - let seconds = Math.floor(time - hours * 3600 - minutes * 60); - console.log(`${hours}h ${minutes}m ${seconds}s`); - }) +async function main() { + try { + console.log('🔐 Authentification en cours...'); + const account = await loadOrAuthenticateAccount(); - launch.on('speed', (speed) => { - console.log(`${(speed / 1067008).toFixed(2)} Mb/s`) - }) + console.log('📡 Récupération des données de l\'instance...'); + const instanceData = await fetchInstanceData(); - launch.on('patch', patch => { - console.log(patch); - }); + console.log(`🎮 Lancement de ${instanceData.name}...`); + const launcher = new Launch(); + const options = buildLaunchOptions(instanceData, account); - launch.on('data', (e) => { - console.log(e); - }) + setupLauncherListeners(launcher); + launcher.Launch(options); - launch.on('close', code => { - console.log(code); - }); + } catch (error) { + console.error('❌ Erreur fatale:', error.message); + process.exit(1); + } +} - launch.on('error', err => { - console.log(err); - }); -})() +main(); diff --git a/test/offline.js b/test/offline.js new file mode 100755 index 00000000..5f6cac69 --- /dev/null +++ b/test/offline.js @@ -0,0 +1,26 @@ +const { Launch, Mojang } = require('minecraft-java-core'); +const launcher = new Launch(); +(async () => { + await launcher.Launch({ + path: './minecraft', + authenticator: await Mojang.login('Luuxis'), + version: '1.16.5', + intelEnabledMac: true, + bypassOffline: true, + loader: { + path: './', + type: 'fabric', + build: 'latest', + enable: true + }, + memory: { + min: '2G', + max: '4G' + } + }); + + launcher.on('progress', (progress, size) => console.log(`[DL] ${((progress / size) * 100).toFixed(2)}%`)) + .on('patch', pacth => process.stdout.write(pacth)) + .on('data', line => process.stdout.write(line)) + .on('close', () => console.log('Game exited.')); +})(); diff --git a/tsconfig.esm.json b/tsconfig.esm.json new file mode 100644 index 00000000..97fbc4e4 --- /dev/null +++ b/tsconfig.esm.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "ES2020", + "lib": [ + "ES2021", + "DOM" + ], + "outDir": "build/esm" + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 076ada6f..829aeccf 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,14 +1,20 @@ { + "$schema": "https://json.schemastore.org/tsconfig", "include": [ "src/**/*" ], "compilerOptions": { - "sourceMap": false, + "sourceMap": true, "target": "ES2020", "module": "CommonJS", - "lib": ["ES2021"], - "declaration": true, + "moduleResolution": "Node", + "lib": [ + "ES2021", + "DOM" + ], + "declaration": true, "outDir": "build", - "esModuleInterop": true + "esModuleInterop": true, + "skipLibCheck": true } } \ No newline at end of file diff --git a/webfiles/php/scandir.php b/webfiles/php/scandir.php index a26254cf..7312a3c3 100755 --- a/webfiles/php/scandir.php +++ b/webfiles/php/scandir.php @@ -22,7 +22,6 @@ function scanFolder($dir) { $filePath = $dir . '/' . $filename; if ($filename == "php") continue; if (is_dir($filePath)) $result[] = $filename; - } return $result; } @@ -37,7 +36,7 @@ function dirToArray($dir) { $url_req = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); $url = "http://$_SERVER[HTTP_HOST]$url_req$dir/$path"; - $res[] = array("url" => $url, "size" => $size, "hash" => $hash, "path" => $path); + $res[] = array("url" => $url, "size" => $size, "hash" => $hash, "path" => $path); } return str_replace("\\", "", json_encode($res)); }