From fc8081b01bec020cee27055dcaa4d20a99f2a505 Mon Sep 17 00:00:00 2001 From: Max Alex Date: Thu, 22 May 2025 00:33:25 +0800 Subject: [PATCH 01/86] Update zh_cn.json Sync with en_us.json in 4274d79 --- src/main/resources/assets/automodpack/lang/zh_cn.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/resources/assets/automodpack/lang/zh_cn.json b/src/main/resources/assets/automodpack/lang/zh_cn.json index c11e717ef..74eb0981b 100644 --- a/src/main/resources/assets/automodpack/lang/zh_cn.json +++ b/src/main/resources/assets/automodpack/lang/zh_cn.json @@ -1,6 +1,7 @@ { "automodpack.restart.full": "模组已下载完成!", "automodpack.restart.update": "模组已完成更新!", + "automodpack.restart.select": "服务器所需的模组包已被选择!", "automodpack.restart.automodpack": "需要将AutoModpack模组更新至服务器要求的版本!", "automodpack.restart.description": "要完成此操作,您必须重启游戏。", "automodpack.restart.secDescription": "您现在要重启游戏吗?", @@ -40,8 +41,15 @@ "automodpack.fetch": "正在从Modrinth和CurseForge直接获取URL...", "automodpack.fetch.found": "找到%s个URL。", - "automodpack.select": "选择模组", + "automodpack.validation.text": "您正在连接到一个未知的 AutoModpack 主机", + "automodpack.validation.description": "请验证服务器的证书指纹", + "automodpack.validation.secDescription": "如果您没有指纹,请向您的服务器管理员索取", + "automodpack.validation.thiDescription": "此安全检查可防止在模组包下载过程中连接被篡改", + "automodpack.validation.run": "验证", + "automodpack.validation.failed": "验证失败", + "automodpack.retry": "重试", + "automodpack.select": "选择模组", "automodpack.yes": "是", "automodpack.no": "否", "automodpack.cancel": "取消", From b9896a9654b9318405c9853e2aa365c9856b5e36 Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 24 May 2025 15:19:46 +0200 Subject: [PATCH 02/86] Init Sinytra docs --- docs/_meta.json | 26 +++++++++++++++ docs/commands/_meta.json | 6 ++++ docs/commands/commands.mdx | 8 +++++ docs/configuration/_meta.json | 6 ++++ docs/configuration/configuration.mdx | 49 ++++++++++++++++++++++++++++ docs/faq.mdx | 49 ++++++++++++++++++++++++++++ docs/sinytra-wiki.json | 7 ++++ docs/technicals/_meta.json | 18 ++++++++++ docs/technicals/certificate.mdx | 0 docs/technicals/connection.mdx | 0 docs/technicals/file_structure.mdx | 0 docs/technicals/modpack_host.mdx | 0 12 files changed, 169 insertions(+) create mode 100644 docs/_meta.json create mode 100644 docs/commands/_meta.json create mode 100644 docs/commands/commands.mdx create mode 100644 docs/configuration/_meta.json create mode 100644 docs/configuration/configuration.mdx create mode 100644 docs/faq.mdx create mode 100644 docs/sinytra-wiki.json create mode 100644 docs/technicals/_meta.json create mode 100644 docs/technicals/certificate.mdx create mode 100644 docs/technicals/connection.mdx create mode 100644 docs/technicals/file_structure.mdx create mode 100644 docs/technicals/modpack_host.mdx diff --git a/docs/_meta.json b/docs/_meta.json new file mode 100644 index 000000000..f51476b05 --- /dev/null +++ b/docs/_meta.json @@ -0,0 +1,26 @@ +{ + "usage": { + "name": "Usage", + "icon": null + }, + "configuration": { + "name": "\uD83D\uDEE0\uFE0F Configuration", + "icon": null + }, + "commands": { + "name": "⌨\uFE0F Commands", + "icon": null + }, + "examples": { + "name": "\uD83D\uDCF7 Examples", + "icon": null + }, + "technicals": { + "name": "\uD83D\uDC68\u200D\uD83D\uDCBB Technical Details", + "icon": null + }, + "faq.mdx": { + "name": "❔ FAQ", + "icon": null + } +} \ No newline at end of file diff --git a/docs/commands/_meta.json b/docs/commands/_meta.json new file mode 100644 index 000000000..cd9c084df --- /dev/null +++ b/docs/commands/_meta.json @@ -0,0 +1,6 @@ +{ + "commands.mdx": { + "name": "Commands", + "icon": null + } +} diff --git a/docs/commands/commands.mdx b/docs/commands/commands.mdx new file mode 100644 index 000000000..5f99effe2 --- /dev/null +++ b/docs/commands/commands.mdx @@ -0,0 +1,8 @@ +- `/automodpack` - Status of automodpack and general help. +- `/automodpack generate` - Generate modpack. +- `/automodpack host` - Status of modpack hosting. +- `/automodpack host start` - Start modpack hosting. +- `/automodpack host stop` - Stop modpack hosting. +- `/automodpack host restart` - Restart modpack hosting. +- `/automodpack host connections` - Lists all currently active modpack host connections. +- `/automodpack config reload` - Reload config files. \ No newline at end of file diff --git a/docs/configuration/_meta.json b/docs/configuration/_meta.json new file mode 100644 index 000000000..4d359cb71 --- /dev/null +++ b/docs/configuration/_meta.json @@ -0,0 +1,6 @@ +{ + "configuration.mdx": { + "name": "Configuration", + "icon": null + } +} diff --git a/docs/configuration/configuration.mdx b/docs/configuration/configuration.mdx new file mode 100644 index 000000000..5d3346de3 --- /dev/null +++ b/docs/configuration/configuration.mdx @@ -0,0 +1,49 @@ +### Server Config File +`~/automodpack/automodpack-server.json` + +| Name | Default Value | Description | | +|-------------------------------|--------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----| +| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | | +| `modpackName` | | The name of the server modpack, shows while downloading modpack and that's how modpack directory gets called inside `~/automodpack/modpacks/`. | | +| `modpackHost` | `true` | Starts modpack host server. | | +| `generateModpackOnStart` | `true` | Automatically regenerate modpack metadata when the server starts. | | +| `syncedFiles` | `"/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"` | A list of *relative paths* from the root folder of the Minecraft server that will be **synced to the modpack**. Use wildcards like `*` to include multiple files from this directory (e.g., `/mods/*.jar`). Or use `**` (double star) to recursively include all files and subdirectories from a folder (e.g., `/config/**`). Prefix a path with `!` to **exclude** it from syncing. **Note:** This does **not** copy the files; it only defines what will be available on the client side under `~/.minecraft/`. | | +| `allowEditsInFiles` | `"/options.txt", "/config/**"` | A list of files that clients are allowed to edit. In other words, files that are downloaded only one time and then ignored from updating. Configuration works exactly the same way `syncedFiles` does. | | +| `autoExcludeUnnecessaryFiles` | `true` | Auto skip files which are: empty, hidden, temporary, disabled or backup. | | +| `requireAutoModpackOnClient` | `true` | Whether or not this mod is optional for clients to join server. | | +| `nagUnModdedClients` | `true` | If `true` clients without AutoModpack will be nagged with a chat message on join. To work requires `requireAutoModpackOnClient` to be `false`. | | +| `nagMessage` | `"This server provides dedicated modpack through AutoModpack!"` | The message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | | +| `nagClickableMessage` | `"Click here to get the AutoModpack!"` | The clickable part of the message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | | +| `nagClickableLink` | `"https://modrinth.com/project/automodpack"` | The link that will be opened when the message is clicked. To work requires `nagUnModdedClients` to be `true`. | | +| `autoExcludeServerSideMods` | `true` | Automatically excludes server-side mods from the modpack. (Works only if mod developer specified environment type in mod metadata) | | +| `hostModpackOnMinecraftPort` | `true` | Injects into minecraft network IO thanks to which modpack hosting doesn't require any additional port routing. | | +| `hostIp` | | The IP address on which the host server binds. | | +| `hostLocalIp` | | The local IP address on which the host server binds. | | +| `updateIpsOnEveryStart` | `false` | Updates `hostIp` and `hostLocalIp` on every server start. Might be useful if you have dynamic IP address. | | +| `hostPort` | `-1` | The port number on which the host server listens. | | +| `reverseProxy` | `false` | If `true` AutoModpack won't be adding configurable port from `hostPort` to the end of `hostIp`, `localHostIp` and `externalModpackHostLink`. | | +| `bandwidthLimit` | `0` | Upload limit in Mbps that modpack host server is restricted to. (Value has to be an Integer, `0` - means unlimited) | | +| `secretLifetime` | `336` | Time in hours that a player's secret remains valid. | | +| `validateSecrets` | `true` | Makes modpack host validate and authorize modpack download attempts using unique secrets. | | +| `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | | +| `acceptedLoaders` | `""` | Allows players from different modloaders to connect to your server (as long, automodpack support that loader and other mods on your server aren't incompatible with each other) with the same modpack. (use with caution, some mods may not work on both loaders) | | + +### Client Config File + +`/automodpack/automodpack-client.json` + +| Name | Default Value | Description | +|---------------------|------------------|-----------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | +| `selectedModpack` | | The main folder of the modpack that you want to play. Typing the name of the modpack here will cause it to be loaded. | +| `installedModpacks` | | A list of modpacks that are installed on the client. | +| `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | + + + +## Troubleshooting Config Issues +When finished editing configuration files, make sure to reload them by executing command `automodpack config reload` or restart the server/game. + +If you can't find the config files, make sure the client/server was started at least once, so that the files are generated. + +If values get restored to default it might mean that your config file is malformed make sure to use editor with JSON parser build-in. Or check in some online one e.g. https://jsonformatter.org/json-parser. \ No newline at end of file diff --git a/docs/faq.mdx b/docs/faq.mdx new file mode 100644 index 000000000..4e3318cdc --- /dev/null +++ b/docs/faq.mdx @@ -0,0 +1,49 @@ +### Is it server side mod? +No, it's both. You need to have it installed on server as well as on client. + +### How do i add client only mods to the modpack? (e.g. server crashes with some mod but i want it for clients) +See [modpack creation page](https://github.com/Skidamek/AutoModpack/wiki/Modpack-Creation) + +### How to verify the certificate fingerprint? +Just copy the fingerprint from your server console to the game. This prevents attacks such as [MITM](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). Its recommended to share this fingerprint with players ahead of time. + +However please do not think of it like a password to the modpack, its not a password or any secret, its a public verifiable piece of server certificate, anyone can download modpack without previous knowledge of such fingerprint e.g. by bypassing this check with `I AM INCREDIBLY STUPID`. (don't do it) + +If you don't want players to explicitly verify the certificate and you own a domain (required), you can provide your own CA signed certificate e.g. using [Certbot](https://eff-certbot.readthedocs.io/en/stable/install.html). On the server in the `~/automodpack/.private/` directory, replace the `cert.crt` (with the full chain certificate) and `key.pem` files ([key has to be in PKCS#8 (PEM) format](https://netty.io/wiki/sslcontextbuilder-and-private-key.html)) - here be cautious as interception of these files may result in impersonation of the server, [read more](https://security.stackexchange.com/a/16694). If you're hosting modpack on different sub/domain than your minecraft server, make sure that your certificate verifies both of the sub/domains. + +### How to restrict access to my modpack? +Enable `validateSecrets` in the [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration), turn on `online-mode` in the server.properties and enable whitelist or ban someone. + +### Does it work on lan (single-player world with option for other players to join)? +Yes. Just remember to generate modpack via command /`automodpack generate` and start modpack host /`automodpack host start`. + +### Does it work with single-player world shared via essential, world host, e4mc? +Likely no, most of these type of mods forward only minecraft packets. These mods would need to support automodpack protocol or just relay all tcp packets instead of limiting to minecraft protocol. + +### Can I use non modpack mods on client? +Yes, you can add any mod you want to use alongside downloaded modpack by just placing the mod into the standard mods folder. + +### How can I delete files from modpack? +Just delete file from server, or exclude it from sync, read about `syncedFiles` in [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration#server-config-file) + +### Can I remove/disable non modpack files/mods from player use? +Not yet, it may come in the future. + +### Do I have to open any port on my router? +No, you don't need to. By default AutoModpack injects into minecraft networking I/O which reuses your minecraft server port. (e.g. 25565) + +If you want to use different port. Disable `hostModpackOnMinecraftPort` and change `hostPort` in [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration#server-config-file) + +### What happens when I update AutoModpack version on server? +Fear no more, clients **will not** get out of sync. + +Clients always try to sync to the server's version of AutoModpack which is always downloaded from Modrinth. + +### Why is the host modpack path so long? +There are plans to implement ability of adding more than just one, `main` modpack. + +### Which minecraft versions are supported? +All of the versions you see in the [latest release](https://github.com/Skidamek/AutoModpack/releases/latest). + +### Does this mod updates other mods? +No, it's out of scope for this project. This mod only synchronizes server modpack to the clients. \ No newline at end of file diff --git a/docs/sinytra-wiki.json b/docs/sinytra-wiki.json new file mode 100644 index 000000000..6f1d73b11 --- /dev/null +++ b/docs/sinytra-wiki.json @@ -0,0 +1,7 @@ +{ + "id": "automodpack", + "platforms": { + "modrinth": "automodpack", + "curseforge": "automodpack" + } +} \ No newline at end of file diff --git a/docs/technicals/_meta.json b/docs/technicals/_meta.json new file mode 100644 index 000000000..e1941bbc0 --- /dev/null +++ b/docs/technicals/_meta.json @@ -0,0 +1,18 @@ +{ + "connection.mdx": { + "name": "Connection Flow", + "icon": null + }, + "certificate.mdx": { + "name": "Certificate Handling", + "icon": null + }, + "file_structure.mdx": { + "name": "File Structure", + "icon": null + }, + "modpack_host.mdx": { + "name": "Modpack Hosting", + "icon": null + } +} diff --git a/docs/technicals/certificate.mdx b/docs/technicals/certificate.mdx new file mode 100644 index 000000000..e69de29bb diff --git a/docs/technicals/connection.mdx b/docs/technicals/connection.mdx new file mode 100644 index 000000000..e69de29bb diff --git a/docs/technicals/file_structure.mdx b/docs/technicals/file_structure.mdx new file mode 100644 index 000000000..e69de29bb diff --git a/docs/technicals/modpack_host.mdx b/docs/technicals/modpack_host.mdx new file mode 100644 index 000000000..e69de29bb From 6b635a15fe842fa07168555e9345348b4493ea31 Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 24 May 2025 16:50:06 +0200 Subject: [PATCH 03/86] Improve README clarity and detail for AutoModpack connection and update process And update docs --- README.md | 20 +++++++++++--------- docs/faq.mdx | 7 ------- docs/technicals/_meta.json | 4 ++++ docs/technicals/certificate.mdx | 6 ++++++ docs/technicals/file_structure.mdx | 17 +++++++++++++++++ docs/technicals/modpack_creation.mdx | 13 +++++++++++++ 6 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 docs/technicals/modpack_creation.mdx diff --git a/README.md b/README.md index 0925a4972..71c354ba3 100644 --- a/README.md +++ b/README.md @@ -40,16 +40,16 @@ This isn't just another mod; it's a game-changer for private servers. Here's why ## 🛠️ How the Magic Happens -AutoModpack works by generating a modpack (**metadata file**) on the server, containing all the files of your modpack. The server then hosts this file and the modpack files. +AutoModpack works by generating a modpack (**metadata file**) on the server, which contains all the files of your modpack. The server then hosts this file and the modpack files. -When a client connects to server (simplified): +When a client connects to the server: -1. AutoModpack securely connects to the modpack host server and fetches the metadata. -2. It uses Modrinth and CurseForge APIs to download directly your modpack's files where possible. -3. All files are downloaded to the client's automodpack folder. -4. Restart the game, AutoModpack loads the modpack and the client is perfectly synced and ready to play! +1. Connection; AutoModpack establishes a secure connection and prompts you to [verify the server's certificate fingerprint](https://github.com/Skidamek/AutoModpack/wiki/FAQ#how-to-verify-the-certificate-fingerprint). +2. Direct links; Fetches the APIs for direct downloads of your modpack's files from Modrinth and CurseForge, where possible (mods, resource packs, shaders). +3. Modpack download; All files are downloaded to the client's automodpack folder. +4. Game restart; AutoModpack loads the modpack, and the client is perfectly synced and ready to play! -On subsequent game boots, AutoModpack checks for updates. If changes are detected, it updates the modpack in the background, no additional restarts required! +On subsequent game launches, AutoModpack checks for updates. If changes are detected, it updates the modpack in the background—no additional restarts are required! ## ⚠️ Security and Trust - Read This! @@ -59,17 +59,19 @@ Because it downloads files directly into your game folder, it's crucial to **onl While AutoModpack itself tries to be as secure as possible, due to the nature of the internet, the creators and contributors of AutoModpack are not responsible for any harm, damage, loss, or issues that may result from files downloaded from a server you connect to using the mod. **By using AutoModpack, you acknowledge and accept this risk.** -**If you have valuable security insights or concerns, please reach out!** You can contact privately on [Discord](https://discordapp.com/users/464522287618457631) or open an issue on GitHub. +**If you have valuable security insights or concerns, please reach out!** You can contact privately on [Discord](https://discordapp.com/users/464522287618457631) or publicly on [Discord server](https://discord.gg/hS6aMyeA9P) or just open an issue on [GitHub](https://github.com/Skidamek/AutoModpack/issues). ## 🚀 Getting Started is a Breeze! Installing AutoModpack is as simple as installing any other mod. 1. Download the AutoModpack from the releases page on [GitHub](https://github.com/Skidamek/AutoModpack/releases), [CurseForge](https://www.curseforge.com/minecraft/mc-mods/automodpack), or [Modrinth](https://modrinth.com/mod/automodpack). 2. Place the downloaded file into the `/mods/` folder of both your server and client Minecraft installations. +3. Start your server and let AutoModpack generate the initial modpack metadata. +4. Connect to your server with the mod installed on your client. That's typically all you need to do! AutoModpack will automatically create the modpack from your server's mods. -**Want to customize your modpack further?** Add configs, client-side-only mods, and more? **Check out the [wiki](https://github.com/Skidamek/AutoModpack/wiki)!** It *hopefully* has all the details you need to tailor AutoModpack to your specific needs. +**Want to customize your modpack further?** Add configs, client-side-only mods, and more? **Check out the [wiki]https://moddedmc.wiki/en/project/automodpack/docs)!** It *hopefully* has all the details you need to tailor AutoModpack to your specific needs. If you encounter any issues or have questions, feel free to [join Discord server](https://discord.gg/hS6aMyeA9P) or open an issue on [GitHub](https://github.com/Skidamek/AutoModpack/issues). Prefer an all-in-one solution? You can also use our [modified Fabric installer](https://github.com/Skidamek/AutoModpack-Installer/releases/tag/Latest) which downloads AutoModpack alongside the Fabric loader. diff --git a/docs/faq.mdx b/docs/faq.mdx index 4e3318cdc..0800c9d11 100644 --- a/docs/faq.mdx +++ b/docs/faq.mdx @@ -4,13 +4,6 @@ No, it's both. You need to have it installed on server as well as on client. ### How do i add client only mods to the modpack? (e.g. server crashes with some mod but i want it for clients) See [modpack creation page](https://github.com/Skidamek/AutoModpack/wiki/Modpack-Creation) -### How to verify the certificate fingerprint? -Just copy the fingerprint from your server console to the game. This prevents attacks such as [MITM](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). Its recommended to share this fingerprint with players ahead of time. - -However please do not think of it like a password to the modpack, its not a password or any secret, its a public verifiable piece of server certificate, anyone can download modpack without previous knowledge of such fingerprint e.g. by bypassing this check with `I AM INCREDIBLY STUPID`. (don't do it) - -If you don't want players to explicitly verify the certificate and you own a domain (required), you can provide your own CA signed certificate e.g. using [Certbot](https://eff-certbot.readthedocs.io/en/stable/install.html). On the server in the `~/automodpack/.private/` directory, replace the `cert.crt` (with the full chain certificate) and `key.pem` files ([key has to be in PKCS#8 (PEM) format](https://netty.io/wiki/sslcontextbuilder-and-private-key.html)) - here be cautious as interception of these files may result in impersonation of the server, [read more](https://security.stackexchange.com/a/16694). If you're hosting modpack on different sub/domain than your minecraft server, make sure that your certificate verifies both of the sub/domains. - ### How to restrict access to my modpack? Enable `validateSecrets` in the [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration), turn on `online-mode` in the server.properties and enable whitelist or ban someone. diff --git a/docs/technicals/_meta.json b/docs/technicals/_meta.json index e1941bbc0..3a05eb07b 100644 --- a/docs/technicals/_meta.json +++ b/docs/technicals/_meta.json @@ -7,6 +7,10 @@ "name": "Certificate Handling", "icon": null }, + "modpack_creation.mdx": { + "name": "Modpack Creation", + "icon": null + }, "file_structure.mdx": { "name": "File Structure", "icon": null diff --git a/docs/technicals/certificate.mdx b/docs/technicals/certificate.mdx index e69de29bb..6158e1fc5 100644 --- a/docs/technicals/certificate.mdx +++ b/docs/technicals/certificate.mdx @@ -0,0 +1,6 @@ +### How to verify the certificate fingerprint? +Just copy the fingerprint from your server console to the game. This prevents attacks such as [MITM](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). Its recommended to share this fingerprint with players ahead of time. + +However please do not think of it like a password to the modpack, its not a password or any secret, its a public verifiable piece of server certificate, anyone can download modpack without previous knowledge of such fingerprint e.g. by bypassing this check with `I AM INCREDIBLY STUPID`. (don't do it) + +If you don't want players to explicitly verify the certificate and you own a domain (required), you can provide your own CA signed certificate e.g. using [Certbot](https://eff-certbot.readthedocs.io/en/stable/install.html). On the server in the `~/automodpack/.private/` directory, replace the `cert.crt` (with the full chain certificate) and `key.pem` files ([key has to be in PKCS#8 (PEM) format](https://netty.io/wiki/sslcontextbuilder-and-private-key.html)) - here be cautious as interception of these files may result in impersonation of the server, [read more](https://security.stackexchange.com/a/16694). If you're hosting modpack on different sub/domain than your minecraft server, make sure that your certificate verifies both of the sub/domains. diff --git a/docs/technicals/file_structure.mdx b/docs/technicals/file_structure.mdx index e69de29bb..3dc883894 100644 --- a/docs/technicals/file_structure.mdx +++ b/docs/technicals/file_structure.mdx @@ -0,0 +1,17 @@ +All of the AutoModpack files are kept inside `~/automodpack/` folder. + +### Server +Modpack is generated from files inside `~/automodpack/host-modpack/main/` and/or `syncedFiles` (read [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration)) which translates to `~/.minecraft/` (root minecraft instance) folder on client. + +Modpack generation means creation of `~/automodpack/host-modpack/automodpack-content.json` which contains metadata of every single file of the modapck. You **should NOT** touch this file manually. + +### Client +All of downloaded modpacks are kept inside `~/automodpack/modpacks/`. + +All of the selected modpack files are copied to `~/.minecraft/` besides **mods*** (with some exceptions) + +Mods are not copied for a reason, it gives AutoModpack full control of what and when load into the loader. + +Also AutoModpack tries to delete most of duplicated mods from standard mods folder in favor of AutoModpack's one. + +Thanks to that modpack can be seamlessly updated on boot before loading any of modpack mods and just after update it can load whole up-to-date modpack and continue with loading process, otherwise it would require a restart. diff --git a/docs/technicals/modpack_creation.mdx b/docs/technicals/modpack_creation.mdx new file mode 100644 index 000000000..edfc7de5a --- /dev/null +++ b/docs/technicals/modpack_creation.mdx @@ -0,0 +1,13 @@ +**Yes, you can add *anything* to your modpack.** + +#### Two ways of adding content to the modpack. + +- To re-use already existing files on server use `syncedFiles` read more in [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration#server-config-file). As an example with default config it creates modpack containing *every* mod (file ending with .jar) from server `~/mods/` folder. + +- To add more files to modpack use separate directory `~/automodpack/host-modpack/main/`. +For example to add more mods create folder `mods` and place there mods so that will be `~/automodpack/host-modpack/main/mods/`**`your-mod.jar`**. +Or to add resourcepacks analogically create folder `resourcepacks` and place there resourcepacks so that will be `~/automodpack/host-modpack/main/resourcepacks/`**`your-resourcepack.zip`**. + +Both of these methods translate to the `~/.minecraft/` on client side (root directory of minecraft instance). + +Remember to regenerate the modpack using `/automodpack generate` (see [commands](https://github.com/Skidamek/AutoModpack/wiki/Commands)) \ No newline at end of file From 1c802499bf8ba645b9adb052506deff23ca9ec23 Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 24 May 2025 16:51:20 +0200 Subject: [PATCH 04/86] Update link to certificate faq --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71c354ba3..43ff774be 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ AutoModpack works by generating a modpack (**metadata file**) on the server, whi When a client connects to the server: -1. Connection; AutoModpack establishes a secure connection and prompts you to [verify the server's certificate fingerprint](https://github.com/Skidamek/AutoModpack/wiki/FAQ#how-to-verify-the-certificate-fingerprint). +1. Connection; AutoModpack establishes a secure connection and prompts you to [verify the server's certificate fingerprint](https://moddedmc.wiki/en/project/automodpack/docs/technicals/certificate). 2. Direct links; Fetches the APIs for direct downloads of your modpack's files from Modrinth and CurseForge, where possible (mods, resource packs, shaders). 3. Modpack download; All files are downloaded to the client's automodpack folder. 4. Game restart; AutoModpack loads the modpack, and the client is perfectly synced and ready to play! From 2a40931431f2b71de5667dc638221ae05640cf66 Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 24 May 2025 16:54:22 +0200 Subject: [PATCH 05/86] Fix link in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43ff774be..c53d83995 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ Installing AutoModpack is as simple as installing any other mod. That's typically all you need to do! AutoModpack will automatically create the modpack from your server's mods. -**Want to customize your modpack further?** Add configs, client-side-only mods, and more? **Check out the [wiki]https://moddedmc.wiki/en/project/automodpack/docs)!** It *hopefully* has all the details you need to tailor AutoModpack to your specific needs. If you encounter any issues or have questions, feel free to [join Discord server](https://discord.gg/hS6aMyeA9P) or open an issue on [GitHub](https://github.com/Skidamek/AutoModpack/issues). +**Want to customize your modpack further?** Add configs, client-side-only mods, and more? **Check out the [wiki](https://moddedmc.wiki/en/project/automodpack/docs)!** It *hopefully* has all the details you need to tailor AutoModpack to your specific needs. If you encounter any issues or have questions, feel free to join [Discord server](https://discord.gg/hS6aMyeA9P) or open an issue on [GitHub](https://github.com/Skidamek/AutoModpack/issues). Prefer an all-in-one solution? You can also use our [modified Fabric installer](https://github.com/Skidamek/AutoModpack-Installer/releases/tag/Latest) which downloads AutoModpack alongside the Fabric loader. From 30beb0c9c8ba266e813287730fec4bb5d3cacf86 Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 24 May 2025 22:40:57 +0200 Subject: [PATCH 06/86] Enhance the docs a lot --- docs/_homepage.mdx | 7 ++++ docs/_meta.json | 4 ++ docs/configuration/client-config.mdx | 10 +++++ .../{configuration.mdx => server-config.mdx} | 20 ---------- docs/configuration/troubleshooting.mdx | 6 +++ docs/faq.mdx | 16 +++++--- docs/quick-start.mdx | 35 +++++++++++++++++ docs/technicals/_meta.json | 8 ++-- docs/technicals/certificate.mdx | 39 +++++++++++++++++-- docs/technicals/connection.mdx | 35 +++++++++++++++++ docs/technicals/file-structure.mdx | 21 ++++++++++ docs/technicals/file_structure.mdx | 17 -------- ...pack_creation.mdx => modpack-creation.mdx} | 8 +++- docs/technicals/modpack-hosting.mdx | 3 ++ docs/technicals/modpack_host.mdx | 0 stonecutter.gradle.kts | 7 ++++ 16 files changed, 184 insertions(+), 52 deletions(-) create mode 100644 docs/_homepage.mdx create mode 100644 docs/configuration/client-config.mdx rename docs/configuration/{configuration.mdx => server-config.mdx} (91%) create mode 100644 docs/configuration/troubleshooting.mdx create mode 100644 docs/quick-start.mdx create mode 100644 docs/technicals/file-structure.mdx delete mode 100644 docs/technicals/file_structure.mdx rename docs/technicals/{modpack_creation.mdx => modpack-creation.mdx} (64%) create mode 100644 docs/technicals/modpack-hosting.mdx delete mode 100644 docs/technicals/modpack_host.mdx diff --git a/docs/_homepage.mdx b/docs/_homepage.mdx new file mode 100644 index 000000000..80abc0baf --- /dev/null +++ b/docs/_homepage.mdx @@ -0,0 +1,7 @@ +Welcome to the AutoModpack documentation! + +If you are new to AutoModpack, we recommend starting with the [Quick Start Guide](quick-start/quick-start) to get familiar with the basics of using AutoModpack. + +If you are looking for more detailed information, you can find it in the `👨‍💻 Technical Details` section. + +Got any questions or need help? Join our [Discord server](https://discord.gg/hS6aMyeA9P). \ No newline at end of file diff --git a/docs/_meta.json b/docs/_meta.json index f51476b05..9dc308a94 100644 --- a/docs/_meta.json +++ b/docs/_meta.json @@ -1,4 +1,8 @@ { + "quick-start.mdx": { + "name": "🚀 Quick Start", + "icon": null + }, "usage": { "name": "Usage", "icon": null diff --git a/docs/configuration/client-config.mdx b/docs/configuration/client-config.mdx new file mode 100644 index 000000000..912446994 --- /dev/null +++ b/docs/configuration/client-config.mdx @@ -0,0 +1,10 @@ +### Client Config File + +`/automodpack/automodpack-client.json` + +| Name | Default Value | Description | +|---------------------|------------------|-----------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | +| `selectedModpack` | | The main folder of the modpack that you want to play. Typing the name of the modpack here will cause it to be loaded. | +| `installedModpacks` | | A list of modpacks that are installed on the client. | +| `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | diff --git a/docs/configuration/configuration.mdx b/docs/configuration/server-config.mdx similarity index 91% rename from docs/configuration/configuration.mdx rename to docs/configuration/server-config.mdx index 5d3346de3..fd18e7755 100644 --- a/docs/configuration/configuration.mdx +++ b/docs/configuration/server-config.mdx @@ -27,23 +27,3 @@ | `validateSecrets` | `true` | Makes modpack host validate and authorize modpack download attempts using unique secrets. | | | `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | | | `acceptedLoaders` | `""` | Allows players from different modloaders to connect to your server (as long, automodpack support that loader and other mods on your server aren't incompatible with each other) with the same modpack. (use with caution, some mods may not work on both loaders) | | - -### Client Config File - -`/automodpack/automodpack-client.json` - -| Name | Default Value | Description | -|---------------------|------------------|-----------------------------------------------------------------------------------------------------------------------| -| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | -| `selectedModpack` | | The main folder of the modpack that you want to play. Typing the name of the modpack here will cause it to be loaded. | -| `installedModpacks` | | A list of modpacks that are installed on the client. | -| `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | - - - -## Troubleshooting Config Issues -When finished editing configuration files, make sure to reload them by executing command `automodpack config reload` or restart the server/game. - -If you can't find the config files, make sure the client/server was started at least once, so that the files are generated. - -If values get restored to default it might mean that your config file is malformed make sure to use editor with JSON parser build-in. Or check in some online one e.g. https://jsonformatter.org/json-parser. \ No newline at end of file diff --git a/docs/configuration/troubleshooting.mdx b/docs/configuration/troubleshooting.mdx new file mode 100644 index 000000000..d0c8b88a4 --- /dev/null +++ b/docs/configuration/troubleshooting.mdx @@ -0,0 +1,6 @@ +## Troubleshooting Config Issues +When finished editing configuration files, make sure to reload them by executing command `automodpack config reload` or restart the server/game. + +If you can't find the config files, make sure the client/server was started at least once, so that the files are generated. + +If values get restored to default it might mean that your config file is malformed make sure to use editor with JSON parser build-in. Or check in some online one e.g. https://jsonformatter.org/json-parser. diff --git a/docs/faq.mdx b/docs/faq.mdx index 0800c9d11..5035ecc26 100644 --- a/docs/faq.mdx +++ b/docs/faq.mdx @@ -5,7 +5,7 @@ No, it's both. You need to have it installed on server as well as on client. See [modpack creation page](https://github.com/Skidamek/AutoModpack/wiki/Modpack-Creation) ### How to restrict access to my modpack? -Enable `validateSecrets` in the [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration), turn on `online-mode` in the server.properties and enable whitelist or ban someone. +Enable `validateSecrets` in the [config](configuration/server-config), turn on `online-mode` in the server.properties and enable whitelist or ban someone. ### Does it work on lan (single-player world with option for other players to join)? Yes. Just remember to generate modpack via command /`automodpack generate` and start modpack host /`automodpack host start`. @@ -28,9 +28,7 @@ No, you don't need to. By default AutoModpack injects into minecraft networking If you want to use different port. Disable `hostModpackOnMinecraftPort` and change `hostPort` in [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration#server-config-file) ### What happens when I update AutoModpack version on server? -Fear no more, clients **will not** get out of sync. - -Clients always try to sync to the server's version of AutoModpack which is always downloaded from Modrinth. +Client always try to sync to the server's version of AutoModpack. It updates or downgrades itself to the server's version if such is found on the Modrinth. ### Why is the host modpack path so long? There are plans to implement ability of adding more than just one, `main` modpack. @@ -38,5 +36,11 @@ There are plans to implement ability of adding more than just one, `main` modpac ### Which minecraft versions are supported? All of the versions you see in the [latest release](https://github.com/Skidamek/AutoModpack/releases/latest). -### Does this mod updates other mods? -No, it's out of scope for this project. This mod only synchronizes server modpack to the clients. \ No newline at end of file +### Does this mod updates my modpack mods to the latest versions? +No, it's out of scope for this project. This mod only synchronizes server modpack to the clients. All your mods on the server would have to still be updated manually. AutoModpack just makes sure client versions matches the server ones. + +### I found a bug, what should I do? +If you found a bug, please report it on the [issue tracker](https://github.com/Skidamek/AutoModpack/issues) + +### I found a security vulnerability, what should I do? +Please report it privately to the author on [Discord](https://discordapp.com/users/464522287618457631), we will try to resolve it ASAP. \ No newline at end of file diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx new file mode 100644 index 000000000..3c922264b --- /dev/null +++ b/docs/quick-start.mdx @@ -0,0 +1,35 @@ +So you want to start using AutoModpack? Great! Let's get you started. + +## Installation + +1. **Download AutoModpack**: Get the latest version of AutoModpack for your minecraft version from [Modrinth](https://modrinth.com/mod/automodpack) or [CurseForge](https://www.curseforge.com/minecraft/mc-mods/automodpack). +2. **Install AutoModpack**: Place the downloaded mod into your `mods` folder of your minecraft instance. +3. **Start your server**: Launch your minecraft server with AutoModpack installed. This will generate the initial modpack metadata. + +Now you are in most cases ready to go, at this point, its recommended to try connecting to your server with just the automodpack installed on the client and see if everything works. + +## First Connection +On first connection you will be prompted to verify the server's certificate fingerprint. This is a security measure to ensure that you are connecting to the correct server and not a malicious one. Read more about [certificate verification](technicals/certificate). +After successful verification, AutoModpack will asks you to confirm the modpack installation. Confirm and modpack should be downloaded and installed automatically. + +Now restart your game and your servers' modpack should loaded and you should be able to connect to your server again but this time spawning in the overworld! + + +If you don't see your modpack mods in the mods folder, don't worry. Mods won't be installed to your standard mods folder, they will be installed in the `~/.minecraft/automodpack/modapcks//mods/` folder instead. There are some [good reasons for that](technicals/file-structure). + + +## Troubleshooting +If you encounter any issues at this point, here are some common troubleshooting steps: +- Make sure you are using the same version of AutoModpack on both server and client. +- Ensure that your server is running and accessible without AutoModpack. +- Check the server logs there might be some nudge about what went wrong. +- Read the [faq page](faq) for common questions and answers. + +If you are still having issues, join our [Discord server](https://discord.gg/hS6aMyeA9P) and create a post in the `#support` channel, we will try to help! + +## Next Steps + +Now that you have AutoModpack installed and working, you can start customizing your modpack. Here are some things you can do next: + +- Read the [modpack creation page](technicals/modpack-creation) to learn how to manage your modpack. +- Read the [configuration page](configuration/server-config) to learn how to configure AutoModpack to your needs. diff --git a/docs/technicals/_meta.json b/docs/technicals/_meta.json index 3a05eb07b..8d68a11b8 100644 --- a/docs/technicals/_meta.json +++ b/docs/technicals/_meta.json @@ -1,21 +1,21 @@ { "connection.mdx": { - "name": "Connection Flow", + "name": "Connection", "icon": null }, "certificate.mdx": { "name": "Certificate Handling", "icon": null }, - "modpack_creation.mdx": { + "modpack-creation.mdx": { "name": "Modpack Creation", "icon": null }, - "file_structure.mdx": { + "file-structure.mdx": { "name": "File Structure", "icon": null }, - "modpack_host.mdx": { + "modpack-hosting.mdx": { "name": "Modpack Hosting", "icon": null } diff --git a/docs/technicals/certificate.mdx b/docs/technicals/certificate.mdx index 6158e1fc5..9de8e72b7 100644 --- a/docs/technicals/certificate.mdx +++ b/docs/technicals/certificate.mdx @@ -1,6 +1,39 @@ ### How to verify the certificate fingerprint? -Just copy the fingerprint from your server console to the game. This prevents attacks such as [MITM](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). Its recommended to share this fingerprint with players ahead of time. +Just copy the fingerprint from your server console to the game. This prevents attacks such as [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). Fingerprint is the same for everyone on your server, its recommended to share it with players ahead of time. -However please do not think of it like a password to the modpack, its not a password or any secret, its a public verifiable piece of server certificate, anyone can download modpack without previous knowledge of such fingerprint e.g. by bypassing this check with `I AM INCREDIBLY STUPID`. (don't do it) +However please do not think of it like a password to the modpack, its not a password or any secret, its a public verifiable piece of server certificate, **anyone could download** your modpack without previous knowledge of such fingerprint e.g. by bypassing this check with `I AM INCREDIBLY STUPID`. (don't do it) + +To provide modpack for only authorized/whitelisted players, use `validateSecrets` option in the [server config](../configuration/server-config) (its enabled by default). + -If you don't want players to explicitly verify the certificate and you own a domain (required), you can provide your own CA signed certificate e.g. using [Certbot](https://eff-certbot.readthedocs.io/en/stable/install.html). On the server in the `~/automodpack/.private/` directory, replace the `cert.crt` (with the full chain certificate) and `key.pem` files ([key has to be in PKCS#8 (PEM) format](https://netty.io/wiki/sslcontextbuilder-and-private-key.html)) - here be cautious as interception of these files may result in impersonation of the server, [read more](https://security.stackexchange.com/a/16694). If you're hosting modpack on different sub/domain than your minecraft server, make sure that your certificate verifies both of the sub/domains. +If you don't want players to explicitly verify the certificate manually and you own a domain (required), you can provide your own CA signed certificate. + +### How to provide your own CA signed certificate? + +You can use tools like [Certbot](https://eff-certbot.readthedocs.io/en/stable/install.html). + +After obtaining the certificate, on the server in the `~/automodpack/.private/` directory, replace the `cert.crt` (with the full chain certificate) and `key.pem` files ([key has to be in PKCS#8 (PEM) format](https://netty.io/wiki/sslcontextbuilder-and-private-key.html)) - here be cautious as interception of these files may result in impersonation of the server, [read more](https://security.stackexchange.com/a/16694). If you're hosting modpack on different sub/domain than your minecraft server, make sure that your certificate verifies both of the sub/domains. + +### Little tutorial on how to obtain a certificate using Certbot + +Install Certbot on your pc or server, then run the example command and follow further certbot instructions to obtain a certificate for your domain: + +```bash +# certbot certonly --manual --preferred-challenges dns -d +``` + +Never copy-paste random commands from the internet, always read the documentation of the tool you're using! + + +After obtaining the certificate, the files you need are `fullchain.pem` (the full chain certificate, copy and rename to `cert.crt`) and `privkey.pem` (the private key, copy and rename to `key.pem`). + +### Wait but why do I need all this? Its just a modpack for my friends! + +If someone could easily take control over the automodpack connection, they could download on your (or your friends) computers anything they want. +Minecraft isn't sandboxed in any way and mods **can run any arbitrary code** so if someone could easily inject some malicious piece of code to any automodpack modpack no matter who the victim is, that would be a huge win for them anyway, they could steal passwords from your browser, accounts to various services or even delete your very important folder with funny cat memes, there are many possibilities. +That's why i believe it's important to have some security measures. + +### Hmm... ok but nobody knows about my server besides my friends, why should I care? + +Even if you think that nobody knows about your server, it may not be true. There are many ways how someone could find out about your server. +For example there are many minecraft server scanners which scan the entire internet for minecraft servers, and you could be a victim if your server would be found vulnerable and we wouldn't verify the certificate. \ No newline at end of file diff --git a/docs/technicals/connection.mdx b/docs/technicals/connection.mdx index e69de29bb..3ca995fb3 100644 --- a/docs/technicals/connection.mdx +++ b/docs/technicals/connection.mdx @@ -0,0 +1,35 @@ +### Encryption + +Modpack download connection is secured using TLS 1.3. + +This is the same security standard used by HTTPS, the protocol which you're using all the time to browse the web (even now!). + +TLS has method of verifying the server's identity using a certificate which is crucial to prevent attacks such as [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). + +That's why AutoModpack requires you to verify the server's certificate fingerprint when connecting to the server for the first time. Read [how to verify the certificate fingerprint](certificate) for more details. + +### Direct Downloads + +AutoModpack uses direct download links from Modrinth and CurseForge APIs to download most of your modpack files. +This means that the mod authors get credit for every download. + +However if file you are providing in the modpack is not available on Modrinth or CurseForge, AutoModpack will download it (also directly) but from your server. + +### Compression + +To speed up the download, AutoModpack compresses the modpack files using [Zstandard](https://facebook.github.io/zstd/) compression algorithm. +This algorithm is very fast and provides great compression ratio, which means that the modpack files are smaller and faster to download. +This is especially useful for larger modpacks or slower internet connections. +However this only applies to files downloaded from your server. + +### Authorization + + +Do not confuse this with [certificate fingerprint verification](certificate), which is used to verify the server's identity. + + +AutoModpack uses a simple authorization mechanism to prevent unauthorized access to your modpack. +When you connect to the server, in the background AutoModpack generates a random unique secret for each player. This secret is then send to the player and used to authorize the player to download the modpack files. +Every time you reconnect to the server, the old secret is invalidated and a new one is generated. +This prevents unauthorized access to your modpack files and ensures that only whitelisted and not banned players can download the modpack. +If you don't want to use this feature, you can disable it (`validateSecrets`) in the [server config](../configuration/server-config). diff --git a/docs/technicals/file-structure.mdx b/docs/technicals/file-structure.mdx new file mode 100644 index 000000000..a97bbfd79 --- /dev/null +++ b/docs/technicals/file-structure.mdx @@ -0,0 +1,21 @@ +All of the AutoModpack files are kept inside `~/automodpack/` folder. + +### Server +Modpack is generated from files inside `~/automodpack/host-modpack/main/` and/or `syncedFiles` (read [config](../configuration/server-config)) which translates to `~/.minecraft/` (root minecraft instance) folder on client. + +Modpack generation means creation of `~/automodpack/host-modpack/automodpack-content.json` which contains metadata of every single file of the modapck. You **should NOT** touch this file manually, use config instead. + +### Client +All of downloaded modpacks are kept inside `~/automodpack/modpacks/`. + +All of the selected modpack files are copied to `~/.minecraft/` besides **mods** (with some exceptions, due to mod loader limitations) + +If detected, AutoModpack also tries to get rid of most of if not all of duplicated mods from your standard mods folder in favor of AutoModpack's mods folder. + +It shouldn't cause any crashes, but if you notice that automodpack removes some dependency causing game to crash, please report it! + + +Mods are separated for reasons. Such approach mitigates common drawbacks of placing all modpack mods into the standard mods folder, like: +- **Mod conflict crashes**: It won't resolve them, but it atleast gives the server owner ability to remotely remove or fix conflicting mods without manual intervention on the client side. +- **Debugging**: Its easier to see what mods are part of the modpack and what are not. Since clients can add their own mods to the standard mods folder, it's easier to debug when something breaks or conflicts. +- **Reboots**: If all of the modpack mods were placed into the standard mods folder, it would require a reboot to update the modpack mods. Now it can be done seamlessly on boot without any restart! \ No newline at end of file diff --git a/docs/technicals/file_structure.mdx b/docs/technicals/file_structure.mdx deleted file mode 100644 index 3dc883894..000000000 --- a/docs/technicals/file_structure.mdx +++ /dev/null @@ -1,17 +0,0 @@ -All of the AutoModpack files are kept inside `~/automodpack/` folder. - -### Server -Modpack is generated from files inside `~/automodpack/host-modpack/main/` and/or `syncedFiles` (read [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration)) which translates to `~/.minecraft/` (root minecraft instance) folder on client. - -Modpack generation means creation of `~/automodpack/host-modpack/automodpack-content.json` which contains metadata of every single file of the modapck. You **should NOT** touch this file manually. - -### Client -All of downloaded modpacks are kept inside `~/automodpack/modpacks/`. - -All of the selected modpack files are copied to `~/.minecraft/` besides **mods*** (with some exceptions) - -Mods are not copied for a reason, it gives AutoModpack full control of what and when load into the loader. - -Also AutoModpack tries to delete most of duplicated mods from standard mods folder in favor of AutoModpack's one. - -Thanks to that modpack can be seamlessly updated on boot before loading any of modpack mods and just after update it can load whole up-to-date modpack and continue with loading process, otherwise it would require a restart. diff --git a/docs/technicals/modpack_creation.mdx b/docs/technicals/modpack-creation.mdx similarity index 64% rename from docs/technicals/modpack_creation.mdx rename to docs/technicals/modpack-creation.mdx index edfc7de5a..a95f58ae9 100644 --- a/docs/technicals/modpack_creation.mdx +++ b/docs/technicals/modpack-creation.mdx @@ -2,12 +2,16 @@ #### Two ways of adding content to the modpack. -- To re-use already existing files on server use `syncedFiles` read more in [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration#server-config-file). As an example with default config it creates modpack containing *every* mod (file ending with .jar) from server `~/mods/` folder. +- To re-use already existing files on server use `syncedFiles` read more in [config](../configuration/server-config). As an example with default config it creates modpack containing *every* mod (file ending with .jar) from server `~/mods/` folder. - To add more files to modpack use separate directory `~/automodpack/host-modpack/main/`. For example to add more mods create folder `mods` and place there mods so that will be `~/automodpack/host-modpack/main/mods/`**`your-mod.jar`**. Or to add resourcepacks analogically create folder `resourcepacks` and place there resourcepacks so that will be `~/automodpack/host-modpack/main/resourcepacks/`**`your-resourcepack.zip`**. + +Do not place in the modpack cache files, like connector's `.connector` directory, its not supposed to be provided within modpack and it break seamless/reboot-less updates. + + Both of these methods translate to the `~/.minecraft/` on client side (root directory of minecraft instance). -Remember to regenerate the modpack using `/automodpack generate` (see [commands](https://github.com/Skidamek/AutoModpack/wiki/Commands)) \ No newline at end of file +Remember to regenerate the modpack using `/automodpack generate` (see [commands](../commands/commands)) \ No newline at end of file diff --git a/docs/technicals/modpack-hosting.mdx b/docs/technicals/modpack-hosting.mdx new file mode 100644 index 000000000..24a2916c1 --- /dev/null +++ b/docs/technicals/modpack-hosting.mdx @@ -0,0 +1,3 @@ +By default AutoModpack hosts your modpack on your minecraft server port (e.g. 25565). This means that you don't need to open any other port on your router/firewall. +If you want to use different port, disable `hostModpackOnMinecraftPort` and change `hostPort` in [config](../configuration/server-config). + diff --git a/docs/technicals/modpack_host.mdx b/docs/technicals/modpack_host.mdx deleted file mode 100644 index e69de29bb..000000000 diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 3cbc12acc..4030f09b1 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -7,6 +7,13 @@ import java.util.zip.ZipOutputStream plugins { id("dev.kikugie.stonecutter") + id("org.moddedmc.wiki.toolkit") version "0.2.7" +} + +wiki { + docs.create("automodpack") { + root = file("docs") + } } stonecutter active "1.21.1-neoforge" /* [SC] DO NOT EDIT */ From ec8ce8038b90f6d1062ae36753f961618526a9d1 Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 24 May 2025 22:43:11 +0200 Subject: [PATCH 07/86] Fix quick start link --- docs/_homepage.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_homepage.mdx b/docs/_homepage.mdx index 80abc0baf..8a22c6327 100644 --- a/docs/_homepage.mdx +++ b/docs/_homepage.mdx @@ -1,6 +1,6 @@ Welcome to the AutoModpack documentation! -If you are new to AutoModpack, we recommend starting with the [Quick Start Guide](quick-start/quick-start) to get familiar with the basics of using AutoModpack. +If you are new to AutoModpack, we recommend starting with the [Quick Start Guide](quick-start) to get familiar with the basics of using AutoModpack. If you are looking for more detailed information, you can find it in the `👨‍💻 Technical Details` section. From e33c0470719be2898222a3d64f05f236e3487d8f Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 24 May 2025 22:43:11 +0200 Subject: [PATCH 08/86] Fix quick start link --- docs/_homepage.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_homepage.mdx b/docs/_homepage.mdx index 8a22c6327..ff5c66a95 100644 --- a/docs/_homepage.mdx +++ b/docs/_homepage.mdx @@ -1,6 +1,6 @@ Welcome to the AutoModpack documentation! -If you are new to AutoModpack, we recommend starting with the [Quick Start Guide](quick-start) to get familiar with the basics of using AutoModpack. +If you are new to AutoModpack, we recommend starting with the [Quick Start Guide](docs/quick-start) to get familiar with the basics of using AutoModpack. If you are looking for more detailed information, you can find it in the `👨‍💻 Technical Details` section. From 747789f12edc0c5f6816adebb819db48a896654c Mon Sep 17 00:00:00 2001 From: skidam Date: Sun, 25 May 2025 09:40:57 +0200 Subject: [PATCH 09/86] Append host port when configured. --- .../automodpack/networking/packet/DataC2SPacket.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java index 7501ea5b0..6f8314d43 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java @@ -56,13 +56,16 @@ public static CompletableFuture receive(MinecraftClient minecraft modpackAddress = AddressHelpers.format(modpackAddress.getHostString(), modpackAddress.getPort()); if (packetAddress.isBlank()) { - LOGGER.info("Address from connected server: {}:{}", modpackAddress.getHostString(), modpackAddress.getPort()); + if (packetPort != null) { // Server may just send port without address + modpackAddress = InetSocketAddress.createUnresolved(modpackAddress.getHostString(), packetPort); + } + LOGGER.info("Modpack address from connected server: {}:{}", modpackAddress.getHostString(), modpackAddress.getPort()); } else if (packetPort != null) { modpackAddress = InetSocketAddress.createUnresolved(packetAddress, packetPort); - LOGGER.info("Received address packet from server! {}:{}", packetAddress, packetPort); + LOGGER.info("Received modpack address packet {}:{}", packetAddress, packetPort); } else { modpackAddress = AddressHelpers.parse(packetAddress); - LOGGER.info("Received address packet from server! {} With attached port: {}", modpackAddress.getHostString(), modpackAddress.getPort()); + LOGGER.info("Received modpack address packet {} With attached port: {}", modpackAddress.getHostString(), modpackAddress.getPort()); } Boolean needsDisconnecting = null; From fcbf8bc43efae1bccfa87967ca219722f60ff777 Mon Sep 17 00:00:00 2001 From: skidam Date: Sun, 25 May 2025 09:43:00 +0200 Subject: [PATCH 10/86] Improve `updateIpsOnEveryStart` setting --- .../protocol/netty/NettyServer.java | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java index d6939d4b0..263df1974 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java @@ -200,35 +200,37 @@ private boolean canStart() { return false; } - if (serverConfig.hostModpackOnMinecraftPort) { - shouldHost = true; - LOGGER.info("Hosting modpack on Minecraft port"); - return false; - } - - if (serverConfig.updateIpsOnEveryStart || (serverConfig.hostIp == null || serverConfig.hostIp.isEmpty())) { + if (serverConfig.updateIpsOnEveryStart) { String publicIp = AddressHelpers.getPublicIp(); + String localIp = AddressHelpers.getLocalIp(); if (publicIp != null) { serverConfig.hostIp = publicIp; - ConfigTools.save(serverConfigFile, serverConfig); LOGGER.warn("Setting Host IP to {}", serverConfig.hostIp); } else { - LOGGER.error("Host IP isn't set in config, please change it manually! Couldn't get public IP"); - return false; + LOGGER.error("Couldn't get public IP, please change it manually! "); + } + + if (localIp != null) { + serverConfig.hostLocalIp = localIp; + LOGGER.warn("Setting Host Local IP to {}", serverConfig.hostLocalIp); + } else { + LOGGER.error("Couldn't get local IP, please change it manually! "); } - } - if (serverConfig.updateIpsOnEveryStart || (serverConfig.hostLocalIp == null || serverConfig.hostLocalIp.isEmpty())) { try { - serverConfig.hostLocalIp = AddressHelpers.getLocalIp(); ConfigTools.save(serverConfigFile, serverConfig); - LOGGER.warn("Setting Host local IP to {}", serverConfig.hostLocalIp); } catch (Exception e) { e.printStackTrace(); } } - shouldHost = true; - return true; + shouldHost = true; // At this point we know that we want to host the modpack + + if (serverConfig.hostModpackOnMinecraftPort) { + LOGGER.info("Hosting modpack on Minecraft port"); + return false; // Dont start separate server for modpack hosting, use minecraft port instead + } else { + return true; // Start separate server for modpack hosting + } } } From b5a4f98437502aa4ca26e2c017e4e245a82e65d3 Mon Sep 17 00:00:00 2001 From: skidam Date: Sun, 25 May 2025 10:00:03 +0200 Subject: [PATCH 11/86] Update documentation for clarity and consistency, add compatibility page --- docs/_meta.json | 4 ++++ docs/compatibility/_meta.json | 14 ++++++++++++++ docs/compatibility/launchers.mdx | 15 +++++++++++++++ docs/compatibility/mods.mdx | 4 ++++ docs/compatibility/proxies.mdx | 12 ++++++++++++ docs/configuration/_meta.json | 12 ++++++++++-- docs/configuration/client-config.mdx | 12 ++++++------ docs/faq.mdx | 11 +++++++---- docs/quick-start.mdx | 2 +- docs/technicals/certificate.mdx | 10 ++++++---- docs/technicals/file-structure.mdx | 2 +- 11 files changed, 80 insertions(+), 18 deletions(-) create mode 100644 docs/compatibility/_meta.json create mode 100644 docs/compatibility/launchers.mdx create mode 100644 docs/compatibility/mods.mdx create mode 100644 docs/compatibility/proxies.mdx diff --git a/docs/_meta.json b/docs/_meta.json index 9dc308a94..d9751bf7f 100644 --- a/docs/_meta.json +++ b/docs/_meta.json @@ -23,6 +23,10 @@ "name": "\uD83D\uDC68\u200D\uD83D\uDCBB Technical Details", "icon": null }, + "compatibility": { + "name": "\uD83D\uDCDA Compatibility", + "icon": null + }, "faq.mdx": { "name": "❔ FAQ", "icon": null diff --git a/docs/compatibility/_meta.json b/docs/compatibility/_meta.json new file mode 100644 index 000000000..d3e204510 --- /dev/null +++ b/docs/compatibility/_meta.json @@ -0,0 +1,14 @@ +{ + "launchers.mdx": { + "name": "Launchers compatibility", + "icon": null + }, + "mods.mdx": { + "name": "Modifications compatibility", + "icon": null + }, + "proxies.mdx": { + "name": "Proxies compatibility", + "icon": null + } +} diff --git a/docs/compatibility/launchers.mdx b/docs/compatibility/launchers.mdx new file mode 100644 index 000000000..33cea4460 --- /dev/null +++ b/docs/compatibility/launchers.mdx @@ -0,0 +1,15 @@ +Most of the launchers will work without any issues, but some modified custom clients won't work. Here is a list of known launchers and their compatibility with AutoModpack: + +| Launcher Name | Compatibility | Notes | +|---------------------------|---------------|----------------------------------------------------------------------------------------------------------------| +| Vanilla official Launcher | ✅ | Supported | +| Prism/MultiMC Launcher | ✅ | Prism is recommended - Perfect support, with additional features like modloader version synchronization | +| Modrinth Launcher | ✅ | Supported | +| CurseForge | ✅ | Supported | +| GDLauncher | ✅ | Supported | +| ATLauncher | ✅ | Supported | +| PojavLauncher (Android) | ⚠️ | Currently broken, worked on previous versions, [read more](https://github.com/Skidamek/AutoModpack/issues/379) | +| FCL (Android) | ⚠️ | Currently broken, worked on previous versions, [read more](https://github.com/Skidamek/AutoModpack/issues/379) | +| Feather Client | ❌ | Unsupported - breaks modpack loading | +| Lunar Client | ❌ | Unsupported - breaks modpack loading | +| PCL2 | ❌ | Unsupported - breaks modpack loading | diff --git a/docs/compatibility/mods.mdx b/docs/compatibility/mods.mdx new file mode 100644 index 000000000..5ecb23d66 --- /dev/null +++ b/docs/compatibility/mods.mdx @@ -0,0 +1,4 @@ +AutoModpack should work with all minecraft modifications (it would be a bit bad modpack updater if it didn't). However, there are some exceptions and limitations that you should be aware of: + +- Majrusz's Mods: Requires modified Majrusz Library to work with AutoModpack. Read more about it [here](https://github.com/Skidamek/AutoModpack/issues/268). +- [ModSets](https://modrinth.com/mod/mod-sets): It's currently broken due to similarities in mod loading modification process. diff --git a/docs/compatibility/proxies.mdx b/docs/compatibility/proxies.mdx new file mode 100644 index 000000000..0c3efa2db --- /dev/null +++ b/docs/compatibility/proxies.mdx @@ -0,0 +1,12 @@ +Most minecraft proxies won't work with AutoModpack due to early login stage packets. However Gate Lite is supported and works well with AutoModpack. Here is a list of known proxies and their compatibility: + +| Proxy Name | Compatibility | Notes | +|---------------------------------------------------|---------------|-------------| +| [Gate Lite](https://gate.minekube.com/guide/lite) | ✅ | Supported | +| Gate | ❌ | Unsupported | +| Velocity | ❌ | Unsupported | +| Waterfall | ❌ | Unsupported | +| BungeeCord | ❌ | Unsupported | + +If you use Proxy with AutoModpack, you need to set `hostPort` in the [config](../configuration/server-config) to the port that your modpack is hosted on. +If you host modpack on the minecraft port (`hostModpackOnMinecraftPort` is `true`), you need to set `hostPort` to the port that your minecraft server is running on. \ No newline at end of file diff --git a/docs/configuration/_meta.json b/docs/configuration/_meta.json index 4d359cb71..aed4f3058 100644 --- a/docs/configuration/_meta.json +++ b/docs/configuration/_meta.json @@ -1,6 +1,14 @@ { - "configuration.mdx": { - "name": "Configuration", + "server-config.mdx": { + "name": "Server Configuration", + "icon": null + }, + "client-config.mdx": { + "name": "Client Configuration", + "icon": null + }, + "troubleshooting.mdx": { + "name": "Troubleshooting", "icon": null } } diff --git a/docs/configuration/client-config.mdx b/docs/configuration/client-config.mdx index 912446994..ce86efb50 100644 --- a/docs/configuration/client-config.mdx +++ b/docs/configuration/client-config.mdx @@ -2,9 +2,9 @@ `/automodpack/automodpack-client.json` -| Name | Default Value | Description | -|---------------------|------------------|-----------------------------------------------------------------------------------------------------------------------| -| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | -| `selectedModpack` | | The main folder of the modpack that you want to play. Typing the name of the modpack here will cause it to be loaded. | -| `installedModpacks` | | A list of modpacks that are installed on the client. | -| `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | +| Name | Default Value | Description | +|---------------------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | +| `selectedModpack` | | The main folder of the modpack that you want to play. Typing the name of the modpack here will cause it to be loaded. | +| `installedModpacks` | | A list of modpacks that are installed on the client. | +| `selfUpdater` | `false` | Turn on/off automodpack self-updates. This does not affect the mod's activity in installing modpacks. This does not disable AutoModpack version syncing with server | diff --git a/docs/faq.mdx b/docs/faq.mdx index 5035ecc26..48e7f5dae 100644 --- a/docs/faq.mdx +++ b/docs/faq.mdx @@ -2,7 +2,7 @@ No, it's both. You need to have it installed on server as well as on client. ### How do i add client only mods to the modpack? (e.g. server crashes with some mod but i want it for clients) -See [modpack creation page](https://github.com/Skidamek/AutoModpack/wiki/Modpack-Creation) +Take a look at [modpack creation page](techinicals/modpack-creation). ### How to restrict access to my modpack? Enable `validateSecrets` in the [config](configuration/server-config), turn on `online-mode` in the server.properties and enable whitelist or ban someone. @@ -17,7 +17,7 @@ Likely no, most of these type of mods forward only minecraft packets. These mods Yes, you can add any mod you want to use alongside downloaded modpack by just placing the mod into the standard mods folder. ### How can I delete files from modpack? -Just delete file from server, or exclude it from sync, read about `syncedFiles` in [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration#server-config-file) +Just delete file from server, or exclude it from sync, read about `syncedFiles` in [config](configuration/server-config) ### Can I remove/disable non modpack files/mods from player use? Not yet, it may come in the future. @@ -25,7 +25,7 @@ Not yet, it may come in the future. ### Do I have to open any port on my router? No, you don't need to. By default AutoModpack injects into minecraft networking I/O which reuses your minecraft server port. (e.g. 25565) -If you want to use different port. Disable `hostModpackOnMinecraftPort` and change `hostPort` in [config](https://github.com/Skidamek/AutoModpack/wiki/Configuration#server-config-file) +If you want to use different port. Disable `hostModpackOnMinecraftPort` and change `hostPort` in [config](configuration/server-config). ### What happens when I update AutoModpack version on server? Client always try to sync to the server's version of AutoModpack. It updates or downgrades itself to the server's version if such is found on the Modrinth. @@ -36,11 +36,14 @@ There are plans to implement ability of adding more than just one, `main` modpac ### Which minecraft versions are supported? All of the versions you see in the [latest release](https://github.com/Skidamek/AutoModpack/releases/latest). +### Is my launcher supported? +Take a look at the [compatibility page](compatibility/launchers) for more information about supported launchers. + ### Does this mod updates my modpack mods to the latest versions? No, it's out of scope for this project. This mod only synchronizes server modpack to the clients. All your mods on the server would have to still be updated manually. AutoModpack just makes sure client versions matches the server ones. ### I found a bug, what should I do? -If you found a bug, please report it on the [issue tracker](https://github.com/Skidamek/AutoModpack/issues) +If you found a bug, please report it on the [issue tracker](https://github.com/Skidamek/AutoModpack/issues). ### I found a security vulnerability, what should I do? Please report it privately to the author on [Discord](https://discordapp.com/users/464522287618457631), we will try to resolve it ASAP. \ No newline at end of file diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx index 3c922264b..970f6bfc6 100644 --- a/docs/quick-start.mdx +++ b/docs/quick-start.mdx @@ -15,7 +15,7 @@ After successful verification, AutoModpack will asks you to confirm the modpack Now restart your game and your servers' modpack should loaded and you should be able to connect to your server again but this time spawning in the overworld! -If you don't see your modpack mods in the mods folder, don't worry. Mods won't be installed to your standard mods folder, they will be installed in the `~/.minecraft/automodpack/modapcks//mods/` folder instead. There are some [good reasons for that](technicals/file-structure). +If you don't see your modpack mods in the mods folder, don't worry. Mods won't be installed to your standard mods folder, they will be installed in the `~/.minecraft/automodpack/modapcks/{your-modpack}/mods/` folder instead. There are some [good reasons for that](technicals/file-structure). ## Troubleshooting diff --git a/docs/technicals/certificate.mdx b/docs/technicals/certificate.mdx index 9de8e72b7..3585b1047 100644 --- a/docs/technicals/certificate.mdx +++ b/docs/technicals/certificate.mdx @@ -1,5 +1,5 @@ ### How to verify the certificate fingerprint? -Just copy the fingerprint from your server console to the game. This prevents attacks such as [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). Fingerprint is the same for everyone on your server, its recommended to share it with players ahead of time. +**Just copy the fingerprint from your server console to the game.** This prevents attacks such as [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). Fingerprint is the same for everyone on your server, its recommended to share it with players ahead of time. However please do not think of it like a password to the modpack, its not a password or any secret, its a public verifiable piece of server certificate, **anyone could download** your modpack without previous knowledge of such fingerprint e.g. by bypassing this check with `I AM INCREDIBLY STUPID`. (don't do it) @@ -12,14 +12,15 @@ If you don't want players to explicitly verify the certificate manually and you You can use tools like [Certbot](https://eff-certbot.readthedocs.io/en/stable/install.html). -After obtaining the certificate, on the server in the `~/automodpack/.private/` directory, replace the `cert.crt` (with the full chain certificate) and `key.pem` files ([key has to be in PKCS#8 (PEM) format](https://netty.io/wiki/sslcontextbuilder-and-private-key.html)) - here be cautious as interception of these files may result in impersonation of the server, [read more](https://security.stackexchange.com/a/16694). If you're hosting modpack on different sub/domain than your minecraft server, make sure that your certificate verifies both of the sub/domains. +After obtaining the certificate, on the server in the `~/automodpack/.private/` directory, replace the `cert.crt` (with the full chain certificate) and `key.pem` files ([key has to be in PKCS#8 (PEM) format](https://netty.io/wiki/sslcontextbuilder-and-private-key.html)) - here be cautious as interception of these files may result in impersonation of the server, [read more](https://security.stackexchange.com/a/16694). +If you're hosting modpack on different sub/domain than your minecraft server, make sure that your certificate verifies both of the sub/domains. ### Little tutorial on how to obtain a certificate using Certbot Install Certbot on your pc or server, then run the example command and follow further certbot instructions to obtain a certificate for your domain: ```bash -# certbot certonly --manual --preferred-challenges dns -d +# certbot certonly --manual --preferred-challenges dns -d ``` Never copy-paste random commands from the internet, always read the documentation of the tool you're using! @@ -36,4 +37,5 @@ That's why i believe it's important to have some security measures. ### Hmm... ok but nobody knows about my server besides my friends, why should I care? Even if you think that nobody knows about your server, it may not be true. There are many ways how someone could find out about your server. -For example there are many minecraft server scanners which scan the entire internet for minecraft servers, and you could be a victim if your server would be found vulnerable and we wouldn't verify the certificate. \ No newline at end of file +For example there are many minecraft server scanners which scan the entire internet for minecraft servers, and you could be a victim if your server would be found vulnerable and we wouldn't verify the certificate. +However nothing we do here stops someone from taking control over your server in different ways, (e.g. gaining access to it via ssh or your server hosting panel) that's why it's very important that you as a server owner take your security seriously and use strong passwords, enable two-factor authentication and keep your server software up to date. diff --git a/docs/technicals/file-structure.mdx b/docs/technicals/file-structure.mdx index a97bbfd79..d22282b7e 100644 --- a/docs/technicals/file-structure.mdx +++ b/docs/technicals/file-structure.mdx @@ -3,7 +3,7 @@ All of the AutoModpack files are kept inside `~/automodpack/` folder. ### Server Modpack is generated from files inside `~/automodpack/host-modpack/main/` and/or `syncedFiles` (read [config](../configuration/server-config)) which translates to `~/.minecraft/` (root minecraft instance) folder on client. -Modpack generation means creation of `~/automodpack/host-modpack/automodpack-content.json` which contains metadata of every single file of the modapck. You **should NOT** touch this file manually, use config instead. +Modpack generation means creation of `~/automodpack/host-modpack/automodpack-content.json` which contains metadata of every single file of the modapck. You **should NOT** touch this file manually, edit config instead. ### Client All of downloaded modpacks are kept inside `~/automodpack/modpacks/`. From 4076a18244e105d4c14882582b1715c9d3a2f45a Mon Sep 17 00:00:00 2001 From: skidam Date: Sun, 25 May 2025 10:11:44 +0200 Subject: [PATCH 12/86] Clarify README instructions and update documentation links --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c53d83995..ed2c14c85 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,10 @@ AutoModpack works by generating a modpack (**metadata file**) on the server, whi When a client connects to the server: -1. Connection; AutoModpack establishes a secure connection and prompts you to [verify the server's certificate fingerprint](https://moddedmc.wiki/en/project/automodpack/docs/technicals/certificate). -2. Direct links; Fetches the APIs for direct downloads of your modpack's files from Modrinth and CurseForge, where possible (mods, resource packs, shaders). -3. Modpack download; All files are downloaded to the client's automodpack folder. -4. Game restart; AutoModpack loads the modpack, and the client is perfectly synced and ready to play! +1. Connection: AutoModpack establishes a secure connection and prompts you to [verify the server's certificate fingerprint](https://moddedmc.wiki/en/project/automodpack/docs/technicals/certificate). +2. Direct links: Fetches the APIs for direct downloads of your modpack's files from Modrinth and CurseForge, where possible (mods, resource packs, shaders). +3. Modpack download: All files are downloaded to the client's automodpack folder. +4. Game restart: AutoModpack loads the modpack, and the client is perfectly synced and ready to play! On subsequent game launches, AutoModpack checks for updates. If changes are detected, it updates the modpack in the background—no additional restarts are required! @@ -55,7 +55,7 @@ On subsequent game launches, AutoModpack checks for updates. If changes are dete > With great power comes great responsibility. -Because it downloads files directly into your game folder, it's crucial to **only use it on servers you absolutely trust**. A malicious server (administrator) *can* include harmful files. +Be aware that this mod allows remote servers to download *arbitrary executable* files directly into your game folder. It's crucial to **only use it on servers you absolutely trust**. A malicious server (administrator) *can* include malicious/harmful files. While AutoModpack itself tries to be as secure as possible, due to the nature of the internet, the creators and contributors of AutoModpack are not responsible for any harm, damage, loss, or issues that may result from files downloaded from a server you connect to using the mod. **By using AutoModpack, you acknowledge and accept this risk.** @@ -71,7 +71,7 @@ Installing AutoModpack is as simple as installing any other mod. That's typically all you need to do! AutoModpack will automatically create the modpack from your server's mods. -**Want to customize your modpack further?** Add configs, client-side-only mods, and more? **Check out the [wiki](https://moddedmc.wiki/en/project/automodpack/docs)!** It *hopefully* has all the details you need to tailor AutoModpack to your specific needs. If you encounter any issues or have questions, feel free to join [Discord server](https://discord.gg/hS6aMyeA9P) or open an issue on [GitHub](https://github.com/Skidamek/AutoModpack/issues). +**Want to customize your modpack further?** Add configs, client-side-only mods, and more? **Check out the [documentation](https://moddedmc.wiki/en/project/automodpack/docs)!** There's also a start guide covering more stuff. If you encounter any issues or have questions, feel free to join [Discord server](https://discord.gg/hS6aMyeA9P) or open an issue on [GitHub](https://github.com/Skidamek/AutoModpack/issues). Prefer an all-in-one solution? You can also use our [modified Fabric installer](https://github.com/Skidamek/AutoModpack-Installer/releases/tag/Latest) which downloads AutoModpack alongside the Fabric loader. From 1dae48c8ede9620304ad493823babd11533e67c0 Mon Sep 17 00:00:00 2001 From: Skidam <67871298+Skidamek@users.noreply.github.com> Date: Mon, 26 May 2025 07:12:01 +0200 Subject: [PATCH 13/86] Update quick-start.mdx --- docs/quick-start.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx index 970f6bfc6..f1f490499 100644 --- a/docs/quick-start.mdx +++ b/docs/quick-start.mdx @@ -12,7 +12,7 @@ Now you are in most cases ready to go, at this point, its recommended to try con On first connection you will be prompted to verify the server's certificate fingerprint. This is a security measure to ensure that you are connecting to the correct server and not a malicious one. Read more about [certificate verification](technicals/certificate). After successful verification, AutoModpack will asks you to confirm the modpack installation. Confirm and modpack should be downloaded and installed automatically. -Now restart your game and your servers' modpack should loaded and you should be able to connect to your server again but this time spawning in the overworld! +Now restart your game and your servers' modpack should be loaded and you should be able to connect to your server again but this time spawning in the overworld! If you don't see your modpack mods in the mods folder, don't worry. Mods won't be installed to your standard mods folder, they will be installed in the `~/.minecraft/automodpack/modapcks/{your-modpack}/mods/` folder instead. There are some [good reasons for that](technicals/file-structure). From ed7cd024a50fd91131660c338037aba64409c3b3 Mon Sep 17 00:00:00 2001 From: Skidam <67871298+Skidamek@users.noreply.github.com> Date: Mon, 26 May 2025 08:25:11 +0200 Subject: [PATCH 14/86] Update certificate.mdx --- docs/technicals/certificate.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/technicals/certificate.mdx b/docs/technicals/certificate.mdx index 3585b1047..bccc77909 100644 --- a/docs/technicals/certificate.mdx +++ b/docs/technicals/certificate.mdx @@ -20,8 +20,9 @@ If you're hosting modpack on different sub/domain than your minecraft server, ma Install Certbot on your pc or server, then run the example command and follow further certbot instructions to obtain a certificate for your domain: ```bash -# certbot certonly --manual --preferred-challenges dns -d +# certbot certonly --manual --preferred-challenges dns -d -d ``` +> If you're hosting modpack on the same port as your minecraft server, in most cases these domains will match, if so you don't need to provide the second domain Never copy-paste random commands from the internet, always read the documentation of the tool you're using! From b5045011f0cae107ec41ec8958d386cfdfb84419 Mon Sep 17 00:00:00 2001 From: Skidam <67871298+Skidamek@users.noreply.github.com> Date: Mon, 26 May 2025 11:26:47 +0200 Subject: [PATCH 15/86] Update certificate.mdx --- docs/technicals/certificate.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/technicals/certificate.mdx b/docs/technicals/certificate.mdx index bccc77909..0ac20f1c7 100644 --- a/docs/technicals/certificate.mdx +++ b/docs/technicals/certificate.mdx @@ -22,7 +22,8 @@ Install Certbot on your pc or server, then run the example command and follow fu ```bash # certbot certonly --manual --preferred-challenges dns -d -d ``` -> If you're hosting modpack on the same port as your minecraft server, in most cases these domains will match, if so you don't need to provide the second domain +If you're hosting modpack on the same port as your minecraft server, in most cases these domains will match, if so you don't need to provide the second domain. + Never copy-paste random commands from the internet, always read the documentation of the tool you're using! From 026f5573fbf174c9a9a334fae422a47f8b4181ed Mon Sep 17 00:00:00 2001 From: skidam Date: Mon, 26 May 2025 18:44:45 +0200 Subject: [PATCH 16/86] Lowercase address by default ref: #382 --- .../java/pl/skidam/automodpack_core/utils/AddressHelpers.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java b/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java index f135ba31f..bc5a75f05 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java +++ b/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java @@ -91,6 +91,7 @@ public static InetSocketAddress format(String host, int port) { if (host.endsWith(".")) { // It breaks our checks and looks ugly, but its a valid domain... host = host.substring(0, host.length() - 1); } + host = host.toLowerCase(); // #382 return InetSocketAddress.createUnresolved(host, port); } From 9945d76184dfba26e4e706327097bec0911717e1 Mon Sep 17 00:00:00 2001 From: Skidam <67871298+Skidamek@users.noreply.github.com> Date: Mon, 26 May 2025 18:58:41 +0200 Subject: [PATCH 17/86] Update bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c44455092..755175316 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -44,7 +44,6 @@ body: Please send the corresponding Minecraft log from the `logs` folder here. Please upload the log file as an attachment, or upload the log to [mclo.gs](https://mclo.gs/) and paste the url here - mclo.gs sanitizes sensitive information such as ip addresses. Please refrain from pasting the entire log file directly. - Leave empty if there is none. placeholder: https://mclo.gs/ - type: input id: versions @@ -93,5 +92,5 @@ body: required: true - label: I have provided all the necessary information to reproduce the issue. required: true - - label: I have verified that the issue does not occur without the AutoModpack - Ignore you are reporting a mod conflict issue. + - label: I have verified that the issue does not occur without the AutoModpack - Ignore if you are reporting a mod conflict issue. required: false From 4eeeaeed92ad5c4d23deae1b23e043219c6c7fb0 Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 3 Jun 2025 10:55:42 +0200 Subject: [PATCH 18/86] Update CI configuration to use windows-latest for testing --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e822e1f87..05b235732 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,7 @@ on: [workflow_call] jobs: tests: - runs-on: windows-2019 + runs-on: windows-latest steps: - uses: actions/checkout@v4 - name: Set up JDK 21 From aaeef18abdb8857b38afea91784d04a489154780 Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 3 Jun 2025 12:29:29 +0200 Subject: [PATCH 19/86] Add clickable certificate fingerprint to modpack hosting status --- .../pl/skidam/automodpack/modpack/Commands.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/pl/skidam/automodpack/modpack/Commands.java b/src/main/java/pl/skidam/automodpack/modpack/Commands.java index 2e255c12b..148cb0a95 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/Commands.java +++ b/src/main/java/pl/skidam/automodpack/modpack/Commands.java @@ -5,6 +5,9 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.context.CommandContext; import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.ClickEvent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.util.Util; import pl.skidam.automodpack.client.ui.versioned.VersionedCommandSource; @@ -154,11 +157,23 @@ private static int restartModpackHost(CommandContext contex return Command.SINGLE_SUCCESS; } - private static int modpackHostAbout(CommandContext context) { Formatting statusColor = hostServer.isRunning() ? Formatting.GREEN : Formatting.RED; String status = hostServer.isRunning() ? "running" : "not running"; send(context, "Modpack hosting status", Formatting.GREEN, status, statusColor, false); + String fingerprint = hostServer.getCertificateFingerprint(); + Text fingerprintText = VersionedText.literal(fingerprint) + .formatted(Formatting.YELLOW).styled(style -> style + /*? if >1.21.4 {*/ + /*.withClickEvent(new ClickEvent.CopyToClipboard(fingerprint))); + *//*?} else {*/ + .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, fingerprint))); + /*?}*/ + VersionedCommandSource.sendFeedback(context, + VersionedText.literal("Certificate fingerprint: ") + .formatted(Formatting.WHITE) + .append(fingerprintText), false); + return Command.SINGLE_SUCCESS; } From f2e9ec1d332883359cf749deeb11d5af776a2bd2 Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 3 Jun 2025 12:29:29 +0200 Subject: [PATCH 20/86] Add clickable certificate fingerprint to modpack hosting status --- src/main/java/pl/skidam/automodpack/modpack/Commands.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/pl/skidam/automodpack/modpack/Commands.java b/src/main/java/pl/skidam/automodpack/modpack/Commands.java index 148cb0a95..e2f681630 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/Commands.java +++ b/src/main/java/pl/skidam/automodpack/modpack/Commands.java @@ -6,7 +6,6 @@ import com.mojang.brigadier.context.CommandContext; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.text.ClickEvent; -import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.util.Util; From 1b60c80476086a0073e7f2c791f4b5e1a7737c93 Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 3 Jun 2025 19:42:30 +0200 Subject: [PATCH 21/86] Enhance certificate fingerprint display with hover text and improved click event --- .../protocol/netty/NettyServer.java | 4 ++- .../skidam/automodpack/modpack/Commands.java | 36 ++++++++++++------- .../networking/packet/DataS2CPacket.java | 2 +- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java index 263df1974..03f6a2590 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java @@ -107,7 +107,9 @@ public Optional start() { // generate sha256 from cert as a fingerprint certificateFingerprint = NetUtils.getFingerprint(cert); - LOGGER.warn("Certificate fingerprint for client validation: {}", certificateFingerprint); + if (certificateFingerprint != null) { + LOGGER.warn("Certificate fingerprint: {}", certificateFingerprint); + } if (!canStart()) { new TrafficShaper(null); diff --git a/src/main/java/pl/skidam/automodpack/modpack/Commands.java b/src/main/java/pl/skidam/automodpack/modpack/Commands.java index e2f681630..9cb5ee252 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/Commands.java +++ b/src/main/java/pl/skidam/automodpack/modpack/Commands.java @@ -6,7 +6,8 @@ import com.mojang.brigadier.context.CommandContext; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.text.ClickEvent; -import net.minecraft.text.Text; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.MutableText; import net.minecraft.util.Formatting; import net.minecraft.util.Util; import pl.skidam.automodpack.client.ui.versioned.VersionedCommandSource; @@ -161,17 +162,17 @@ private static int modpackHostAbout(CommandContext context) String status = hostServer.isRunning() ? "running" : "not running"; send(context, "Modpack hosting status", Formatting.GREEN, status, statusColor, false); String fingerprint = hostServer.getCertificateFingerprint(); - Text fingerprintText = VersionedText.literal(fingerprint) - .formatted(Formatting.YELLOW).styled(style -> style - /*? if >1.21.4 {*/ - /*.withClickEvent(new ClickEvent.CopyToClipboard(fingerprint))); - *//*?} else {*/ - .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, fingerprint))); - /*?}*/ - VersionedCommandSource.sendFeedback(context, - VersionedText.literal("Certificate fingerprint: ") - .formatted(Formatting.WHITE) - .append(fingerprintText), false); + if (fingerprint != null) { + MutableText fingerprintText = VersionedText.literal(fingerprint).styled(style -> style + /*? if >1.21.4 {*/ + /*.withHoverEvent(new HoverEvent.ShowText, VersionedText.translatable("chat.copy.click")) + /*.withClickEvent(new ClickEvent.CopyToClipboard(fingerprint))); + *//*?} else {*/ + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, VersionedText.translatable("chat.copy.click"))) + .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, fingerprint))); + /*?}*/ + send(context, "Certificate fingerprint", Formatting.WHITE, fingerprintText, Formatting.YELLOW, false); + } return Command.SINGLE_SUCCESS; } @@ -219,4 +220,15 @@ private static void send(CommandContext context, String msg .formatted(appendMsgColor)), broadcast); } + + private static void send(CommandContext context, String msg, Formatting msgColor, MutableText appendMsg, Formatting appendMsgColor, boolean broadcast) { + VersionedCommandSource.sendFeedback(context, + VersionedText.literal(msg) + .formatted(msgColor) + .append(VersionedText.literal(" - ") + .formatted(Formatting.WHITE)) + .append(appendMsg + .formatted(appendMsgColor)), + broadcast); + } } diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java index e82bd0c44..ed5dc3320 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java @@ -32,7 +32,7 @@ public static void receive(MinecraftServer server, ServerLoginNetworkHandler han String clientHasUpdate = buf.readString(Short.MAX_VALUE); if ("true".equals(clientHasUpdate)) { // disconnect - LOGGER.warn("{} has not installed modpack. Certificate fingerprint to verify: {}", profile.getName(), hostServer.getCertificateFingerprint()); + LOGGER.warn("{} has not installed modpack. Certificate fingerprint: {}", profile.getName(), hostServer.getCertificateFingerprint()); Text reason = VersionedText.literal("[AutoModpack] Install/Update modpack to join"); ClientConnection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); connection.send(new LoginDisconnectS2CPacket(reason)); From 966ca10b210909dd9bde4436df543c9243ebd748 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 4 Jun 2025 15:24:28 +0200 Subject: [PATCH 22/86] Separate fingerprint command and add an `amp` shortcut to the root `automodpack` command --- .../skidam/automodpack/modpack/Commands.java | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/main/java/pl/skidam/automodpack/modpack/Commands.java b/src/main/java/pl/skidam/automodpack/modpack/Commands.java index 9cb5ee252..ea0d3bc71 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/Commands.java +++ b/src/main/java/pl/skidam/automodpack/modpack/Commands.java @@ -24,7 +24,7 @@ public class Commands { public static void register(CommandDispatcher dispatcher) { - dispatcher.register( + var automodpackNode = dispatcher.register( literal("automodpack") .executes(Commands::about) .then(literal("generate") @@ -50,6 +50,9 @@ public static void register(CommandDispatcher dispatcher) { .requires((source) -> source.hasPermissionLevel(3)) .executes(Commands::connections) ) + .then(literal("fingerprint")) + .requires((source) -> source.hasPermissionLevel(3)) + .executes(Commands::fingerprint) ) .then(literal("config") .requires((source) -> source.hasPermissionLevel(3)) @@ -59,14 +62,39 @@ public static void register(CommandDispatcher dispatcher) { ) ) ); + + dispatcher.register( + literal("amp") + .executes(Commands::about) + .redirect(automodpackNode) + ); } - private static int connections(CommandContext serverCommandSourceCommandContext) { + private static int fingerprint(CommandContext context) { + String fingerprint = hostServer.getCertificateFingerprint(); + if (fingerprint != null) { + MutableText fingerprintText = VersionedText.literal(fingerprint).styled(style -> style + /*? if >1.21.4 {*/ + /*.withHoverEvent(new HoverEvent.ShowText, VersionedText.translatable("chat.copy.click")) + /*.withClickEvent(new ClickEvent.CopyToClipboard(fingerprint))); + *//*?} else {*/ + .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, VersionedText.translatable("chat.copy.click"))) + .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, fingerprint))); + /*?}*/ + send(context, "Certificate fingerprint", Formatting.WHITE, fingerprintText, Formatting.YELLOW, false); + } else { + send(context, "Certificate fingerprint is not available. Make sure the server is running with TLS enabled.", Formatting.RED, false); + } + + return Command.SINGLE_SUCCESS; + } + + private static int connections(CommandContext context) { Util.getMainWorkerExecutor().execute(() -> { var connections = hostServer.getConnections(); var uniqueSecrets = Set.copyOf(connections.values()); - send(serverCommandSourceCommandContext, String.format("Active connections: %d Unique connections: %d ", connections.size(), uniqueSecrets.size()), Formatting.YELLOW, false); + send(context, String.format("Active connections: %d Unique connections: %d ", connections.size(), uniqueSecrets.size()), Formatting.YELLOW, false); for (String secret : uniqueSecrets) { var playerSecretPair = SecretsStore.getHostSecret(secret); @@ -77,7 +105,7 @@ private static int connections(CommandContext serverCommand long connNum = connections.values().stream().filter(secret::equals).count(); - send(serverCommandSourceCommandContext, String.format("Player: %s (%s) is downloading modpack using %d connections", profile.getName(), playerId, connNum), Formatting.GREEN, false); + send(context, String.format("Player: %s (%s) is downloading modpack using %d connections", profile.getName(), playerId, connNum), Formatting.GREEN, false); } }); @@ -161,26 +189,13 @@ private static int modpackHostAbout(CommandContext context) Formatting statusColor = hostServer.isRunning() ? Formatting.GREEN : Formatting.RED; String status = hostServer.isRunning() ? "running" : "not running"; send(context, "Modpack hosting status", Formatting.GREEN, status, statusColor, false); - String fingerprint = hostServer.getCertificateFingerprint(); - if (fingerprint != null) { - MutableText fingerprintText = VersionedText.literal(fingerprint).styled(style -> style - /*? if >1.21.4 {*/ - /*.withHoverEvent(new HoverEvent.ShowText, VersionedText.translatable("chat.copy.click")) - /*.withClickEvent(new ClickEvent.CopyToClipboard(fingerprint))); - *//*?} else {*/ - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, VersionedText.translatable("chat.copy.click"))) - .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, fingerprint))); - /*?}*/ - send(context, "Certificate fingerprint", Formatting.WHITE, fingerprintText, Formatting.YELLOW, false); - } - return Command.SINGLE_SUCCESS; } private static int about(CommandContext context) { send(context, "AutoModpack", Formatting.GREEN, AM_VERSION, Formatting.WHITE, false); send(context, "/automodpack generate", Formatting.YELLOW, false); - send(context, "/automodpack host start/stop/restart/connections", Formatting.YELLOW, false); + send(context, "/automodpack host start/stop/restart/connections/fingerprint", Formatting.YELLOW, false); send(context, "/automodpack config reload", Formatting.YELLOW, false); return Command.SINGLE_SUCCESS; } @@ -213,11 +228,11 @@ private static void send(CommandContext context, String msg private static void send(CommandContext context, String msg, Formatting msgColor, String appendMsg, Formatting appendMsgColor, boolean broadcast) { VersionedCommandSource.sendFeedback(context, VersionedText.literal(msg) - .formatted(msgColor) - .append(VersionedText.literal(" - ") - .formatted(Formatting.WHITE)) - .append(VersionedText.literal(appendMsg) - .formatted(appendMsgColor)), + .formatted(msgColor) + .append(VersionedText.literal(" - ") + .formatted(Formatting.WHITE)) + .append(VersionedText.literal(appendMsg) + .formatted(appendMsgColor)), broadcast); } From 14780f108856ffd064d8976bfc7767a1bfa42e47 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 4 Jun 2025 19:40:34 +0200 Subject: [PATCH 23/86] Update mods.mdx to add Cesium incompatibility note --- docs/compatibility/mods.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/compatibility/mods.mdx b/docs/compatibility/mods.mdx index 5ecb23d66..9b32fb541 100644 --- a/docs/compatibility/mods.mdx +++ b/docs/compatibility/mods.mdx @@ -1,4 +1,5 @@ AutoModpack should work with all minecraft modifications (it would be a bit bad modpack updater if it didn't). However, there are some exceptions and limitations that you should be aware of: -- Majrusz's Mods: Requires modified Majrusz Library to work with AutoModpack. Read more about it [here](https://github.com/Skidamek/AutoModpack/issues/268). +- Majrusz's Mods: Require a modified Majrusz Library to work with AutoModpack. [Github Issue](https://github.com/Skidamek/AutoModpack/issues/268). - [ModSets](https://modrinth.com/mod/mod-sets): It's currently broken due to similarities in mod loading modification process. +- [Cesium](https://modrinth.com/mod/cesium): Cesium is currently incompatible due to wrong way of zstd packaging on AutoModpack side. It will be fixed soon. [GitHub Issue](https://github.com/Skidamek/AutoModpack/issues/384) \ No newline at end of file From 76c5bb32c06ddbc6c2d3341bf4620fb2572c9b24 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 4 Jun 2025 19:43:35 +0200 Subject: [PATCH 24/86] Create subdirectories for mods, config, shaderpacks, and resourcepacks in host content modpack directory by default to reduce confusion --- .../pl/skidam/automodpack_core/modpack/ModpackExecutor.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackExecutor.java b/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackExecutor.java index e2d29761d..08ad7383b 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackExecutor.java +++ b/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackExecutor.java @@ -23,6 +23,10 @@ private ModpackContent init() { try { if (!Files.exists(hostContentModpackDir)) { Files.createDirectories(hostContentModpackDir); + Files.createDirectory(hostContentModpackDir.resolve("mods")); + Files.createDirectory(hostContentModpackDir.resolve("config")); + Files.createDirectory(hostContentModpackDir.resolve("shaderpacks")); + Files.createDirectory(hostContentModpackDir.resolve("resourcepacks")); } } catch (IOException e) { e.printStackTrace(); From 96fd7bf41370bdcd35a494d1f239cd96994b4849 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 4 Jun 2025 19:49:35 +0200 Subject: [PATCH 25/86] Add instructions for retrieving certificate fingerprint and introduce `/amp` alias for `/automodpack` --- docs/commands/commands.mdx | 3 +++ docs/technicals/certificate.mdx | 2 ++ 2 files changed, 5 insertions(+) diff --git a/docs/commands/commands.mdx b/docs/commands/commands.mdx index 5f99effe2..579e83233 100644 --- a/docs/commands/commands.mdx +++ b/docs/commands/commands.mdx @@ -1,3 +1,5 @@ +`/amp` is an alias for `/automodpack`. + - `/automodpack` - Status of automodpack and general help. - `/automodpack generate` - Generate modpack. - `/automodpack host` - Status of modpack hosting. @@ -5,4 +7,5 @@ - `/automodpack host stop` - Stop modpack hosting. - `/automodpack host restart` - Restart modpack hosting. - `/automodpack host connections` - Lists all currently active modpack host connections. +- `/automodpack host fingerprint` - Get the [certificate fingerprint](../technicals/certificate) of the modpack host. - `/automodpack config reload` - Reload config files. \ No newline at end of file diff --git a/docs/technicals/certificate.mdx b/docs/technicals/certificate.mdx index 0ac20f1c7..f3e2b3676 100644 --- a/docs/technicals/certificate.mdx +++ b/docs/technicals/certificate.mdx @@ -1,6 +1,8 @@ ### How to verify the certificate fingerprint? **Just copy the fingerprint from your server console to the game.** This prevents attacks such as [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). Fingerprint is the same for everyone on your server, its recommended to share it with players ahead of time. +If you can't find the fingerprint in the console, you can retrieve it at any time using the `/automodpack host fingerprint` [command](../commands/commands). + However please do not think of it like a password to the modpack, its not a password or any secret, its a public verifiable piece of server certificate, **anyone could download** your modpack without previous knowledge of such fingerprint e.g. by bypassing this check with `I AM INCREDIBLY STUPID`. (don't do it) To provide modpack for only authorized/whitelisted players, use `validateSecrets` option in the [server config](../configuration/server-config) (its enabled by default). From 0b26d2dcca15d90c0bff9a66527cbd0454249fe9 Mon Sep 17 00:00:00 2001 From: skidam Date: Thu, 5 Jun 2025 16:36:21 +0200 Subject: [PATCH 26/86] Fix formatting path edge cases ref #387 --- .../pl/skidam/automodpack_core/utils/CustomFileUtils.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java b/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java index 116eb7531..46e9ba3a0 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java +++ b/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java @@ -144,10 +144,10 @@ public static String formatPath(final Path modpackFile, final Path modpackPath) String formattedFile = modpackFileStr; // Checks if in file parents paths (absolute path) there is modpack directory (absolute path) - if (modpackFileStrAbs.contains(modpackPathStrAbs)) { - formattedFile = modpackFileStrAbs.replace(modpackPathStrAbs, ""); - } else if (modpackFileStrAbs.contains(cwdStrAbs)) { - formattedFile = modpackFileStrAbs.replace(cwdStrAbs, ""); + if (modpackFileStrAbs.startsWith(modpackPathStrAbs)) { + formattedFile = modpackFileStrAbs.replaceFirst(modpackPathStrAbs, ""); + } else if (modpackFileStrAbs.startsWith(cwdStrAbs)) { + formattedFile = modpackFileStrAbs.replaceFirst(cwdStrAbs, ""); } else if (!modpackFileStrAbs.equals(modpackFileStr)) { // possible in e.g. docker LOGGER.error("File: {} ({}) is not in modpack directory: {} ({}) or current working directory: {}", modpackFileStr, modpackFileStrAbs, modpackPath, modpackPathStrAbs, cwdStrAbs); } From fe41d950938f27d3b00732460a32246619fa0dfb Mon Sep 17 00:00:00 2001 From: skidam Date: Thu, 5 Jun 2025 17:10:40 +0200 Subject: [PATCH 27/86] Don't serialize nulls to save a bit space in jsons because why not --- .../automodpack_core/config/ConfigTools.java | 1 - .../platforms/CurseForgeAPI.java | 2 +- .../platforms/ModrinthAPI.java | 27 +------------------ .../utils/FetchManager.java | 2 +- 4 files changed, 3 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java b/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java index 72f9458c9..7ba9335e3 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java +++ b/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java @@ -15,7 +15,6 @@ public class ConfigTools { public static Gson GSON = new GsonBuilder() - .serializeNulls() .disableHtmlEscaping() .setPrettyPrinting() .registerTypeAdapter(InetSocketAddress.class, new InetSocketAddressTypeAdapter()) diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/CurseForgeAPI.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/CurseForgeAPI.java index 779394ee2..484f45f43 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/CurseForgeAPI.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/CurseForgeAPI.java @@ -39,7 +39,7 @@ public static List getModInfosFromFingerPrints(Map hashes) { + private static CurseForgeAPI parseJsonObject(JsonObject JSONObject, Map hashes) { if (JSONObject == null) { LOGGER.error("CurseForgeAPI Can't parse null object"); return null; diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/ModrinthAPI.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/ModrinthAPI.java index 2aa98d1d1..bbaf5b50b 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/ModrinthAPI.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/ModrinthAPI.java @@ -125,32 +125,7 @@ public static List getModsInfosFromListOfSHA1(List listOfSh return modrinthAPIList; } - // https://docs.modrinth.com/#tag/version-files - public static ModrinthAPI getModInfoFromSHA1(String sha1) { - if (sha1 == null || sha1.isEmpty()) { - return null; - } - - String requestUrl = BASE_URL + "/version_file/" + sha1 + "?algorithm=sha1"; - requestUrl = requestUrl.replaceAll("\"", "%22"); // so important! - - try { - JsonObject JSONObject = Json.fromUrl(requestUrl); - return parseJsonObject(JSONObject, List.of(sha1)); - } catch (IndexOutOfBoundsException e) { - LOGGER.warn("Something gone wrong while getting info from Modrinth API: {}", requestUrl); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - -// public static void main(String[] args) { -// var ww = getModsInfosFromListOfSHA1(List.of("2ff512a70c437c20523de01ea95b6fc9b164a5c0")); -// ww.forEach(System.out::println); -// } - - public static ModrinthAPI parseJsonObject(JsonObject JSONObject, List listOfSha1) { + private static ModrinthAPI parseJsonObject(JsonObject JSONObject, List listOfSha1) { if (JSONObject == null) { return null; } diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/FetchManager.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/FetchManager.java index 04f22b4e1..3d19baf6d 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/FetchManager.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/FetchManager.java @@ -40,7 +40,7 @@ public void fetch() { List mo = new ArrayList<>(); for (Map.Entry entry : fetchDatas.entrySet()) { FetchData fetchData = entry.getValue().fetchData(); - if (fetchData.murmur != null) { + if (fetchData.murmur != null && !fetchData.murmur.isBlank()) { cf.put(fetchData.sha1, fetchData.murmur); } From 9ccb46f63e5c3894ab999f96ef0916dd5ee76fc9 Mon Sep 17 00:00:00 2001 From: skidam Date: Thu, 5 Jun 2025 17:23:53 +0200 Subject: [PATCH 28/86] Ignore null or blank rules in rules to not crash the server with `null` entries in `syncedFiles` or `allowEditsInFiles` --- .../java/pl/skidam/automodpack_core/utils/WildCards.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java b/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java index 75a16e4ea..fe6cd8051 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java +++ b/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java @@ -26,6 +26,10 @@ public Map getWildcardMatches() { public void separateRules(List rules) { for (String rule : rules) { + if (rule == null || rule.isBlank()) { + continue; + } + if (rule.startsWith("!")) { blackListRules.add(rule.substring(1)); } else { @@ -187,6 +191,10 @@ public Map> composeRules(List rules) { Map> directoryRulePathsMap = new HashMap<>(rules.size()); for (String rule : rules) { + if (rule == null || rule.isBlank()) { + continue; + } + int lastSlashIndex = rule.lastIndexOf("/"); if (lastSlashIndex == -1) { continue; From fc7ddfeab2d20056b3074d532ab334d6dda4e5cd Mon Sep 17 00:00:00 2001 From: skidam Date: Thu, 5 Jun 2025 17:27:24 +0200 Subject: [PATCH 29/86] Update changelog summary to reflect total files added and removed instead of just mods --- .../client/ui/ChangelogScreen.java | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java index f0d63e985..20c06abc3 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java @@ -109,24 +109,11 @@ private void drawSummaryOfChanges(VersionedMatrices matrices) { modpackContent = ConfigTools.loadModpackContent(optionalModpackContentFile.get()); } - int modsAdded = 0; - int modsRemoved = 0; if (modpackContent == null) return; - for (var changelog : changelogs.changesAddedList.entrySet()) { - String fileType = ModpackContentTools.getFileType(changelog.getKey(), modpackContent); - if (fileType.equals("mod")) { - modsAdded++; - } - } - - for (var changelog : changelogs.changesDeletedList.entrySet()) { - String fileType = ModpackContentTools.getFileType(changelog.getKey(), modpackContent); - if (fileType.equals("mod")) { - modsRemoved++; - } - } + int filesAdded = changelogs.changesAddedList.size(); + int filesRemoved = changelogs.changesDeletedList.size(); - String summary = "Mods + " + modsAdded + " | - " + modsRemoved; + String summary = "Files: + " + filesAdded + " | - " + filesRemoved; drawCenteredTextWithShadow(matrices, textRenderer, VersionedText.literal(summary), this.width / 2, 5, 16777215); } From 1223f9e14a83ee28fa8d44123221a56517ccd54b Mon Sep 17 00:00:00 2001 From: skidam Date: Thu, 5 Jun 2025 18:48:36 +0200 Subject: [PATCH 30/86] Refactor file path formatting to use substring instead of replace for performance, improved clarity (this fixes some weird edge cases too) win-win-win --- .../pl/skidam/automodpack_core/utils/CustomFileUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java b/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java index 46e9ba3a0..8fa727290 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java +++ b/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java @@ -145,9 +145,9 @@ public static String formatPath(final Path modpackFile, final Path modpackPath) // Checks if in file parents paths (absolute path) there is modpack directory (absolute path) if (modpackFileStrAbs.startsWith(modpackPathStrAbs)) { - formattedFile = modpackFileStrAbs.replaceFirst(modpackPathStrAbs, ""); + formattedFile = modpackFileStrAbs.substring(modpackPathStrAbs.length()); } else if (modpackFileStrAbs.startsWith(cwdStrAbs)) { - formattedFile = modpackFileStrAbs.replaceFirst(cwdStrAbs, ""); + formattedFile = modpackFileStrAbs.substring(cwdStrAbs.length()); } else if (!modpackFileStrAbs.equals(modpackFileStr)) { // possible in e.g. docker LOGGER.error("File: {} ({}) is not in modpack directory: {} ({}) or current working directory: {}", modpackFileStr, modpackFileStrAbs, modpackPath, modpackPathStrAbs, cwdStrAbs); } From c72a246e28b106d5f1a8d3b458815bd0427c99e2 Mon Sep 17 00:00:00 2001 From: skidam Date: Thu, 5 Jun 2025 18:54:42 +0200 Subject: [PATCH 31/86] Refactor download percentage calculation to handle zero values and improve accuracy, remove unused variables for clarity Should fix some weird ass visual edge cases i guess --- .../utils/DownloadManager.java | 9 ++++- .../automodpack/client/ui/DownloadScreen.java | 40 +++---------------- 2 files changed, 12 insertions(+), 37 deletions(-) diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/DownloadManager.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/DownloadManager.java index 6db5cc58b..e063d6edb 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/DownloadManager.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/DownloadManager.java @@ -230,8 +230,13 @@ public long getTotalBytesRemaining() { return bytesToDownload - bytesDownloaded; } - public float getTotalPercentageOfFileSizeDownloaded() { - return (float) bytesDownloaded / bytesToDownload * 100; + public int getTotalPercentageOfFileSizeDownloaded() { + if (bytesDownloaded == 0 || bytesToDownload == 0) { + return 0; + } + + int percentage = (int) (bytesDownloaded * 100 / bytesToDownload); + return Math.max(0, Math.min(100, percentage)); } public String getStage() { diff --git a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java index 33062112f..0cd97effe 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java @@ -26,13 +26,10 @@ public class DownloadScreen extends VersionedScreen { private static long ticks = 0; private ButtonWidget cancelButton; - // Temp save for the last download values -// private final Map mapOfFileStats = new HashMap<>(); // URL, Percentage of download private String lastStage = "-1"; private int lastPercentage = -1; private String lastSpeed = "-1"; private String lastETA = "-1"; - private float lastDownloadedScale = 0.0F; public DownloadScreen(DownloadManager downloadManager, String header) { super(VersionedText.literal("DownloadScreen")); @@ -50,22 +47,8 @@ protected void init() { Util.getMainWorkerExecutor().execute(() -> { while (downloadManager != null && downloadManager.isRunning()) { - -// for (Map.Entry map : ModpackUpdater.downloadManager.downloadsInProgress.entrySet()) { -// mapOfFileStats.put(map.getKey(), ModpackUpdater.downloadManager.getPercentageOfFileSizeDownloaded(map.getKey()) + "%"); -// } -// -// for (Map.Entry map : mapOfFileStats.entrySet()) { -// if (!ModpackUpdater.downloadManager.downloadsInProgress.containsKey(map.getKey())) { -// mapOfFileStats.remove(map.getKey()); -// } -// } - - // TODO make it work better pls lastStage = downloadManager.getStage(); - lastPercentage = (int) downloadManager.getTotalPercentageOfFileSizeDownloaded(); - lastDownloadedScale = (float) (downloadManager.getTotalPercentageOfFileSizeDownloaded() * 0.01); - + lastPercentage = downloadManager.getTotalPercentageOfFileSizeDownloaded(); lastSpeed = SpeedMeter.formatDownloadSpeedToMbps(downloadManager.getSpeedMeter().getCurrentSpeedInBytes()); lastETA = SpeedMeter.formatETAToSeconds(downloadManager.getSpeedMeter().getETAInSeconds()); } @@ -111,7 +94,7 @@ private Text getTotalETA() { } private float getDownloadScale() { - return lastDownloadedScale; + return Math.max(0, Math.min(100, lastPercentage)) * 0.01F; // Convert the clamped percentage to a scale between 0.0f and 1.0f } private void drawDownloadingFiles(VersionedMatrices matrices) { @@ -128,23 +111,14 @@ private void drawDownloadingFiles(VersionedMatrices matrices) { int currentY = y + 15; synchronized (downloadManager.downloadsInProgress) { for (DownloadManager.DownloadData downloadData : downloadManager.downloadsInProgress.values()) { - String text = downloadData.getFileName(); - -// DownloadManager.DownloadData downloadData = map.getValue(); -// String percentage = mapOfFileStats.get(map.getKey()); -// -// if (percentage != null) { -// text += " " + percentage; -// } - - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.literal(text).formatted(Formatting.GRAY), (int) (this.width / 2 * scale), currentY, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.literal(text).formatted(Formatting.GRAY), (int) ((float) this.width / 2 * scale), currentY, 16777215); currentY += 10; } } } else { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.download.noFiles"), (int) (this.width / 2 * scale), y, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.wait").formatted(Formatting.BOLD), (int) (this.width / 2 * scale), y + 25, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.download.noFiles"), (int) ((float) this.width / 2 * scale), y, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.wait").formatted(Formatting.BOLD), (int) ((float) this.width / 2 * scale), y + 25, 16777215); } matrices.pop(); @@ -207,11 +181,7 @@ public void cancelDownload() { downloadManager.cancelAllAndShutdown(); } - // TODO delete files that were downloaded - // we will use the same method as to modpacks manager - new ScreenManager().title(); - } catch (Exception e) { e.printStackTrace(); } From e11fa8a21f21100e0f50f74f7113956b94b0eab9 Mon Sep 17 00:00:00 2001 From: Skidam <67871298+Skidamek@users.noreply.github.com> Date: Thu, 5 Jun 2025 18:59:19 +0200 Subject: [PATCH 32/86] Improve server config and allow TLS separation for reverse proxies (#386) * Change server config scheme to V2 adds bindAddress and bindPort completely separated from address/port to send remove reverseProxy option since it no longer has any need to be there add disableInternalTLS option, implementation not finished yet * Implement disableInternalTLS option * Fallback to TLS for reverse proxies * Remove dummy files after self-update in Preload * Change socket connection timeout values and address formatting * Send to client information if it requires use of magic packets to connect or not * Track and store magic packet usage * Fix `requiresMagic` var and improve logging * Use existing installed addresses directly * Reduce duplicated code in ProtocolServerHandler * Refactor server configuration to use bindPort = -1 instead of hostModpackOnMinecraftPort * Trust server with `requiresMagic` bool, don't overwrite it and simplify connection code * Use softLoad method instead of manually merging the old config * Update documentation to reflect the new changes * Fix and improve modpack address handling We have to serialize nulls in DataPacket for e.g. `port` which if null, client should use the modpack server port it used to connect to * Require magic only on `bindPort` `-1` * Refactor port handling to use primitive int and simplify * Clarify `disableInternalTLS` documentation to emphasize the need for loopback address with reverse proxy * Actually its better to not serialize nulls there to avoid null pointers * Enhance server config documentation to clarify stuff better * spelling Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * Log full stacktrace on catch in PreValidationConnection * spelling Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * spelling and clarification Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * spelling Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * spelling Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * spelling Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * spelling Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * spelling Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * Reformat table * Always send `minecraftServerPort` if `portToSend` is `-1` * Refactor stop method to fix host closure logic * Improve port selection logic for modpack hosting * Improve server config schema v2 update * Refine error and warning messages for AutoModpack host server configuration * Remove useless proxy instructions as from now * Update core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * Update src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * Update src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * Add loopback address check in order to `disableInternalTLS` and allow binding host without providing the `bindAddress` * Update core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> * Update server configuration documentation Add empty strings as default values where its true Include information what happens if `bindAddress` is empty * Refactor loopback address handling in NettyServer and remove redundant isLoopback method * Refine internal TLS check and warning messages and update server configuration documentation for clarity --------- Co-authored-by: duckymirror <33291860+duckymirror@users.noreply.github.com> --- .../automodpack_core/GlobalVariables.java | 2 +- .../pl/skidam/automodpack_core/Server.java | 7 +- .../automodpack_core/config/ConfigTools.java | 20 +--- .../skidam/automodpack_core/config/Jsons.java | 38 ++++++-- .../protocol/DownloadClient.java | 39 +++++--- .../protocol/netty/NettyServer.java | 91 +++++++++++-------- .../netty/handler/ProtocolServerHandler.java | 40 ++++---- .../utils/AddressHelpers.java | 4 +- docs/compatibility/proxies.mdx | 3 - docs/configuration/server-config.mdx | 53 +++++------ docs/technicals/modpack-hosting.mdx | 3 +- .../automodpack_loader_core/Preload.java | 59 +++++++++--- .../client/ModpackUtils.java | 5 +- .../mixin/core/ServerNetworkIoMixin.java | 2 +- .../skidam/automodpack/modpack/Commands.java | 2 +- .../networking/content/DataPacket.java | 6 +- .../networking/packet/DataC2SPacket.java | 28 +++--- .../networking/packet/DataS2CPacket.java | 23 ++--- .../networking/packet/HandshakeS2CPacket.java | 39 +++----- 19 files changed, 260 insertions(+), 204 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/GlobalVariables.java b/core/src/main/java/pl/skidam/automodpack_core/GlobalVariables.java index 24c78a192..4b743f828 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/GlobalVariables.java +++ b/core/src/main/java/pl/skidam/automodpack_core/GlobalVariables.java @@ -25,7 +25,7 @@ public class GlobalVariables { public static Path MODS_DIR; public static ModpackExecutor modpackExecutor; public static NettyServer hostServer; - public static Jsons.ServerConfigFields serverConfig; + public static Jsons.ServerConfigFieldsV2 serverConfig; public static Jsons.ClientConfigFieldsV2 clientConfig; public static Jsons.KnownHostsFields knownHosts; public static final Path automodpackDir = Path.of("automodpack"); diff --git a/core/src/main/java/pl/skidam/automodpack_core/Server.java b/core/src/main/java/pl/skidam/automodpack_core/Server.java index 7bfacf514..064883a4f 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/Server.java +++ b/core/src/main/java/pl/skidam/automodpack_core/Server.java @@ -35,14 +35,13 @@ public static void main(String[] args) { serverConfigFile = modpackDir.resolve("automodpack-server.json"); serverCoreConfigFile = modpackDir.resolve("automodpack-core.json"); - serverConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFields.class); + serverConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFieldsV2.class); if (serverConfig != null) { serverConfig.syncedFiles = new ArrayList<>(); - serverConfig.hostModpackOnMinecraftPort = false; serverConfig.validateSecrets = false; ConfigTools.save(serverConfigFile, serverConfig); - if (serverConfig.hostPort == -1) { + if (serverConfig.bindPort == -1) { LOGGER.error("Host port not set in config!"); return; } @@ -72,7 +71,7 @@ public static void main(String[] args) { modpackExecutor.stop(); - LOGGER.info("Starting server on port {}", serverConfig.hostPort); + LOGGER.info("Starting server on port {}", serverConfig.bindPort); server.start(); // wait for server to stop while (server.isRunning()) { diff --git a/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java b/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java index 7ba9335e3..7f921017e 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java +++ b/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java @@ -44,27 +44,13 @@ public static T getConfigObject(Class configClass) { } // Config stuff - public static T loadCheck(Path configFile, Class configClass) { + public static T softLoad(Path configFile, Class configClass) { try { if (Files.isRegularFile(configFile)) { String json = Files.readString(configFile); - T obj = GSON.fromJson(json, configClass); - if (obj == null) { - LOGGER.error("Parsed object is null. Possible JSON syntax error in file: " + configFile); - return null; - } - - return obj; + return GSON.fromJson(json, configClass); } - } catch (JsonSyntaxException e) { - LOGGER.error("JSON syntax error while loading config! {} {}", configClass, e.getMessage()); - LOGGER.error("This error most often happens when you e.g. forget to put a comma between fields in JSON file. Check the file: " + configFile.toAbsolutePath().normalize()); - return null; - } catch (Exception e) { - LOGGER.error("Couldn't load config! " + configClass); - e.printStackTrace(); - } - + } catch (Exception ignored) { } return null; } diff --git a/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java b/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java index 60e738127..71a02841d 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java +++ b/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java @@ -33,6 +33,7 @@ public static class ClientConfigFieldsV2 { public static class ModpackAddresses { public InetSocketAddress hostAddress; // modpack host address public InetSocketAddress serverAddress; // minecraft server address + public boolean requiresMagic; // if true, client will use magic packets to connect to the modpack host public ModpackAddresses() { // Default constructor for Gson @@ -45,9 +46,10 @@ public ModpackAddresses() { * @param serverAddress minecraft server address that represents the target address * which client uses to connect. This value CANNOT be manipulated by the server. */ - public ModpackAddresses(InetSocketAddress hostAddress, InetSocketAddress serverAddress) { + public ModpackAddresses(InetSocketAddress hostAddress, InetSocketAddress serverAddress, boolean requiresMagic) { this.hostAddress = hostAddress; this.serverAddress = serverAddress; + this.requiresMagic = requiresMagic; } public boolean isAnyEmpty() { @@ -55,7 +57,7 @@ public boolean isAnyEmpty() { } } - public static class ServerConfigFields { + public static class ServerConfigFieldsV1 { public int DO_NOT_CHANGE_IT = 1; // file version public String modpackName = ""; public boolean modpackHost = true; @@ -63,16 +65,12 @@ public static class ServerConfigFields { public List syncedFiles = List.of("/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"); public List allowEditsInFiles = List.of("/options.txt", "/config/**"); public boolean autoExcludeUnnecessaryFiles = true; -// public List forceLoad = List.of("/resourcepacks/someResourcePack.zip", "/shaderpacks/someShaderPack.zip"); -// public List> forceLoad = new ArrayList<>(); public boolean requireAutoModpackOnClient = true; public boolean nagUnModdedClients = true; public String nagMessage = "This server provides dedicated modpack through AutoModpack!"; public String nagClickableMessage = "Click here to get the AutoModpack!"; public String nagClickableLink = "https://modrinth.com/project/automodpack"; public boolean autoExcludeServerSideMods = true; -// public boolean velocityMode = false; compat plugin... someday I hope -// public boolean forceToDisableAllOtherModsOnClients = false; public boolean hostModpackOnMinecraftPort = true; public String hostIp = ""; public String hostLocalIp = ""; @@ -86,6 +84,34 @@ public static class ServerConfigFields { public List acceptedLoaders; } + public static class ServerConfigFieldsV2 { + public int DO_NOT_CHANGE_IT = 2; // file version + public String modpackName = ""; + public boolean modpackHost = true; + public boolean generateModpackOnStart = true; + public List syncedFiles = List.of("/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"); + public List allowEditsInFiles = List.of("/options.txt", "/config/**"); + public boolean autoExcludeServerSideMods = true; + public boolean autoExcludeUnnecessaryFiles = true; + public boolean requireAutoModpackOnClient = true; + public boolean nagUnModdedClients = true; + public String nagMessage = "This server provides dedicated modpack through AutoModpack!"; + public String nagClickableMessage = "Click here to get the AutoModpack!"; + public String nagClickableLink = "https://modrinth.com/project/automodpack"; + public String bindAddress = ""; + public int bindPort = -1; + public String addressToSend = ""; + public String localAddressToSend = ""; + public int portToSend = -1; + public boolean disableInternalTLS = false; + public boolean updateIpsOnEveryStart = false; + public int bandwidthLimit = 0; + public boolean validateSecrets = true; + public long secretLifetime = 336; // 336 hours = 14 days + public boolean selfUpdater = false; + public List acceptedLoaders; + } + public static class ServerCoreConfigFields { public String automodpackVersion = "4.0.0-beta36"; // TODO: dont hardcode it public String loader = "fabric"; diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/DownloadClient.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/DownloadClient.java index b4e059160..912c4c22b 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/DownloadClient.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/DownloadClient.java @@ -187,20 +187,31 @@ class PreValidationConnection { public PreValidationConnection(InetSocketAddress resolvedHostAddress, Jsons.ModpackAddresses modpackAddresses, KeyStore keyStore) throws IOException, KeyStoreException { // Step 1. Create a plain TCP connection. Socket plainSocket = new Socket(); - plainSocket.connect(resolvedHostAddress, 15000); // To create socket, we need to pass a resolved socket address - plainSocket.setSoTimeout(15000); - DataOutputStream plainOut = new DataOutputStream(plainSocket.getOutputStream()); - DataInputStream plainIn = new DataInputStream(plainSocket.getInputStream()); - - // Step 2. Send the handshake (AMMC magic) over the plain socket. - plainOut.writeInt(MAGIC_AMMC); - plainOut.flush(); - - // Step 3. Wait for the server’s reply (AMOK magic). - int handshakeResponse = plainIn.readInt(); - if (handshakeResponse != MAGIC_AMOK) { - plainSocket.close(); - throw new IOException("Invalid handshake response from server: " + handshakeResponse); + plainSocket.connect(resolvedHostAddress, 10000); // To create socket, we need to pass a resolved socket address + plainSocket.setSoTimeout(10000); + + if (modpackAddresses.requiresMagic) { + try { + DataOutputStream plainOut = new DataOutputStream(plainSocket.getOutputStream()); + DataInputStream plainIn = new DataInputStream(plainSocket.getInputStream()); + + // Step 2. Send the handshake (AMMC magic) over the plain socket. + plainOut.writeInt(MAGIC_AMMC); + plainOut.flush(); + + // Step 3. Wait for the server’s reply (AMOK magic). + int handshakeResponse = plainIn.readInt(); + if (handshakeResponse != MAGIC_AMOK) { + throw new IOException("Invalid response from server: " + handshakeResponse); + } + } catch (IOException e) { + LOGGER.error("AutoModpack magic handshake failed", e); + plainSocket.close(); + } + } + + if (plainSocket.isClosed() || !plainSocket.isConnected()) { + throw new IOException("Failed to establish a plain socket connection to " + resolvedHostAddress); } // Step 4. Upgrade the plain socket to TLS using the same underlying connection. diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java index 03f6a2590..70f9eaf5a 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java @@ -79,36 +79,54 @@ public Optional start() { } try { - if (!Files.exists(serverCertFile) || !Files.exists(serverPrivateKeyFile)) { - // Create a self-signed certificate - KeyPair keyPair = NetUtils.generateKeyPair(); - X509Certificate cert = NetUtils.selfSign(keyPair); - - // save it to the file - NetUtils.saveCertificate(cert, serverCertFile); - NetUtils.savePrivateKey(keyPair.getPrivate(), serverPrivateKeyFile); + String address = serverConfig.bindAddress; + int port = serverConfig.bindPort; + InetSocketAddress bindAddress; + if (address == null || address.isBlank()) { + bindAddress = new InetSocketAddress(port); + } else { + bindAddress = new InetSocketAddress(address, port); } - X509Certificate cert = NetUtils.loadCertificate(serverCertFile); + boolean bindsOnLoopback = bindAddress.getAddress().isLoopbackAddress(); + if (serverConfig.disableInternalTLS && serverConfig.bindPort != -1 && bindsOnLoopback) { + LOGGER.warn("Internal TLS is disabled. Clients will not be able to connect directly; you must use e.g. a reverse proxy with TLS."); + } else { + if (serverConfig.disableInternalTLS) { + LOGGER.error("Internal TLS cannot be disabled. You have to bind modpack host on a loopback address with a separate port."); + } - if (cert == null) { - throw new IllegalStateException("Server certificate couldn't be loaded"); - } + if (!Files.exists(serverCertFile) || !Files.exists(serverPrivateKeyFile)) { + // Create a self-signed certificate + KeyPair keyPair = NetUtils.generateKeyPair(); + X509Certificate cert = NetUtils.selfSign(keyPair); + + // save it to the file + NetUtils.saveCertificate(cert, serverCertFile); + NetUtils.savePrivateKey(keyPair.getPrivate(), serverPrivateKeyFile); + } + + X509Certificate cert = NetUtils.loadCertificate(serverCertFile); - // Shiny TLS 1.3 - sslCtx = SslContextBuilder.forServer(serverCertFile.toFile(), serverPrivateKeyFile.toFile()) - .sslProvider(SslProvider.JDK) - .protocols("TLSv1.3") - .ciphers(Arrays.asList( - "TLS_AES_128_GCM_SHA256", - "TLS_AES_256_GCM_SHA384", - "TLS_CHACHA20_POLY1305_SHA256")) - .build(); - - // generate sha256 from cert as a fingerprint - certificateFingerprint = NetUtils.getFingerprint(cert); - if (certificateFingerprint != null) { - LOGGER.warn("Certificate fingerprint: {}", certificateFingerprint); + if (cert == null) { + throw new IllegalStateException("Server certificate couldn't be loaded"); + } + + // Shiny TLS 1.3 + sslCtx = SslContextBuilder.forServer(serverCertFile.toFile(), serverPrivateKeyFile.toFile()) + .sslProvider(SslProvider.JDK) + .protocols("TLSv1.3") + .ciphers(Arrays.asList( + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256")) + .build(); + + // generate sha256 from cert as a fingerprint + certificateFingerprint = NetUtils.getFingerprint(cert); + if (certificateFingerprint != null) { + LOGGER.warn("Certificate fingerprint: {}", certificateFingerprint); + } } if (!canStart()) { @@ -116,8 +134,6 @@ public Optional start() { return Optional.empty(); } - int port = serverConfig.hostPort; - InetSocketAddress bindAddress = new InetSocketAddress("0.0.0.0", port); LOGGER.info("Starting modpack host server on {}", bindAddress); Class socketChannelClass; @@ -159,14 +175,13 @@ public boolean shouldHost() { // Returns true if stopped successfully public boolean stop() { try { - if (serverChannel == null) { - if (shouldHost) { - shouldHost = false; - } - } else { + if (serverChannel != null) { serverChannel.channel().close().sync(); + serverChannel = null; } + shouldHost = false; + TrafficShaper.close(); if (eventLoopGroup != null) { @@ -206,15 +221,15 @@ private boolean canStart() { String publicIp = AddressHelpers.getPublicIp(); String localIp = AddressHelpers.getLocalIp(); if (publicIp != null) { - serverConfig.hostIp = publicIp; - LOGGER.warn("Setting Host IP to {}", serverConfig.hostIp); + serverConfig.addressToSend = publicIp; + LOGGER.warn("Setting Host IP to {}", serverConfig.addressToSend); } else { LOGGER.error("Couldn't get public IP, please change it manually! "); } if (localIp != null) { - serverConfig.hostLocalIp = localIp; - LOGGER.warn("Setting Host Local IP to {}", serverConfig.hostLocalIp); + serverConfig.localAddressToSend = localIp; + LOGGER.warn("Setting Host Local IP to {}", serverConfig.localAddressToSend); } else { LOGGER.error("Couldn't get local IP, please change it manually! "); } @@ -228,7 +243,7 @@ private boolean canStart() { shouldHost = true; // At this point we know that we want to host the modpack - if (serverConfig.hostModpackOnMinecraftPort) { + if (serverConfig.bindPort == -1) { LOGGER.info("Hosting modpack on Minecraft port"); return false; // Dont start separate server for modpack hosting, use minecraft port instead } else { diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java index 94cd0b1fc..04f0cc2c8 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java @@ -10,6 +10,7 @@ import java.util.List; +import static pl.skidam.automodpack_core.GlobalVariables.serverConfig; import static pl.skidam.automodpack_core.protocol.NetUtils.*; public class ProtocolServerHandler extends ByteToMessageDecoder { @@ -27,7 +28,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { } int magic = in.getInt(0); - if (magic == MAGIC_AMMC) { + if (magic == MAGIC_AMMC) { // Server should always support AMMC protocol (magic packets) (preferred way to connect, required for hosting on Minecraft port and good for backwards compatibility) // Consume the packet in.skipBytes(in.readableBytes()); @@ -40,24 +41,9 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { var handlers = ctx.pipeline().toMap(); handlers.forEach((name, handler) -> ctx.pipeline().remove(handler)); -// InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress(); -// boolean isLocalConnection = AddressHelpers.isLocal(address); -// -// // Use compression only for non-local connections -// ctx.pipeline().channel().attr(NetUtils.USE_COMPRESSION).set(!isLocalConnection); - - ctx.pipeline().channel().attr(NettyServer.USE_COMPRESSION).set(true); - - // Set up the pipeline for our protocol - ctx.pipeline() - .addLast("traffic-shaper", TrafficShaper.trafficShaper.getTrafficShapingHandler()) - .addLast("tls", sslCtx.newHandler(ctx.alloc())) - .addLast("zstd-encoder", new ZstdEncoder()) - .addLast("zstd-decoder", new ZstdDecoder()) - .addLast("chunked-write", new ChunkedWriteHandler()) - .addLast("protocol-msg-decoder", new ProtocolMessageDecoder()) - .addLast("msg-handler", new ServerMessageHandler()) - .addLast("error-printer", new ErrorPrinter()); + setupPipeline(ctx); + } else if (sslCtx == null || serverConfig.bindPort != -1) { // However if there's no magic packet and we don't use internal TLS or we are hosting on a separate port, we have to try to connect anyway, for use with reverse proxy setups + setupPipeline(ctx); } // Always remove this handler after processing if its still there @@ -65,4 +51,20 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { ctx.pipeline().remove(this); } } + + private void setupPipeline(ChannelHandlerContext ctx) { + ctx.pipeline().channel().attr(NettyServer.USE_COMPRESSION).set(true); + + ctx.pipeline().addLast("traffic-shaper", TrafficShaper.trafficShaper.getTrafficShapingHandler()); + if (sslCtx != null) { // If SSL context is provided, add TLS handler + ctx.pipeline().addLast("tls", sslCtx.newHandler(ctx.alloc())); + } + ctx.pipeline() // Add the rest + .addLast("zstd-encoder", new ZstdEncoder()) + .addLast("zstd-decoder", new ZstdDecoder()) + .addLast("chunked-write", new ChunkedWriteHandler()) + .addLast("protocol-msg-decoder", new ProtocolMessageDecoder()) + .addLast("msg-handler", new ServerMessageHandler()) + .addLast("error-printer", new ErrorPrinter()); + } } \ No newline at end of file diff --git a/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java b/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java index bc5a75f05..28f19dd66 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java +++ b/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java @@ -104,11 +104,11 @@ public static InetSocketAddress parse(String address) { String host = address.substring(0, portIndex); String port = address.substring(portIndex + 1); if (port.matches("\\d+")) { - socketAddress = InetSocketAddress.createUnresolved(host, Integer.parseInt(port)); + socketAddress = format(host, Integer.parseInt(port)); } } if (socketAddress == null) { - socketAddress = InetSocketAddress.createUnresolved(address, 0); + socketAddress = format(address, 0); } } catch (Exception e) { LOGGER.error("Error while parsing address", e); diff --git a/docs/compatibility/proxies.mdx b/docs/compatibility/proxies.mdx index 0c3efa2db..7281b3cbf 100644 --- a/docs/compatibility/proxies.mdx +++ b/docs/compatibility/proxies.mdx @@ -7,6 +7,3 @@ Most minecraft proxies won't work with AutoModpack due to early login stage pack | Velocity | ❌ | Unsupported | | Waterfall | ❌ | Unsupported | | BungeeCord | ❌ | Unsupported | - -If you use Proxy with AutoModpack, you need to set `hostPort` in the [config](../configuration/server-config) to the port that your modpack is hosted on. -If you host modpack on the minecraft port (`hostModpackOnMinecraftPort` is `true`), you need to set `hostPort` to the port that your minecraft server is running on. \ No newline at end of file diff --git a/docs/configuration/server-config.mdx b/docs/configuration/server-config.mdx index fd18e7755..b05c023f9 100644 --- a/docs/configuration/server-config.mdx +++ b/docs/configuration/server-config.mdx @@ -1,29 +1,30 @@ ### Server Config File `~/automodpack/automodpack-server.json` -| Name | Default Value | Description | | -|-------------------------------|--------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----| -| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | | -| `modpackName` | | The name of the server modpack, shows while downloading modpack and that's how modpack directory gets called inside `~/automodpack/modpacks/`. | | -| `modpackHost` | `true` | Starts modpack host server. | | -| `generateModpackOnStart` | `true` | Automatically regenerate modpack metadata when the server starts. | | -| `syncedFiles` | `"/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"` | A list of *relative paths* from the root folder of the Minecraft server that will be **synced to the modpack**. Use wildcards like `*` to include multiple files from this directory (e.g., `/mods/*.jar`). Or use `**` (double star) to recursively include all files and subdirectories from a folder (e.g., `/config/**`). Prefix a path with `!` to **exclude** it from syncing. **Note:** This does **not** copy the files; it only defines what will be available on the client side under `~/.minecraft/`. | | -| `allowEditsInFiles` | `"/options.txt", "/config/**"` | A list of files that clients are allowed to edit. In other words, files that are downloaded only one time and then ignored from updating. Configuration works exactly the same way `syncedFiles` does. | | -| `autoExcludeUnnecessaryFiles` | `true` | Auto skip files which are: empty, hidden, temporary, disabled or backup. | | -| `requireAutoModpackOnClient` | `true` | Whether or not this mod is optional for clients to join server. | | -| `nagUnModdedClients` | `true` | If `true` clients without AutoModpack will be nagged with a chat message on join. To work requires `requireAutoModpackOnClient` to be `false`. | | -| `nagMessage` | `"This server provides dedicated modpack through AutoModpack!"` | The message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | | -| `nagClickableMessage` | `"Click here to get the AutoModpack!"` | The clickable part of the message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | | -| `nagClickableLink` | `"https://modrinth.com/project/automodpack"` | The link that will be opened when the message is clicked. To work requires `nagUnModdedClients` to be `true`. | | -| `autoExcludeServerSideMods` | `true` | Automatically excludes server-side mods from the modpack. (Works only if mod developer specified environment type in mod metadata) | | -| `hostModpackOnMinecraftPort` | `true` | Injects into minecraft network IO thanks to which modpack hosting doesn't require any additional port routing. | | -| `hostIp` | | The IP address on which the host server binds. | | -| `hostLocalIp` | | The local IP address on which the host server binds. | | -| `updateIpsOnEveryStart` | `false` | Updates `hostIp` and `hostLocalIp` on every server start. Might be useful if you have dynamic IP address. | | -| `hostPort` | `-1` | The port number on which the host server listens. | | -| `reverseProxy` | `false` | If `true` AutoModpack won't be adding configurable port from `hostPort` to the end of `hostIp`, `localHostIp` and `externalModpackHostLink`. | | -| `bandwidthLimit` | `0` | Upload limit in Mbps that modpack host server is restricted to. (Value has to be an Integer, `0` - means unlimited) | | -| `secretLifetime` | `336` | Time in hours that a player's secret remains valid. | | -| `validateSecrets` | `true` | Makes modpack host validate and authorize modpack download attempts using unique secrets. | | -| `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | | -| `acceptedLoaders` | `""` | Allows players from different modloaders to connect to your server (as long, automodpack support that loader and other mods on your server aren't incompatible with each other) with the same modpack. (use with caution, some mods may not work on both loaders) | | +| Name | Default Value | Description | +|-------------------------------|--------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | +| `modpackName` | `""` | The name of the server modpack, shows while downloading modpack and that's how modpack directory gets called inside `~/automodpack/modpacks/`. | +| `modpackHost` | `true` | Starts modpack host server. | +| `generateModpackOnStart` | `true` | Automatically regenerate modpack metadata when the server starts. | +| `syncedFiles` | `"/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"` | A list of *relative paths* from the root folder of the Minecraft server that will be **synced to the modpack**. Use wildcards like `*` to include multiple files from this directory (e.g., `/mods/*.jar`). Or use `**` (double star) to recursively include all files and subdirectories from a folder (e.g., `/config/**`). Prefix a path with `!` to **exclude** it from syncing. **Note:** This does **not** copy the files; it only defines what will be available on the client side under `~/.minecraft/`. | +| `allowEditsInFiles` | `"/options.txt", "/config/**"` | A list of files that clients are allowed to edit. In other words, files that are downloaded only one time and then ignored from updating. Configuration works exactly the same way `syncedFiles` does. | +| `autoExcludeServerSideMods` | `true` | Automatically excludes server-side mods from the modpack. (Works only if mod developer specified environment type in mod metadata) | +| `autoExcludeUnnecessaryFiles` | `true` | Auto skip files which are: empty, hidden, temporary, disabled or backup. | +| `requireAutoModpackOnClient` | `true` | Whether or not this mod is optional for clients to join server. | +| `nagUnModdedClients` | `true` | If `true` clients without AutoModpack will be nagged with a chat message on join. To work requires `requireAutoModpackOnClient` to be `false`. | +| `nagMessage` | `"This server provides dedicated modpack through AutoModpack!"` | The message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | +| `nagClickableMessage` | `"Click here to get the AutoModpack!"` | The clickable part of the message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | +| `nagClickableLink` | `"https://modrinth.com/project/automodpack"` | The link that will be opened when the message is clicked. To work requires `nagUnModdedClients` to be `true`. | +| `bindAddress` | `""` | The address to which the modpack host server binds. Leave this empty to automatically determine a local address (typically `0.0.0.0` or `::0`). When `bindPort` is set to `-1`, this value is **ignored** and the modpack host will be bound to the same address as the Minecraft server, as specified in the `server.properties` file. | +| `bindPort` | `-1` | The port number on which the modpack host server listens. If set to `-1`, the modpack will be hosted directly on the port of your Minecraft server. | +| `addressToSend` | `""` | The address to the modpack host which will be used by clients to download the modpack. If empty, clients will use the same address they used to connect to the Minecraft server. (Do **not** include the port there, use `portToSend` instead) | +| `localAddressToSend` | `""` | Same as `addressToSend`, but sent only to the clients connecting from the same private network as the minecraft server is hosted on. | +| `portToSend` | `-1` | The port number that is sent alongside the `addressToSend` or `localAddressToSend`. If set to `-1`, the port to which the modpack host is bound will be sent. | +| `disableInternalTLS` | `false` | Disables internal TLS management. If enabled, you must manage TLS externally, e.g. on a reverse proxy. When using this option, change `bindPort` and set `bindAddress` to a loopback address (e.g. `127.0.0.1`), so the unencrypted traffic between the modpack host and the reverse proxy isn't leaked. | +| `updateIpsOnEveryStart` | `false` | Updates `addressToSend` and `localAddressToSend` on every server start. Might be useful if you have dynamic IP address. (Use only if you really need it!) | +| `bandwidthLimit` | `0` | Upload limit in Mbps that modpack host server is restricted to. (Value has to be an Integer, `0` - means unlimited) | +| `validateSecrets` | `true` | Makes modpack host validate and authorize modpack download attempts using unique secrets. | +| `secretLifetime` | `336` | Time in hours that a player's secret remains valid. | +| `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | +| `acceptedLoaders` | `""` | Allows players from different modloaders to connect to your server (as long, automodpack support that loader and other mods on your server aren't incompatible with each other) with the same modpack. (use with caution, some mods may not work on both loaders) | diff --git a/docs/technicals/modpack-hosting.mdx b/docs/technicals/modpack-hosting.mdx index 24a2916c1..5e019b673 100644 --- a/docs/technicals/modpack-hosting.mdx +++ b/docs/technicals/modpack-hosting.mdx @@ -1,3 +1,2 @@ By default AutoModpack hosts your modpack on your minecraft server port (e.g. 25565). This means that you don't need to open any other port on your router/firewall. -If you want to use different port, disable `hostModpackOnMinecraftPort` and change `hostPort` in [config](../configuration/server-config). - +If you want to use a different port, change `bindPort` in [config](../configuration/server-config). diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java index d2a941114..5bd4bfba6 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java @@ -52,19 +52,22 @@ private void updateAll() { selectedModpackDir = optionalSelectedModpackDir.get(); InetSocketAddress selectedModpackAddress = null; InetSocketAddress selectedServerAddress = null; + boolean requiresMagic = true; // Default to true if (!clientConfig.selectedModpack.isBlank() && clientConfig.installedModpacks.containsKey(clientConfig.selectedModpack)) { var entry = clientConfig.installedModpacks.get(clientConfig.selectedModpack); selectedModpackAddress = entry.hostAddress; selectedServerAddress = entry.serverAddress; + requiresMagic = entry.requiresMagic; } // Only selfupdate if no modpack is selected if (selectedModpackAddress == null) { SelfUpdater.update(); + CustomFileUtils.deleteDummyFiles(Path.of(System.getProperty("user.dir")), null); } else { Secrets.Secret secret = SecretsStore.getClientSecret(clientConfig.selectedModpack); - Jsons.ModpackAddresses modpackAddresses = new Jsons.ModpackAddresses(selectedModpackAddress, selectedServerAddress); + Jsons.ModpackAddresses modpackAddresses = new Jsons.ModpackAddresses(selectedModpackAddress, selectedServerAddress, requiresMagic); var optionalLatestModpackContent = ModpackUtils.requestServerModpackContent(modpackAddresses, secret, false); var latestModpackContent = ConfigTools.loadModpackContent(selectedModpackDir.resolve(hostModpackContentFile.getFileName())); @@ -116,24 +119,20 @@ private void loadConfigs() { // load client config if (clientConfigOverride == null) { - var clientConfigVersion = ConfigTools.loadCheck(clientConfigFile, Jsons.VersionConfigField.class); + var clientConfigVersion = ConfigTools.softLoad(clientConfigFile, Jsons.VersionConfigField.class); if (clientConfigVersion != null) { - // Update the configs schemes to not crash the game if loaded with old config! if (clientConfigVersion.DO_NOT_CHANGE_IT == 1) { + // Update the configs schemes to not crash the game if loaded with old config! var clientConfigV1 = ConfigTools.load(clientConfigFile, Jsons.ClientConfigFieldsV1.class); - // update to v2 - just delete the installedModpacks - if (clientConfigV1 != null) { - clientConfigV1.installedModpacks = null; - clientConfigV1.DO_NOT_CHANGE_IT = 2; + if (clientConfigV1 != null) { // update to V2 - just delete the installedModpacks clientConfigVersion.DO_NOT_CHANGE_IT = 2; + clientConfigV1.DO_NOT_CHANGE_IT = 2; + clientConfigV1.installedModpacks = null; } + ConfigTools.save(clientConfigFile, clientConfigV1); LOGGER.info("Updated client config version to {}", clientConfigVersion.DO_NOT_CHANGE_IT); } - -// if (clientConfigVersion.DO_NOT_CHANGE_IT == 2) { -// // Noice! -// } } clientConfig = ConfigTools.load(clientConfigFile, Jsons.ClientConfigFieldsV2.class); @@ -145,8 +144,44 @@ private void loadConfigs() { clientConfig = ConfigTools.load(clientConfigOverride, Jsons.ClientConfigFieldsV2.class); } + var serverConfigVersion = ConfigTools.softLoad(serverConfigFile, Jsons.VersionConfigField.class); + if (serverConfigVersion != null) { + if (serverConfigVersion.DO_NOT_CHANGE_IT == 1) { + // Update the configs schemes to make this update not as breaking as it could be + var serverConfigV1 = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFieldsV1.class); + var serverConfigV2 = ConfigTools.softLoad(serverConfigFile, Jsons.ServerConfigFieldsV2.class); + if (serverConfigV1 != null && serverConfigV2 != null) { + serverConfigVersion.DO_NOT_CHANGE_IT = 2; + serverConfigV2.DO_NOT_CHANGE_IT = 2; + + if (serverConfigV1.hostIp.isBlank()) { + serverConfigV2.addressToSend = ""; + } else { + serverConfigV2.addressToSend = AddressHelpers.parse(serverConfigV1.hostIp).getHostString(); + } + + if (serverConfigV1.hostLocalIp.isBlank()) { + serverConfigV2.localAddressToSend = ""; + } else { + serverConfigV2.localAddressToSend = AddressHelpers.parse(serverConfigV1.hostLocalIp).getHostString(); + } + + if (serverConfigV1.hostModpackOnMinecraftPort) { + serverConfigV2.bindPort = -1; + serverConfigV2.portToSend = -1; + } else { + serverConfigV2.bindPort = serverConfigV1.hostPort; + serverConfigV2.portToSend = serverConfigV1.hostPort; + } + } + + ConfigTools.save(serverConfigFile, serverConfigV2); + LOGGER.info("Updated server config version to {}", serverConfigVersion.DO_NOT_CHANGE_IT); + } + } + // load server config - serverConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFields.class); + serverConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFieldsV2.class); if (serverConfig != null) { // Add current loader to the list diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java index 8828b535a..b504f2249 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java @@ -308,8 +308,6 @@ public static Path renameModpackDir(Jsons.ModpackContentFields serverModpackCont String serverModpackName = serverModpackContent.modpackName; if (installedModpackAddresses != null && !serverModpackName.equals(installedModpackName) && !serverModpackName.isEmpty()) { - InetSocketAddress installedModpackAddress = installedModpackAddresses.hostAddress; - InetSocketAddress installedServerAddress = installedModpackAddresses.serverAddress; Path newModpackDir = modpackDir.getParent().resolve(serverModpackName); @@ -324,8 +322,7 @@ public static Path renameModpackDir(Jsons.ModpackContentFields serverModpackCont e.printStackTrace(); } - Jsons.ModpackAddresses modpackAddresses = new Jsons.ModpackAddresses(installedModpackAddress, installedServerAddress); - selectModpack(newModpackDir, modpackAddresses, Set.of()); + selectModpack(newModpackDir, installedModpackAddresses, Set.of()); return newModpackDir; } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java index 7b0994afe..a13b04d06 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java @@ -18,7 +18,7 @@ public abstract class ServerNetworkIoMixin { at = @At("TAIL") ) private void injectAutoModpackHost(Channel channel, CallbackInfo ci) { - if (!serverConfig.hostModpackOnMinecraftPort) { + if (serverConfig.bindPort != -1) { return; } diff --git a/src/main/java/pl/skidam/automodpack/modpack/Commands.java b/src/main/java/pl/skidam/automodpack/modpack/Commands.java index ea0d3bc71..d0d38bb1a 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/Commands.java +++ b/src/main/java/pl/skidam/automodpack/modpack/Commands.java @@ -114,7 +114,7 @@ private static int connections(CommandContext context) { private static int reload(CommandContext context) { Util.getMainWorkerExecutor().execute(() -> { - var tempServerConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFields.class); + var tempServerConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFieldsV2.class); if (tempServerConfig != null) { serverConfig = tempServerConfig; send(context, "AutoModpack server config reloaded!", Formatting.GREEN, true); diff --git a/src/main/java/pl/skidam/automodpack/networking/content/DataPacket.java b/src/main/java/pl/skidam/automodpack/networking/content/DataPacket.java index aa2775d88..913bad6c7 100644 --- a/src/main/java/pl/skidam/automodpack/networking/content/DataPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/content/DataPacket.java @@ -5,17 +5,19 @@ public class DataPacket { public String address; - public Integer port; + public int port; public String modpackName; public Secrets.Secret secret; public boolean modRequired; + public boolean requiresMagic; - public DataPacket(String address, Integer port, String modpackName, Secrets.Secret secret, boolean modRequired) { + public DataPacket(String address, int port, String modpackName, Secrets.Secret secret, boolean modRequired, boolean requiresMagic) { this.address = address; this.port = port; this.modpackName = modpackName; this.secret = secret; this.modRequired = modRequired; + this.requiresMagic = requiresMagic; } public String toJson() { diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java index 6f8314d43..d5fc811ea 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java @@ -30,13 +30,14 @@ public class DataC2SPacket { public static CompletableFuture receive(MinecraftClient minecraftClient, ClientLoginNetworkHandler handler, PacketByteBuf buf) { try { String serverResponse = buf.readString(Short.MAX_VALUE); - DataPacket dataPacket = DataPacket.fromJson(serverResponse); + String packetAddress = dataPacket.address; - Integer packetPort = dataPacket.port; + int packetPort = dataPacket.port; String modpackName = dataPacket.modpackName; Secrets.Secret secret = dataPacket.secret; boolean modRequired = dataPacket.modRequired; + boolean requiresMagic = dataPacket.requiresMagic; if (modRequired) { // TODO set screen to refreshed danger screen which will ask user to install modpack with two options @@ -52,27 +53,26 @@ public static CompletableFuture receive(MinecraftClient minecraft } // Get actual address of the server client have connected to and format it - InetSocketAddress modpackAddress = (InetSocketAddress) ((ClientLoginNetworkHandlerAccessor) handler).getConnection().getAddress(); - modpackAddress = AddressHelpers.format(modpackAddress.getHostString(), modpackAddress.getPort()); + InetSocketAddress connectedAddress = (InetSocketAddress) ((ClientLoginNetworkHandlerAccessor) handler).getConnection().getAddress(); + String effectiveHost; + // If the packet specifies a non-blank address, use it or else use address from the server client have connected to. if (packetAddress.isBlank()) { - if (packetPort != null) { // Server may just send port without address - modpackAddress = InetSocketAddress.createUnresolved(modpackAddress.getHostString(), packetPort); - } - LOGGER.info("Modpack address from connected server: {}:{}", modpackAddress.getHostString(), modpackAddress.getPort()); - } else if (packetPort != null) { - modpackAddress = InetSocketAddress.createUnresolved(packetAddress, packetPort); - LOGGER.info("Received modpack address packet {}:{}", packetAddress, packetPort); + effectiveHost = connectedAddress.getHostString(); } else { - modpackAddress = AddressHelpers.parse(packetAddress); - LOGGER.info("Received modpack address packet {} With attached port: {}", modpackAddress.getHostString(), modpackAddress.getPort()); + effectiveHost = packetAddress; } + // Construct the final modpack address + InetSocketAddress modpackAddress = AddressHelpers.format(effectiveHost, packetPort); + + LOGGER.info("Modpack address: {}:{} Requires to follow magic protocol: {}", modpackAddress.getHostString(), modpackAddress.getPort(), requiresMagic); + Boolean needsDisconnecting = null; PacketByteBuf response = new PacketByteBuf(Unpooled.buffer()); Path modpackDir = ModpackUtils.getModpackPath(modpackAddress, modpackName); - Jsons.ModpackAddresses modpackAddresses = new Jsons.ModpackAddresses(modpackAddress, serverAddress); + Jsons.ModpackAddresses modpackAddresses = new Jsons.ModpackAddresses(modpackAddress, serverAddress, requiresMagic); var optionalServerModpackContent = ModpackUtils.requestServerModpackContent(modpackAddresses, secret, true); if (optionalServerModpackContent.isPresent()) { diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java index ed5dc3320..8dc5c30f4 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java @@ -9,7 +9,6 @@ import net.minecraft.text.Text; import pl.skidam.automodpack.networking.PacketSender; import pl.skidam.automodpack.networking.server.ServerLoginNetworking; -import pl.skidam.automodpack_core.GlobalVariables; import pl.skidam.automodpack.client.ui.versioned.VersionedText; import pl.skidam.automodpack.mixin.core.ServerLoginNetworkHandlerAccessor; @@ -47,26 +46,24 @@ public static void receive(MinecraftServer server, ServerLoginNetworkHandler han LOGGER.error("Host server error. AutoModpack host server is down or server is not configured correctly"); - if (serverConfig.hostModpackOnMinecraftPort) { - LOGGER.warn("You are hosting AutoModpack host server on the minecraft port."); - LOGGER.warn("However client can't access it, try making `hostIp` and `hostLocalIp` blank in the server config."); - LOGGER.warn("If that doesn't work, follow the steps bellow."); + if (serverConfig.bindPort == -1) { + LOGGER.warn("You are hosting AutoModpack host server on the Minecraft port."); LOGGER.warn(""); } else { - LOGGER.warn("Please check if AutoModpack host server (TCP) port '{}' is forwarded / opened correctly", GlobalVariables.serverConfig.hostPort); + LOGGER.warn("Please check if AutoModpack host server (TCP) port '{}' is forwarded / opened correctly", serverConfig.bindPort); LOGGER.warn(""); } - LOGGER.warn("Make sure that host IP '{}' and host local IP '{}' are correct in the config file!", GlobalVariables.serverConfig.hostIp, GlobalVariables.serverConfig.hostLocalIp); - LOGGER.warn("host IP should be an ip which are players outside of server network connecting to and host local IP should be an ip which are players inside of server network connecting to"); - LOGGER.warn("It can be Ip or a correctly set domain"); - LOGGER.warn("If you need, change port in config file, forward / open it and restart server"); + LOGGER.warn("Make sure that 'addressToSend' and 'localAddressToSend' are correct in the config file!"); + LOGGER.warn("It can be either an IP address or a domain pointing to your modpack host server."); + LOGGER.warn("If nothing works, try changing the 'bindPort' in the config file, then forward / open it and restart server"); + LOGGER.warn("Note that some hosting providers may proxy this port internally and give you a different address and port to use. In this case, separate the given address with ':', and set the first part as 'addressToSend' and the second part as 'portToSend' in the config file."); - if (serverConfig.reverseProxy) { - LOGGER.error("Turn off reverseProxy in config, if you don't actually use it!"); + if (serverConfig.bindPort != serverConfig.portToSend && serverConfig.bindPort != -1 && serverConfig.portToSend != -1) { + LOGGER.error("bindPort '{}' is different than portToSend '{}'. If you are not using reverse proxy, match them! If you do use reverse proxy, make sure it is setup correctly.", serverConfig.bindPort, serverConfig.portToSend); } - LOGGER.warn("Server certificate fingerprint to verify: {}", hostServer.getCertificateFingerprint()); + LOGGER.warn("Server certificate fingerprint: {}", hostServer.getCertificateFingerprint()); } } catch (Exception e) { LOGGER.error("Error while handling DataS2CPacket", e); diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java index 5f68a5467..7c1751d8c 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java @@ -113,9 +113,9 @@ public static void handleHandshake(ClientConnection connection, GameProfile prof // If the player is connecting locally, use the local host IP if (AddressHelpers.isLocal(playerAddress)) { - addressToSend = serverConfig.hostLocalIp; + addressToSend = serverConfig.localAddressToSend; } else { - addressToSend = serverConfig.hostIp; + addressToSend = serverConfig.addressToSend; } // now we know player is authenticated, packets are encrypted and player is whitelisted @@ -123,35 +123,24 @@ public static void handleHandshake(ClientConnection connection, GameProfile prof Secrets.Secret secret = Secrets.generateSecret(); SecretsStore.saveHostSecret(profile.getId().toString(), secret); - // We send empty string if hostIp/hostLocalIp is not specified in server config. Client will use ip by which it connected to the server in first place. - DataPacket dataPacket = new DataPacket(addressToSend, null, serverConfig.modpackName, secret, serverConfig.requireAutoModpackOnClient); - - if (serverConfig.reverseProxy) { - // With reverse proxy we dont append port to the link, it should be already included in the link - // But we need to check if the port is set in the config, since that's where modpack is actually hosted - if (serverConfig.hostPort == -1 && !serverConfig.hostModpackOnMinecraftPort) { - LOGGER.error("Reverse proxy is enabled but host port is not set in config! Please set it manually."); - } - - LOGGER.info("Sending {} modpack url: {}", profile.getName(), addressToSend); - } else { // Append server port - int portToSend; - if (serverConfig.hostModpackOnMinecraftPort) { + int portToSend = serverConfig.portToSend; + if (portToSend == -1) { + if (serverConfig.bindPort == -1) { portToSend = minecraftServerPort; } else { - portToSend = serverConfig.hostPort; - - if (serverConfig.hostPort == -1) { - LOGGER.error("Host port is not set in config! Please set it manually."); - } + portToSend = serverConfig.bindPort; } + } - if (!addressToSend.isBlank()) { - LOGGER.info("Sending {} modpack url: {}:{}", profile.getName(), addressToSend, portToSend); - } - dataPacket = new DataPacket(addressToSend, portToSend, serverConfig.modpackName, secret, serverConfig.requireAutoModpackOnClient); + boolean requiresMagic = serverConfig.bindPort == -1; + + if (addressToSend.isBlank()) { + LOGGER.info("Sending {} modpack host port: {}", profile.getName(), portToSend); + } else { + LOGGER.info("Sending {} modpack host address: {}:{}", profile.getName(), addressToSend, portToSend); } + DataPacket dataPacket = new DataPacket(addressToSend, portToSend, serverConfig.modpackName, secret, serverConfig.requireAutoModpackOnClient, requiresMagic); String packetContentJson = dataPacket.toJson(); PacketByteBuf outBuf = new PacketByteBuf(Unpooled.buffer()); From 20e02f2cb5d40539f294fe32b9713d3f0bcd1b41 Mon Sep 17 00:00:00 2001 From: Juste Yanis <108187132+justeyanis@users.noreply.github.com> Date: Fri, 6 Jun 2025 10:42:57 +0200 Subject: [PATCH 33/86] Update fr_fr.json (#390) * Update fr_fr.json * fix format, encode file as UTF-8 without BOM --------- Co-authored-by: TheYalis <108187132+TheYalis@users.noreply.github.com> Co-authored-by: skidam --- .../assets/automodpack/lang/fr_fr.json | Bin 5450 -> 3433 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/automodpack/lang/fr_fr.json b/src/main/resources/assets/automodpack/lang/fr_fr.json index 5ec44f2b5a2bcf7b70853fd5b276dba058dd58c3..cff53b2c475acedcfc81563903dc5357f60df17d 100644 GIT binary patch literal 3433 zcmai1%Wm676y5tPrUn8hfWz*+DGbE{3bctE2icFwOL^Sk3_TCoR?&ayu2t6Eb;%#= zxkE~%q%m#4fGKhAoAOS&C7ymbJ%2e$Y%3XPi1TZo0&yI5e@77wVk=Lg`%B#0=;lLt(o}|nk{mrm zHP>(`!Mp;6+fewq9#ib%& zI@NM%obH;tcXV%+sT`f-Pc2PJYi{bEVlfA?r}D#ov~M0$d-}~{JLJehi%_)UEK_J_ zcp74bJ9R+QRUqG!dID>aV1Tl#2 zdvKUcY$a{ghYpK8rM%jx5F2e+1o`)CnVy3J%F? z+M3N=s}q90j;8qnGkselGpL#C*aIXxLvUiSqwTU-DSF)biL7f?-tpi{p_LB=iJ`6Z zaz7JqcPIV3oLF{W7sf8qInU8AV+qQ~K!WNTHVdS)!!$;*EtzY%O#9$-TsfAyM3YOD z78&Au3fT@T`)IV4RZ&V^MlFLf%OuA1NJWuNygNlf^4njjY1t96;7qs>Z+x!j-J$It zL#io%jGnldJ#Fkq1qT}8VlEWOz^>b4l;v}5i}04AWsdNL-tV1ZgP3v1@$liSEwEQa zi?Makxp3Xa+X%-@UJ3Ybb@xvLjmQ<1oMSLd2KTr`a?U14_$`uthy+1c>~f!mu#uX@ ziv=t$e#`-tP8anSqo14BdarJg6h64$Y)KANXS^67^jWg|fLN+QK_raY6_y^>YWj%l z)7GWIokmYgj;moNGR7;h`K{YHRjag{v1IUR0#vjb4uaaTZc(jfZ5?A)Qz-8eqqslC z7NH|(G;A~uV z8%ppUm$s5x;jV^iiU1eq2x+G#XM~R#E^N*6#q8g8|H4lAlC<5a%J4h9EWo5h_)4byj z^_i?1W#F0Mhcz^@`^mDvYbykW&a=9i^nJYi7laMH?Hld&Di9c$GD5?c8{p7=hkvt0 z)>8W(hOJr>BTsnZTaUqJ4LqED2ikYC8Gz;fBy7)coM4ibr42j6Pj=pF7=*-m9EF(mv9)?VH#@P z8R=J}>zSTg=yedj)!Kvbz3x{#t13NRg*!cer*A$NdXK~VxXZbIf2EaA2g%eLhD=hr0q=UVRmIpIy zK{b^I*ZRD#NgeHK66Qj^)a(CPHqQXZeio6`LWv|ip11WxNSzN=c%ds;)winYwP<%5 zYiAgRzNsy^hKFCW#&F+EB&(KZ*3w}me5R9YdLmqAZIjOZaeNYAJZ(`fglwt*r{Pxr z82SrcZ4@IzxsA8S;rKyxXAvFpZo@M%1FY}{%T46}%V;sKup3u%;Urp?5pffq55_l+ z9-C>$wRU!U{U`+UXqQ=(da2L8MTXjAytQ$cZ(c_XcwsK9yXM!rLc@uW!Ouqb;WHNC z|4plNNydX6*f-5Er`lnq7mQ)Yj+1?6mui~II27uK_-G!t+<;_sG}aMW=6&$;gk@UO zxrzUz@#tASpOedZ+ecr{S`f1KdaFU6wZo#RkE&6%nLiRsFQbp^UL4ioWArY&Rc(Bc zso=b`np)9kLwQW~QRX8m$2Ry_wRNJ2xYxyTyerc)R~J=prLD0EEiG~^n;E0a;)Z?C zwG+s!J2;9~+^u4>$lJA^o=2@V`sFpF_vC)kV1Lgk~D;PZdg%7$%?z?I;Ae|lSL1i&brtN!qsJ}W4X^r_)`{y*>Ck9eq5de|=rC&}s0W7D=vY9xEORG9TjR(YLlsr>;Hwf()Fv5dGa<#dY9rbC<}m(gxMk9SYG z)yOaFh{Li~4tJMPbKUtcyp~zJY7J4!PytN*>vE6_HuSwX+29AeX5dW=({d_~Puu?(oD)_w9_tA{OnK z^xkU)vMSXzc7U<3q8F$v`(^D8HT)ziGgG<-WaWoYru7lr``8QA*gZWjI+dmTKR@9~ z&l_@?tHd|INlwQUYVN!^M#%DR51YkHqbt{Qy^wvG&e5{GmwP8Zayscaj)kebUfyUm my|1r6Rc<2_rCl Date: Fri, 6 Jun 2025 18:09:26 +0200 Subject: [PATCH 34/86] Add translated Polish welcome message to the documentation --- docs/.translated/pl_pl/_homepage.mdx | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docs/.translated/pl_pl/_homepage.mdx diff --git a/docs/.translated/pl_pl/_homepage.mdx b/docs/.translated/pl_pl/_homepage.mdx new file mode 100644 index 000000000..0adcf7818 --- /dev/null +++ b/docs/.translated/pl_pl/_homepage.mdx @@ -0,0 +1,7 @@ +Witaj w dokumentacji AutoModpack'a! + +Jeśli jesteś nowy do AutoModpack'a, zalecamy rozpoczęcie od [Przewodnika Szybkiego Startu](docs/quick-start), aby zapoznać się z podstawami korzystania z AutoModpack'a. + +Jeśli szukasz bardziej szczegółowych informacji, znajdziesz je w sekcji `👨‍💻 Szczegóły Techniczne`. + +Masz pytania lub potrzebujesz pomocy? Dołącz na nasz [serwer Discord](https://discord.gg/hS6aMyeA9P). \ No newline at end of file From 70aede5ebcb9e1c3b78dd4de678dea77c6dab6e3 Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 6 Jun 2025 18:24:12 +0200 Subject: [PATCH 35/86] Fix address binding logic to handle cases with unassigned ports --- .../protocol/netty/NettyServer.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java index 70f9eaf5a..5b479ab2d 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java @@ -19,7 +19,9 @@ import pl.skidam.automodpack_core.utils.AddressHelpers; import pl.skidam.automodpack_core.utils.ObservableMap; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.nio.file.Files; import java.nio.file.Path; import java.security.KeyPair; @@ -81,14 +83,20 @@ public Optional start() { try { String address = serverConfig.bindAddress; int port = serverConfig.bindPort; - InetSocketAddress bindAddress; - if (address == null || address.isBlank()) { - bindAddress = new InetSocketAddress(port); + InetSocketAddress bindAddress = null; + InetAddress inetAddress; + if (port == -1) { + inetAddress = InetAddress.getByName(address); } else { - bindAddress = new InetSocketAddress(address, port); + if (address == null || address.isBlank()) { + bindAddress = new InetSocketAddress(port); + } else { + bindAddress = new InetSocketAddress(address, port); + } + inetAddress = bindAddress.getAddress(); } - boolean bindsOnLoopback = bindAddress.getAddress().isLoopbackAddress(); + boolean bindsOnLoopback = inetAddress.isLoopbackAddress(); if (serverConfig.disableInternalTLS && serverConfig.bindPort != -1 && bindsOnLoopback) { LOGGER.warn("Internal TLS is disabled. Clients will not be able to connect directly; you must use e.g. a reverse proxy with TLS."); } else { From 4678aeb21f066e20e48cae986a52a31068a4aa6a Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 6 Jun 2025 18:35:26 +0200 Subject: [PATCH 36/86] Refactor SpeedMeter to calculate average download speed over the last 3 seconds and update ETA formatting --- .../utils/SpeedMeter.java | 31 ++++++++++--------- .../automodpack/client/ui/DownloadScreen.java | 2 +- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/SpeedMeter.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/SpeedMeter.java index 8289164ab..8867e461e 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/SpeedMeter.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/SpeedMeter.java @@ -6,7 +6,7 @@ public class SpeedMeter { private final DownloadManager downloadManager; private final ConcurrentSkipListMap bytesDownloadedPerSec = new ConcurrentSkipListMap<>(); - private static final int MAX_ENTRIES = 5; + private static final int MAX_ENTRIES = 3; public SpeedMeter(DownloadManager downloadManager) { this.downloadManager = downloadManager; @@ -26,20 +26,18 @@ public synchronized void addDownloadedBytes(long newBytes) { } /** - * Get the download speed in bytes per second. + * Get the average download speed in bytes per second from the last few seconds. */ - public synchronized long getCurrentSpeedInBytes() { - long lastTimeBucket = System.currentTimeMillis() / 1000 * 1000 - 1000; + public long getAverageSpeedOfLastFewSeconds(int seconds) { + long totalBytes = 0; + int count = 0; - Long value = -1L; - - if (bytesDownloadedPerSec.containsKey(lastTimeBucket)) { - value = bytesDownloadedPerSec.get(lastTimeBucket); - } else if (bytesDownloadedPerSec.containsKey(lastTimeBucket - 1000)) { - value = bytesDownloadedPerSec.get(lastTimeBucket - 1000); + for (Long bytes : bytesDownloadedPerSec.values()) { + totalBytes += bytes; + count++; } - return value != null ? value : -1; + return count >= seconds ? totalBytes / count : -1; } /** @@ -47,7 +45,7 @@ public synchronized long getCurrentSpeedInBytes() { */ public long getETAInSeconds() { long totalBytesRemaining = downloadManager.getTotalBytesRemaining(); - long speed = getCurrentSpeedInBytes(); + long speed = getAverageSpeedOfLastFewSeconds(3); if (speed <= 0) { return -1; @@ -70,13 +68,18 @@ public static String formatDownloadSpeedToMbps(long currentSpeedInBytes) { } /** - * Format ETA into HH:MM:SS. + * Format ETA into MM:SS format or HH:MM:SS format. */ public static String formatETAToSeconds(long seconds) { - if (seconds < 0) { + seconds++; // Increment by 1 to avoid showing 00:00:00 for 0 seconds + if (seconds < 1) { return "-1"; } + if (seconds < 3600) { + return String.format("%02d:%02d", seconds / 60, seconds % 60); + } + return String.format("%02d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, seconds % 60); } } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java index 0cd97effe..aa67002d3 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java @@ -49,7 +49,7 @@ protected void init() { while (downloadManager != null && downloadManager.isRunning()) { lastStage = downloadManager.getStage(); lastPercentage = downloadManager.getTotalPercentageOfFileSizeDownloaded(); - lastSpeed = SpeedMeter.formatDownloadSpeedToMbps(downloadManager.getSpeedMeter().getCurrentSpeedInBytes()); + lastSpeed = SpeedMeter.formatDownloadSpeedToMbps(downloadManager.getSpeedMeter().getAverageSpeedOfLastFewSeconds(1)); lastETA = SpeedMeter.formatETAToSeconds(downloadManager.getSpeedMeter().getETAInSeconds()); } }); From 6136590b4cffa77cea1eb6cd82ea569804678c89 Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 6 Jun 2025 18:35:37 +0200 Subject: [PATCH 37/86] Fix fingerprint command --- src/main/java/pl/skidam/automodpack/modpack/Commands.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/pl/skidam/automodpack/modpack/Commands.java b/src/main/java/pl/skidam/automodpack/modpack/Commands.java index d0d38bb1a..9f26a0fce 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/Commands.java +++ b/src/main/java/pl/skidam/automodpack/modpack/Commands.java @@ -50,9 +50,10 @@ public static void register(CommandDispatcher dispatcher) { .requires((source) -> source.hasPermissionLevel(3)) .executes(Commands::connections) ) - .then(literal("fingerprint")) - .requires((source) -> source.hasPermissionLevel(3)) - .executes(Commands::fingerprint) + .then(literal("fingerprint") + .requires((source) -> source.hasPermissionLevel(3)) + .executes(Commands::fingerprint) + ) ) .then(literal("config") .requires((source) -> source.hasPermissionLevel(3)) From c0fa9175988d94b459597b36a5ddb13cdec82aa7 Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 6 Jun 2025 18:36:04 +0200 Subject: [PATCH 38/86] Remove `Files` text from the summary --- .../java/pl/skidam/automodpack/client/ui/ChangelogScreen.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java index 20c06abc3..d769eade4 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java @@ -113,7 +113,7 @@ private void drawSummaryOfChanges(VersionedMatrices matrices) { int filesAdded = changelogs.changesAddedList.size(); int filesRemoved = changelogs.changesDeletedList.size(); - String summary = "Files: + " + filesAdded + " | - " + filesRemoved; + String summary = "+ " + filesAdded + " | - " + filesRemoved; drawCenteredTextWithShadow(matrices, textRenderer, VersionedText.literal(summary), this.width / 2, 5, 16777215); } From 869081f0dc659c1e2ee3be5fc48cee7ea9375fc1 Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 6 Jun 2025 18:49:43 +0200 Subject: [PATCH 39/86] Bump versions to beta 36 --- core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java | 2 +- gradle.properties | 2 +- loader/loader-forge.gradle.kts | 2 +- versions/1.20.1-fabric/gradle.properties | 2 +- versions/1.20.4-fabric/gradle.properties | 2 +- versions/1.20.6-fabric/gradle.properties | 2 +- versions/1.21.1-fabric/gradle.properties | 2 +- versions/1.21.3-fabric/gradle.properties | 2 +- versions/1.21.4-fabric/gradle.properties | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java b/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java index 71a02841d..a633ef77a 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java +++ b/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java @@ -113,7 +113,7 @@ public static class ServerConfigFieldsV2 { } public static class ServerCoreConfigFields { - public String automodpackVersion = "4.0.0-beta36"; // TODO: dont hardcode it + public String automodpackVersion = "4.0.0-beta37"; // TODO: dont hardcode it public String loader = "fabric"; public String loaderVersion = "0.16.14"; public String mcVersion = "1.21.1"; diff --git a/gradle.properties b/gradle.properties index 8076bac0c..d2b8d0f82 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,6 +16,6 @@ mixin_extras = 0.5.0-rc.2 mod_id = automodpack mod_name = AutoModpack -mod_version = 4.0.0-beta36 +mod_version = 4.0.0-beta37 mod_group = pl.skidam.automodpack mod_description = Enjoy a seamless modpack installation process and effortless updates with a user-friendly solution that simplifies management, making your gaming experience a breeze. diff --git a/loader/loader-forge.gradle.kts b/loader/loader-forge.gradle.kts index f4de4a140..7ade63daa 100644 --- a/loader/loader-forge.gradle.kts +++ b/loader/loader-forge.gradle.kts @@ -40,7 +40,7 @@ dependencies { implementation("org.tomlj:tomlj:1.1.1") implementation("org.bouncycastle:bcpkix-jdk18on:1.80") implementation("com.github.luben:zstd-jni:1.5.7-3") - implementation("org.apache.httpcomponents.client5:httpclient5:5.4.4") + implementation("org.apache.httpcomponents.client5:httpclient5:5.5") if (project.name.contains("neoforge")) { "neoForge"("net.neoforged:neoforge:${property("loader_neoforge")}") diff --git a/versions/1.20.1-fabric/gradle.properties b/versions/1.20.1-fabric/gradle.properties index 967732db3..be0dcb541 100644 --- a/versions/1.20.1-fabric/gradle.properties +++ b/versions/1.20.1-fabric/gradle.properties @@ -2,7 +2,7 @@ minecraft_version=1.20.1 yarn_mappings=1.20.1+build.10 loom.platform=fabric minecraft_dependency=>=1.20 <=1.20.1 -fabric_version=0.92.5+1.20.1 +fabric_version=0.92.6+1.20.1 # The target mc versions for the mod during mod publishing, separated with \n game_versions=1.20\n1.20.1 \ No newline at end of file diff --git a/versions/1.20.4-fabric/gradle.properties b/versions/1.20.4-fabric/gradle.properties index eab4d0458..fdb9ada15 100644 --- a/versions/1.20.4-fabric/gradle.properties +++ b/versions/1.20.4-fabric/gradle.properties @@ -2,7 +2,7 @@ minecraft_version=1.20.4 yarn_mappings=1.20.4+build.3 loom.platform=fabric minecraft_dependency=1.20.4 -fabric_version=0.97.1+1.20.4 +fabric_version=0.97.2+1.20.4 # The target mc versions for the mod during mod publishing, separated with \n game_versions=1.20.4 \ No newline at end of file diff --git a/versions/1.20.6-fabric/gradle.properties b/versions/1.20.6-fabric/gradle.properties index afcb225bb..0cf8f8e49 100644 --- a/versions/1.20.6-fabric/gradle.properties +++ b/versions/1.20.6-fabric/gradle.properties @@ -2,7 +2,7 @@ minecraft_version=1.20.6 yarn_mappings=1.20.6+build.3 loom.platform=fabric minecraft_dependency=1.20.6 -fabric_version=0.100.0+1.20.6 +fabric_version=0.100.8+1.20.6 # The target mc versions for the mod during mod publishing, separated with \n game_versions=1.20.6 \ No newline at end of file diff --git a/versions/1.21.1-fabric/gradle.properties b/versions/1.21.1-fabric/gradle.properties index 5a8155fee..d76de054b 100644 --- a/versions/1.21.1-fabric/gradle.properties +++ b/versions/1.21.1-fabric/gradle.properties @@ -2,7 +2,7 @@ minecraft_version=1.21.1 yarn_mappings=1.21.1+build.3 loom.platform=fabric minecraft_dependency=>=1.21 <=1.21.1 -fabric_version=0.116.0+1.21.1 +fabric_version=0.116.1+1.21.1 # The target mc versions for the mod during mod publishing, separated with \n game_versions=1.21\n1.21.1 \ No newline at end of file diff --git a/versions/1.21.3-fabric/gradle.properties b/versions/1.21.3-fabric/gradle.properties index a4bcde35e..8c1754d57 100644 --- a/versions/1.21.3-fabric/gradle.properties +++ b/versions/1.21.3-fabric/gradle.properties @@ -2,7 +2,7 @@ minecraft_version=1.21.3 yarn_mappings=1.21.3+build.2 loom.platform=fabric minecraft_dependency=>=1.21.2 <=1.21.3 -fabric_version=0.114.0+1.21.3 +fabric_version=0.114.1+1.21.3 # The target mc versions for the mod during mod publishing, separated with \n game_versions=1.21.2\n1.21.3 \ No newline at end of file diff --git a/versions/1.21.4-fabric/gradle.properties b/versions/1.21.4-fabric/gradle.properties index 36b761cca..90b002725 100644 --- a/versions/1.21.4-fabric/gradle.properties +++ b/versions/1.21.4-fabric/gradle.properties @@ -2,7 +2,7 @@ minecraft_version=1.21.4 yarn_mappings=1.21.4+build.8 loom.platform=fabric minecraft_dependency=>=1.21.4 -fabric_version=0.119.2+1.21.4 +fabric_version=0.119.3+1.21.4 # The target mc versions for the mod during mod publishing, separated with \n game_versions=1.21.4 \ No newline at end of file From a5dc901aa135c0e85d62f95481adab512b7514d7 Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 6 Jun 2025 19:33:09 +0200 Subject: [PATCH 40/86] Fix 1.21.5 commands compile --- src/main/java/pl/skidam/automodpack/modpack/Commands.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/pl/skidam/automodpack/modpack/Commands.java b/src/main/java/pl/skidam/automodpack/modpack/Commands.java index 9f26a0fce..b62278192 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/Commands.java +++ b/src/main/java/pl/skidam/automodpack/modpack/Commands.java @@ -76,8 +76,8 @@ private static int fingerprint(CommandContext context) { if (fingerprint != null) { MutableText fingerprintText = VersionedText.literal(fingerprint).styled(style -> style /*? if >1.21.4 {*/ - /*.withHoverEvent(new HoverEvent.ShowText, VersionedText.translatable("chat.copy.click")) - /*.withClickEvent(new ClickEvent.CopyToClipboard(fingerprint))); + /*.withHoverEvent(new HoverEvent.ShowText(VersionedText.translatable("chat.copy.click"))) + .withClickEvent(new ClickEvent.CopyToClipboard(fingerprint))); *//*?} else {*/ .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, VersionedText.translatable("chat.copy.click"))) .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, fingerprint))); From 0c6bb8dd3d05de00e45ca3d9a69f3ba1670204f4 Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 7 Jun 2025 13:21:25 +0200 Subject: [PATCH 41/86] Add Java toolchain configuration for version 17 finally found a way to force gradle to make sure it compiles the core module for java 17 --- core/build.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 965cd2955..7848288a7 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -32,6 +32,10 @@ java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + withSourcesJar() } From a982665d01e82f463335eb6d6aee3644c2e9a098 Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 7 Jun 2025 13:22:04 +0200 Subject: [PATCH 42/86] Update httpclient5 dependency to version 5.5 --- core/build.gradle.kts | 2 +- loader/loader-fabric-core.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 7848288a7..a8ae4e434 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -22,7 +22,7 @@ dependencies { implementation("org.bouncycastle:bcpkix-jdk18on:1.80") implementation("com.github.luben:zstd-jni:1.5.7-3") implementation("org.tomlj:tomlj:1.1.1") - implementation("org.apache.httpcomponents.client5:httpclient5:5.4.4") + implementation("org.apache.httpcomponents.client5:httpclient5:5.5") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") } diff --git a/loader/loader-fabric-core.gradle.kts b/loader/loader-fabric-core.gradle.kts index 273276445..36d3a2abb 100644 --- a/loader/loader-fabric-core.gradle.kts +++ b/loader/loader-fabric-core.gradle.kts @@ -28,7 +28,7 @@ dependencies { implementation("org.tomlj:tomlj:1.1.1") implementation("org.bouncycastle:bcpkix-jdk18on:1.80") implementation("com.github.luben:zstd-jni:1.5.7-3") - implementation("org.apache.httpcomponents.client5:httpclient5:5.4.4") + implementation("org.apache.httpcomponents.client5:httpclient5:5.5") compileOnly("net.fabricmc:fabric-loader:${property("loader_fabric")}") } From 7a9a215a2588952cf68509e04f923a34872add6f Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 7 Jun 2025 13:25:46 +0200 Subject: [PATCH 43/86] Add Java toolchain configuration for version 17 and 21 everywhere --- build.gradle.kts | 8 ++++++++ loader/loader-core.gradle.kts | 4 ++++ loader/loader-fabric-core.gradle.kts | 4 ++++ loader/loader-fabric.gradle.kts | 4 ++++ loader/loader-forge.gradle.kts | 8 ++++++++ 5 files changed, 28 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 351d40fc0..622316ede 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -241,9 +241,17 @@ java { if (minecraftVersion.needsJava21) { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 + + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } } else { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 + + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } } withSourcesJar() diff --git a/loader/loader-core.gradle.kts b/loader/loader-core.gradle.kts index 8b88d1d45..d0a01009c 100644 --- a/loader/loader-core.gradle.kts +++ b/loader/loader-core.gradle.kts @@ -26,6 +26,10 @@ java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + withSourcesJar() } diff --git a/loader/loader-fabric-core.gradle.kts b/loader/loader-fabric-core.gradle.kts index 36d3a2abb..1fa530690 100644 --- a/loader/loader-fabric-core.gradle.kts +++ b/loader/loader-fabric-core.gradle.kts @@ -83,6 +83,10 @@ java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + withSourcesJar() } diff --git a/loader/loader-fabric.gradle.kts b/loader/loader-fabric.gradle.kts index 50d47b0dc..bea430ccb 100644 --- a/loader/loader-fabric.gradle.kts +++ b/loader/loader-fabric.gradle.kts @@ -42,6 +42,10 @@ java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + withSourcesJar() } diff --git a/loader/loader-forge.gradle.kts b/loader/loader-forge.gradle.kts index 7ade63daa..5c758b635 100644 --- a/loader/loader-forge.gradle.kts +++ b/loader/loader-forge.gradle.kts @@ -95,9 +95,17 @@ java { if (mcVer >= 1206) { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 + + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } } else { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 + + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } } withSourcesJar() From 2026967980b106ec607fbd7da9042463ae321285 Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 7 Jun 2025 14:00:09 +0200 Subject: [PATCH 44/86] Update to stonecutter 0.7-alpha.21 --- .github/workflows/build.yml | 3 +-- CONTRIBUTING.md | 2 +- build.gradle.kts | 5 +++++ settings.gradle.kts | 5 +++-- .../client/ui/widget/ListEntryWidget.java | 2 +- .../mixin/core/ConnectScreenMixin.java | 2 +- .../mixin/core/MusicTrackerMixin.java | 4 ++-- .../mixin/core/PlayerManagerMixin.java | 2 +- .../skidam/automodpack/modpack/Commands.java | 2 +- .../networking/LoginQueryParser.java | 2 +- stonecutter.gradle.kts | 20 ++++++++++--------- 11 files changed, 28 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b2a39e938..ffcd23a5c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,8 +48,7 @@ jobs: if [ -z "${{ inputs.target_subproject }}" ]; then echo "Building all subprojects" ./gradlew clean - ./gradlew chiseledBuild -x mergeJars - ./gradlew mergeJars + ./gradlew build else args=$(echo "${{ inputs.target_subproject }}" | tr ',' '\n' | sed 's/$/:build/' | paste -sd ' ') echo "Building with arguments=$args" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83226ad34..91803241c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ If you're a developer, you can contribute to the AutoModpack project by writing 1. Discuss the changes you want to make. By e.g. creating an issue. 2. Fork the repository and create a new branch for your feature or bug fix. 3. Make your code changes. -4. Build code with `./gradlew chiseledBuild` command. +4. Build code with `./gradlew build` command. 5. Test your changes thoroughly to prevent regressions. 6. Submit a pull request, describing your changes and providing relevant context. diff --git a/build.gradle.kts b/build.gradle.kts index 622316ede..5a7ce50c6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,7 @@ import java.util.* plugins { + id("dev.kikugie.stonecutter") id("dev.architectury.loom") } @@ -265,4 +266,8 @@ tasks.named("jar") { from(rootProject.file("LICENSE")) { rename { "${it}_${mod.id}" } } +} + +tasks.named("build") { + finalizedBy(rootProject.tasks.named("mergeJars")) } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index cf90ae829..a1113558f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,14 +6,15 @@ pluginManagement { maven { url = uri("https://maven.fabricmc.net/") } maven { url = uri("https://maven.neoforged.net/releases") } maven { url = uri("https://files.minecraftforge.net/maven/") } + maven { url = uri("https://maven.kikugie.dev/snapshots") } mavenLocal() } } plugins { - id("com.github.johnrengelman.shadow") version "8.1.1" apply false + id("dev.kikugie.stonecutter") version "0.7-alpha.21" id("dev.architectury.loom") version "1.9-SNAPSHOT" apply false // with 1.10 wait for remap fixes on neo/forge - id("dev.kikugie.stonecutter") version "0.6" + id("com.github.johnrengelman.shadow") version "8.1.1" apply false } include(":core") diff --git a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java index 016c39976..e63d099b4 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java @@ -58,7 +58,7 @@ public ListEntryWidget(Map changelogs, MinecraftClient client, i } *//*?}*/ - /*? if >1.21.3 {*/ + /*? if >1.21.4 {*/ /*public double getScrollAmount() { return this.getScrollY(); } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java index 58d98bf70..af0ebf729 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java @@ -26,7 +26,7 @@ public abstract class ConnectScreenMixin { /*? if >= 1.20.5 {*/ @Inject(method = "connect(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ServerAddress;Lnet/minecraft/client/network/ServerInfo;Lnet/minecraft/client/network/CookieStorage;)V", at = @At("HEAD")) public void onConnect(MinecraftClient client, ServerAddress address, ServerInfo info, CookieStorage cookieStorage, CallbackInfo ci) { - /*?} else if > 1.19.2 {*/ + /*?} else if > 1.19.3 {*/ /*@Inject(method = "connect(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ServerAddress;Lnet/minecraft/client/network/ServerInfo;)V", at = @At("HEAD")) public void onConnect(MinecraftClient client, ServerAddress address, ServerInfo info, CallbackInfo ci) { *//*?} else {*/ diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java index 38f39ac66..e2caa1748 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java @@ -1,7 +1,7 @@ package pl.skidam.automodpack.mixin.core; -/*? if >1.21.3 {*/ +/*? if >1.21.4 {*/ /*import net.minecraft.client.sound.MusicInstance; *//*?} else {*/ import net.minecraft.sound.MusicSound; @@ -21,7 +21,7 @@ public class MusicTrackerMixin { at = @At("HEAD"), cancellable = true ) - /*? if >1.21.3 {*/ + /*? if >1.21.4 {*/ /*private void play(MusicInstance music, CallbackInfo ci) { *//*?} else {*/ private void play(MusicSound type, CallbackInfo ci) { diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java index 76e62d66d..08bb93d94 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java @@ -46,7 +46,7 @@ private void onPlayerConnect(ClientConnection connection, ServerPlayerEntity pla // Send chat nag message which is clickable and opens the link Text nagText = VersionedText.literal(serverConfig.nagMessage).styled(style -> style.withBold(true)); Text nagClickableText = VersionedText.literal(serverConfig.nagClickableMessage).styled(style -> style.withUnderline(true).withColor(TextColor.fromFormatting(Formatting.BLUE)) - /*? if >1.21.4 {*/ + /*? if >1.21.5 {*/ /*.withClickEvent(new ClickEvent.OpenUrl(URI.create(serverConfig.nagClickableLink)))); *//*?} else {*/ .withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, serverConfig.nagClickableLink))); diff --git a/src/main/java/pl/skidam/automodpack/modpack/Commands.java b/src/main/java/pl/skidam/automodpack/modpack/Commands.java index b62278192..fd24ba443 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/Commands.java +++ b/src/main/java/pl/skidam/automodpack/modpack/Commands.java @@ -75,7 +75,7 @@ private static int fingerprint(CommandContext context) { String fingerprint = hostServer.getCertificateFingerprint(); if (fingerprint != null) { MutableText fingerprintText = VersionedText.literal(fingerprint).styled(style -> style - /*? if >1.21.4 {*/ + /*? if >1.21.5 {*/ /*.withHoverEvent(new HoverEvent.ShowText(VersionedText.translatable("chat.copy.click"))) .withClickEvent(new ClickEvent.CopyToClipboard(fingerprint))); *//*?} else {*/ diff --git a/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java b/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java index 5ab854890..de155e776 100644 --- a/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java +++ b/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java @@ -1,6 +1,6 @@ package pl.skidam.automodpack.networking; -/*? if <=1.19.2 {*/ +/*? if <=1.19.3 {*/ /*import net.minecraft.network.Packet; *//*?} else {*/ import net.minecraft.network.packet.Packet; diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 4030f09b1..97cbbc0ee 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -18,15 +18,17 @@ wiki { stonecutter active "1.21.1-neoforge" /* [SC] DO NOT EDIT */ -stonecutter registerChiseled tasks.register("chiseledBuild", stonecutter.chiseled) { - group = "project" - ofTask("build") - finalizedBy("mergeJars") -} - -stonecutter parameters { - val loader = metadata.project.substringAfterLast("-") - consts(loader, "fabric", "forge", "neoforge") +//stonecutter registerChiseled tasks.register("chiseledBuild", stonecutter.chiseled) { +// group = "project" +// ofTask("build") +// finalizedBy("mergeJars") +//} + +stonecutter.parameters { + constants { + val loader: String = metadata.project.substringAfter('-') + match(loader, "fabric", "forge", "neoforge") + } } // Non stonecutter stuff From 873a1e9e1757c5dd94be8ce36fcdf6cec1044728 Mon Sep 17 00:00:00 2001 From: skidam Date: Sat, 7 Jun 2025 21:01:28 +0200 Subject: [PATCH 45/86] Enhance documentation for certificate verification and add donation link --- docs/_homepage.mdx | 4 +++- docs/technicals/certificate.mdx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/_homepage.mdx b/docs/_homepage.mdx index ff5c66a95..d2945a29e 100644 --- a/docs/_homepage.mdx +++ b/docs/_homepage.mdx @@ -4,4 +4,6 @@ If you are new to AutoModpack, we recommend starting with the [Quick Start Guide If you are looking for more detailed information, you can find it in the `👨‍💻 Technical Details` section. -Got any questions or need help? Join our [Discord server](https://discord.gg/hS6aMyeA9P). \ No newline at end of file +Got any questions or need help? Join our [Discord server](https://discord.gg/hS6aMyeA9P). + +Found this project useful? Consider supporting its ongoing development and maintenance by donating to the author on [Ko-fi](https://ko-fi.com/skidam). diff --git a/docs/technicals/certificate.mdx b/docs/technicals/certificate.mdx index f3e2b3676..ecde1d062 100644 --- a/docs/technicals/certificate.mdx +++ b/docs/technicals/certificate.mdx @@ -1,5 +1,5 @@ ### How to verify the certificate fingerprint? -**Just copy the fingerprint from your server console to the game.** This prevents attacks such as [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). Fingerprint is the same for everyone on your server, its recommended to share it with players ahead of time. +To verify, simply **copy the fingerprint from your server console to the game.** This is a one-time verification, meaning you won't need to repeat it after the initial connection. This process helps prevent security threats like [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). The fingerprint is the same for everyone on your server and it never changes, its recommended to share it with players ahead of time. If you can't find the fingerprint in the console, you can retrieve it at any time using the `/automodpack host fingerprint` [command](../commands/commands). From 79db008671ee032db817c8e06ff2311c109f9e21 Mon Sep 17 00:00:00 2001 From: skidam Date: Mon, 9 Jun 2025 14:33:21 +0200 Subject: [PATCH 46/86] Update stonecutter to 0.7-alpha.22 --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index a1113558f..5387b7937 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,7 +12,7 @@ pluginManagement { } plugins { - id("dev.kikugie.stonecutter") version "0.7-alpha.21" + id("dev.kikugie.stonecutter") version "0.7-alpha.22" id("dev.architectury.loom") version "1.9-SNAPSHOT" apply false // with 1.10 wait for remap fixes on neo/forge id("com.github.johnrengelman.shadow") version "8.1.1" apply false } From 0d2ff712ce50194c3f632027dd56ab52880e598f Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 11 Jun 2025 12:21:28 +0200 Subject: [PATCH 47/86] Don't require loopback address for `disableInternalTLS` --- .../protocol/netty/NettyServer.java | 33 ++++++++----------- docs/configuration/server-config.mdx | 2 +- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java index 5b479ab2d..c4d05799a 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java @@ -19,9 +19,7 @@ import pl.skidam.automodpack_core.utils.AddressHelpers; import pl.skidam.automodpack_core.utils.ObservableMap; -import java.net.InetAddress; import java.net.InetSocketAddress; -import java.net.SocketAddress; import java.nio.file.Files; import java.nio.file.Path; import java.security.KeyPair; @@ -81,27 +79,11 @@ public Optional start() { } try { - String address = serverConfig.bindAddress; - int port = serverConfig.bindPort; - InetSocketAddress bindAddress = null; - InetAddress inetAddress; - if (port == -1) { - inetAddress = InetAddress.getByName(address); - } else { - if (address == null || address.isBlank()) { - bindAddress = new InetSocketAddress(port); - } else { - bindAddress = new InetSocketAddress(address, port); - } - inetAddress = bindAddress.getAddress(); - } - - boolean bindsOnLoopback = inetAddress.isLoopbackAddress(); - if (serverConfig.disableInternalTLS && serverConfig.bindPort != -1 && bindsOnLoopback) { + if (serverConfig.disableInternalTLS && serverConfig.bindPort != -1) { LOGGER.warn("Internal TLS is disabled. Clients will not be able to connect directly; you must use e.g. a reverse proxy with TLS."); } else { if (serverConfig.disableInternalTLS) { - LOGGER.error("Internal TLS cannot be disabled. You have to bind modpack host on a loopback address with a separate port."); + LOGGER.error("Internal TLS cannot be disabled. You have to bind modpack host on a separate port, preferably also on a loopback address or atleast some private one."); } if (!Files.exists(serverCertFile) || !Files.exists(serverPrivateKeyFile)) { @@ -142,6 +124,17 @@ public Optional start() { return Optional.empty(); } + String address = serverConfig.bindAddress; + int port = serverConfig.bindPort; + InetSocketAddress bindAddress = null; + if (port != -1) { + if (address == null || address.isBlank()) { + bindAddress = new InetSocketAddress(port); + } else { + bindAddress = new InetSocketAddress(address, port); + } + } + LOGGER.info("Starting modpack host server on {}", bindAddress); Class socketChannelClass; diff --git a/docs/configuration/server-config.mdx b/docs/configuration/server-config.mdx index b05c023f9..b3798d548 100644 --- a/docs/configuration/server-config.mdx +++ b/docs/configuration/server-config.mdx @@ -21,7 +21,7 @@ | `addressToSend` | `""` | The address to the modpack host which will be used by clients to download the modpack. If empty, clients will use the same address they used to connect to the Minecraft server. (Do **not** include the port there, use `portToSend` instead) | | `localAddressToSend` | `""` | Same as `addressToSend`, but sent only to the clients connecting from the same private network as the minecraft server is hosted on. | | `portToSend` | `-1` | The port number that is sent alongside the `addressToSend` or `localAddressToSend`. If set to `-1`, the port to which the modpack host is bound will be sent. | -| `disableInternalTLS` | `false` | Disables internal TLS management. If enabled, you must manage TLS externally, e.g. on a reverse proxy. When using this option, change `bindPort` and set `bindAddress` to a loopback address (e.g. `127.0.0.1`), so the unencrypted traffic between the modpack host and the reverse proxy isn't leaked. | +| `disableInternalTLS` | `false` | Disables internal TLS management. If enabled, you must manage TLS externally, e.g. on a reverse proxy. When using this option, you have to use different `bindPort` than `-1` and you should consider setting `bindAddress` to a loopback address (e.g. `127.0.0.1`), so the unencrypted traffic between the modpack host and the reverse proxy isn't leaked. | | `updateIpsOnEveryStart` | `false` | Updates `addressToSend` and `localAddressToSend` on every server start. Might be useful if you have dynamic IP address. (Use only if you really need it!) | | `bandwidthLimit` | `0` | Upload limit in Mbps that modpack host server is restricted to. (Value has to be an Integer, `0` - means unlimited) | | `validateSecrets` | `true` | Makes modpack host validate and authorize modpack download attempts using unique secrets. | From ff440bcdc2b3d7c320539c03a541406718cc3a44 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 11 Jun 2025 12:28:45 +0200 Subject: [PATCH 48/86] Remove `localAddressToSend` since it seems not very useful --- .../skidam/automodpack_core/config/Jsons.java | 1 - .../protocol/netty/NettyServer.java | 8 -------- docs/configuration/server-config.mdx | 5 ++--- .../automodpack_loader_core/Preload.java | 6 ------ .../networking/packet/DataS2CPacket.java | 4 +--- .../networking/packet/HandshakeS2CPacket.java | 18 ++++-------------- 6 files changed, 7 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java b/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java index a633ef77a..da1e958ce 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java +++ b/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java @@ -101,7 +101,6 @@ public static class ServerConfigFieldsV2 { public String bindAddress = ""; public int bindPort = -1; public String addressToSend = ""; - public String localAddressToSend = ""; public int portToSend = -1; public boolean disableInternalTLS = false; public boolean updateIpsOnEveryStart = false; diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java index c4d05799a..83eb84684 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/NettyServer.java @@ -220,7 +220,6 @@ private boolean canStart() { if (serverConfig.updateIpsOnEveryStart) { String publicIp = AddressHelpers.getPublicIp(); - String localIp = AddressHelpers.getLocalIp(); if (publicIp != null) { serverConfig.addressToSend = publicIp; LOGGER.warn("Setting Host IP to {}", serverConfig.addressToSend); @@ -228,13 +227,6 @@ private boolean canStart() { LOGGER.error("Couldn't get public IP, please change it manually! "); } - if (localIp != null) { - serverConfig.localAddressToSend = localIp; - LOGGER.warn("Setting Host Local IP to {}", serverConfig.localAddressToSend); - } else { - LOGGER.error("Couldn't get local IP, please change it manually! "); - } - try { ConfigTools.save(serverConfigFile, serverConfig); } catch (Exception e) { diff --git a/docs/configuration/server-config.mdx b/docs/configuration/server-config.mdx index b3798d548..f7ab7b70f 100644 --- a/docs/configuration/server-config.mdx +++ b/docs/configuration/server-config.mdx @@ -19,10 +19,9 @@ | `bindAddress` | `""` | The address to which the modpack host server binds. Leave this empty to automatically determine a local address (typically `0.0.0.0` or `::0`). When `bindPort` is set to `-1`, this value is **ignored** and the modpack host will be bound to the same address as the Minecraft server, as specified in the `server.properties` file. | | `bindPort` | `-1` | The port number on which the modpack host server listens. If set to `-1`, the modpack will be hosted directly on the port of your Minecraft server. | | `addressToSend` | `""` | The address to the modpack host which will be used by clients to download the modpack. If empty, clients will use the same address they used to connect to the Minecraft server. (Do **not** include the port there, use `portToSend` instead) | -| `localAddressToSend` | `""` | Same as `addressToSend`, but sent only to the clients connecting from the same private network as the minecraft server is hosted on. | -| `portToSend` | `-1` | The port number that is sent alongside the `addressToSend` or `localAddressToSend`. If set to `-1`, the port to which the modpack host is bound will be sent. | +| `portToSend` | `-1` | The port number that is sent alongside the `addressToSend`. If set to `-1`, the port to which the modpack host is bound will be sent. | | `disableInternalTLS` | `false` | Disables internal TLS management. If enabled, you must manage TLS externally, e.g. on a reverse proxy. When using this option, you have to use different `bindPort` than `-1` and you should consider setting `bindAddress` to a loopback address (e.g. `127.0.0.1`), so the unencrypted traffic between the modpack host and the reverse proxy isn't leaked. | -| `updateIpsOnEveryStart` | `false` | Updates `addressToSend` and `localAddressToSend` on every server start. Might be useful if you have dynamic IP address. (Use only if you really need it!) | +| `updateIpsOnEveryStart` | `false` | Updates `addressToSend` on every server start. Might be useful if you have dynamic IP address. (Use it only if you really need it!) | | `bandwidthLimit` | `0` | Upload limit in Mbps that modpack host server is restricted to. (Value has to be an Integer, `0` - means unlimited) | | `validateSecrets` | `true` | Makes modpack host validate and authorize modpack download attempts using unique secrets. | | `secretLifetime` | `336` | Time in hours that a player's secret remains valid. | diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java index 5bd4bfba6..60217a188 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java @@ -160,12 +160,6 @@ private void loadConfigs() { serverConfigV2.addressToSend = AddressHelpers.parse(serverConfigV1.hostIp).getHostString(); } - if (serverConfigV1.hostLocalIp.isBlank()) { - serverConfigV2.localAddressToSend = ""; - } else { - serverConfigV2.localAddressToSend = AddressHelpers.parse(serverConfigV1.hostLocalIp).getHostString(); - } - if (serverConfigV1.hostModpackOnMinecraftPort) { serverConfigV2.bindPort = -1; serverConfigV2.portToSend = -1; diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java index 8dc5c30f4..aed073df4 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java @@ -48,13 +48,11 @@ public static void receive(MinecraftServer server, ServerLoginNetworkHandler han if (serverConfig.bindPort == -1) { LOGGER.warn("You are hosting AutoModpack host server on the Minecraft port."); - LOGGER.warn(""); } else { LOGGER.warn("Please check if AutoModpack host server (TCP) port '{}' is forwarded / opened correctly", serverConfig.bindPort); - LOGGER.warn(""); } - LOGGER.warn("Make sure that 'addressToSend' and 'localAddressToSend' are correct in the config file!"); + LOGGER.warn("Make sure that 'addressToSend' is correctly set in the config file!"); LOGGER.warn("It can be either an IP address or a domain pointing to your modpack host server."); LOGGER.warn("If nothing works, try changing the 'bindPort' in the config file, then forward / open it and restart server"); LOGGER.warn("Note that some hosting providers may proxy this port internally and give you a different address and port to use. In this case, separate the given address with ':', and set the first part as 'addressToSend' and the second part as 'portToSend' in the config file."); diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java index 7c1751d8c..c2ea57ef2 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java @@ -18,7 +18,6 @@ import pl.skidam.automodpack.networking.server.ServerLoginNetworking; import pl.skidam.automodpack_core.auth.Secrets; import pl.skidam.automodpack_core.auth.SecretsStore; -import pl.skidam.automodpack_core.utils.AddressHelpers; import java.nio.charset.StandardCharsets; import java.util.UUID; @@ -48,9 +47,9 @@ public static void receive(MinecraftServer server, ServerLoginNetworkHandler han profile = new GameProfile(offlineUUID, playerName); } - if (!connection.isEncrypted()) { - LOGGER.warn("Connection is not encrypted for player: {}", playerName); - } +// if (!connection.isEncrypted()) { +// LOGGER.warn("Connection is not encrypted for player: {}", playerName); +// } if (!GameHelpers.isPlayerAuthorized(connection.getAddress(), profile)) { return; @@ -108,21 +107,12 @@ public static void handleHandshake(ClientConnection connection, GameProfile prof return; } - String playerAddress = connection.getAddress().toString(); - String addressToSend; - - // If the player is connecting locally, use the local host IP - if (AddressHelpers.isLocal(playerAddress)) { - addressToSend = serverConfig.localAddressToSend; - } else { - addressToSend = serverConfig.addressToSend; - } - // now we know player is authenticated, packets are encrypted and player is whitelisted // regenerate unique secret Secrets.Secret secret = Secrets.generateSecret(); SecretsStore.saveHostSecret(profile.getId().toString(), secret); + String addressToSend = serverConfig.addressToSend; int portToSend = serverConfig.portToSend; if (portToSend == -1) { if (serverConfig.bindPort == -1) { From 7366e3fd3efa90f7380b4391d0417621024d420c Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 11 Jun 2025 12:30:26 +0200 Subject: [PATCH 49/86] Refine description for `acceptedLoaders` in server configuration documentation --- docs/configuration/server-config.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/server-config.mdx b/docs/configuration/server-config.mdx index f7ab7b70f..ce90d2291 100644 --- a/docs/configuration/server-config.mdx +++ b/docs/configuration/server-config.mdx @@ -26,4 +26,4 @@ | `validateSecrets` | `true` | Makes modpack host validate and authorize modpack download attempts using unique secrets. | | `secretLifetime` | `336` | Time in hours that a player's secret remains valid. | | `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | -| `acceptedLoaders` | `""` | Allows players from different modloaders to connect to your server (as long, automodpack support that loader and other mods on your server aren't incompatible with each other) with the same modpack. (use with caution, some mods may not work on both loaders) | +| `acceptedLoaders` | `""` | Allows players from different modloaders to connect to your server. (Use with caution, most mods work only on one loader) | From c75d3604901eb7f704a57c6a72425aabce94eebc Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 11 Jun 2025 12:48:45 +0200 Subject: [PATCH 50/86] Don't send minecraft server port by default, let clients use the port they have used to connect by default. This change makes reverse proxies such as playit.gg work by default however proxies such as Gate will need to set `portToSend` to some value. --- docs/compatibility/proxies.mdx | 5 +++++ docs/configuration/server-config.mdx | 2 +- .../networking/packet/DataC2SPacket.java | 9 ++++++++- .../networking/packet/HandshakeS2CPacket.java | 14 +------------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/compatibility/proxies.mdx b/docs/compatibility/proxies.mdx index 7281b3cbf..12b17a994 100644 --- a/docs/compatibility/proxies.mdx +++ b/docs/compatibility/proxies.mdx @@ -7,3 +7,8 @@ Most minecraft proxies won't work with AutoModpack due to early login stage pack | Velocity | ❌ | Unsupported | | Waterfall | ❌ | Unsupported | | BungeeCord | ❌ | Unsupported | + +When using Gate Lite, it's crucial to correctly configure `portToSend`. By default, clients attempt to connect to the modpack host using the same port they used to connect to your Minecraft server. However, when using a proxy like Gate, this port differs from your actual Minecraft server's port, preventing clients from connecting to download the modpack. + +Therefore, if your modpack is hosted with `bindPort` set to `-1`, `portToSend` must be set to the port from which your Minecraft server is accessible from (typically, this will be the same port on which Minecraft server listens). +Similarly, if `bindPort` is set to a specific port, `portToSend` must be set to the port from which modpack host is accessible from (typically, this will be the same as `bindPort`). \ No newline at end of file diff --git a/docs/configuration/server-config.mdx b/docs/configuration/server-config.mdx index ce90d2291..09f840de5 100644 --- a/docs/configuration/server-config.mdx +++ b/docs/configuration/server-config.mdx @@ -19,7 +19,7 @@ | `bindAddress` | `""` | The address to which the modpack host server binds. Leave this empty to automatically determine a local address (typically `0.0.0.0` or `::0`). When `bindPort` is set to `-1`, this value is **ignored** and the modpack host will be bound to the same address as the Minecraft server, as specified in the `server.properties` file. | | `bindPort` | `-1` | The port number on which the modpack host server listens. If set to `-1`, the modpack will be hosted directly on the port of your Minecraft server. | | `addressToSend` | `""` | The address to the modpack host which will be used by clients to download the modpack. If empty, clients will use the same address they used to connect to the Minecraft server. (Do **not** include the port there, use `portToSend` instead) | -| `portToSend` | `-1` | The port number that is sent alongside the `addressToSend`. If set to `-1`, the port to which the modpack host is bound will be sent. | +| `portToSend` | `-1` | The port number that is sent alongside the `addressToSend`. If set to `-1`, clients will use the same port they used to connect to the Minecraft server. | | `disableInternalTLS` | `false` | Disables internal TLS management. If enabled, you must manage TLS externally, e.g. on a reverse proxy. When using this option, you have to use different `bindPort` than `-1` and you should consider setting `bindAddress` to a loopback address (e.g. `127.0.0.1`), so the unencrypted traffic between the modpack host and the reverse proxy isn't leaked. | | `updateIpsOnEveryStart` | `false` | Updates `addressToSend` on every server start. Might be useful if you have dynamic IP address. (Use it only if you really need it!) | | `bandwidthLimit` | `0` | Upload limit in Mbps that modpack host server is restricted to. (Value has to be an Integer, `0` - means unlimited) | diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java index d5fc811ea..e97796c03 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java @@ -55,6 +55,7 @@ public static CompletableFuture receive(MinecraftClient minecraft // Get actual address of the server client have connected to and format it InetSocketAddress connectedAddress = (InetSocketAddress) ((ClientLoginNetworkHandlerAccessor) handler).getConnection().getAddress(); String effectiveHost; + int effectivePort; // If the packet specifies a non-blank address, use it or else use address from the server client have connected to. if (packetAddress.isBlank()) { @@ -63,8 +64,14 @@ public static CompletableFuture receive(MinecraftClient minecraft effectiveHost = packetAddress; } + if (packetPort == -1) { + effectivePort = connectedAddress.getPort(); + } else { + effectivePort = packetPort; + } + // Construct the final modpack address - InetSocketAddress modpackAddress = AddressHelpers.format(effectiveHost, packetPort); + InetSocketAddress modpackAddress = AddressHelpers.format(effectiveHost, effectivePort); LOGGER.info("Modpack address: {}:{} Requires to follow magic protocol: {}", modpackAddress.getHostString(), modpackAddress.getPort(), requiresMagic); diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java index c2ea57ef2..8b6c92df6 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java @@ -114,21 +114,9 @@ public static void handleHandshake(ClientConnection connection, GameProfile prof String addressToSend = serverConfig.addressToSend; int portToSend = serverConfig.portToSend; - if (portToSend == -1) { - if (serverConfig.bindPort == -1) { - portToSend = minecraftServerPort; - } else { - portToSend = serverConfig.bindPort; - } - } - boolean requiresMagic = serverConfig.bindPort == -1; - if (addressToSend.isBlank()) { - LOGGER.info("Sending {} modpack host port: {}", profile.getName(), portToSend); - } else { - LOGGER.info("Sending {} modpack host address: {}:{}", profile.getName(), addressToSend, portToSend); - } + LOGGER.info("Sending {} modpack host address: {}:{}", profile.getName(), addressToSend, portToSend); DataPacket dataPacket = new DataPacket(addressToSend, portToSend, serverConfig.modpackName, secret, serverConfig.requireAutoModpackOnClient, requiresMagic); String packetContentJson = dataPacket.toJson(); From 98cfc7e6bd738591c2482c84466c2161398a8001 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 18 Jun 2025 15:17:07 +0200 Subject: [PATCH 51/86] 1.21.6 --- .../client/ui/ChangelogScreen.java | 4 +- .../automodpack/client/ui/DangerScreen.java | 6 +- .../automodpack/client/ui/DownloadScreen.java | 14 ++--- .../automodpack/client/ui/ErrorScreen.java | 2 +- .../automodpack/client/ui/FetchScreen.java | 6 +- .../automodpack/client/ui/RestartScreen.java | 6 +- .../automodpack/client/ui/TextColors.java | 63 +++++++++++++++++++ .../client/ui/ValidationScreen.java | 8 +-- .../ui/versioned/VersionedMatrices.java | 40 +++++++++--- .../client/ui/versioned/VersionedScreen.java | 32 +++++----- .../client/ui/widget/ListEntry.java | 15 +++-- .../mixin/core/DrawContextAccessor.java | 21 ------- .../resources/automodpack-main.mixins.json | 1 - src/main/resources/pack.mcmeta | 6 -- 14 files changed, 144 insertions(+), 80 deletions(-) create mode 100644 src/main/java/pl/skidam/automodpack/client/ui/TextColors.java delete mode 100644 src/main/java/pl/skidam/automodpack/mixin/core/DrawContextAccessor.java delete mode 100644 src/main/resources/pack.mcmeta diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java index d769eade4..e90c36996 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java @@ -86,7 +86,7 @@ private void initWidgets() { @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - this.listEntryWidget.render(matrices, mouseX, mouseY, delta); + this.listEntryWidget.render(matrices.getContext(), mouseX, mouseY, delta); ListEntry selectedEntry = listEntryWidget.getSelectedOrNull(); if (selectedEntry != null) { @@ -115,7 +115,7 @@ private void drawSummaryOfChanges(VersionedMatrices matrices) { String summary = "+ " + filesAdded + " | - " + filesRemoved; - drawCenteredTextWithShadow(matrices, textRenderer, VersionedText.literal(summary), this.width / 2, 5, 16777215); + drawCenteredTextWithShadow(matrices, textRenderer, VersionedText.literal(summary), this.width / 2, 5, TextColors.WHITE); } private void updateChangelogs() { diff --git a/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java index cfc204cf9..530f197eb 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java @@ -40,9 +40,9 @@ protected void init() { @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger").formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger.description"), this.width / 2, this.height / 2 - 35, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger.secDescription"), this.width / 2, this.height / 2 - 25, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger").formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger.description"), this.width / 2, this.height / 2 - 35, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger.secDescription"), this.width / 2, this.height / 2 - 25, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java index aa67002d3..80bc9fbc4 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java @@ -105,20 +105,20 @@ private void drawDownloadingFiles(VersionedMatrices matrices) { matrices.scale(scale, scale, scale); if (downloadManager != null && !downloadManager.downloadsInProgress.isEmpty()) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.download.downloading").formatted(Formatting.BOLD), this.width / 2, y, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.download.downloading").formatted(Formatting.BOLD), this.width / 2, y, TextColors.WHITE); // Use a separate variable for the current y position int currentY = y + 15; synchronized (downloadManager.downloadsInProgress) { for (DownloadManager.DownloadData downloadData : downloadManager.downloadsInProgress.values()) { String text = downloadData.getFileName(); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.literal(text).formatted(Formatting.GRAY), (int) ((float) this.width / 2 * scale), currentY, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.literal(text), (int) ((float) this.width / 2 * scale), currentY, TextColors.GRAY); currentY += 10; } } } else { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.download.noFiles"), (int) ((float) this.width / 2 * scale), y, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.wait").formatted(Formatting.BOLD), (int) ((float) this.width / 2 * scale), y + 25, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.download.noFiles"), (int) ((float) this.width / 2 * scale), y, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.wait").formatted(Formatting.BOLD), (int) ((float) this.width / 2 * scale), y + 25, TextColors.WHITE); } matrices.pop(); @@ -142,15 +142,15 @@ private void checkAndStartMusic() { public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { drawDownloadingFiles(matrices); MutableText titleText = VersionedText.literal(header).formatted(Formatting.BOLD); - drawCenteredTextWithShadow(matrices, this.textRenderer, titleText, this.width / 2, this.height / 2 - 110, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, titleText, this.width / 2, this.height / 2 - 110, TextColors.WHITE); if (downloadManager != null && downloadManager.isRunning()) { MutableText percentage = (MutableText) this.getPercentage(); MutableText stage = (MutableText) this.getStage(); MutableText eta = (MutableText) this.getTotalETA(); MutableText speed = (MutableText) this.getTotalDownloadSpeed(); - drawCenteredTextWithShadow(matrices, this.textRenderer, stage, this.width / 2, this.height / 2 - 10, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, eta, this.width / 2, this.height / 2 + 10, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, stage, this.width / 2, this.height / 2 - 10, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.textRenderer, eta, this.width / 2, this.height / 2 + 10, TextColors.WHITE); // Render progress bar diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java index 52c0b947c..6d9b63fc4 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java @@ -39,7 +39,7 @@ private void initWidgets() { @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.literal("[AutoModpack] Error! ").append(VersionedText.translatable("automodpack.error").formatted(Formatting.RED)), this.width / 2, this.height / 2 - 50, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.literal("[AutoModpack] Error! ").append(VersionedText.translatable("automodpack.error").formatted(Formatting.RED)), this.width / 2, this.height / 2 - 50, TextColors.WHITE); for (int i = 0; i < this.errorMessages.length; i++) { drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable(this.errorMessages[i]), this.width / 2, this.height / 2 - 20 + i * 12, 14687790); } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java index 8b00fcb57..95aea4412 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java @@ -51,9 +51,9 @@ public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, } // Fetching direct url's from Modrinth and CurseForge. - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.fetch").formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.wait"), this.width / 2, this.height / 2 - 48, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.fetch.found", getFetchesDone()), this.width / 2, this.height / 2 - 30, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.fetch").formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.wait"), this.width / 2, this.height / 2 - 48, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.fetch.found", getFetchesDone()), this.width / 2, this.height / 2 - 30, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java index a569654ef..1b881facb 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java @@ -64,9 +64,9 @@ public void initWidgets() { @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart." + updateType.toString()).formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart.description"), this.width / 2, this.height / 2 - 35, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart.secDescription"), this.width / 2, this.height / 2 - 25, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart." + updateType.toString()).formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart.description"), this.width / 2, this.height / 2 - 35, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart.secDescription"), this.width / 2, this.height / 2 - 25, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/TextColors.java b/src/main/java/pl/skidam/automodpack/client/ui/TextColors.java new file mode 100644 index 000000000..b164b7d29 --- /dev/null +++ b/src/main/java/pl/skidam/automodpack/client/ui/TextColors.java @@ -0,0 +1,63 @@ +package pl.skidam.automodpack.client.ui; + +@SuppressWarnings("unused") +public class TextColors { + + // Contains constants for commonly used colors in 0xAARRGGBB format. + + /** + * Represents the color white, {@code 0xFFFFFFFF}. + */ + public static final int WHITE = -1; // previously 0xFFFFFF or 16777215 + /** + * Represents the color black, {@code 0xFF000000}. + */ + public static final int BLACK = -16777216; + /** + * Represents the color gray, {@code 0xFF808080}. + */ + public static final int GRAY = -8355712; + public static final int DARK_GRAY = -12566464; + /** + * Represents the color light gray, {@code 0xFFA0A0A0}. + */ + public static final int LIGHT_GRAY = -6250336; + /** + * Represents a variant of the color white, used so that the two alternate with + * each other, {@code 0xFFBABABA}. + */ + public static final int ALTERNATE_WHITE = -4539718; + /** + * Represents the color red, {@code 0xFFFF0000}. + */ + public static final int RED = -65536; + /** + * Represents the color light red, {@code 0xFFDF5050}. + */ + public static final int LIGHT_RED = -2142128; + /** + * Represents the color green, {@code 0xFF00FF00}. + */ + public static final int GREEN = -16711936; + /** + * Represents the color blue, {@code 0xFF0000FF}. + */ + public static final int BLUE = -16776961; + /** + * Represents the color yellow, {@code 0xFFFFFF00}. + */ + public static final int YELLOW = -256; + /** + * Represents the color light yellow, {@code 0xFFFFFF55}. + */ + public static final int LIGHT_YELLOW = -171; + /** + * Represents the color purple, {@code 0xFF500050}. + */ + public static final int PURPLE = -11534256; + /** + * Represents the color cyan, {@code 0xFF57FFE1}. + */ + public static final int CYAN = -11010079; + public static final int LIGHT_PINK = -13108; +} diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java index 7d67c36ed..7f6003efc 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java @@ -86,13 +86,13 @@ private void validate(String input) { @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.text").formatted(Formatting.BOLD), - this.width / 2, this.height / 2 - 100, 16777215); + this.width / 2, this.height / 2 - 100, TextColors.WHITE); drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.description"), - this.width / 2, this.height / 2 - 75, 16777215); + this.width / 2, this.height / 2 - 75, TextColors.WHITE); drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.secDescription"), - this.width / 2, this.height / 2 - 65, 16777215); + this.width / 2, this.height / 2 - 65, TextColors.WHITE); drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.thiDescription"), - this.width / 2, this.height / 2 - 55, 16777215); + this.width / 2, this.height / 2 - 55, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java index 82dee9503..33e31e6ae 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java @@ -1,30 +1,52 @@ package pl.skidam.automodpack.client.ui.versioned; /*? if >=1.20 {*/ -import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.render.VertexConsumerProvider; /*?} else {*/ /*import net.minecraft.client.util.math.MatrixStack; *//*?}*/ -public class VersionedMatrices extends /*? if >=1.20 >>*/ DrawContext /*? if <1.20 >>*/ /*MatrixStack*/ { +public class VersionedMatrices /*? if <1.20 >>*/ /*extends MatrixStack*/ { /*? if >=1.20 {*/ - public VersionedMatrices(MinecraftClient client, VertexConsumerProvider.Immediate vertexConsumers) { - super(client, vertexConsumers); + private final DrawContext context; + + public VersionedMatrices(DrawContext context) { + this.context = context; + } + + public DrawContext getContext() { + return context; } + /*? if >=1.21.6 {*/ + /*public void push() { + context.getMatrices().pushMatrix(); + } + + public void pop() { + context.getMatrices().popMatrix(); + } + + public void scale(float x, float y, float z) { + context.getMatrices().scale(x, y); + } + *//*?} else {*/ public void push() { - getMatrices().push(); + context.getMatrices().push(); } public void pop() { - getMatrices().pop(); + context.getMatrices().pop(); } public void scale(float x, float y, float z) { - getMatrices().scale(x, y, z); + context.getMatrices().scale(x, y, z); } -/*?}*/ + /*?}*/ +/*?} else {*/ + /*public MatrixStack getContext() { + return this; + } +*//*?}*/ } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java index c5b69ebdc..f182fa20a 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java @@ -21,10 +21,11 @@ /*? if >=1.20 {*/ import net.minecraft.client.gui.DrawContext; -import pl.skidam.automodpack.mixin.core.DrawContextAccessor; /*?}*/ -/*? if >=1.21.2 {*/ +/*? if >=1.21.6 {*/ +/*import net.minecraft.client.gl.RenderPipelines; +*//*?} elif >=1.21.2 {*/ /*import net.minecraft.client.render.RenderLayer; import java.util.function.Function; *//*?}*/ @@ -42,22 +43,23 @@ public void render(MatrixStack matrix, int mouseX, int mouseY, float delta) { *//*?} else {*/ @Override public void render(DrawContext matrix, int mouseX, int mouseY, float delta) { - VersionedMatrices matrices = new VersionedMatrices(this.client, ((DrawContextAccessor) matrix).vertexConsumers()); - /*?}*/ + VersionedMatrices matrices = new VersionedMatrices(matrix); + /*?}*/ // Render background /*? if <1.20.2 {*/ - /*super.renderBackground(matrices); + /*super.renderBackground(matrices.getContext()); *//*?} elif <1.20.6 {*/ - /*super.renderBackground(matrices, mouseX, mouseY, delta); + /*super.renderBackground(matrices.getContext(), mouseX, mouseY, delta); *//*?} else {*/ - super.render(matrices, mouseX, mouseY, delta); + super.render(matrix, mouseX, mouseY, delta); /*?}*/ + // Render the rest of our screen versionedRender(matrices, mouseX, mouseY, delta); /*? if <1.20.6 {*/ - /*super.render(matrices, mouseX, mouseY, delta); + /*super.render(matrices.getContext(), mouseX, mouseY, delta); *//*?}*/ } @@ -77,11 +79,11 @@ public void addDrawableChild(T child) { /*? if >=1.20 {*/ public static void drawCenteredTextWithShadow(VersionedMatrices matrices, TextRenderer textRenderer, MutableText text, int centerX, int y, int color) { - matrices.drawCenteredTextWithShadow(textRenderer, text, centerX, y, color); + matrices.getContext().drawCenteredTextWithShadow(textRenderer, text, centerX, y, color); } /*?} else {*/ /*public static void drawCenteredTextWithShadow(VersionedMatrices matrices, TextRenderer textRenderer, MutableText text, int centerX, int y, int color) { - textRenderer.drawWithShadow(matrices, text, (float)(centerX - textRenderer.getWidth(text) / 2), (float)y, color); + textRenderer.drawWithShadow(matrices.getContext(), text, (float)(centerX - textRenderer.getWidth(text) / 2), (float)y, color); } *//*?}*/ @@ -103,15 +105,17 @@ public static ButtonWidget buttonWidget(int x, int y, int width, int height, Tex ^//^?} else {^/ RenderSystem.setShaderTexture(0, textureID); /^?}^/ - DrawableHelper.drawTexture(matrices, x, y, u, v, width, height, textureWidth, textureHeight); + DrawableHelper.drawTexture(matrices.getContext(), x, y, u, v, width, height, textureWidth, textureHeight); } *//*?} else {*/ public static void drawTexture(Identifier textureID, VersionedMatrices matrices, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) { - /*? if >=1.21.2 {*/ + /*? if >=1.21.6 {*/ + /*matrices.getContext().drawTexture(RenderPipelines.GUI_TEXTURED, textureID, x, y, u, v, width, height, textureWidth, textureHeight); + *//*?} elif >=1.21.2 {*/ /*Function renderLayers = RenderLayer::getGuiTextured; - matrices.drawTexture(renderLayers, textureID, x, y, u, v, width, height, textureWidth, textureHeight); + matrices.getContext().drawTexture(renderLayers, textureID, x, y, u, v, width, height, textureWidth, textureHeight); *//*?} else {*/ - matrices.drawTexture(textureID, x, y, u, v, width, height, textureWidth, textureHeight); + matrices.getContext().drawTexture(textureID, x, y, u, v, width, height, textureWidth, textureHeight); /*?}*/ } /*?}*/ diff --git a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java index a1c6172ba..23444cb20 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java @@ -4,6 +4,7 @@ import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; import net.minecraft.text.MutableText; import net.minecraft.text.Text; +import pl.skidam.automodpack.client.ui.TextColors; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; @@ -11,7 +12,6 @@ /*import net.minecraft.client.util.math.MatrixStack; *//*?} else {*/ import net.minecraft.client.gui.DrawContext; -import pl.skidam.automodpack.mixin.core.DrawContextAccessor; /*?}*/ public class ListEntry extends AlwaysSelectedEntryListWidget.Entry { @@ -45,11 +45,14 @@ public Text getNarration() { @Override /*? if <1.20 {*/ /*public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - VersionedMatrices versionedMatrices = new VersionedMatrices(); + VersionedMatrices versionedMatrices = new VersionedMatrices(); *//*?} else {*/ - public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - VersionedMatrices versionedMatrices = new VersionedMatrices(this.client, ((DrawContextAccessor) context).vertexConsumers()); + public void render(DrawContext drawContext, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + VersionedMatrices versionedMatrices = new VersionedMatrices(drawContext); /*?}*/ + versionedRender(versionedMatrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, hovered, tickDelta); + } + public void versionedRender(VersionedMatrices versionedMatrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { versionedMatrices.push(); int centeredX = x + entryWidth / 2; @@ -63,7 +66,7 @@ public void render(DrawContext context, int index, int y, int x, int entryWidth, centeredY = centeredY - 10 / 2; } - VersionedScreen.drawCenteredTextWithShadow(versionedMatrices, client.textRenderer, text, centeredX, centeredY, 16777215); + VersionedScreen.drawCenteredTextWithShadow(versionedMatrices, client.textRenderer, text, centeredX, centeredY, TextColors.WHITE); // if (mainPageUrls != null) { // int badgeX = x - 42; @@ -95,4 +98,4 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { public boolean mouseReleased(double mouseX, double mouseY, int button) { return false; } -} +} \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/DrawContextAccessor.java b/src/main/java/pl/skidam/automodpack/mixin/core/DrawContextAccessor.java deleted file mode 100644 index f738234f0..000000000 --- a/src/main/java/pl/skidam/automodpack/mixin/core/DrawContextAccessor.java +++ /dev/null @@ -1,21 +0,0 @@ -package pl.skidam.automodpack.mixin.core; - -import org.spongepowered.asm.mixin.Mixin; -/*? if >=1.20 {*/ -import net.minecraft.client.gui.DrawContext; -import org.spongepowered.asm.mixin.gen.Accessor; -import net.minecraft.client.render.VertexConsumerProvider; - -// TODO find better way to do this, its mixin only for 1.20 and above -@Mixin(DrawContext.class) -/*?} else {*/ -/*import pl.skidam.automodpack.init.Common; -@Mixin(Common.class) -*//*?}*/ -public interface DrawContextAccessor { - - /*? if >=1.20 {*/ - @Accessor("vertexConsumers") - VertexConsumerProvider.Immediate vertexConsumers(); - /*?}*/ -} \ No newline at end of file diff --git a/src/main/resources/automodpack-main.mixins.json b/src/main/resources/automodpack-main.mixins.json index 511da3cb3..36fcdff38 100644 --- a/src/main/resources/automodpack-main.mixins.json +++ b/src/main/resources/automodpack-main.mixins.json @@ -19,7 +19,6 @@ "core.ClientLoginNetworkHandlerAccessor", "core.ClientLoginNetworkHandlerMixin", "core.ConnectScreenMixin", - "core.DrawContextAccessor", "core.MusicTrackerMixin" ], "injectors": { diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta deleted file mode 100644 index d0bc05bac..000000000 --- a/src/main/resources/pack.mcmeta +++ /dev/null @@ -1,6 +0,0 @@ -{ - "pack": { - "description": "${name} Resources", - "pack_format": 1 - } -} \ No newline at end of file From 68aef8e2f048e8d869dcb71c3cafd6a03936c478 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 18 Jun 2025 15:20:18 +0200 Subject: [PATCH 52/86] whoopsie forgot to change the text color there --- .../java/pl/skidam/automodpack/client/ui/DownloadScreen.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java index 80bc9fbc4..23b4b111a 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java @@ -160,8 +160,8 @@ public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, drawTexture(PROGRESS_BAR_EMPTY_TEXTURE, matrices, progressX, progressY, 0, 0, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT); drawTexture(PROGRESS_BAR_FULL_TEXTURE, matrices, progressX, progressY, 0, 0, (int) (PROGRESS_BAR_WIDTH * getDownloadScale()), PROGRESS_BAR_HEIGHT, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT); - drawCenteredTextWithShadow(matrices, this.textRenderer, percentage, this.width / 2, this.height / 2 + 36, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, speed, this.width / 2, this.height / 2 + 60, 16777215); + drawCenteredTextWithShadow(matrices, this.textRenderer, percentage, this.width / 2, this.height / 2 + 36, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.textRenderer, speed, this.width / 2, this.height / 2 + 60, TextColors.WHITE); checkAndStartMusic(); cancelButton.active = true; From ebe360b220eecde3ac2fa07960280977e8da649d Mon Sep 17 00:00:00 2001 From: Skidam <67871298+Skidamek@users.noreply.github.com> Date: Tue, 24 Jun 2025 12:57:05 +0200 Subject: [PATCH 53/86] Ditch the arch loom (#394) --- .github/workflows/build.yml | 52 +--- .github/workflows/gradle.yml | 7 +- .github/workflows/release.yml | 20 +- .github/workflows/scripts/summary.py | 73 ----- .github/workflows/tests.yml | 5 +- build.fabric.gradle.kts | 73 +++++ build.forge.gradle.kts | 59 ++++ build.gradle.kts | 273 ----------------- build.neoforge.gradle.kts | 51 ++++ buildSrc/build.gradle.kts | 14 + .../main/kotlin/automodpack.common.gradle.kts | 247 ++++++++++++++++ core/build.gradle.kts | 18 +- gradle.properties | 21 +- gradle/wrapper/gradle-wrapper.properties | 2 +- loader/fabric/15/gradle.properties | 5 +- loader/fabric/16/gradle.properties | 5 +- loader/fabric/core/gradle.properties | 5 +- loader/forge/fml40/gradle.properties | 5 +- loader/forge/fml47/gradle.properties | 5 +- loader/loader-core.gradle.kts | 17 +- loader/loader-fabric-core.gradle.kts | 18 +- loader/loader-fabric.gradle.kts | 33 +-- loader/loader-forge.gradle.kts | 75 ++--- loader/loader-neoforge.gradle.kts | 100 +++++++ loader/neoforge/fml2/gradle.properties | 5 +- loader/neoforge/fml4/gradle.properties | 5 +- settings.gradle.kts | 70 ++--- .../skidam/automodpack/client/ScreenImpl.java | 12 +- .../client/audio/AudioManager.java | 49 ++- .../client/audio/CustomSoundInstance.java | 24 +- .../client/ui/ChangelogScreen.java | 64 ++-- .../automodpack/client/ui/DangerScreen.java | 22 +- .../automodpack/client/ui/DownloadScreen.java | 62 ++-- .../automodpack/client/ui/ErrorScreen.java | 16 +- .../automodpack/client/ui/FetchScreen.java | 15 +- .../automodpack/client/ui/RestartScreen.java | 29 +- .../client/ui/ValidationScreen.java | 55 ++-- .../ui/versioned/VersionedCommandSource.java | 32 +- .../ui/versioned/VersionedMatrices.java | 38 +-- .../client/ui/versioned/VersionedScreen.java | 84 +++--- .../client/ui/versioned/VersionedText.java | 26 +- .../automodpack/client/ui/widget/Badge.java | 7 +- .../client/ui/widget/ListEntry.java | 44 +-- .../client/ui/widget/ListEntryWidget.java | 56 ++-- .../pl/skidam/automodpack/init/Common.java | 8 +- .../mixin/core/ClientConnectionAccessor.java | 4 +- .../ClientLoginNetworkHandlerAccessor.java | 8 +- .../core/ClientLoginNetworkHandlerMixin.java | 16 +- .../mixin/core/ConnectScreenMixin.java | 30 +- .../mixin/core/FabricLoginMixin.java | 4 +- .../core/LoginQueryRequestS2CPacketMixin.java | 12 +- .../LoginQueryResponseC2SPacketMixin.java | 14 +- .../mixin/core/MinecraftServerMixin.java | 4 +- .../mixin/core/MusicTrackerMixin.java | 25 +- .../mixin/core/PlayerManagerMixin.java | 55 ++-- .../ServerLoginNetworkHandlerAccessor.java | 14 +- .../core/ServerLoginNetworkHandlerMixin.java | 18 +- .../mixin/core/ServerNetworkIoMixin.java | 2 +- .../automodpack/mixin/dev/TestButton.java | 11 +- .../skidam/automodpack/modpack/Commands.java | 155 +++++----- .../automodpack/modpack/GameHelpers.java | 16 +- .../networking/LoginNetworkingIDs.java | 11 +- .../networking/LoginQueryParser.java | 45 ++- .../automodpack/networking/ModPackets.java | 16 +- .../automodpack/networking/PacketSender.java | 13 +- .../automodpack/networking/PayloadHelper.java | 10 +- .../client/ClientLoginNetworkAddon.java | 28 +- .../client/ClientLoginNetworking.java | 20 +- .../client/LoginResponsePayload.java | 14 +- .../networking/packet/DataC2SPacket.java | 27 +- .../networking/packet/DataS2CPacket.java | 26 +- .../networking/packet/HandshakeC2SPacket.java | 18 +- .../networking/packet/HandshakeS2CPacket.java | 38 +-- .../server/LoginRequestPayload.java | 13 +- .../server/ServerLoginNetworkAddon.java | 34 +-- .../server/ServerLoginNetworking.java | 18 +- .../resources/META-INF/accesstransformer.cfg | 1 + src/main/resources/META-INF/mods.toml | 6 +- .../resources/META-INF/neoforge.mods.toml | 6 +- src/main/resources/automodpack.accesswidener | 2 +- src/main/resources/fabric.mod.json | 2 +- stonecutter.gradle.kts | 278 ++---------------- versions/1.18.2-fabric/gradle.properties | 13 +- versions/1.18.2-forge/gradle.properties | 13 +- versions/1.19.2-fabric/gradle.properties | 13 +- versions/1.19.2-forge/gradle.properties | 13 +- versions/1.19.4-fabric/gradle.properties | 13 +- versions/1.19.4-forge/gradle.properties | 13 +- versions/1.20.1-fabric/gradle.properties | 13 +- versions/1.20.1-forge/gradle.properties | 13 +- versions/1.20.4-fabric/gradle.properties | 13 +- versions/1.20.4-neoforge/gradle.properties | 13 +- versions/1.20.6-fabric/gradle.properties | 13 +- versions/1.20.6-neoforge/gradle.properties | 13 +- versions/1.21.1-fabric/gradle.properties | 13 +- versions/1.21.1-neoforge/gradle.properties | 13 +- versions/1.21.3-fabric/gradle.properties | 13 +- versions/1.21.3-neoforge/gradle.properties | 13 +- versions/1.21.4-fabric/gradle.properties | 13 +- versions/1.21.4-neoforge/gradle.properties | 13 +- versions/1.21.5-fabric/gradle.properties | 5 + versions/1.21.5-neoforge/gradle.properties | 5 + versions/1.21.6-fabric/gradle.properties | 4 + versions/1.21.6-neoforge/gradle.properties | 4 + 104 files changed, 1473 insertions(+), 1699 deletions(-) delete mode 100644 .github/workflows/scripts/summary.py create mode 100644 build.fabric.gradle.kts create mode 100644 build.forge.gradle.kts delete mode 100644 build.gradle.kts create mode 100644 build.neoforge.gradle.kts create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/automodpack.common.gradle.kts create mode 100644 loader/loader-neoforge.gradle.kts create mode 100644 src/main/resources/META-INF/accesstransformer.cfg create mode 100644 versions/1.21.5-fabric/gradle.properties create mode 100644 versions/1.21.5-neoforge/gradle.properties create mode 100644 versions/1.21.6-fabric/gradle.properties create mode 100644 versions/1.21.6-neoforge/gradle.properties diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ffcd23a5c..73ab596fc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,12 +12,6 @@ on: type: string required: false default: '' -# # [FEATURE] MIXIN_AUDITOR -# mixin_audit: -# description: run mixin audit for Minecraft server after build -# type: boolean -# required: false -# default: false jobs: build: @@ -58,52 +52,8 @@ jobs: BUILD_ID: ${{ github.run_number }} BUILD_RELEASE: ${{ inputs.release }} - # # [FEATURE] MIXIN_AUDITOR - # - name: Run mixin audit check for Minecraft server - # if: ${{ inputs.mixin_audit }} - # timeout-minutes: 10 - # run: | - # mkdir -p ./run - # echo eula=true > ./run/eula.txt - # ./gradlew runServerMixinAudit - - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: build-artifacts - path: merged - - # # [FEATURE] FALLENS_MAVEN - # - name: Publish with gradle - # if: inputs.release || github.ref == 'refs/heads/dev' - # run: | - # if [ -z "${{ inputs.target_subproject }}" ]; then - # echo "Publishing all subprojects" - # ./gradlew publish - # else - # args=$(echo "${{ inputs.target_subproject }}" | tr ',' '\n' | sed 's/$/:publish/' | paste -sd ' ') - # echo "Publishing with arguments=$args" - # ./gradlew $args - # fi - # env: - # BUILD_RELEASE: ${{ inputs.release }} - # FALLENS_MAVEN_TOKEN: ${{ secrets.FALLENS_MAVEN_TOKEN }} - -# summary: -# runs-on: ubuntu-22.04 -# needs: -# - build -# -# steps: -# - uses: actions/checkout@v4 -# -# - name: Download build artifacts -# uses: actions/download-artifact@v4 -# with: -# name: build-artifacts -# path: build-artifacts -# -# - name: Make build summary -# run: python3 .github/workflows/scripts/summary.py # ubuntu-22.04 uses Python 3.10.6 -# env: -# TARGET_SUBPROJECT: ${{ inputs.target_subproject }} \ No newline at end of file + path: merged \ No newline at end of file diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index bc9ef67e2..0595316a3 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,6 +1,6 @@ name: Dev Builds -on: [workflow_dispatch, pull_request, push] +on: [workflow_dispatch, push] jobs: tests: # runs on linux and windows @@ -8,7 +8,4 @@ jobs: secrets: inherit build: # runs on linux uses: ./.github/workflows/build.yml - secrets: inherit -# # [FEATURE] MIXIN_AUDITOR -# with: -# mixin_audit: true \ No newline at end of file + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cff308c48..b4a4ef76b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,25 +103,25 @@ jobs: fi - name: Read common properties - id: properties_common + id: properties_c uses: christian-draeger/read-properties@1.1.1 with: path: gradle.properties - properties: 'mod_name mod_version' + properties: 'mod.name mod.version' - name: Read version-specific properties - id: properties_versioned + id: properties_v uses: christian-draeger/read-properties@1.1.1 with: path: ${{ format('versions/{0}/gradle.properties', matrix.subproject) }} - properties: 'minecraft_version game_versions' + properties: 'publish.versions' - name: Fix game version - id: game_versions + id: publish.versions run: | - # Fixed \n in game_versions isn't parsed by christian-draeger/read-properties as a line separator + # Fixed \n in publish.versions isn't parsed by christian-draeger/read-properties as a line separator echo 'value<> $GITHUB_OUTPUT - echo -e "${{ steps.properties_versioned.outputs.game_versions }}" >> $GITHUB_OUTPUT + echo -e "${{ steps.properties_v.outputs.publish.versions }}" >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT - name: Prepare file information @@ -166,12 +166,12 @@ jobs: files: ${{ steps.file_info.outputs.path }} - name: ${{ format('{0} {1} {2}', steps.properties_common.outputs.mod_name, steps.properties_common.outputs.mod_version, matrix.mod_brand) }} - version: ${{ steps.properties_common.outputs.mod_version }} + name: ${{ format('{0} {1} {2}', steps.properties_c.outputs.mod.name, steps.properties_c.outputs.mod.version, matrix.mod_brand) }} + version: ${{ steps.properties_c.outputs.mod.version }} version-type: release loaders: ${{ matrix.mod_brand }} - game-versions: ${{ steps.game_versions.outputs.value }} + game-versions: ${{ steps.publish.versions.outputs.value }} game-version-filter: any dependencies: '' # declare the dependencies explicitly, so mc-publish won't try to load from fabric.mod.json diff --git a/.github/workflows/scripts/summary.py b/.github/workflows/scripts/summary.py deleted file mode 100644 index 3da4c2f93..000000000 --- a/.github/workflows/scripts/summary.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -A script to scan through all valid mod jars in build-artifacts.zip/$version/build/libs, -and generate an artifact summary table for that to GitHub action step summary -""" -__author__ = 'Fallen_Breath' - -import functools -import glob -import hashlib -import json -import os - - -def read_prop(file_name: str, key: str) -> str: - with open(file_name) as prop: - return next(filter( - lambda l: l.split('=', 1)[0].strip() == key, - prop.readlines() - )).split('=', 1)[1].lstrip() - - -def get_sha256_hash(file_path: str) -> str: - sha256_hash = hashlib.sha256() - - with open(file_path, 'rb') as f: - for buf in iter(functools.partial(f.read, 4096), b''): - sha256_hash.update(buf) - - return sha256_hash.hexdigest() - - -def main(): - target_subproject_env = os.environ.get('TARGET_SUBPROJECT', '') - target_subprojects = list(filter(None, target_subproject_env.split(',') if target_subproject_env != '' else [])) - print('target_subprojects: {}'.format(target_subprojects)) - - with open('settings.json') as f: - settings: dict = json.load(f) - - with open(os.environ['GITHUB_STEP_SUMMARY'], 'w') as f: - f.write('## Build Artifacts Summary\n\n') - f.write('| Subproject | for Minecraft | File | Size | SHA-256 |\n') - f.write('| --- | --- | --- | --- | --- |\n') - - warnings = [] - for subproject in settings['versions']: - if len(target_subprojects) > 0 and subproject not in target_subprojects: - print('skipping {}'.format(subproject)) - continue - game_versions = read_prop('versions/{}/gradle.properties'.format(subproject), 'game_versions') - game_versions = game_versions.strip().replace('\\n', ', ') - file_paths = glob.glob('build-artifacts/{}/build/libs/*.jar'.format(subproject)) - file_paths = list(filter(lambda fp: not fp.endswith('-sources.jar') and not fp.endswith('-dev.jar') and not fp.endswith('-shadow.jar'), file_paths)) - if len(file_paths) == 0: - file_name = '*not found*' - sha256 = '*N/A*' - else: - file_name = '`{}`'.format(os.path.basename(file_paths[0])) - file_size = '{} B'.format(os.path.getsize(file_paths[0])) - sha256 = '`{}`'.format(get_sha256_hash(file_paths[0])) - if len(file_paths) > 1: - warnings.append('Found too many build files in subproject {}: {}'.format(subproject, ', '.join(file_paths))) - - f.write('| {} | {} | {} | {} | {} |\n'.format(subproject, game_versions, file_name, file_size, sha256)) - - if len(warnings) > 0: - f.write('\n### Warnings\n\n') - for warning in warnings: - f.write('- {}\n'.format(warning)) - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 05b235732..d667148fa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,10 @@ on: [workflow_call] jobs: tests: - runs-on: windows-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest, ubuntu-latest] steps: - uses: actions/checkout@v4 - name: Set up JDK 21 diff --git a/build.fabric.gradle.kts b/build.fabric.gradle.kts new file mode 100644 index 000000000..5cd6ed502 --- /dev/null +++ b/build.fabric.gradle.kts @@ -0,0 +1,73 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + kotlin("jvm") + id("automodpack.common") + id("fabric-loom") +} + +version = "${property("mod.version")}" +group = "${property("mod.group")}" +base.archivesName.set("${property("mod.name")}-mc${property("deps.minecraft")}-fabric".lowercase()) + +loom { + accessWidenerPath = rootProject.file("src/main/resources/automodpack.accesswidener") +} + +dependencies { + implementation(project(":core")) + implementation(project(":loader-core")) + + minecraft("com.mojang:minecraft:${property("deps.minecraft")}") + mappings(loom.layered { + officialMojangMappings() + if (hasProperty("deps.parchment")) + parchment("org.parchmentmc.data:parchment-${property("deps.parchment")}@zip") + }) + + modImplementation("net.fabricmc:fabric-loader:${property("deps.fabric-loader")}") + + // TODO transitive false + setOf( + "api-base", // Required by modules below + "resource-loader-v0", // Required for translatable texts + "registry-sync-v0", // Required for custom sounds + "networking-api-v1" // Required by registry sync module + ).forEach { + include(modImplementation(fabricApi.module("fabric-$it", property("deps.fabric-api") as String))!!) + } + + if (stonecutter.eval(stonecutter.current.version, "<1.19.2")) { + include(modImplementation(fabricApi.module("fabric-command-api-v1", property("deps.fabric-api") as String))!!) // TODO transitive false + } else { + include(modImplementation(fabricApi.module("fabric-command-api-v2", property("deps.fabric-api") as String))!!) // TODO transitive false + } + + include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:${property("deps.mixin-extras")}")!!)!!) +} + +java { + if (stonecutter.eval(stonecutter.current.version, ">=1.20.5")) { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + } else { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) + } + withSourcesJar() +} + +tasks { + processResources { + exclude("**/neoforge.mods.toml", "**/mods.toml", "**/accesstransformer.cfg") + } + + register("buildAndCollect") { + group = "build" + from(remapJar.map { it.archiveFile }) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod.version")}")) + dependsOn("build") + } +} \ No newline at end of file diff --git a/build.forge.gradle.kts b/build.forge.gradle.kts new file mode 100644 index 000000000..a3c40af61 --- /dev/null +++ b/build.forge.gradle.kts @@ -0,0 +1,59 @@ +plugins { + kotlin("jvm") + id("automodpack.common") + id("net.neoforged.moddev.legacyforge") +} + +version = "${property("mod.version")}" +group = "${property("mod.group")}" +base.archivesName.set("${property("mod.name")}-mc${property("deps.minecraft")}-forge".lowercase()) + +legacyForge { + version = property("deps.forge") as String + validateAccessTransformers = true + + if (hasProperty("deps.parchment")) parchment { + val (mc, ver) = (property("deps.parchment") as String).split(':') + mappingsVersion = ver + minecraftVersion = mc + } +} + +dependencies { + implementation(project(":core")) + implementation(project(":loader-core")) + + compileOnly("net.fabricmc.fabric-api:fabric-api:0.92.2+1.20.1") + compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:${property("deps.mixin-extras")}")!!) + implementation(jarJar("io.github.llamalad7:mixinextras-forge:${property("deps.mixin-extras")}")!!) +} + +tasks { + processResources { + exclude("**/fabric.mod.json", "**/automodpack.accesswidener") + } + + named("createMinecraftArtifacts") { + dependsOn("stonecutterGenerate") + } + + register("buildAndCollect") { + group = "build" + from(jar.map { it.archiveFile }) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod.version")}")) + dependsOn("build") + } +} + +java { + if (stonecutter.eval(stonecutter.current.version, ">=1.20.5")) { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + } else { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) + } + withSourcesJar() +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 5a7ce50c6..000000000 --- a/build.gradle.kts +++ /dev/null @@ -1,273 +0,0 @@ -import java.util.* - -plugins { - id("dev.kikugie.stonecutter") - id("dev.architectury.loom") -} - -class ModData { - val id = property("mod_id").toString() - val name = property("mod_name").toString() - val version = property("mod_version").toString() - val group = property("mod_group").toString() - val minecraftDependency = property("minecraft_dependency").toString() - val description = property("mod_description").toString() -} - -class LoaderData { - private val name = loom.platform.get().name.lowercase() - val isFabric = name == "fabric" - val isForge = name == "forge" - val isNeoForge = name == "neoforge" - - fun getVersion() : String? { - return if (isForge) { - property("loader_forge")?.toString() - } else if (isNeoForge) { - property("loader_neoforge")?.toString() - } else if (isFabric) { - property("loader_fabric")?.toString() - } else { - null - } - } - - override fun toString(): String { - return name - } -} - -class MinecraftVersionData { - private val name = stonecutter.current.version.substringBeforeLast("-") - val needsJava21 = greaterOrEqual("1.20.5") - - fun greaterThan(other: String) : Boolean { - return stonecutter.compare(name, other.lowercase()) > 0 - } - - fun lessThan(other: String) : Boolean { - return stonecutter.compare(name, other.lowercase()) < 0 - } - - fun greaterOrEqual(other: String) : Boolean { - return stonecutter.compare(name, other.lowercase()) >= 0 - } - - fun lessOrEqual(other: String) : Boolean { - return stonecutter.compare(name, other.lowercase()) <= 0 - } - - override fun equals(other: Any?) : Boolean { - return name == other - } - - override fun toString(): String { - return name - } - - override fun hashCode(): Int { - var result = name.hashCode() - result = 31 * result + needsJava21.hashCode() - return result - } -} - -val mod = ModData() -val loader = LoaderData() -val minecraftVersion = MinecraftVersionData() - - -version = mod.version -group = mod.group -base.archivesName.set("${mod.name}-mc$minecraftVersion-$loader".lowercase(Locale.getDefault())) - -repositories { - mavenCentral() - mavenLocal() - maven { url = uri("https://api.modrinth.com/maven") } - maven { url = uri("https://maven.neoforged.net/releases") } - maven { url = uri("https://files.minecraftforge.net/maven/") } - maven { url = uri("https://libraries.minecraft.net/") } -} - -dependencies { - // just for IDE not complaining (its already included in the shadow jar of core-${mod_brand}) - // + needed for runtime in dev env - implementation(project(":core")) - implementation(project(":loader-core")) - - testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2") - - // TODO fix dev env launch - - minecraft("com.mojang:minecraft:$minecraftVersion") - - // Mappings have to be patched for new neoforge - if (loader.isNeoForge && minecraftVersion.equals("1.20.6")) { - mappings( - loom.layered { - mappings("net.fabricmc:yarn:${property("yarn_mappings")}:v2") - mappings("dev.architectury:yarn-mappings-patch-neoforge:1.20.6+build.4") - } - ) - } else if (loader.isNeoForge && minecraftVersion.greaterOrEqual("1.21")) { - mappings( - loom.layered { - mappings("net.fabricmc:yarn:${property("yarn_mappings")}:v2") - mappings("dev.architectury:yarn-mappings-patch-neoforge:1.21+build.4") - } - ) - } else { - mappings("net.fabricmc:yarn:${property("yarn_mappings")}:v2") - } - - if (loader.isFabric) { - setOf( - "fabric-api-base", // Required by modules below - "fabric-resource-loader-v0", // Required for translatable texts - "fabric-registry-sync-v0", // Required for custom sounds - "fabric-networking-api-v1" // Required by registry sync module - ).forEach { - include(modImplementation(fabricApi.module(it, property("fabric_version") as String))!!) // TODO transitive false - } - if (minecraftVersion.lessThan("1.19.2")) { - include(modImplementation(fabricApi.module("fabric-command-api-v1", property("fabric_version") as String))!!) // TODO transitive false - } else { - include(modImplementation(fabricApi.module("fabric-command-api-v2", property("fabric_version") as String))!!) // TODO transitive false - } - - // JiJ lastest version of mixin extras so all mods work (workaround) - remove when we detect such incompatibilities and copy the jij mod to the mods folder, currently the stable loader version would be loaded instead of the lastest required by some mods - include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:${property("mixin_extras")}")!!)!!) - } - - if (loader.isFabric) { - modImplementation("net.fabricmc:fabric-loader:${loader.getVersion()}") - } else if (loader.isForge) { - "forge"("net.minecraftforge:forge:$minecraftVersion-${loader.getVersion()}") - - // For pseudo mixin (compat with Forgified Fabric API) - compileOnly(fabricApi.module("fabric-networking-api-v1", "0.92.2+1.20.1")) // version is not important - - implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:${property("mixin_extras")}")!!) - implementation(include("io.github.llamalad7:mixinextras-forge:${property("mixin_extras")}")!!) - } else if (loader.isNeoForge) { - - // For pseudo mixin (compat with Forgified Fabric API) - compileOnly(fabricApi.module("fabric-networking-api-v1", "0.92.2+1.20.1")) // version is not important - - "neoForge"("net.neoforged:neoforge:${loader.getVersion()}") - - // Bundle mixin extras to 1.20.2 since it has too old version of it - if (minecraftVersion.equals("1.20.2")) { - implementation(include("io.github.llamalad7:mixinextras-neoforge:${property("mixin_extras")}")!!) - } - } -} - -loom { - runConfigs["client"].apply { - ideConfigGenerated(true) - vmArgs("-Dmixin.debug.export=true") - runDir = "../../run" - } - - runConfigs["server"].apply { - ideConfigGenerated(true) - vmArgs("-Dmixin.debug.export=true") - runDir = "../../run" - } - - if (loader.isForge) { - forge { - convertAccessWideners = true - mixinConfigs = listOf("automodpack-main.mixins.json") - } - } - - accessWidenerPath = file("../../src/main/resources/automodpack.accesswidener") -} - -if (stonecutter.current.isActive) { - rootProject.tasks.register("buildActive") { - group = "project" - dependsOn(tasks.named("build")) - finalizedBy("mergeJars") - } -} - -tasks.processResources { - val map = mapOf( - "id" to mod.id, - "name" to mod.name, - "version" to mod.version, - "minecraft_dependency" to mod.minecraftDependency, - "description" to mod.description - ) - - inputs.properties(map) - - if (loader.isFabric) { - exclude("META-INF/neoforge.mods.toml") - exclude("META-INF/mods.toml") - filesMatching("fabric.mod.json") { - expand(map) - } - } else if (loader.isNeoForge) { - exclude("fabric.mod.json") - exclude("META-INF/mods.toml") - filesMatching("META-INF/neoforge.mods.toml") { - expand(map) - } - } else if (loader.isForge) { - exclude("fabric.mod.json") - exclude("META-INF/neoforge-mods.toml") - filesMatching("META-INF/mods.toml") { - expand(map) - } - } - - if (loader.isForge || loader.isNeoForge) { - filesMatching("assets/automodpack/icon.png") { - path = "icon.png" - } - } - - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - from("../../src/main/resources") { - include("META-INF/services/**") - } -} - -java { - if (minecraftVersion.needsJava21) { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 - - toolchain { - languageVersion.set(JavaLanguageVersion.of(21)) - } - } else { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - } - - withSourcesJar() -} - -tasks.withType { - options.encoding = "UTF-8" -} - -tasks.named("jar") { - from(rootProject.file("LICENSE")) { - rename { "${it}_${mod.id}" } - } -} - -tasks.named("build") { - finalizedBy(rootProject.tasks.named("mergeJars")) -} \ No newline at end of file diff --git a/build.neoforge.gradle.kts b/build.neoforge.gradle.kts new file mode 100644 index 000000000..d50727426 --- /dev/null +++ b/build.neoforge.gradle.kts @@ -0,0 +1,51 @@ +plugins { + kotlin("jvm") + id("automodpack.common") + id("net.neoforged.moddev") +} + +version = "${property("mod.version")}" +group = "${property("mod.group")}" +base.archivesName.set("${property("mod.name")}-mc${property("deps.minecraft")}-neoforge".lowercase()) + +neoForge { + version = property("deps.neoforge") as String + validateAccessTransformers = true + + if (hasProperty("deps.parchment")) parchment { + val (mc, ver) = (property("deps.parchment") as String).split(':') + mappingsVersion = ver + minecraftVersion = mc + } +} + +dependencies { + implementation(project(":core")) + implementation(project(":loader-core")) + + compileOnly("net.fabricmc.fabric-api:fabric-api:0.92.2+1.20.1") +} + +tasks { + processResources { + exclude("**/fabric.mod.json", "**/automodpack.accesswidener", "**/forge.mods.toml") + } + + named("createMinecraftArtifacts") { + dependsOn("stonecutterGenerate") + } + + register("buildAndCollect") { + group = "build" + from(jar.map { it.archiveFile }) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod.version")}")) + dependsOn("build") + } +} + +java { + withSourcesJar() + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 000000000..eaf927569 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + `kotlin-dsl` + kotlin("jvm") version "2.1.21" +} + +repositories { + google() + mavenCentral() + gradlePluginPortal() +} + +dependencies { + +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/automodpack.common.gradle.kts b/buildSrc/src/main/kotlin/automodpack.common.gradle.kts new file mode 100644 index 000000000..e94d35697 --- /dev/null +++ b/buildSrc/src/main/kotlin/automodpack.common.gradle.kts @@ -0,0 +1,247 @@ +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream + +plugins { + idea +} + +idea { + module { + isDownloadJavadoc = true + isDownloadSources = true + } +} + +repositories { + fun strictMaven(url: String, vararg groups: String) = exclusiveContent { + forRepository { maven(url) } + filter { groups.forEach(::includeGroup) } + } + strictMaven("https://maven.parchmentmc.org", "org.parchmentmc.data") + maven("https://maven.fabricmc.net/") +} + +tasks.named("processResources") { + fun prop(name: String) = project.property(name) as String + + val props = HashMap().apply { + this["version"] = prop("mod.version") + this["minecraft"] = prop("meta.minecraft") + this["id"] = prop("mod.id") + this["name"] = prop("mod.name") + this["description"] = prop("mod.description") + } + + filesMatching(listOf("fabric.mod.json", "META-INF/neoforge.mods.toml", "META-INF/mods.toml")) { + expand(props) + } +} + +tasks.named("build") { + // We need to build the loader modules first, so that we can merge the jars later + dependsOn(":core:build", ":loader-core:build") + when { + project.name.contains("fabric") -> { + dependsOn(":loader-fabric-core:build", ":loader-fabric-15:build", ":loader-fabric-16:build") + } + project.name.contains("neoforge") -> { + dependsOn(":loader-neoforge-fml2:build", ":loader-neoforge-fml4:build") + } + project.name.contains("forge") -> { + dependsOn(":loader-forge-fml40:build", ":loader-forge-fml47:build") + } + } + + finalizedBy(tasks.named("mergeJar")) +} + +val mergedDir = File("${rootProject.projectDir}/merged") + +tasks.named("clean") { + finalizedBy("cleanMerged") +} + +tasks.register("cleanMerged") { + doLast { + mergedDir.deleteRecursively() + } +} + +// TODO make it faster +// If something goes wrong, try to run the "clean" task +tasks.register("mergeJar") { + doLast { + mergedDir.mkdirs() + val buildDirLibs = project.layout.buildDirectory.dir("libs").get().asFile + val jarToMerge = buildDirLibs.listFiles() + ?.firstOrNull { file -> file.isFile && !file.name.endsWith("-sources.jar") && file.name.endsWith(".jar") } + ?: error("No jar found to merge in build/libs directory! ${buildDirLibs.absolutePath}") + + val time = System.currentTimeMillis() + println("Found ${jarToMerge} merge. Merging...") + + val minecraftVersionStr = jarToMerge.name.substringAfterLast("-mc").substringBefore("-") + if (minecraftVersionStr.isEmpty()) { + error("Could not identify Minecraft version in the jar name: ${jarToMerge.name}") + } + + var loaderModule = "" + if (jarToMerge.name.contains("fabric")) { + loaderModule = "fabric-core" + } else if (jarToMerge.name.contains("neoforge")) { + loaderModule = when (minecraftVersionStr) { + "1.20.6", "1.20.4", "1.20.1", "1.19.4", "1.19.2", "1.18.2" -> "neoforge-fml2" + else -> "neoforge-fml4" + } + } else if (jarToMerge.name.contains("forge")) { + loaderModule = if (minecraftVersionStr == "1.18.2") { + "forge-fml40" + } else { + "forge-fml47" + } + } + + val loaderModuleProject = rootProject.findProject("loader-$loaderModule") + ?: error("Loader module '$loaderModule' not found in the project.") + + val loaderFile = loaderModuleProject.layout.buildDirectory.dir("libs").get().asFile.listFiles() + ?.single { it.isFile && !it.name.endsWith("-sources.jar") && it.name.endsWith(".jar") } + ?: error("No loader jar found in loader/$loaderModule/build/libs directory! Make sure to build the loader module first.") + + val finalJar = File("$mergedDir/${jarToMerge.name}") + loaderFile.copyTo(finalJar, overwrite = true) + appendFileToZip(finalJar, jarToMerge, "automodpack-mod.jar") + + println("Merged: ${jarToMerge.name} into: ${finalJar.name} from: ${loaderFile.name} Took: ${System.currentTimeMillis() - time}ms") + } +} + +fun appendFileToZip(zipFile: File, fileToAppend: File, entryName: String) { + val entries = ZipInputStream(FileInputStream(zipFile)).use { zipStream -> + generateSequence { zipStream.nextEntry } + .toList() + } + + val graph = mutableMapOf>() + + entries.forEach { entry -> + val children = entry.name.split("/") + var currentParent = "" + children.forEach { child -> + if (child.isNotEmpty()) { + currentParent = "$currentParent$child/" + val parent = graph.getOrPut(currentParent) { mutableListOf() } + parent.add(child) + } + } + } + +// graph.forEach { (parent, children) -> +// println("$parent -> $children") +// } + + val filteredGraph = filterEntries(entries, graph, zipFile) + + // Doing with temp file since for some reason just adding the file breaks the zip/jar file + val tempFile = File("$zipFile.temp") + tempFile.createNewFile() + + ZipOutputStream(FileOutputStream(tempFile)).use { zipStream -> + ZipInputStream(FileInputStream(zipFile)).use { existingZipStream -> + while (true) { + val entry = existingZipStream.nextEntry ?: break + if (filteredGraph.containsKey("${entry.name}/")) { + try { + val zipEntry = ZipEntry(entry.name) + zipStream.putNextEntry(zipEntry) + existingZipStream.copyTo(zipStream) + zipStream.closeEntry() + } catch (_: Exception) { + println("Error while copying entry: ${entry.name}") + } + } + } + } + + // Add the new entry + zipStream.putNextEntry(ZipEntry(entryName)) + FileInputStream(fileToAppend).use { fileInputStream -> + fileInputStream.copyTo(zipStream) + } + zipStream.closeEntry() + } + + // Replace the original zip file with the one containing the new entry + zipFile.delete() + tempFile.renameTo(zipFile) +} + +val dupesDir = File("${rootProject.projectDir}/dupes") + +fun filterEntries(entries: List, graph: Map>, zipFile: File): Map> { + + val emptyDirs = mutableSetOf() + + // find empty directories + graph.forEach { (parent, children) -> + if (children.size == 0) { + emptyDirs.add(parent) + } + } + + // dump duplicate entries + dupesDir.deleteRecursively() + dupesDir.mkdirs() + + dumpDupeEntries(zipFile, entries) + + // filter empty directories + // filter single duplicate files (leave only one) + val dupes = mutableSetOf() + + val filteredGraph = graph.filter { (parent, children) -> + if (emptyDirs.contains(parent)) { + println("Filtering empty dir: $parent -> $children") + return@filter false + } + + val count = graph.count { parent == it.key } + if (count > 1 && !dupes.add(children[0])) { + return@filter false + } + + return@filter true + } + + return filteredGraph +} + +fun dumpDupeEntries(zipFile: File, entries: List) { + // check for duplicates + val entryNames = mutableSetOf() + entries.forEach { duplicate -> + if (!entryNames.add(duplicate.name)) { + println("Duplicate entry: $duplicate") + + // write the entry to the file + ZipInputStream(FileInputStream(zipFile)).use { zipStream -> + var i = 0 + generateSequence { zipStream.nextEntry } + .filter { it.name == duplicate.name } + .forEach { _ -> + i++ + val dupeFile = File("$dupesDir/$i-$duplicate.dupe") + println("Extracting to: $dupeFile") + dupeFile.parentFile.mkdirs() + dupeFile.createNewFile() + FileOutputStream(dupeFile).use { fileOutputStream -> + zipStream.copyTo(fileOutputStream) + } + } + } + } + } +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts index a8ae4e434..87cad8110 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -2,13 +2,13 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { java - id("com.github.johnrengelman.shadow") + id("com.gradleup.shadow") } base { - archivesName = property("mod_id") as String + "-" + project.name - version = property("mod_version") as String - group = property("mod_group") as String + archivesName = property("mod.id") as String + "-" + project.name + version = property("mod.version") as String + group = property("mod.group") as String } repositories { @@ -16,9 +16,9 @@ repositories { } dependencies { - implementation("org.apache.logging.log4j:log4j-core:2.20.0") + implementation("org.apache.logging.log4j:log4j-core:2.19.0") implementation("com.google.code.gson:gson:2.10.1") - implementation("io.netty:netty-all:4.1.118.Final") + compileOnly("io.netty:netty-all:4.1.118.Final") implementation("org.bouncycastle:bcpkix-jdk18on:1.80") implementation("com.github.luben:zstd-jni:1.5.7-3") implementation("org.tomlj:tomlj:1.1.1") @@ -31,11 +31,7 @@ java { // leave it on java 17 to be compatible with older versions and we dont really need 21 there anyway sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 - - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) withSourcesJar() } diff --git a/gradle.properties b/gradle.properties index d2b8d0f82..fb5cd261f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,21 +1,16 @@ org.gradle.jvmargs = -Xmx8G # Run clean task before building if doesn't work -org.gradle.parallel = false +org.gradle.parallel = true org.gradle.caching = true -org.gradle.caching.debug = false org.gradle.configureondemand = true -fabric_versions = 1.18.2, 1.19.2, 1.19.4, 1.20.1, 1.20.4, 1.20.6, 1.21.1, 1.21.3, 1.21.4 -neoforge_versions = 1.20.4, 1.20.6, 1.21.1, 1.21.3, 1.21.4 -forge_versions = 1.18.2, 1.19.2, 1.19.4, 1.20.1 - core_modules = core, fabric-core, fabric-15, fabric-16, forge-fml40, forge-fml47, neoforge-fml2, neoforge-fml4 -loader_fabric = 0.15.11 -mixin_extras = 0.5.0-rc.2 +deps.fabric-loader = 0.15.11 +deps.mixin-extras = 0.5.0-rc.2 -mod_id = automodpack -mod_name = AutoModpack -mod_version = 4.0.0-beta37 -mod_group = pl.skidam.automodpack -mod_description = Enjoy a seamless modpack installation process and effortless updates with a user-friendly solution that simplifies management, making your gaming experience a breeze. +mod.id = automodpack +mod.name = AutoModpack +mod.version = 4.0.0-beta37 +mod.group = pl.skidam.automodpack +mod.description = Enjoy a seamless modpack installation process and effortless updates with a user-friendly solution that simplifies management, making your gaming experience a breeze. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a793a..ff23a68d7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/loader/fabric/15/gradle.properties b/loader/fabric/15/gradle.properties index 83f11cd8c..146492225 100644 --- a/loader/fabric/15/gradle.properties +++ b/loader/fabric/15/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=fabric -loader_fabric=0.15.11 \ No newline at end of file +deps.fabric=0.15.11 \ No newline at end of file diff --git a/loader/fabric/16/gradle.properties b/loader/fabric/16/gradle.properties index 117c56d7a..5ebca380c 100644 --- a/loader/fabric/16/gradle.properties +++ b/loader/fabric/16/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=fabric -loader_fabric=0.16.9 \ No newline at end of file +deps.fabric=0.16.14 \ No newline at end of file diff --git a/loader/fabric/core/gradle.properties b/loader/fabric/core/gradle.properties index 83f11cd8c..146492225 100644 --- a/loader/fabric/core/gradle.properties +++ b/loader/fabric/core/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=fabric -loader_fabric=0.15.11 \ No newline at end of file +deps.fabric=0.15.11 \ No newline at end of file diff --git a/loader/forge/fml40/gradle.properties b/loader/forge/fml40/gradle.properties index adf61db65..b6ad2295f 100644 --- a/loader/forge/fml40/gradle.properties +++ b/loader/forge/fml40/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.18.2 -yarn_mappings=1.18.2+build.4 -loom.platform=forge -loader_forge=40.2.10 \ No newline at end of file +deps.forge=1.18.2-40.3.10 \ No newline at end of file diff --git a/loader/forge/fml47/gradle.properties b/loader/forge/fml47/gradle.properties index 5371a7bc7..bde194594 100644 --- a/loader/forge/fml47/gradle.properties +++ b/loader/forge/fml47/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=forge -loader_forge=47.1.3 \ No newline at end of file +deps.forge=1.20.1-47.1.3 diff --git a/loader/loader-core.gradle.kts b/loader/loader-core.gradle.kts index d0a01009c..224b81892 100644 --- a/loader/loader-core.gradle.kts +++ b/loader/loader-core.gradle.kts @@ -3,9 +3,9 @@ plugins { } base { - archivesName = property("mod_id") as String + "-" + project.name - version = property("mod_version") as String - group = property("mod_group") as String + archivesName = property("mod.id") as String + "-" + project.name + version = property("mod.version") as String + group = property("mod.group") as String } repositories { @@ -17,19 +17,14 @@ dependencies { // our needed dependencies implementation("com.google.code.gson:gson:2.10.1") - implementation("org.apache.logging.log4j:log4j-core:2.20.0") + implementation("org.apache.logging.log4j:log4j-core:2.19.0") implementation("org.tomlj:tomlj:1.1.1") } -java { - // leave it on java 17 to be compatible with older versions and we dont really need 21 there anyway +java { // leave it on java 17 to be compatible with older versions and we dont really need 21 there anyway sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 - - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) withSourcesJar() } diff --git a/loader/loader-fabric-core.gradle.kts b/loader/loader-fabric-core.gradle.kts index 1fa530690..c23286541 100644 --- a/loader/loader-fabric-core.gradle.kts +++ b/loader/loader-fabric-core.gradle.kts @@ -2,13 +2,13 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { java - id("com.github.johnrengelman.shadow") + id("com.gradleup.shadow") } base { - archivesName = property("mod_id") as String + "-" + project.name - version = property("mod_version") as String - group = property("mod_group") as String + archivesName = property("mod.id") as String + "-" + project.name + version = property("mod.version") as String + group = property("mod.group") as String } repositories { @@ -23,14 +23,14 @@ dependencies { compileOnly(project(":loader-fabric-16")) compileOnly("com.google.code.gson:gson:2.10.1") - compileOnly("org.apache.logging.log4j:log4j-core:2.20.0") + compileOnly("org.apache.logging.log4j:log4j-core:2.8.1") implementation("org.tomlj:tomlj:1.1.1") implementation("org.bouncycastle:bcpkix-jdk18on:1.80") implementation("com.github.luben:zstd-jni:1.5.7-3") implementation("org.apache.httpcomponents.client5:httpclient5:5.5") - compileOnly("net.fabricmc:fabric-loader:${property("loader_fabric")}") + compileOnly("net.fabricmc:fabric-loader:${property("deps.fabric")}") } configurations { @@ -82,11 +82,7 @@ tasks.named("shadowJar") { java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 - - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) withSourcesJar() } diff --git a/loader/loader-fabric.gradle.kts b/loader/loader-fabric.gradle.kts index bea430ccb..ef77b834d 100644 --- a/loader/loader-fabric.gradle.kts +++ b/loader/loader-fabric.gradle.kts @@ -1,21 +1,11 @@ -import net.fabricmc.loom.task.RemapJarTask - plugins { - id("dev.architectury.loom") - id("com.github.johnrengelman.shadow") -} - -val loader = property("loom.platform") as String -var mcVer = (property("minecraft_version") as String).replace(".", "").toInt() -// make from 3 char release 4 char release e.g. 1.21 -> 1.21.0 == 1210 -if (mcVer < 1000) { - mcVer *= 10 + java } base { - archivesName = property("mod_id") as String + "-" + project.name - version = property("mod_version") as String - group = property("mod_group") as String + archivesName = property("mod.id") as String + "-" + project.name + version = property("mod.version") as String + group = property("mod.group") as String } repositories { @@ -28,31 +18,20 @@ dependencies { compileOnly(project(":core")) compileOnly(project(":loader-core")) - minecraft("com.mojang:minecraft:${property("minecraft_version")}") - mappings(loom.officialMojangMappings()) // We dont really use minecraft code there so we can use mojang mappings - compileOnly("com.google.code.gson:gson:2.10.1") compileOnly("org.apache.logging.log4j:log4j-core:2.20.0") implementation("org.tomlj:tomlj:1.1.1") - modImplementation("net.fabricmc:fabric-loader:${property("loader_fabric")}") + implementation("net.fabricmc:fabric-loader:${property("deps.fabric")}") } java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 - - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) withSourcesJar() } tasks.withType { options.encoding = "UTF-8" } - -tasks.named("remapJar") { - isEnabled = false -} \ No newline at end of file diff --git a/loader/loader-forge.gradle.kts b/loader/loader-forge.gradle.kts index 5c758b635..d5b0170a6 100644 --- a/loader/loader-forge.gradle.kts +++ b/loader/loader-forge.gradle.kts @@ -1,51 +1,44 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import net.fabricmc.loom.task.RemapJarTask plugins { - id("dev.architectury.loom") - id("com.github.johnrengelman.shadow") -} - -val loader = property("loom.platform") as String -var mcVer = (property("minecraft_version") as String).replace(".", "").toInt() -// make from 3 char release 4 char release e.g. 1.21 -> 1.21.0 == 1210 -if (mcVer < 1000) { - mcVer *= 10 + java + id("net.neoforged.moddev.legacyforge") + id("com.gradleup.shadow") } base { - archivesName = property("mod_id") as String + "-" + project.name - version = property("mod_version") as String - group = property("mod_group") as String + archivesName = property("mod.id") as String + "-" + project.name + version = property("mod.version") as String + group = property("mod.group") as String } -repositories { - mavenCentral() - maven { url = uri("https://maven.fabricmc.net/") } - maven { url = uri("https://maven.neoforged.net/releases") } - maven { url = uri("https://files.minecraftforge.net/maven/") } - maven { url = uri("https://libraries.minecraft.net/") } +legacyForge { + version = property("deps.forge") as String } dependencies { compileOnly(project(":core")) compileOnly(project(":loader-core")) - minecraft("com.mojang:minecraft:${property("minecraft_version")}") - mappings(loom.officialMojangMappings()) // We dont really use minecraft code there so we can use mojang mappings - compileOnly("com.google.code.gson:gson:2.10.1") - compileOnly("org.apache.logging.log4j:log4j-core:2.20.0") + compileOnly("org.apache.logging.log4j:log4j-core:2.8.1") implementation("org.tomlj:tomlj:1.1.1") implementation("org.bouncycastle:bcpkix-jdk18on:1.80") implementation("com.github.luben:zstd-jni:1.5.7-3") implementation("org.apache.httpcomponents.client5:httpclient5:5.5") +} - if (project.name.contains("neoforge")) { - "neoForge"("net.neoforged:neoforge:${property("loader_neoforge")}") - } else { - "forge"("net.minecraftforge:forge:${property("minecraft_version")}-${property("loader_forge")}") +tasks { + processResources { + exclude("**/fabric.mod.json", "**/automodpack.accesswidener") + } + + register("buildAndCollect") { + group = "build" + from(jar.map { it.archiveFile }) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod.version")}")) + dependsOn("build") } } @@ -92,22 +85,9 @@ tasks.named("shadowJar") { } java { - if (mcVer >= 1206) { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 - - toolchain { - languageVersion.set(JavaLanguageVersion.of(21)) - } - } else { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } - } - + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) withSourcesJar() } @@ -115,15 +95,6 @@ tasks.withType { options.encoding = "UTF-8" } -tasks.named("remapJar") { - isEnabled = false - -} - -tasks.named("jar") { - isEnabled = false -} - tasks.named("assemble") { dependsOn("shadowJar") } \ No newline at end of file diff --git a/loader/loader-neoforge.gradle.kts b/loader/loader-neoforge.gradle.kts new file mode 100644 index 000000000..37ccd3e92 --- /dev/null +++ b/loader/loader-neoforge.gradle.kts @@ -0,0 +1,100 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + java + id("net.neoforged.moddev") + id("com.gradleup.shadow") +} + +base { + archivesName = property("mod.id") as String + "-" + project.name + version = property("mod.version") as String + group = property("mod.group") as String +} + +neoForge { + version = property("deps.neoforge") as String +} + +dependencies { + compileOnly(project(":core")) + compileOnly(project(":loader-core")) + + compileOnly("com.google.code.gson:gson:2.10.1") + compileOnly("org.apache.logging.log4j:log4j-core:2.8.1") + + implementation("org.tomlj:tomlj:1.1.1") + implementation("org.bouncycastle:bcpkix-jdk18on:1.80") + implementation("com.github.luben:zstd-jni:1.5.7-3") + implementation("org.apache.httpcomponents.client5:httpclient5:5.5") +} + +tasks { + processResources { + exclude("**/fabric.mod.json", "**/automodpack.accesswidener") + } + + register("buildAndCollect") { + group = "build" + from(jar.map { it.archiveFile }) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod.version")}")) + dependsOn("build") + } +} + +configurations { + create("shadowImplementation") { + extendsFrom(configurations.getByName("implementation")) + isCanBeResolved = true + } +} + +tasks.named("shadowJar") { + archiveClassifier.set("") + + from(project(":core").sourceSets.main.get().output) + from(project(":loader-core").sourceSets.main.get().output) + + // Include the tomlj dependency in the shadow jar + configurations = listOf(project.configurations.getByName("shadowImplementation")) + + val reloc = "am_libs" + relocate("org.antlr", "${reloc}.org.antlr") + relocate("org.tomlj", "${reloc}.org.tomlj") + relocate("org.apache.hc", "${reloc}.org.apache.hc") + relocate("org.checkerframework", "${reloc}.org.checkerframework") + relocate("org.slf4j", "${reloc}.org.slf4j") +// relocate("com.github.luben", "${reloc}.com.github.luben") // cant relocate - natives + relocate("org.bouncycastle", "${reloc}.org.bouncycastle") + + if (project.name.contains("neoforge")) { + relocate("pl.skidam.automodpack_loader_core_neoforge", "pl.skidam.automodpack_loader_core") + } else { + relocate("pl.skidam.automodpack_loader_core_forge", "pl.skidam.automodpack_loader_core") + } + + exclude("pl/skidam/automodpack_loader_core/loader/LoaderManager.class") + exclude("pl/skidam/automodpack_loader_core/mods/ModpackLoader.class") + exclude("log4j2.xml") + + manifest { + attributes["AutoModpack-Version"] = version + } + + mergeServiceFiles() +} + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + withSourcesJar() +} + +tasks.withType { + options.encoding = "UTF-8" +} + +tasks.named("assemble") { + dependsOn("shadowJar") +} \ No newline at end of file diff --git a/loader/neoforge/fml2/gradle.properties b/loader/neoforge/fml2/gradle.properties index b55507b93..5cf92ca53 100644 --- a/loader/neoforge/fml2/gradle.properties +++ b/loader/neoforge/fml2/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.20.4 -yarn_mappings=1.20.4+build.3 -loom.platform=neoforge -loader_neoforge=20.4.237 \ No newline at end of file +deps.neoforge=20.4.248 \ No newline at end of file diff --git a/loader/neoforge/fml4/gradle.properties b/loader/neoforge/fml4/gradle.properties index 0574b0042..cc919d069 100644 --- a/loader/neoforge/fml4/gradle.properties +++ b/loader/neoforge/fml4/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.21.1 -yarn_mappings=1.21.1+build.3 -loom.platform=neoforge -loader_neoforge=21.1.6 \ No newline at end of file +deps.neoforge=21.1.182 \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 5387b7937..f1775351b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,24 +1,27 @@ pluginManagement { repositories { + mavenLocal() mavenCentral() gradlePluginPortal() - maven { url = uri("https://maven.architectury.dev/") } - maven { url = uri("https://maven.fabricmc.net/") } - maven { url = uri("https://maven.neoforged.net/releases") } - maven { url = uri("https://files.minecraftforge.net/maven/") } - maven { url = uri("https://maven.kikugie.dev/snapshots") } - mavenLocal() + maven("https://maven.architectury.dev/") { name = "Architectury" } + maven("https://maven.fabricmc.net/") { name = "Fabric" } + maven("https://maven.neoforged.net/releases/") { name = "NeoForged" } + maven("https://maven.kikugie.dev/snapshots") { name = "KikuGie" } } } plugins { - id("dev.kikugie.stonecutter") version "0.7-alpha.22" - id("dev.architectury.loom") version "1.9-SNAPSHOT" apply false // with 1.10 wait for remap fixes on neo/forge - id("com.github.johnrengelman.shadow") version "8.1.1" apply false + // For some reason, this plugin is crucial - do not remove + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" + id("dev.kikugie.stonecutter") version "0.7-beta.4" } include(":core") +fun getProperty(key: String): String? { + return settings.extra[key] as? String +} + val coreModules = getProperty("core_modules")!!.split(',').map { it.trim() } coreModules.forEach { module -> @@ -31,36 +34,33 @@ coreModules.forEach { module -> "core" -> project.buildFileName = "../loader-core.gradle.kts" "fabric-core" -> project.buildFileName = "../../loader-fabric-core.gradle.kts" "fabric-15", "fabric-16" -> project.buildFileName = "../../loader-fabric.gradle.kts" - "forge-fml40", "forge-fml47", "neoforge-fml2", "neoforge-fml4" -> project.buildFileName = "../../loader-forge.gradle.kts" + "forge-fml40", "forge-fml47" -> project.buildFileName = "../../loader-forge.gradle.kts" + "neoforge-fml2", "neoforge-fml4" -> project.buildFileName = "../../loader-neoforge.gradle.kts" } } -fun getProperty(key: String): String? { - return settings.extra[key] as? String -} - -fun getVersions(key: String): Set { - return getProperty(key)!!.split(',').map { it.trim() }.toSet() -} - -val versions = mapOf( - "forge" to getVersions("forge_versions"), - "fabric" to getVersions("fabric_versions"), - "neoforge" to getVersions("neoforge_versions") -) - -val sharedVersions = versions.map { entry -> - val loader = entry.key - entry.value.map { "$it-$loader" } -}.flatten().toSet() - stonecutter { - kotlinController = true - centralScript = "build.gradle.kts" + create(rootProject) { + fun match(version: String, vararg loaders: String) = loaders + .forEach { vers("$version-$it", version).buildscript = "build.$it.gradle.kts" } - shared { - versions(sharedVersions) - } + // Configure your targets here! + match("1.21.6", "fabric", "neoforge") + match("1.21.5", "fabric", "neoforge") + match("1.21.4", "fabric", "neoforge") + match("1.21.3", "fabric", "neoforge") + match("1.21.1", "fabric", "neoforge") + match("1.20.6", "fabric", "neoforge") + match("1.20.4", "fabric", "neoforge") + match("1.20.1", "fabric", "forge") + match("1.19.4", "fabric", "forge") + match("1.19.2", "fabric", "forge") + match("1.18.2", "fabric", "forge") - create(rootProject) + // This is the default target. + // https://stonecutter.kikugie.dev/stonecutter/guide/setup#settings-settings-gradle-kts + vcsVersion = "1.21.6-neoforge" + } } + +rootProject.name = "AutoModpack" \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/client/ScreenImpl.java b/src/main/java/pl/skidam/automodpack/client/ScreenImpl.java index fd3f9a8ed..08b2e45cb 100644 --- a/src/main/java/pl/skidam/automodpack/client/ScreenImpl.java +++ b/src/main/java/pl/skidam/automodpack/client/ScreenImpl.java @@ -1,9 +1,5 @@ package pl.skidam.automodpack.client; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.TitleScreen; -import net.minecraft.util.Util; import pl.skidam.automodpack.client.ui.*; import pl.skidam.automodpack_loader_core.client.Changelogs; import pl.skidam.automodpack_loader_core.client.ModpackUpdater; @@ -14,6 +10,10 @@ import java.nio.file.Path; import java.util.Optional; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.TitleScreen; public class ScreenImpl implements ScreenService { @@ -75,12 +75,12 @@ public Optional getScreen() { private static class Screens { private static Screen getScreen() { - return MinecraftClient.getInstance().currentScreen; + return Minecraft.getInstance().screen; } public static void setScreen(Screen screen) { // required for forge to handle it properly - Util.getMainWorkerExecutor().execute(() -> MinecraftClient.getInstance().execute(() -> MinecraftClient.getInstance().setScreen(screen))); + Util.backgroundExecutor().execute(() -> Minecraft.getInstance().execute(() -> Minecraft.getInstance().setScreen(screen))); } public static void download(Object downloadManager, Object header) { diff --git a/src/main/java/pl/skidam/automodpack/client/audio/AudioManager.java b/src/main/java/pl/skidam/automodpack/client/audio/AudioManager.java index ea478e461..631ea2985 100644 --- a/src/main/java/pl/skidam/automodpack/client/audio/AudioManager.java +++ b/src/main/java/pl/skidam/automodpack/client/audio/AudioManager.java @@ -1,44 +1,43 @@ package pl.skidam.automodpack.client.audio; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.sound.SoundManager; -import net.minecraft.sound.SoundEvent; -import net.minecraft.util.Identifier; import java.util.function.Supplier; -import pl.skidam.automodpack.init.Common; +/*? if >=1.19.3 && !forge {*/ +import net.minecraft.core.registries.BuiltInRegistries; +/*?}*/ +import pl.skidam.automodpack.init.Common; +import net.minecraft.client.Minecraft; +import net.minecraft.client.sounds.SoundManager; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; /*? if neoforge {*/ import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.registries.DeferredRegister; import static pl.skidam.automodpack_core.GlobalVariables.MOD_ID; -/*?} elif forge {*/ +/*?} else if forge {*/ /*import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import static pl.skidam.automodpack_core.GlobalVariables.MOD_ID; *//*?} else {*/ /*/^? if >=1.19.3 {^/ -import net.minecraft.registry.Registry; -/^?} else {^/ -/^import net.minecraft.util.registry.Registry; +import net.minecraft.core.Registry; + /^?} else {^/ +/^import net.minecraft.core.Registry; ^//^?}^/ *//*?}*/ -/*? if >=1.19.3 && !forge {*/ -import net.minecraft.registry.Registries; -/*?}*/ - public class AudioManager { private static CustomSoundInstance SOUND_INSTANCE; private static SoundManager soundManager; private static boolean playing = false; - private static final Identifier WAITING_MUSIC_ID = Common.id("waiting_music"); + private static final ResourceLocation WAITING_MUSIC_ID = Common.id("waiting_music"); -/*? if >=1.19.3 {*/ - public static final SoundEvent WAITING_MUSIC_EVENT = SoundEvent.of(WAITING_MUSIC_ID); -/*?} else {*/ + /*? if >= 1.19.3 {*/ + public static final SoundEvent WAITING_MUSIC_EVENT = SoundEvent.createVariableRangeEvent(WAITING_MUSIC_ID); + /*?} else {*/ /*private static final SoundEvent WAITING_MUSIC_EVENT = new SoundEvent(WAITING_MUSIC_ID); *//*?}*/ @@ -54,23 +53,23 @@ public class AudioManager { /*? if neoforge {*/ public AudioManager(IEventBus eventBus) { - DeferredRegister SOUND_REGISTER = DeferredRegister.create(Registries.SOUND_EVENT, MOD_ID); + DeferredRegister SOUND_REGISTER = DeferredRegister.create(BuiltInRegistries.SOUND_EVENT, MOD_ID); SOUND_REGISTER.register(eventBus); WAITING_MUSIC = SOUND_REGISTER.register(WAITING_MUSIC_ID.getPath(),()-> WAITING_MUSIC_EVENT); } /*?}*/ -/*? if fabric {*/ + /*? if fabric {*/ /*public AudioManager() { SoundEvent waiting_music = register(); WAITING_MUSIC = () -> waiting_music; } private SoundEvent register() { - Identifier id = Common.id("waiting_music"); + ResourceLocation id = Common.id("waiting_music"); /^? if >=1.19.3 {^/ - Registry register = Registries.SOUND_EVENT; -/^?} else {^/ + Registry register = BuiltInRegistries.SOUND_EVENT; + /^?} else {^/ /^Registry register = Registry.SOUND_EVENT; ^//^?}^/ @@ -85,7 +84,7 @@ public static void playMusic() { SOUND_INSTANCE = new CustomSoundInstance(WAITING_MUSIC); } - getSoundManager().stopAll(); + getSoundManager().stop(); getSoundManager().play(SOUND_INSTANCE); playing = true; } @@ -93,13 +92,13 @@ public static void playMusic() { public static void stopMusic() { if (!playing || SOUND_INSTANCE == null) return; - getSoundManager().stopAll(); + getSoundManager().stop(); playing = false; } private static SoundManager getSoundManager() { if(soundManager == null) { - soundManager = MinecraftClient.getInstance().getSoundManager(); + soundManager = Minecraft.getInstance().getSoundManager(); } return soundManager; } diff --git a/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java b/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java index 6ea9ea881..3570affa2 100644 --- a/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java +++ b/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java @@ -1,26 +1,24 @@ package pl.skidam.automodpack.client.audio; -import net.minecraft.client.sound.AbstractSoundInstance; -import net.minecraft.sound.SoundCategory; -import net.minecraft.sound.SoundEvent; - import java.util.function.Supplier; - +import net.minecraft.client.resources.sounds.AbstractSoundInstance; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; /*? if >=1.19.1 {*/ -import net.minecraft.util.math.random.Random; +import net.minecraft.util.RandomSource; /*?}*/ public class CustomSoundInstance extends AbstractSoundInstance { public CustomSoundInstance(Supplier event) { /*? if >=1.21.2 {*/ - /*super(event.get().id(), SoundCategory.MASTER, Random.create()); - *//*?} elif >=1.19.1 {*/ - super(event.get().getId(), SoundCategory.MASTER, Random.create()); - /*?} else {*/ - /*super(event.get().getId(), SoundCategory.MASTER); + super(event.get().location(), SoundSource.MASTER, RandomSource.create()); + /*?} else if >=1.19.1 {*/ + /*super(event.get().getLocation(), SoundSource.MASTER, RandomSource.create()); + *//*?} else {*/ + /*super(event.get().getLocation(), SoundSource.MASTER); *//*?}*/ - this.attenuationType = AttenuationType.NONE; + this.attenuation = Attenuation.NONE; } @Override @@ -34,7 +32,7 @@ public float getPitch() { } @Override - public boolean isRepeatable() { + public boolean isLooping() { return true; } } \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java index e90c36996..a20a6fc1c 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java @@ -1,9 +1,5 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.util.Util; import pl.skidam.automodpack_loader_core.client.Changelogs; import pl.skidam.automodpack.client.audio.AudioManager; import pl.skidam.automodpack.client.ui.versioned.VersionedText; @@ -18,6 +14,12 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import net.minecraft.Util; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; public class ChangelogScreen extends VersionedScreen { private final Screen parent; @@ -25,9 +27,9 @@ public class ChangelogScreen extends VersionedScreen { private final Changelogs changelogs; private static Map formattedChanges; private ListEntryWidget listEntryWidget; - private TextFieldWidget searchField; - private ButtonWidget backButton; - private ButtonWidget openMainPageButton; + private EditBox searchField; + private Button backButton; + private Button openMainPageButton; public ChangelogScreen(Screen parent, Path modpackDir, Changelogs changelogs) { super(VersionedText.literal("ChangelogScreen")); @@ -48,37 +50,37 @@ protected void init() { initWidgets(); - this.addDrawableChild(this.listEntryWidget); - this.addDrawableChild(this.searchField); - this.addDrawableChild(this.backButton); - this.addDrawableChild(this.openMainPageButton); + this.addRenderableWidget(this.listEntryWidget); + this.addRenderableWidget(this.searchField); + this.addRenderableWidget(this.backButton); + this.addRenderableWidget(this.openMainPageButton); this.setInitialFocus(this.searchField); } private void initWidgets() { - this.listEntryWidget = new ListEntryWidget(formattedChanges, this.client, this.width, this.height, 48, this.height - 50, 20); // 38 + this.listEntryWidget = new ListEntryWidget(formattedChanges, this.minecraft, this.width, this.height, 48, this.height - 50, 20); // 38 - this.searchField = new TextFieldWidget(this.textRenderer, this.width / 2 - 100, 20, 200, 20, + this.searchField = new EditBox(this.font, this.width / 2 - 100, 20, 200, 20, VersionedText.literal("") ); - this.searchField.setChangedListener((textField) -> updateChangelogs()); // Update the changelogs display based on the search query + this.searchField.setResponder((textField) -> updateChangelogs()); // Update the changelogs display based on the search query this.backButton = buttonWidget(this.width / 2 - 140, this.height - 30, 140, 20, VersionedText.translatable("automodpack.back"), - button -> this.client.setScreen(this.parent) + button -> this.minecraft.setScreen(this.parent) ); this.openMainPageButton = buttonWidget(this.width / 2 + 20, this.height - 30, 140, 20, VersionedText.translatable("automodpack.changelog.openPage"), button -> { - ListEntry selectedEntry = listEntryWidget.getSelectedOrNull(); + ListEntry selectedEntry = listEntryWidget.getSelected(); if (selectedEntry == null) { return; } String mainPageUrl = selectedEntry.getMainPageUrl(); - Util.getOperatingSystem().open(mainPageUrl); + Util.getPlatform().openUri(mainPageUrl); } ); @@ -88,7 +90,7 @@ private void initWidgets() { public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { this.listEntryWidget.render(matrices.getContext(), mouseX, mouseY, delta); - ListEntry selectedEntry = listEntryWidget.getSelectedOrNull(); + ListEntry selectedEntry = listEntryWidget.getSelected(); if (selectedEntry != null) { this.openMainPageButton.active = selectedEntry.getMainPageUrl() != null; } else { @@ -115,18 +117,18 @@ private void drawSummaryOfChanges(VersionedMatrices matrices) { String summary = "+ " + filesAdded + " | - " + filesRemoved; - drawCenteredTextWithShadow(matrices, textRenderer, VersionedText.literal(summary), this.width / 2, 5, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, font, VersionedText.literal(summary), this.width / 2, 5, TextColors.WHITE); } private void updateChangelogs() { // If the search field is empty, reset the changelogs to the original list - if (this.searchField.getText().isEmpty()) { + if (this.searchField.getValue().isEmpty()) { formattedChanges = reFormatChanges(); } else { // Filter the changelogs based on the search query using a case-insensitive search Map filteredChangelogs = new HashMap<>(); for (Map.Entry changelog : reFormatChanges().entrySet()) { - if (changelog.getKey().toLowerCase().contains(this.searchField.getText().toLowerCase())) { + if (changelog.getKey().toLowerCase().contains(this.searchField.getValue().toLowerCase())) { filteredChangelogs.put(changelog.getKey(), changelog.getValue()); } } @@ -135,17 +137,17 @@ private void updateChangelogs() { // remove method is only available in 1.17+ /*? if >=1.17 {*/ - this.remove(this.listEntryWidget); - this.remove(this.backButton); - this.remove(this.openMainPageButton); + this.removeWidget(this.listEntryWidget); + this.removeWidget(this.backButton); + this.removeWidget(this.openMainPageButton); /*?}*/ - this.listEntryWidget = new ListEntryWidget(formattedChanges, this.client, this.width, this.height, 48, this.height - 50, 20); // 38 + this.listEntryWidget = new ListEntryWidget(formattedChanges, this.minecraft, this.width, this.height, 48, this.height - 50, 20); // 38 - this.addDrawableChild(this.listEntryWidget); - this.addDrawableChild(this.searchField); - this.addDrawableChild(this.backButton); - this.addDrawableChild(this.openMainPageButton); + this.addRenderableWidget(this.listEntryWidget); + this.addRenderableWidget(this.searchField); + this.addRenderableWidget(this.backButton); + this.addRenderableWidget(this.openMainPageButton); } private Map reFormatChanges() { @@ -169,8 +171,8 @@ private Map reFormatChanges() { @Override public boolean shouldCloseOnEsc() { - assert this.client != null; - this.client.setScreen(this.parent); + assert this.minecraft != null; + this.minecraft.setScreen(this.parent); return false; } } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java index 530f197eb..b2ac9e950 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java @@ -1,9 +1,9 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.util.Formatting; -import net.minecraft.util.Util; import pl.skidam.automodpack_loader_core.client.ModpackUpdater; +import net.minecraft.ChatFormatting; +import net.minecraft.Util; +import net.minecraft.client.gui.screens.Screen; import pl.skidam.automodpack.client.audio.AudioManager; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; @@ -27,22 +27,22 @@ public DangerScreen(Screen parent, ModpackUpdater modpackUpdaterInstance) { @Override protected void init() { super.init(); - assert this.client != null; + assert this.minecraft != null; - this.addDrawableChild(buttonWidget(this.width / 2 - 115, this.height / 2 + 50, 120, 20, VersionedText.translatable("automodpack.danger.cancel"), button -> { - this.client.setScreen(parent); + this.addRenderableWidget(buttonWidget(this.width / 2 - 115, this.height / 2 + 50, 120, 20, VersionedText.translatable("automodpack.danger.cancel"), button -> { + this.minecraft.setScreen(parent); })); - this.addDrawableChild(buttonWidget(this.width / 2 + 15, this.height / 2 + 50, 120, 20, VersionedText.translatable("automodpack.danger.confirm").formatted(Formatting.BOLD), button -> { - Util.getMainWorkerExecutor().execute(modpackUpdaterInstance::startUpdate); + this.addRenderableWidget(buttonWidget(this.width / 2 + 15, this.height / 2 + 50, 120, 20, VersionedText.translatable("automodpack.danger.confirm").withStyle(ChatFormatting.BOLD), button -> { + Util.backgroundExecutor().execute(modpackUpdaterInstance::startUpdate); })); } @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger").formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger.description"), this.width / 2, this.height / 2 - 35, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger.secDescription"), this.width / 2, this.height / 2 - 25, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.danger").withStyle(ChatFormatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.danger.description"), this.width / 2, this.height / 2 - 35, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.danger.secDescription"), this.width / 2, this.height / 2 - 25, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java index 23b4b111a..399869c81 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java @@ -1,14 +1,14 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.Identifier; -import net.minecraft.util.Util; import pl.skidam.automodpack.init.Common; import pl.skidam.automodpack_loader_core.screen.ScreenManager; import pl.skidam.automodpack_loader_core.utils.DownloadManager; +import net.minecraft.ChatFormatting; +import net.minecraft.Util; +import net.minecraft.client.gui.components.Button; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; import pl.skidam.automodpack.client.audio.AudioManager; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; @@ -17,14 +17,14 @@ public class DownloadScreen extends VersionedScreen { - private static final Identifier PROGRESS_BAR_EMPTY_TEXTURE = Common.id("gui/progress-bar-empty.png"); - private static final Identifier PROGRESS_BAR_FULL_TEXTURE = Common.id("gui/progress-bar-full.png"); + private static final ResourceLocation PROGRESS_BAR_EMPTY_TEXTURE = Common.id("gui/progress-bar-empty.png"); + private static final ResourceLocation PROGRESS_BAR_FULL_TEXTURE = Common.id("gui/progress-bar-full.png"); private static final int PROGRESS_BAR_WIDTH = 250; private static final int PROGRESS_BAR_HEIGHT = 20; private final DownloadManager downloadManager; private final String header; private static long ticks = 0; - private ButtonWidget cancelButton; + private Button cancelButton; private String lastStage = "-1"; private int lastPercentage = -1; @@ -43,9 +43,9 @@ protected void init() { initWidgets(); - this.addDrawableChild(cancelButton); + this.addRenderableWidget(cancelButton); - Util.getMainWorkerExecutor().execute(() -> { + Util.backgroundExecutor().execute(() -> { while (downloadManager != null && downloadManager.isRunning()) { lastStage = downloadManager.getStage(); lastPercentage = downloadManager.getTotalPercentageOfFileSizeDownloaded(); @@ -64,14 +64,14 @@ private void initWidgets() { ); } - private Text getStage() { + private Component getStage() { if (lastStage.equals("-1")) { return VersionedText.translatable("automodpack.download.calculating"); } return VersionedText.literal(lastStage); } - private Text getPercentage() { + private Component getPercentage() { if (lastPercentage == -1) { return VersionedText.translatable("automodpack.download.calculating"); } @@ -79,14 +79,14 @@ private Text getPercentage() { } - private Text getTotalDownloadSpeed() { + private Component getTotalDownloadSpeed() { if (lastSpeed.equals("-1")) { return VersionedText.translatable("automodpack.download.calculating"); } return VersionedText.literal(lastSpeed); } - private Text getTotalETA() { + private Component getTotalETA() { if (lastETA.equals("-1")) { return VersionedText.translatable("automodpack.download.calculating"); } @@ -101,27 +101,27 @@ private void drawDownloadingFiles(VersionedMatrices matrices) { float scale = 1.0F; int y = this.height / 2 - 90; - matrices.push(); + matrices.pushPose(); matrices.scale(scale, scale, scale); if (downloadManager != null && !downloadManager.downloadsInProgress.isEmpty()) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.download.downloading").formatted(Formatting.BOLD), this.width / 2, y, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.download.downloading").withStyle(ChatFormatting.BOLD), this.width / 2, y, TextColors.WHITE); // Use a separate variable for the current y position int currentY = y + 15; synchronized (downloadManager.downloadsInProgress) { for (DownloadManager.DownloadData downloadData : downloadManager.downloadsInProgress.values()) { String text = downloadData.getFileName(); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.literal(text), (int) ((float) this.width / 2 * scale), currentY, TextColors.GRAY); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.literal(text), (int) ((float) this.width / 2 * scale), currentY, TextColors.GRAY); currentY += 10; } } } else { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.download.noFiles"), (int) ((float) this.width / 2 * scale), y, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.wait").formatted(Formatting.BOLD), (int) ((float) this.width / 2 * scale), y + 25, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.download.noFiles"), (int) ((float) this.width / 2 * scale), y, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.wait").withStyle(ChatFormatting.BOLD), (int) ((float) this.width / 2 * scale), y + 25, TextColors.WHITE); } - matrices.pop(); + matrices.popPose(); } private void checkAndStartMusic() { @@ -141,16 +141,16 @@ private void checkAndStartMusic() { @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { drawDownloadingFiles(matrices); - MutableText titleText = VersionedText.literal(header).formatted(Formatting.BOLD); - drawCenteredTextWithShadow(matrices, this.textRenderer, titleText, this.width / 2, this.height / 2 - 110, TextColors.WHITE); + MutableComponent titleText = VersionedText.literal(header).withStyle(ChatFormatting.BOLD); + drawCenteredTextWithShadow(matrices, this.font, titleText, this.width / 2, this.height / 2 - 110, TextColors.WHITE); if (downloadManager != null && downloadManager.isRunning()) { - MutableText percentage = (MutableText) this.getPercentage(); - MutableText stage = (MutableText) this.getStage(); - MutableText eta = (MutableText) this.getTotalETA(); - MutableText speed = (MutableText) this.getTotalDownloadSpeed(); - drawCenteredTextWithShadow(matrices, this.textRenderer, stage, this.width / 2, this.height / 2 - 10, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, eta, this.width / 2, this.height / 2 + 10, TextColors.WHITE); + MutableComponent percentage = (MutableComponent) this.getPercentage(); + MutableComponent stage = (MutableComponent) this.getStage(); + MutableComponent eta = (MutableComponent) this.getTotalETA(); + MutableComponent speed = (MutableComponent) this.getTotalDownloadSpeed(); + drawCenteredTextWithShadow(matrices, this.font, stage, this.width / 2, this.height / 2 - 10, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, eta, this.width / 2, this.height / 2 + 10, TextColors.WHITE); // Render progress bar @@ -160,8 +160,8 @@ public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, drawTexture(PROGRESS_BAR_EMPTY_TEXTURE, matrices, progressX, progressY, 0, 0, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT); drawTexture(PROGRESS_BAR_FULL_TEXTURE, matrices, progressX, progressY, 0, 0, (int) (PROGRESS_BAR_WIDTH * getDownloadScale()), PROGRESS_BAR_HEIGHT, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT); - drawCenteredTextWithShadow(matrices, this.textRenderer, percentage, this.width / 2, this.height / 2 + 36, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, speed, this.width / 2, this.height / 2 + 60, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, percentage, this.width / 2, this.height / 2 + 36, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, speed, this.width / 2, this.height / 2 + 60, TextColors.WHITE); checkAndStartMusic(); cancelButton.active = true; diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java index 6d9b63fc4..4a6f3f5f9 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java @@ -1,7 +1,7 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.util.Formatting; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.Button; import pl.skidam.automodpack.client.audio.AudioManager; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; @@ -10,7 +10,7 @@ public class ErrorScreen extends VersionedScreen { private final String[] errorMessages; - private ButtonWidget backButton; + private Button backButton; public ErrorScreen(String... errorMessages) { super(VersionedText.literal("ErrorScreen")); @@ -27,21 +27,21 @@ protected void init() { initWidgets(); - this.addDrawableChild(backButton); + this.addRenderableWidget(backButton); } private void initWidgets() { backButton = buttonWidget(this.width / 2 - 100, this.height / 2 + 50, 200, 20, VersionedText.translatable("automodpack.back"), button -> { - assert client != null; - client.setScreen(null); + assert minecraft != null; + minecraft.setScreen(null); }); } @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.literal("[AutoModpack] Error! ").append(VersionedText.translatable("automodpack.error").formatted(Formatting.RED)), this.width / 2, this.height / 2 - 50, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.literal("[AutoModpack] Error! ").append(VersionedText.translatable("automodpack.error").withStyle(ChatFormatting.RED)), this.width / 2, this.height / 2 - 50, TextColors.WHITE); for (int i = 0; i < this.errorMessages.length; i++) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable(this.errorMessages[i]), this.width / 2, this.height / 2 - 20 + i * 12, 14687790); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable(this.errorMessages[i]), this.width / 2, this.height / 2 - 20 + i * 12, 14687790); } } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java index 95aea4412..ede95d699 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java @@ -1,8 +1,7 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.util.Formatting; - +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.Button; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; import pl.skidam.automodpack.client.ui.versioned.VersionedText; @@ -11,7 +10,7 @@ public class FetchScreen extends VersionedScreen { - private ButtonWidget cancelButton; + private Button cancelButton; private final FetchManager fetchManager; public FetchScreen(FetchManager fetchManager) { @@ -25,7 +24,7 @@ protected void init() { initWidgets(); - this.addDrawableChild(cancelButton); + this.addRenderableWidget(cancelButton); } private void initWidgets() { @@ -51,9 +50,9 @@ public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, } // Fetching direct url's from Modrinth and CurseForge. - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.fetch").formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.wait"), this.width / 2, this.height / 2 - 48, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.fetch.found", getFetchesDone()), this.width / 2, this.height / 2 - 30, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.fetch").withStyle(ChatFormatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.wait"), this.width / 2, this.height / 2 - 48, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.fetch.found", getFetchesDone()), this.width / 2, this.height / 2 - 30, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java index 1b881facb..88b052e5a 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java @@ -1,11 +1,10 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.util.Formatting; import pl.skidam.automodpack.client.audio.AudioManager; import java.nio.file.Path; - +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.Button; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; import pl.skidam.automodpack.client.ui.versioned.VersionedText; @@ -17,9 +16,9 @@ public class RestartScreen extends VersionedScreen { private final Path modpackDir; private final UpdateType updateType; private final Changelogs changelogs; - private static ButtonWidget cancelButton; - private static ButtonWidget restartButton; - private static ButtonWidget changelogsButton; + private static Button cancelButton; + private static Button restartButton; + private static Button changelogsButton; public RestartScreen(Path modpackDir, UpdateType updateType, Changelogs changelogs) { super(VersionedText.literal("RestartScreen")); @@ -38,9 +37,9 @@ protected void init() { initWidgets(); - this.addDrawableChild(cancelButton); - this.addDrawableChild(restartButton); - this.addDrawableChild(changelogsButton); + this.addRenderableWidget(cancelButton); + this.addRenderableWidget(restartButton); + this.addRenderableWidget(changelogsButton); if (changelogs == null || changelogs.changesAddedList.isEmpty() && changelogs.changesDeletedList.isEmpty()) { changelogsButton.active = false; @@ -48,12 +47,12 @@ protected void init() { } public void initWidgets() { - assert this.client != null; + assert this.minecraft != null; cancelButton = buttonWidget(this.width / 2 - 155, this.height / 2 + 50, 150, 20, VersionedText.translatable("automodpack.restart.cancel"), button -> { - this.client.setScreen(null); + this.minecraft.setScreen(null); }); - restartButton = buttonWidget(this.width / 2 + 5, this.height / 2 + 50, 150, 20, VersionedText.translatable("automodpack.restart.confirm").formatted(Formatting.BOLD), button -> { + restartButton = buttonWidget(this.width / 2 + 5, this.height / 2 + 50, 150, 20, VersionedText.translatable("automodpack.restart.confirm").withStyle(ChatFormatting.BOLD), button -> { System.exit(0); }); @@ -64,9 +63,9 @@ public void initWidgets() { @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart." + updateType.toString()).formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart.description"), this.width / 2, this.height / 2 - 35, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart.secDescription"), this.width / 2, this.height / 2 - 25, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.restart." + updateType.toString()).withStyle(ChatFormatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.restart.description"), this.width / 2, this.height / 2 - 35, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.restart.secDescription"), this.width / 2, this.height / 2 - 25, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java index 7f6003efc..512f81138 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java @@ -1,12 +1,11 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.client.toast.SystemToast; -import net.minecraft.client.toast.Toast; -import net.minecraft.util.Formatting; - +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.components.toasts.Toast; +import net.minecraft.client.gui.screens.Screen; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; import pl.skidam.automodpack.client.ui.versioned.VersionedText; @@ -18,10 +17,10 @@ public class ValidationScreen extends VersionedScreen { private final Runnable validatedCallback; private final Runnable canceledCallback; private boolean validated = false; - private final Toast failedToast = new SystemToast(SystemToast.Type.PACK_LOAD_FAILURE, VersionedText.translatable("automodpack.validation.failed"), VersionedText.translatable("automodpack.retry")); - private TextFieldWidget textField; - private ButtonWidget backButton; - private ButtonWidget validateButton; + private final Toast failedToast = new SystemToast(SystemToast.SystemToastId.PACK_LOAD_FAILURE, VersionedText.translatable("automodpack.validation.failed"), VersionedText.translatable("automodpack.retry")); + private EditBox textField; + private Button backButton; + private Button validateButton; public ValidationScreen(Screen parent, String serverFingerprint, Runnable validatedCallback, Runnable canceledCallback) { @@ -38,15 +37,15 @@ protected void init() { initWidgets(); - this.addDrawableChild(this.textField); - this.addDrawableChild(this.backButton); - this.addDrawableChild(this.validateButton); + this.addRenderableWidget(this.textField); + this.addRenderableWidget(this.backButton); + this.addRenderableWidget(this.validateButton); this.setInitialFocus(this.textField); } public void initWidgets() { - assert this.client != null; - this.textField = new TextFieldWidget(this.textRenderer, this.width / 2 - 170, this.height / 2 - 20, 340, 20, + assert this.minecraft != null; + this.textField = new EditBox(this.font, this.width / 2 - 170, this.height / 2 - 20, 340, 20, VersionedText.literal("") ); this.textField.setMaxLength(64); // default is 30 which is too little @@ -54,7 +53,7 @@ public void initWidgets() { this.backButton = buttonWidget(this.width / 2 - 155, this.height / 2 + 50, 150, 20, VersionedText.translatable("automodpack.back"), button -> { - this.client.setScreen(parent); + this.minecraft.setScreen(parent); if (!this.validated) { this.canceledCallback.run(); } @@ -63,7 +62,7 @@ public void initWidgets() { this.validateButton = buttonWidget(this.width / 2 + 5, this.height / 2 + 50, 150, 20, VersionedText.translatable("automodpack.validation.run"), - button -> validate(textField.getText())); + button -> validate(textField.getValue())); } private void validate(String input) { @@ -71,27 +70,31 @@ private void validate(String input) { if (input.equals(serverFingerprint) || input.equals("I AM INCREDIBLY STUPID")) { validateButton.active = false; this.validated = true; - if (this.client != null) { - this.client.setScreen(parent); + if (this.minecraft != null) { + this.minecraft.setScreen(parent); } validatedCallback.run(); } else { GlobalVariables.LOGGER.error("Server fingerprint validation failed, try again"); - if (this.client != null) { - this.client.getToastManager().add(failedToast); + if (this.minecraft != null) { + /*? if > 1.21.1 {*/ + this.minecraft.getToastManager().addToast(failedToast); + /*?} else {*/ + /*this.minecraft.getToasts().addToast(failedToast); + *//*?}*/ } } } @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.text").formatted(Formatting.BOLD), + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.validation.text").withStyle(ChatFormatting.BOLD), this.width / 2, this.height / 2 - 100, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.description"), + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.validation.description"), this.width / 2, this.height / 2 - 75, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.secDescription"), + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.validation.secDescription"), this.width / 2, this.height / 2 - 65, TextColors.WHITE); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.thiDescription"), + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.validation.thiDescription"), this.width / 2, this.height / 2 - 55, TextColors.WHITE); } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedCommandSource.java b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedCommandSource.java index 2887de7ab..be8a4ff40 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedCommandSource.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedCommandSource.java @@ -1,29 +1,27 @@ package pl.skidam.automodpack.client.ui.versioned; import com.mojang.brigadier.context.CommandContext; -import net.minecraft.entity.Entity; +import net.minecraft.commands.CommandSource; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.command.CommandOutput; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.Text; -import net.minecraft.util.math.Vec2f; -import net.minecraft.util.math.Vec3d; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; -public class VersionedCommandSource extends ServerCommandSource { +public class VersionedCommandSource extends CommandSourceStack { - public VersionedCommandSource(CommandOutput output, Vec3d pos, Vec2f rot, ServerWorld world, int level, String name, Text displayName, MinecraftServer server, @Nullable Entity entity) { + public VersionedCommandSource(CommandSource output, Vec3 pos, Vec2 rot, ServerLevel world, int level, String name, Component displayName, MinecraftServer server, @Nullable Entity entity) { super(output, pos, rot, world, level, name, displayName, server, entity); } -/*? if >=1.20 {*/ - public static void sendFeedback(CommandContext context, Text message, boolean broadcastToOps) { - context.getSource().sendFeedback(() -> message, broadcastToOps); - } -/*?} else {*/ - /*public static void sendFeedback(CommandContext context, Text message, boolean broadcastToOps) { - context.getSource().sendFeedback(message, broadcastToOps); + public static void sendFeedback(CommandContext context, Component message, boolean broadcastToOps) { + /*? if >=1.20 {*/ + context.getSource().sendSuccess(() -> message, broadcastToOps); + /*?} else {*/ + /*context.getSource().sendSuccess(message, broadcastToOps); + *//*?}*/ } -*//*?}*/ } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java index 33e31e6ae..c7ef8d638 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java @@ -1,51 +1,51 @@ package pl.skidam.automodpack.client.ui.versioned; /*? if >=1.20 {*/ -import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.GuiGraphics; /*?} else {*/ -/*import net.minecraft.client.util.math.MatrixStack; +/*import com.mojang.blaze3d.vertex.PoseStack; *//*?}*/ -public class VersionedMatrices /*? if <1.20 >>*/ /*extends MatrixStack*/ { +public class VersionedMatrices /*? if <1.20 {*/ /*extends PoseStack *//*?}*/ { /*? if >=1.20 {*/ - private final DrawContext context; + private final GuiGraphics context; - public VersionedMatrices(DrawContext context) { + public VersionedMatrices(GuiGraphics context) { this.context = context; } - public DrawContext getContext() { + public GuiGraphics getContext() { return context; } /*? if >=1.21.6 {*/ - /*public void push() { - context.getMatrices().pushMatrix(); + public void pushPose() { + context.pose().pushMatrix(); } - public void pop() { - context.getMatrices().popMatrix(); + public void popPose() { + context.pose().popMatrix(); } public void scale(float x, float y, float z) { - context.getMatrices().scale(x, y); + context.pose().scale(x, y); } - *//*?} else {*/ - public void push() { - context.getMatrices().push(); + /*?} else {*/ + /*public void pushPose() { + context.pose().pushPose(); } - public void pop() { - context.getMatrices().pop(); + public void popPose() { + context.pose().popPose(); } public void scale(float x, float y, float z) { - context.getMatrices().scale(x, y, z); + context.pose().scale(x, y, z); } - /*?}*/ + *//*?}*/ /*?} else {*/ - /*public MatrixStack getContext() { + /*public PoseStack getContext() { return this; } *//*?}*/ diff --git a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java index f182fa20a..c7d81330d 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java @@ -1,48 +1,40 @@ package pl.skidam.automodpack.client.ui.versioned; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; - -/*? if <=1.16.5 {*//* -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.Element; -import net.minecraft.client.gui.widget.ClickableWidget; -*//*?}*/ - -/*? if <1.20 {*/ -/*import net.minecraft.client.gui.DrawableHelper; -import net.minecraft.client.util.math.MatrixStack; -import com.mojang.blaze3d.systems.RenderSystem; -*//*?}*/ - -/*? if >=1.20 {*/ -import net.minecraft.client.gui.DrawContext; -/*?}*/ +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; /*? if >=1.21.6 {*/ -/*import net.minecraft.client.gl.RenderPipelines; -*//*?} elif >=1.21.2 {*/ -/*import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.renderer.RenderPipelines; +/*?} else if >=1.21.2 {*/ +/*import net.minecraft.client.renderer.RenderType; import java.util.function.Function; *//*?}*/ +/*? if <1.20 {*/ +/*import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.GuiComponent; +*//*?} else {*/ +import net.minecraft.client.gui.GuiGraphics; +/*?}*/ + public class VersionedScreen extends Screen { - protected VersionedScreen(Text title) { + protected VersionedScreen(Component title) { super(title); } /*? if <1.20 {*/ /*@Override - public void render(MatrixStack matrix, int mouseX, int mouseY, float delta) { + public void render(PoseStack matrix, int mouseX, int mouseY, float delta) { VersionedMatrices matrices = new VersionedMatrices(); *//*?} else {*/ @Override - public void render(DrawContext matrix, int mouseX, int mouseY, float delta) { + public void render(GuiGraphics matrix, int mouseX, int mouseY, float delta) { VersionedMatrices matrices = new VersionedMatrices(matrix); /*?}*/ @@ -78,45 +70,45 @@ public void addDrawableChild(T child) { *//*?}*/ /*? if >=1.20 {*/ - public static void drawCenteredTextWithShadow(VersionedMatrices matrices, TextRenderer textRenderer, MutableText text, int centerX, int y, int color) { - matrices.getContext().drawCenteredTextWithShadow(textRenderer, text, centerX, y, color); + public static void drawCenteredTextWithShadow(VersionedMatrices matrices, Font textRenderer, MutableComponent text, int centerX, int y, int color) { + matrices.getContext().drawCenteredString(textRenderer, text, centerX, y, color); } /*?} else {*/ - /*public static void drawCenteredTextWithShadow(VersionedMatrices matrices, TextRenderer textRenderer, MutableText text, int centerX, int y, int color) { - textRenderer.drawWithShadow(matrices.getContext(), text, (float)(centerX - textRenderer.getWidth(text) / 2), (float)y, color); + /*public static void drawCenteredTextWithShadow(VersionedMatrices matrices, Font textRenderer, MutableComponent text, int centerX, int y, int color) { + textRenderer.drawShadow(matrices.getContext(), text, (float)(centerX - textRenderer.width(text) / 2), (float)y, color); } *//*?}*/ /*? if <1.19.3 {*/ - /*public static ButtonWidget buttonWidget(int x, int y, int width, int height, Text message, ButtonWidget.PressAction onPress) { - return new ButtonWidget(x, y, width, height, message, onPress); + /*public static Button buttonWidget(int x, int y, int width, int height, Component message, Button.OnPress onPress) { + return new Button(x, y, width, height, message, onPress); } *//*?} else {*/ - public static ButtonWidget buttonWidget(int x, int y, int width, int height, Text message, ButtonWidget.PressAction onPress) { - return ButtonWidget.builder(message, onPress).position(x, y).size(width, height).build(); + public static Button buttonWidget(int x, int y, int width, int height, Component message, Button.OnPress onPress) { + return Button.builder(message, onPress).pos(x, y).size(width, height).build(); } /*?}*/ /*? if <=1.20 {*/ - /*public static void drawTexture(Identifier textureID, VersionedMatrices matrices, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) { + /*public static void drawTexture(ResourceLocation textureID, VersionedMatrices matrices, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) { /^? if <=1.16.5 {^/ - /^MinecraftClient.getInstance().getTextureManager().bindTexture(textureID); + /^Minecraft.getInstance().getTextureManager().bindTexture(textureID); ^//^?} else {^/ RenderSystem.setShaderTexture(0, textureID); /^?}^/ - DrawableHelper.drawTexture(matrices.getContext(), x, y, u, v, width, height, textureWidth, textureHeight); + GuiComponent.blit(matrices.getContext(), x, y, u, v, width, height, textureWidth, textureHeight); } *//*?} else {*/ - public static void drawTexture(Identifier textureID, VersionedMatrices matrices, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) { + public static void drawTexture(ResourceLocation textureID, VersionedMatrices matrices, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) { /*? if >=1.21.6 {*/ - /*matrices.getContext().drawTexture(RenderPipelines.GUI_TEXTURED, textureID, x, y, u, v, width, height, textureWidth, textureHeight); - *//*?} elif >=1.21.2 {*/ - /*Function renderLayers = RenderLayer::getGuiTextured; - matrices.getContext().drawTexture(renderLayers, textureID, x, y, u, v, width, height, textureWidth, textureHeight); + matrices.getContext().blit(RenderPipelines.GUI_TEXTURED, textureID, x, y, u, v, width, height, textureWidth, textureHeight); + /*?} elif >=1.21.2 {*/ + /*Function RenderTypes = RenderType::guiTextured; + matrices.getContext().blit(RenderTypes, textureID, x, y, u, v, width, height, textureWidth, textureHeight); *//*?} else {*/ - matrices.getContext().drawTexture(textureID, x, y, u, v, width, height, textureWidth, textureHeight); - /*?}*/ + /*matrices.getContext().blit(textureID, x, y, u, v, width, height, textureWidth, textureHeight); + *//*?}*/ } /*?}*/ } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedText.java b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedText.java index 9405c473c..102a51b09 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedText.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedText.java @@ -1,25 +1,31 @@ package pl.skidam.automodpack.client.ui.versioned; -import net.minecraft.text.*; +import net.minecraft.network.chat.MutableComponent; +/*? if <= 1.19.1 {*/ +/*import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +*//*?} else {*/ +import net.minecraft.network.chat.Component; +/*?}*/ public class VersionedText { /*? if <=1.19.1 {*/ - /*public static MutableText translatable(String key, Object... args) { - return new TranslatableText(key, args); + /*public static MutableComponent translatable(String key, Object... args) { + return new TranslatableComponent(key, args); } - public static MutableText literal(String string) { - return new LiteralText(string); + public static MutableComponent literal(String string) { + return new TextComponent(string); } *//*?} else {*/ - public static MutableText translatable(String key, Object... args) { - return Text.translatable(key, args); + public static MutableComponent translatable(String key, Object... args) { + return Component.translatable(key, args); } - public static MutableText literal(String string) { - return Text.literal(string); + public static MutableComponent literal(String string) { + return Component.literal(string); } /*?}*/ -} +} \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/client/ui/widget/Badge.java b/src/main/java/pl/skidam/automodpack/client/ui/widget/Badge.java index 7dffde3c2..d5fcbbb54 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/widget/Badge.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/widget/Badge.java @@ -1,15 +1,16 @@ package pl.skidam.automodpack.client.ui.widget; -import net.minecraft.util.Identifier; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; import pl.skidam.automodpack.init.Common; import static pl.skidam.automodpack_core.GlobalVariables.MOD_ID; +import net.minecraft.resources.ResourceLocation; + public class Badge { - private static final Identifier MODRINTH_ICON = Common.id("gui/platform/logo-modrinth.png"); - private static final Identifier CURSEFORGE_ICON = Common.id("gui/platform/logo-curseforge.png"); + private static final ResourceLocation MODRINTH_ICON = Common.id("gui/platform/logo-modrinth.png"); + private static final ResourceLocation CURSEFORGE_ICON = Common.id("gui/platform/logo-curseforge.png"); private static final int textureSize = 32; public static void renderModrinthBadge(VersionedMatrices matrices, int x, int y) { diff --git a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java index 23444cb20..2f9d0ff2d 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java @@ -1,34 +1,34 @@ package pl.skidam.automodpack.client.ui.widget; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.ObjectSelectionList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import pl.skidam.automodpack.client.ui.TextColors; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; -/*? if <1.20 {*/ -/*import net.minecraft.client.util.math.MatrixStack; -*//*?} else {*/ -import net.minecraft.client.gui.DrawContext; -/*?}*/ +/*? if >=1.20 {*/ +import net.minecraft.client.gui.GuiGraphics; +/*?} else {*/ +/*import com.mojang.blaze3d.vertex.PoseStack; +*//*?}*/ -public class ListEntry extends AlwaysSelectedEntryListWidget.Entry { +public class ListEntry extends ObjectSelectionList.Entry { - protected final MinecraftClient client; - private final MutableText text; + protected final Minecraft client; + private final MutableComponent text; private final String mainPageUrl; private final boolean bigFont; - public ListEntry(MutableText text, String mainPageUrl, boolean bigFont, MinecraftClient client) { + public ListEntry(MutableComponent text, String mainPageUrl, boolean bigFont, Minecraft client) { this.text = text; this.mainPageUrl = mainPageUrl; this.client = client; this.bigFont = bigFont; } - public ListEntry(MutableText text, boolean bigFont, MinecraftClient client) { + public ListEntry(MutableComponent text, boolean bigFont, Minecraft client) { this.text = text; this.mainPageUrl = null; this.client = client; @@ -37,23 +37,23 @@ public ListEntry(MutableText text, boolean bigFont, MinecraftClient client) { /*? if >=1.17 {*/ @Override - public Text getNarration() { + public Component getNarration() { return text; } /*?}*/ @Override /*? if <1.20 {*/ - /*public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + /*public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { VersionedMatrices versionedMatrices = new VersionedMatrices(); *//*?} else {*/ - public void render(DrawContext drawContext, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - VersionedMatrices versionedMatrices = new VersionedMatrices(drawContext); + public void render(GuiGraphics GuiGraphics, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + VersionedMatrices versionedMatrices = new VersionedMatrices(GuiGraphics); /*?}*/ versionedRender(versionedMatrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, hovered, tickDelta); } public void versionedRender(VersionedMatrices versionedMatrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - versionedMatrices.push(); + versionedMatrices.pushPose(); int centeredX = x + entryWidth / 2; int centeredY = y + entryHeight / 2; @@ -66,7 +66,7 @@ public void versionedRender(VersionedMatrices versionedMatrices, int index, int centeredY = centeredY - 10 / 2; } - VersionedScreen.drawCenteredTextWithShadow(versionedMatrices, client.textRenderer, text, centeredX, centeredY, TextColors.WHITE); + VersionedScreen.drawCenteredTextWithShadow(versionedMatrices, client.font, text, centeredX, centeredY, TextColors.WHITE); // if (mainPageUrls != null) { // int badgeX = x - 42; @@ -78,10 +78,10 @@ public void versionedRender(VersionedMatrices versionedMatrices, int index, int // } // } - versionedMatrices.pop(); + versionedMatrices.popPose(); } - public MutableText getText() { + public MutableComponent getText() { return this.text; } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java index e63d099b4..6dcf6bd11 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java @@ -1,25 +1,25 @@ package pl.skidam.automodpack.client.ui.widget; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; -import net.minecraft.text.MutableText; -import net.minecraft.util.Formatting; -import net.minecraft.util.math.MathHelper; import pl.skidam.automodpack.client.ui.versioned.VersionedText; import java.util.Map; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.ObjectSelectionList; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.util.Mth; /*? if <1.20 {*/ -/*import net.minecraft.client.util.math.MatrixStack; +/*import com.mojang.blaze3d.vertex.PoseStack; *//*?} elif <1.20.3 {*/ -/*import net.minecraft.client.gui.DrawContext; +/*import net.minecraft.client.gui.GuiGraphics; *//*?}*/ -public class ListEntryWidget extends AlwaysSelectedEntryListWidget { +public class ListEntryWidget extends ObjectSelectionList { private boolean scrolling; - public ListEntryWidget(Map changelogs, MinecraftClient client, int width, int height, int top, int bottom, int itemHeight) { + public ListEntryWidget(Map changelogs, Minecraft client, int width, int height, int top, int bottom, int itemHeight) { /*? if <1.20.3 {*/ /*super(client, width, height, top, bottom, itemHeight); *//*?} else {*/ @@ -30,7 +30,7 @@ public ListEntryWidget(Map changelogs, MinecraftClient client, i this.clearEntries(); if (changelogs == null || changelogs.isEmpty()) { - ListEntry entry = new ListEntry(VersionedText.literal("No changelogs found").formatted(Formatting.BOLD), true, this.client); + ListEntry entry = new ListEntry(VersionedText.literal("No changelogs found").withStyle(ChatFormatting.BOLD), true, this.minecraft); this.addEntry(entry); return; } @@ -39,58 +39,58 @@ public ListEntryWidget(Map changelogs, MinecraftClient client, i String textString = changelog.getKey(); String mainPageUrl = changelog.getValue(); - MutableText text = VersionedText.literal(textString); + MutableComponent text = VersionedText.literal(textString); if (textString.startsWith("+")) { - text = text.formatted(Formatting.GREEN); + text = text.withStyle(ChatFormatting.GREEN); } else if (textString.startsWith("-")) { - text = text.formatted(Formatting.RED); + text = text.withStyle(ChatFormatting.RED); } - ListEntry entry = new ListEntry(text, mainPageUrl, false, this.client); + ListEntry entry = new ListEntry(text, mainPageUrl, false, this.minecraft); this.addEntry(entry); } } /*? if <=1.20.2 {*/ - /*public void render(/^? if <1.20 {^/ /^MatrixStack ^//^?} else {^/ DrawContext /^?}^/ matrices, int mouseX, int mouseY, float delta) { + /*public void render(/^? if <1.20 {^/ /^PoseStack ^//^?} else {^/ GuiGraphics /^?}^/ matrices, int mouseX, int mouseY, float delta) { super.render(matrices, mouseX, mouseY, delta); } *//*?}*/ - /*? if >1.21.4 {*/ - /*public double getScrollAmount() { - return this.getScrollY(); + /*? if >=1.21.4 {*/ + public double getScrollAmount() { + return this.scrollAmount(); } - *//*?}*/ + /*?}*/ public final ListEntry getEntryAtPos(double x, double y) { - int int_5 = MathHelper.floor(y - (double) getTop()) - this.headerHeight + (int) this.getScrollAmount() - 4; + int int_5 = Mth.floor(y - (double) getTop()) - this.headerHeight + (int) this.getScrollAmount() - 4; int index = int_5 / this.itemHeight; - return x < (double) this.getScrollbarX() && x >= (double) getRowLeft() && x <= (double) (getRowLeft() + getRowWidth()) && index >= 0 && int_5 >= 0 && index < this.getEntryCount() ? this.children().get(index) : null; + return x < (double) this.getScrollbarPosition() && x >= (double) getRowLeft() && x <= (double) (getRowLeft() + getRowWidth()) && index >= 0 && int_5 >= 0 && index < this.getItemCount() ? this.children().get(index) : null; } public int getTop() { /*? if <1.20.3 {*/ - /*return this.top; + /*return this.y0; *//*?} else {*/ return this.getY(); /*?}*/ } /*? if <=1.21.3 {*/ - @Override + /*@Override protected void updateScrollingState(double mouseX, double mouseY, int button) { super.updateScrollingState(mouseX, mouseY, button); - this.scrolling = button == 0 && mouseX >= (double) this.getScrollbarX() && mouseX < (double) (this.getScrollbarX() + 6); + this.scrolling = button == 0 && mouseX >= (double) this.getScrollbarPosition() && mouseX < (double) (this.getScrollbarPosition() + 6); } - /*?}*/ + *//*?}*/ @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { /*? if <=1.21.3 {*/ - this.updateScrollingState(mouseX, mouseY, button); - /*?}*/ + /*this.updateScrollingState(mouseX, mouseY, button); + *//*?}*/ if (!this.isMouseOver(mouseX, mouseY)) { return false; } else { @@ -116,7 +116,7 @@ public void setSelected(ListEntry entry) { } } - protected int getScrollbarX() { + protected int getScrollbarPosition() { return this.width - 6; } diff --git a/src/main/java/pl/skidam/automodpack/init/Common.java b/src/main/java/pl/skidam/automodpack/init/Common.java index 2bd6f071a..b69d53471 100644 --- a/src/main/java/pl/skidam/automodpack/init/Common.java +++ b/src/main/java/pl/skidam/automodpack/init/Common.java @@ -1,7 +1,7 @@ package pl.skidam.automodpack.init; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.util.Identifier; import pl.skidam.automodpack.loader.GameCall; import pl.skidam.automodpack.networking.ModPackets; import pl.skidam.automodpack_core.modpack.ModpackExecutor; @@ -63,11 +63,11 @@ public static void beforeShutdownServer() { modpackExecutor.stop(); } - public static Identifier id(String path) { + public static ResourceLocation id(String path) { /*? if >1.19.1 {*/ - return Identifier.of(MOD_ID, path); + return ResourceLocation.tryBuild(MOD_ID, path); /*?} else {*/ - /*return new Identifier(MOD_ID, path); + /*return new ResourceLocation(MOD_ID, path); *//*?}*/ } } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ClientConnectionAccessor.java b/src/main/java/pl/skidam/automodpack/mixin/core/ClientConnectionAccessor.java index a26c445f9..4b03d2637 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ClientConnectionAccessor.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ClientConnectionAccessor.java @@ -1,11 +1,11 @@ package pl.skidam.automodpack.mixin.core; import io.netty.channel.Channel; -import net.minecraft.network.ClientConnection; +import net.minecraft.network.Connection; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(ClientConnection.class) +@Mixin(Connection.class) public interface ClientConnectionAccessor { @Accessor("channel") Channel getChannel(); diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerAccessor.java b/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerAccessor.java index f217c2b77..4f5486d14 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerAccessor.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerAccessor.java @@ -1,12 +1,12 @@ package pl.skidam.automodpack.mixin.core; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.ClientConnection; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.Connection; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(ClientLoginNetworkHandler.class) +@Mixin(ClientHandshakePacketListenerImpl.class) public interface ClientLoginNetworkHandlerAccessor { @Accessor("connection") - ClientConnection getConnection(); + Connection getConnection(); } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerMixin.java index 264484667..9afc36c65 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerMixin.java @@ -1,8 +1,8 @@ package pl.skidam.automodpack.mixin.core; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -12,23 +12,23 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import pl.skidam.automodpack.networking.client.ClientLoginNetworkAddon; -@Mixin(value = ClientLoginNetworkHandler.class, priority = 300) +@Mixin(value = ClientHandshakePacketListenerImpl.class, priority = 300) public class ClientLoginNetworkHandlerMixin { - @Shadow @Final private MinecraftClient client; + @Shadow @Final private Minecraft minecraft; @Unique private ClientLoginNetworkAddon autoModpack$addon; @Inject(method = "", at = @At("RETURN")) private void initAddon(CallbackInfo ci) { - this.autoModpack$addon = new ClientLoginNetworkAddon((ClientLoginNetworkHandler) (Object) this, this.client); + this.autoModpack$addon = new ClientLoginNetworkAddon((ClientHandshakePacketListenerImpl) (Object) this, this.minecraft); } @Inject( - method = "onQueryRequest", + method = "handleCustomQuery", at = @At(value = "HEAD"), cancellable = true ) - private void handleQueryRequest(LoginQueryRequestS2CPacket packet, CallbackInfo ci) { + private void handleQueryRequest(ClientboundCustomQueryPacket packet, CallbackInfo ci) { if (this.autoModpack$addon == null) { return; } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java index af0ebf729..691dc615a 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java @@ -1,8 +1,8 @@ package pl.skidam.automodpack.mixin.core; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ServerAddress; -import net.minecraft.client.network.ServerInfo; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.ConnectScreen; +import net.minecraft.client.multiplayer.resolver.ServerAddress; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -12,27 +12,25 @@ import pl.skidam.automodpack.networking.ModPackets; /*? if >= 1.20.5 {*/ -import net.minecraft.client.network.CookieStorage; +import net.minecraft.client.multiplayer.TransferState; /*?}*/ -/*? if >=1.20.3 {*/ -import net.minecraft.client.gui.screen.multiplayer.ConnectScreen; -/*?} else {*/ -/*import net.minecraft.client.gui.screen.ConnectScreen; - *//*?}*/ +/*? if > 1.19.3 {*/ +import net.minecraft.client.multiplayer.ServerData; +/*?}*/ @Mixin(ConnectScreen.class) public abstract class ConnectScreenMixin { /*? if >= 1.20.5 {*/ - @Inject(method = "connect(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ServerAddress;Lnet/minecraft/client/network/ServerInfo;Lnet/minecraft/client/network/CookieStorage;)V", at = @At("HEAD")) - public void onConnect(MinecraftClient client, ServerAddress address, ServerInfo info, CookieStorage cookieStorage, CallbackInfo ci) { + @Inject(method = "connect", at = @At("HEAD")) + public void onConnect(Minecraft client, ServerAddress address, ServerData info, TransferState cookieStorage, CallbackInfo ci) { /*?} else if > 1.19.3 {*/ - /*@Inject(method = "connect(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ServerAddress;Lnet/minecraft/client/network/ServerInfo;)V", at = @At("HEAD")) - public void onConnect(MinecraftClient client, ServerAddress address, ServerInfo info, CallbackInfo ci) { + /*@Inject(method = "connect", at = @At("HEAD")) + public void onConnect(Minecraft client, ServerAddress address, ServerData info, CallbackInfo ci) { *//*?} else {*/ - /*@Inject(method = "connect(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ServerAddress;)V", at = @At("HEAD")) - public void onConnect(MinecraftClient client, ServerAddress address, CallbackInfo ci) { + /*@Inject(method = "connect", at = @At("HEAD")) + public void onConnect(Minecraft client, ServerAddress address, CallbackInfo ci) { *//*?}*/ - ModPackets.setOriginalServerAddress(AddressHelpers.format(address.getAddress(), address.getPort())); + ModPackets.setOriginalServerAddress(AddressHelpers.format(address.getHost(), address.getPort())); } } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/FabricLoginMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/FabricLoginMixin.java index e218d7849..5f9a19e1f 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/FabricLoginMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/FabricLoginMixin.java @@ -2,7 +2,7 @@ import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import net.fabricmc.fabric.impl.networking.server.ServerLoginNetworkAddon; -import net.minecraft.util.Identifier; +import net.minecraft.resources.ResourceLocation; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; @@ -19,7 +19,7 @@ public class FabricLoginMixin { at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") ) private boolean dontRemoveAutoModpackChannels(Map instance, Object key, Object value) { - if (value instanceof Identifier id) { + if (value instanceof ResourceLocation id) { // If AutoModpack id, return false return LoginNetworkingIDs.getByKey(id) == null; } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryRequestS2CPacketMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryRequestS2CPacketMixin.java index 132f8e4d2..a429126b2 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryRequestS2CPacketMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryRequestS2CPacketMixin.java @@ -2,10 +2,10 @@ import org.spongepowered.asm.mixin.Mixin; /*? if >=1.20.2 {*/ -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestPayload; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; -import net.minecraft.util.Identifier; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.network.protocol.login.custom.CustomQueryPayload; +import net.minecraft.resources.ResourceLocation; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -16,7 +16,7 @@ import pl.skidam.automodpack_core.GlobalVariables; // TODO find better way to do this, its mixin only for 1.20.2 and above -@Mixin(value = LoginQueryRequestS2CPacket.class, priority = 300) +@Mixin(value = ClientboundCustomQueryPacket.class, priority = 300) /*?} else {*/ /*import pl.skidam.automodpack.init.Common; @Mixin(Common.class) @@ -27,7 +27,7 @@ public class LoginQueryRequestS2CPacketMixin { @Shadow @Final private static int MAX_PAYLOAD_SIZE; @Inject(method = "readPayload", at = @At("HEAD"), cancellable = true) - private static void readPayload(Identifier id, PacketByteBuf buf, CallbackInfoReturnable cir) { + private static void readPayload(ResourceLocation id, FriendlyByteBuf buf, CallbackInfoReturnable cir) { if (id.getNamespace().equals(GlobalVariables.MOD_ID)) { cir.setReturnValue(new LoginRequestPayload(id, PayloadHelper.read(buf, MAX_PAYLOAD_SIZE))); } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryResponseC2SPacketMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryResponseC2SPacketMixin.java index d59fa759d..b9c4deb84 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryResponseC2SPacketMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryResponseC2SPacketMixin.java @@ -2,10 +2,10 @@ import org.spongepowered.asm.mixin.Mixin; /*? if >=1.20.2 {*/ -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket; -import net.minecraft.network.packet.c2s.login.LoginQueryResponsePayload; -import net.minecraft.util.Identifier; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.network.protocol.login.custom.CustomQueryAnswerPayload; +import net.minecraft.resources.ResourceLocation; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -16,7 +16,7 @@ import pl.skidam.automodpack.networking.client.LoginResponsePayload; // TODO find better way to do this, its mixin only for 1.20.2 and above -@Mixin(value = LoginQueryResponseC2SPacket.class, priority = 300) +@Mixin(value = ServerboundCustomQueryAnswerPacket.class, priority = 300) /*?} else {*/ /*import pl.skidam.automodpack.init.Common; @Mixin(Common.class) @@ -28,8 +28,8 @@ public class LoginQueryResponseC2SPacketMixin { private static int MAX_PAYLOAD_SIZE; @Inject(method = "readPayload", at = @At("HEAD"), cancellable = true) - private static void readResponse(int queryId, PacketByteBuf buf, CallbackInfoReturnable cir) { - Identifier automodpackID = LoginNetworkingIDs.getByValue(queryId); + private static void readResponse(int queryId, FriendlyByteBuf buf, CallbackInfoReturnable cir) { + ResourceLocation automodpackID = LoginNetworkingIDs.getByValue(queryId); if (automodpackID == null) { return; } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java index 40663df21..756ff84ef 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java @@ -11,7 +11,7 @@ public class MinecraftServerMixin { /*? if >=1.19.3 {*/ - @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;createMetadata()Lnet/minecraft/server/ServerMetadata;", ordinal = 0), method = "runServer") + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;loadStatusIcon()Ljava/util/Optional;"), method = "runServer") /*?} else {*/ /*@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;setFavicon(Lnet/minecraft/server/ServerMetadata;)V", ordinal = 0), method = "runServer") *//*?}*/ @@ -20,7 +20,7 @@ private void afterSetupServer(CallbackInfo info) { Common.afterSetupServer(); } - @Inject(at = @At("HEAD"), method = "shutdown") + @Inject(at = @At("HEAD"), method = "stopServer") private void beforeShutdownServer(CallbackInfo info) { Common.beforeShutdownServer(); } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java index e2caa1748..21f9f97a2 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java @@ -1,31 +1,32 @@ package pl.skidam.automodpack.mixin.core; -/*? if >1.21.4 {*/ -/*import net.minecraft.client.sound.MusicInstance; -*//*?} else {*/ -import net.minecraft.sound.MusicSound; -/*?}*/ -import net.minecraft.client.sound.MusicTracker; +import net.minecraft.client.sounds.MusicManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import pl.skidam.automodpack.client.audio.AudioManager; -@Mixin(MusicTracker.class) +/*? if >1.21.4 {*/ +import net.minecraft.client.sounds.MusicInfo; +/*?} else {*/ +/*import net.minecraft.sounds.Music; +*//*?}*/ + +@Mixin(MusicManager.class) public class MusicTrackerMixin { @Inject( - method = "play", + method = "startPlaying", at = @At("HEAD"), cancellable = true ) /*? if >1.21.4 {*/ - /*private void play(MusicInstance music, CallbackInfo ci) { - *//*?} else {*/ - private void play(MusicSound type, CallbackInfo ci) { - /*?}*/ + private void play(MusicInfo p_383115_, CallbackInfo ci) { + /*?} else {*/ + /*private void play(Music type, CallbackInfo ci) { + *//*?}*/ if (AudioManager.isMusicPlaying()) { ci.cancel(); } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java index 08bb93d94..ebbb4628a 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java @@ -1,16 +1,13 @@ package pl.skidam.automodpack.mixin.core; import com.mojang.authlib.GameProfile; -import net.minecraft.network.ClientConnection; -import net.minecraft.server.PlayerManager; -/*? if >1.20.3 {*/ -import net.minecraft.server.network.ConnectedClientData; -/*?}*/ -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.ClickEvent; -import net.minecraft.text.Text; -import net.minecraft.text.TextColor; -import net.minecraft.util.Formatting; +import net.minecraft.ChatFormatting; +import net.minecraft.network.Connection; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextColor; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.PlayerList; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -18,21 +15,25 @@ import pl.skidam.automodpack.client.ui.versioned.VersionedText; import pl.skidam.automodpack.init.Common; -/*? if >1.21.4 {*/ -/*import java.net.URI; -*//*?}*/ +/*? if >1.20.3 {*/ +import net.minecraft.server.network.CommonListenerCookie; +/*?}*/ + +/*? if >=1.21.5 {*/ +import java.net.URI; +/*?}*/ import static pl.skidam.automodpack_core.GlobalVariables.serverConfig; -@Mixin(PlayerManager.class) +@Mixin(PlayerList.class) public class PlayerManagerMixin { /*? if >1.20.3 {*/ - @Inject(at = @At("TAIL"), method = "onPlayerConnect") - private void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, ConnectedClientData clientData, CallbackInfo ci) { + @Inject(at = @At("TAIL"), method = "placeNewPlayer") + private void onPlayerConnect(Connection connection, ServerPlayer player, CommonListenerCookie clientData, CallbackInfo ci) { /*?} else {*/ -/*@Inject(at = @At("TAIL"), method = "onPlayerConnect") -private void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { +/*@Inject(at = @At("TAIL"), method = "placeNewPlayer") +private void onPlayerConnect(Connection netManager, ServerPlayer player, CallbackInfo ci) { *//*?}*/ GameProfile profile = player.getGameProfile(); String playerName = profile.getName(); @@ -44,15 +45,15 @@ private void onPlayerConnect(ClientConnection connection, ServerPlayerEntity pla if (serverConfig.nagUnModdedClients && !Common.players.get(playerName)) { // Send chat nag message which is clickable and opens the link - Text nagText = VersionedText.literal(serverConfig.nagMessage).styled(style -> style.withBold(true)); - Text nagClickableText = VersionedText.literal(serverConfig.nagClickableMessage).styled(style -> style.withUnderline(true).withColor(TextColor.fromFormatting(Formatting.BLUE)) - /*? if >1.21.5 {*/ - /*.withClickEvent(new ClickEvent.OpenUrl(URI.create(serverConfig.nagClickableLink)))); - *//*?} else {*/ - .withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, serverConfig.nagClickableLink))); - /*?}*/ - player.sendMessage(nagText, false); - player.sendMessage(nagClickableText, false); + Component nagText = VersionedText.literal(serverConfig.nagMessage).withStyle(style -> style.withBold(true)); + Component nagClickableText = VersionedText.literal(serverConfig.nagClickableMessage).withStyle(style -> style.withUnderlined(true).withColor(TextColor.fromLegacyFormat(ChatFormatting.BLUE)) + /*? if >=1.21.5 {*/ + .withClickEvent(new ClickEvent.OpenUrl(URI.create(serverConfig.nagClickableLink)))); + /*?} else {*/ + /*.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, serverConfig.nagClickableLink))); + *//*?}*/ + player.displayClientMessage(nagText, false); + player.displayClientMessage(nagClickableText, false); } } } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerAccessor.java b/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerAccessor.java index fa8c8bef4..99a6dfd8c 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerAccessor.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerAccessor.java @@ -1,19 +1,23 @@ package pl.skidam.automodpack.mixin.core; import com.mojang.authlib.GameProfile; -import net.minecraft.network.ClientConnection; +import net.minecraft.network.Connection; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(ServerLoginNetworkHandler.class) +@Mixin(ServerLoginPacketListenerImpl.class) public interface ServerLoginNetworkHandlerAccessor { - @Accessor("profile") + /*? if >= 1.20.2 {*/ + @Accessor("authenticatedProfile") + /*?} else {*/ + /*@Accessor("gameProfile") + *//*?}*/ GameProfile getGameProfile(); @Accessor - ClientConnection getConnection(); + Connection getConnection(); @Accessor MinecraftServer getServer(); diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerMixin.java index b1f3d1390..89b34384c 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerMixin.java @@ -1,7 +1,7 @@ package pl.skidam.automodpack.mixin.core; -import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket; -import net.minecraft.server.network.ServerLoginNetworkHandler; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -9,10 +9,10 @@ import pl.skidam.automodpack.networking.client.LoginResponsePayload; import pl.skidam.automodpack.networking.server.ServerLoginNetworkAddon; -@Mixin(value = ServerLoginNetworkHandler.class, priority = 300) +@Mixin(value = ServerLoginPacketListenerImpl.class, priority = 300) public abstract class ServerLoginNetworkHandlerMixin { - @Shadow private ServerLoginNetworkHandler.State state; + @Shadow private ServerLoginPacketListenerImpl.State state; @Unique private ServerLoginNetworkAddon automodpack$addon; @Inject( @@ -20,15 +20,15 @@ public abstract class ServerLoginNetworkHandlerMixin { at = @At("RETURN") ) private void initAddon(CallbackInfo ci) { - this.automodpack$addon = new ServerLoginNetworkAddon((ServerLoginNetworkHandler) (Object) this); + this.automodpack$addon = new ServerLoginNetworkAddon((ServerLoginPacketListenerImpl) (Object) this); } @Inject( - method = "onQueryResponse", + method = "handleCustomQueryPacket", at = @At("HEAD"), cancellable = true ) - private void handleCustomPayload(LoginQueryResponseC2SPacket packet, CallbackInfo ci) { + private void handleCustomPayload(ServerboundCustomQueryAnswerPacket packet, CallbackInfo ci) { if (this.automodpack$addon == null) { return; } @@ -38,7 +38,7 @@ private void handleCustomPayload(LoginQueryResponseC2SPacket packet, CallbackInf ci.cancel(); // We have handled it, cancel vanilla behavior } else { /*? if >=1.20.2 {*/ - if (packet.response() instanceof LoginResponsePayload response) { + if (packet.payload() instanceof LoginResponsePayload response) { response.data().skipBytes(response.data().readableBytes()); } /*?}*/ @@ -55,7 +55,7 @@ private void sendOurPackets(CallbackInfo ci) { return; } - if (state != ServerLoginNetworkHandler.State.NEGOTIATING && state != ServerLoginNetworkHandler.State./*? if <1.20.2 {*/ /*READY_TO_ACCEPT *//*?} else {*/VERIFYING/*?}*/) { + if (state != ServerLoginPacketListenerImpl.State.NEGOTIATING && state != ServerLoginPacketListenerImpl.State./*? if <1.20.2 {*/ /*READY_TO_ACCEPT *//*?} else {*/VERIFYING/*?}*/) { return; } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java index a13b04d06..6de05e01c 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java @@ -10,7 +10,7 @@ import static pl.skidam.automodpack_core.GlobalVariables.*; -@Mixin(targets = "net/minecraft/server/ServerNetworkIo$1", priority = 2137) +@Mixin(targets = "net/minecraft/server/network/ServerConnectionListener$1", priority = 2137) public abstract class ServerNetworkIoMixin { @Inject( diff --git a/src/main/java/pl/skidam/automodpack/mixin/dev/TestButton.java b/src/main/java/pl/skidam/automodpack/mixin/dev/TestButton.java index 5a821a87d..40024ef18 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/dev/TestButton.java +++ b/src/main/java/pl/skidam/automodpack/mixin/dev/TestButton.java @@ -1,8 +1,5 @@ package pl.skidam.automodpack.mixin.dev; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.TitleScreen; -import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -14,10 +11,14 @@ import static pl.skidam.automodpack_core.GlobalVariables.LOADER_MANAGER; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.network.chat.Component; + @Mixin(TitleScreen.class) public class TestButton extends Screen { - protected TestButton(Text title) { + protected TestButton(Component title) { super(title); } @@ -33,7 +34,7 @@ private void init(CallbackInfo ci) { } /*? if >=1.17 {*/ - this.addDrawableChild( + this.addRenderableWidget( /*?} else {*//* this.addButton( *//*?}*/ diff --git a/src/main/java/pl/skidam/automodpack/modpack/Commands.java b/src/main/java/pl/skidam/automodpack/modpack/Commands.java index fd24ba443..c804b1fdb 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/Commands.java +++ b/src/main/java/pl/skidam/automodpack/modpack/Commands.java @@ -4,61 +4,60 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.context.CommandContext; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.text.ClickEvent; -import net.minecraft.text.HoverEvent; -import net.minecraft.text.MutableText; -import net.minecraft.util.Formatting; -import net.minecraft.util.Util; import pl.skidam.automodpack.client.ui.versioned.VersionedCommandSource; import pl.skidam.automodpack.client.ui.versioned.VersionedText; import pl.skidam.automodpack_core.auth.SecretsStore; import pl.skidam.automodpack_core.config.ConfigTools; import pl.skidam.automodpack_core.config.Jsons; - import java.util.Set; - -import static net.minecraft.server.command.CommandManager.literal; +import net.minecraft.ChatFormatting; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.MutableComponent; + +import static net.minecraft.commands.Commands.literal; import static pl.skidam.automodpack_core.GlobalVariables.*; public class Commands { - public static void register(CommandDispatcher dispatcher) { + public static void register(CommandDispatcher dispatcher) { var automodpackNode = dispatcher.register( literal("automodpack") .executes(Commands::about) .then(literal("generate") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::generateModpack) ) .then(literal("host") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::modpackHostAbout) .then(literal("start") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::startModpackHost) ) .then(literal("stop") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::stopModpackHost) ) .then(literal("restart") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::restartModpackHost) ) .then(literal("connections") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::connections) ) .then(literal("fingerprint") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::fingerprint) ) ) .then(literal("config") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .then(literal("reload") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::reload) ) ) @@ -71,31 +70,31 @@ public static void register(CommandDispatcher dispatcher) { ); } - private static int fingerprint(CommandContext context) { + private static int fingerprint(CommandContext context) { String fingerprint = hostServer.getCertificateFingerprint(); if (fingerprint != null) { - MutableText fingerprintText = VersionedText.literal(fingerprint).styled(style -> style - /*? if >1.21.5 {*/ - /*.withHoverEvent(new HoverEvent.ShowText(VersionedText.translatable("chat.copy.click"))) + MutableComponent fingerprintText = VersionedText.literal(fingerprint).withStyle(style -> style + /*? if >=1.21.5 {*/ + .withHoverEvent(new HoverEvent.ShowText(VersionedText.translatable("chat.copy.click"))) .withClickEvent(new ClickEvent.CopyToClipboard(fingerprint))); - *//*?} else {*/ - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, VersionedText.translatable("chat.copy.click"))) + /*?} else {*/ + /*.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, VersionedText.translatable("chat.copy.click"))) .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, fingerprint))); - /*?}*/ - send(context, "Certificate fingerprint", Formatting.WHITE, fingerprintText, Formatting.YELLOW, false); + *//*?}*/ + send(context, "Certificate fingerprint", ChatFormatting.WHITE, fingerprintText, ChatFormatting.YELLOW, false); } else { - send(context, "Certificate fingerprint is not available. Make sure the server is running with TLS enabled.", Formatting.RED, false); + send(context, "Certificate fingerprint is not available. Make sure the server is running with TLS enabled.", ChatFormatting.RED, false); } return Command.SINGLE_SUCCESS; } - private static int connections(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { + private static int connections(CommandContext context) { + Util.backgroundExecutor().execute(() -> { var connections = hostServer.getConnections(); var uniqueSecrets = Set.copyOf(connections.values()); - send(context, String.format("Active connections: %d Unique connections: %d ", connections.size(), uniqueSecrets.size()), Formatting.YELLOW, false); + send(context, String.format("Active connections: %d Unique connections: %d ", connections.size(), uniqueSecrets.size()), ChatFormatting.YELLOW, false); for (String secret : uniqueSecrets) { var playerSecretPair = SecretsStore.getHostSecret(secret); @@ -106,65 +105,65 @@ private static int connections(CommandContext context) { long connNum = connections.values().stream().filter(secret::equals).count(); - send(context, String.format("Player: %s (%s) is downloading modpack using %d connections", profile.getName(), playerId, connNum), Formatting.GREEN, false); + send(context, String.format("Player: %s (%s) is downloading modpack using %d connections", profile.getName(), playerId, connNum), ChatFormatting.GREEN, false); } }); return Command.SINGLE_SUCCESS; } - private static int reload(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { + private static int reload(CommandContext context) { + Util.backgroundExecutor().execute(() -> { var tempServerConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFieldsV2.class); if (tempServerConfig != null) { serverConfig = tempServerConfig; - send(context, "AutoModpack server config reloaded!", Formatting.GREEN, true); + send(context, "AutoModpack server config reloaded!", ChatFormatting.GREEN, true); } else { - send(context, "Error while reloading config file!", Formatting.RED, true); + send(context, "Error while reloading config file!", ChatFormatting.RED, true); } }); return Command.SINGLE_SUCCESS; } - private static int startModpackHost(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { + private static int startModpackHost(CommandContext context) { + Util.backgroundExecutor().execute(() -> { if (!hostServer.isRunning()) { - send(context, "Starting modpack hosting...", Formatting.YELLOW, true); + send(context, "Starting modpack hosting...", ChatFormatting.YELLOW, true); hostServer.start(); if (hostServer.isRunning()) { - send(context, "Modpack hosting started!", Formatting.GREEN, true); + send(context, "Modpack hosting started!", ChatFormatting.GREEN, true); } else { - send(context, "Couldn't start server!", Formatting.RED, true); + send(context, "Couldn't start server!", ChatFormatting.RED, true); } } else { - send(context, "Modpack hosting is already running!", Formatting.RED, false); + send(context, "Modpack hosting is already running!", ChatFormatting.RED, false); } }); return Command.SINGLE_SUCCESS; } - private static int stopModpackHost(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { + private static int stopModpackHost(CommandContext context) { + Util.backgroundExecutor().execute(() -> { if (hostServer.isRunning()) { - send(context, "Stopping modpack hosting...", Formatting.RED, true); + send(context, "Stopping modpack hosting...", ChatFormatting.RED, true); if (hostServer.stop()) { - send(context, "Modpack hosting stopped!", Formatting.RED, true); + send(context, "Modpack hosting stopped!", ChatFormatting.RED, true); } else { - send(context, "Couldn't stop server!", Formatting.RED, true); + send(context, "Couldn't stop server!", ChatFormatting.RED, true); } } else { - send(context, "Modpack hosting is not running!", Formatting.RED, false); + send(context, "Modpack hosting is not running!", ChatFormatting.RED, false); } }); return Command.SINGLE_SUCCESS; } - private static int restartModpackHost(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { - send(context, "Restarting modpack hosting...", Formatting.YELLOW, true); + private static int restartModpackHost(CommandContext context) { + Util.backgroundExecutor().execute(() -> { + send(context, "Restarting modpack hosting...", ChatFormatting.YELLOW, true); boolean needStop = hostServer.isRunning(); boolean stopped = false; if (needStop) { @@ -172,13 +171,13 @@ private static int restartModpackHost(CommandContext contex } if (needStop && !stopped) { - send(context, "Couldn't restart server!", Formatting.RED, true); + send(context, "Couldn't restart server!", ChatFormatting.RED, true); } else { hostServer.start(); if (hostServer.isRunning()) { - send(context, "Modpack hosting restarted!", Formatting.GREEN, true); + send(context, "Modpack hosting restarted!", ChatFormatting.GREEN, true); } else { - send(context, "Couldn't restart server!", Formatting.RED, true); + send(context, "Couldn't restart server!", ChatFormatting.RED, true); } } }); @@ -186,65 +185,65 @@ private static int restartModpackHost(CommandContext contex return Command.SINGLE_SUCCESS; } - private static int modpackHostAbout(CommandContext context) { - Formatting statusColor = hostServer.isRunning() ? Formatting.GREEN : Formatting.RED; + private static int modpackHostAbout(CommandContext context) { + ChatFormatting statusColor = hostServer.isRunning() ? ChatFormatting.GREEN : ChatFormatting.RED; String status = hostServer.isRunning() ? "running" : "not running"; - send(context, "Modpack hosting status", Formatting.GREEN, status, statusColor, false); + send(context, "Modpack hosting status", ChatFormatting.GREEN, status, statusColor, false); return Command.SINGLE_SUCCESS; } - private static int about(CommandContext context) { - send(context, "AutoModpack", Formatting.GREEN, AM_VERSION, Formatting.WHITE, false); - send(context, "/automodpack generate", Formatting.YELLOW, false); - send(context, "/automodpack host start/stop/restart/connections/fingerprint", Formatting.YELLOW, false); - send(context, "/automodpack config reload", Formatting.YELLOW, false); + private static int about(CommandContext context) { + send(context, "AutoModpack", ChatFormatting.GREEN, AM_VERSION, ChatFormatting.WHITE, false); + send(context, "/automodpack generate", ChatFormatting.YELLOW, false); + send(context, "/automodpack host start/stop/restart/connections/fingerprint", ChatFormatting.YELLOW, false); + send(context, "/automodpack config reload", ChatFormatting.YELLOW, false); return Command.SINGLE_SUCCESS; } - private static int generateModpack(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { + private static int generateModpack(CommandContext context) { + Util.backgroundExecutor().execute(() -> { if (modpackExecutor.isGenerating()) { - send(context, "Modpack is already generating! Please wait!", Formatting.RED, false); + send(context, "Modpack is already generating! Please wait!", ChatFormatting.RED, false); return; } - send(context, "Generating Modpack...", Formatting.YELLOW, true); + send(context, "Generating Modpack...", ChatFormatting.YELLOW, true); long start = System.currentTimeMillis(); if (modpackExecutor.generateNew()) { - send(context, "Modpack generated! took " + (System.currentTimeMillis() - start) + "ms", Formatting.GREEN, true); + send(context, "Modpack generated! took " + (System.currentTimeMillis() - start) + "ms", ChatFormatting.GREEN, true); } else { - send(context, "Modpack generation failed! Check logs for more info.", Formatting.RED, true); + send(context, "Modpack generation failed! Check logs for more info.", ChatFormatting.RED, true); } }); return Command.SINGLE_SUCCESS; } - private static void send(CommandContext context, String msg, Formatting msgColor, boolean broadcast) { + private static void send(CommandContext context, String msg, ChatFormatting msgColor, boolean broadcast) { VersionedCommandSource.sendFeedback(context, VersionedText.literal(msg) - .formatted(msgColor), + .withStyle(msgColor), broadcast); } - private static void send(CommandContext context, String msg, Formatting msgColor, String appendMsg, Formatting appendMsgColor, boolean broadcast) { + private static void send(CommandContext context, String msg, ChatFormatting msgColor, String appendMsg, ChatFormatting appendMsgColor, boolean broadcast) { VersionedCommandSource.sendFeedback(context, VersionedText.literal(msg) - .formatted(msgColor) + .withStyle(msgColor) .append(VersionedText.literal(" - ") - .formatted(Formatting.WHITE)) + .withStyle(ChatFormatting.WHITE)) .append(VersionedText.literal(appendMsg) - .formatted(appendMsgColor)), + .withStyle(appendMsgColor)), broadcast); } - private static void send(CommandContext context, String msg, Formatting msgColor, MutableText appendMsg, Formatting appendMsgColor, boolean broadcast) { + private static void send(CommandContext context, String msg, ChatFormatting msgColor, MutableComponent appendMsg, ChatFormatting appendMsgColor, boolean broadcast) { VersionedCommandSource.sendFeedback(context, VersionedText.literal(msg) - .formatted(msgColor) + .withStyle(msgColor) .append(VersionedText.literal(" - ") - .formatted(Formatting.WHITE)) + .withStyle(ChatFormatting.WHITE)) .append(appendMsg - .formatted(appendMsgColor)), + .withStyle(appendMsgColor)), broadcast); } } diff --git a/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java b/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java index 1fc9bcd9d..f7661911c 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java +++ b/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java @@ -1,10 +1,10 @@ package pl.skidam.automodpack.modpack; import com.mojang.authlib.GameProfile; -import net.minecraft.util.UserCache; - import java.net.SocketAddress; import java.util.UUID; +import net.minecraft.server.players.GameProfileCache; +import net.minecraft.server.players.PlayerList; import static pl.skidam.automodpack.init.Common.server; @@ -12,14 +12,14 @@ public class GameHelpers { // Simpler version of `PlayerManager.checkCanJoin` public static boolean isPlayerAuthorized(SocketAddress address, GameProfile profile) { - var playerManager = server.getPlayerManager(); - if (playerManager.getUserBanList().contains(profile)) { + var playerManager = server.getPlayerList(); + if (playerManager.getBans().isBanned(profile)) { return false; } - if (!playerManager.isWhitelisted(profile)) { + if (!playerManager.isWhiteListed(profile)) { return false; } - if (playerManager.getIpBanList().isBanned(address)) { + if (playerManager.getIpBans().isBanned(address)) { return false; } @@ -32,9 +32,9 @@ public static GameProfile getPlayerProfile(String id) { String playerName = "Player"; // mock name, name matters less than UUID anyway GameProfile profile = new GameProfile(uuid, playerName); - UserCache userCache = server.getUserCache(); + GameProfileCache userCache = server.getProfileCache(); if (userCache != null) { - profile = userCache.getByUuid(uuid).orElse(profile); + profile = userCache.get(uuid).orElse(profile); } return profile; diff --git a/src/main/java/pl/skidam/automodpack/networking/LoginNetworkingIDs.java b/src/main/java/pl/skidam/automodpack/networking/LoginNetworkingIDs.java index 7567b18f9..62490f45d 100644 --- a/src/main/java/pl/skidam/automodpack/networking/LoginNetworkingIDs.java +++ b/src/main/java/pl/skidam/automodpack/networking/LoginNetworkingIDs.java @@ -1,10 +1,11 @@ package pl.skidam.automodpack.networking; -import net.minecraft.util.Identifier; import pl.skidam.automodpack.init.Common; import static pl.skidam.automodpack_core.GlobalVariables.MOD_ID; +import net.minecraft.resources.ResourceLocation; + public enum LoginNetworkingIDs { // AutoModpack login query id's HANDSHAKE(-100), @@ -20,11 +21,11 @@ public int getValue() { return value; } - public static Identifier getIdentifier(LoginNetworkingIDs ID) { + public static ResourceLocation getResourceLocation(LoginNetworkingIDs ID) { return Common.id(ID.toString().toLowerCase()); } - public static Integer getByKey(Identifier key) { + public static Integer getByKey(ResourceLocation key) { if (key.getNamespace().equalsIgnoreCase(MOD_ID)) { for (var ID : LoginNetworkingIDs.values()) { if (ID.name().equalsIgnoreCase(key.getPath())) { @@ -35,10 +36,10 @@ public static Integer getByKey(Identifier key) { return null; } - public static Identifier getByValue(int value) { + public static ResourceLocation getByValue(int value) { for (var ID : LoginNetworkingIDs.values()) { if (ID.getValue() == value) { - return getIdentifier(ID); + return getResourceLocation(ID); } } return null; diff --git a/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java b/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java index de155e776..15d3cd146 100644 --- a/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java +++ b/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java @@ -1,53 +1,50 @@ package pl.skidam.automodpack.networking; -/*? if <=1.19.3 {*/ -/*import net.minecraft.network.Packet; -*//*?} else {*/ -import net.minecraft.network.packet.Packet; -/*?}*/ -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; -import net.minecraft.util.Identifier; /*? if >=1.20.2 {*/ import pl.skidam.automodpack.networking.client.LoginResponsePayload; import pl.skidam.automodpack.networking.server.LoginRequestPayload; -import net.minecraft.network.packet.c2s.login.LoginQueryResponsePayload; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestPayload; +import net.minecraft.network.protocol.login.custom.CustomQueryAnswerPayload; +import net.minecraft.network.protocol.login.custom.CustomQueryPayload; /*?}*/ import static pl.skidam.automodpack_core.GlobalVariables.LOGGER; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.resources.ResourceLocation; + public class LoginQueryParser { public Packet packet; public boolean success = true; public int queryId; - public PacketByteBuf buf; - public Identifier channelName; + public FriendlyByteBuf buf; + public ResourceLocation channelName; public LoginQueryParser(Packet packet) { - if (packet instanceof LoginQueryRequestS2CPacket packetS2C) { + if (packet instanceof ClientboundCustomQueryPacket packetS2C) { this.packet = packetS2C; /*? if <1.20.2 {*/ - /*this.queryId = packetS2C.getQueryId(); - this.buf = packetS2C.getPayload(); - this.channelName = packetS2C.getChannel(); + /*this.queryId = packetS2C.getTransactionId(); + this.buf = packetS2C.getData(); + this.channelName = packetS2C.getIdentifier(); *//*?} else {*/ - this.queryId = packetS2C.queryId(); - LoginQueryRequestPayload payload = packetS2C.payload(); + this.queryId = packetS2C.transactionId(); + CustomQueryPayload payload = packetS2C.payload(); if (payload instanceof LoginRequestPayload loginRequestPayload) { this.buf = loginRequestPayload.data(); this.channelName = loginRequestPayload.id(); } /*?}*/ - } else if (packet instanceof LoginQueryResponseC2SPacket packetC2S) { + } else if (packet instanceof ServerboundCustomQueryAnswerPacket packetC2S) { this.packet = packetC2S; /*? if <1.20.2 {*/ - /*this.queryId = packetC2S.getQueryId(); - this.buf = packetC2S.getResponse(); + /*this.queryId = packetC2S.getTransactionId(); + this.buf = packetC2S.getData(); *//*?} else {*/ - this.queryId = packetC2S.queryId(); - LoginQueryResponsePayload payload = packetC2S.response(); + this.queryId = packetC2S.transactionId(); + CustomQueryAnswerPayload payload = packetC2S.payload(); if (payload instanceof LoginResponsePayload loginRequestPayload) { this.buf = loginRequestPayload.data(); this.channelName = loginRequestPayload.id(); diff --git a/src/main/java/pl/skidam/automodpack/networking/ModPackets.java b/src/main/java/pl/skidam/automodpack/networking/ModPackets.java index db6532de6..f941bd531 100644 --- a/src/main/java/pl/skidam/automodpack/networking/ModPackets.java +++ b/src/main/java/pl/skidam/automodpack/networking/ModPackets.java @@ -1,10 +1,10 @@ package pl.skidam.automodpack.networking; import io.netty.buffer.Unpooled; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.util.Identifier; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import pl.skidam.automodpack.networking.client.ClientLoginNetworking; import pl.skidam.automodpack.networking.content.HandshakePacket; import pl.skidam.automodpack.networking.packet.HandshakeC2SPacket; @@ -19,8 +19,8 @@ import static pl.skidam.automodpack_core.GlobalVariables.*; public class ModPackets { - public static final Identifier HANDSHAKE = LoginNetworkingIDs.getIdentifier(LoginNetworkingIDs.HANDSHAKE); - public static final Identifier DATA = LoginNetworkingIDs.getIdentifier(LoginNetworkingIDs.DATA); + public static final ResourceLocation HANDSHAKE = LoginNetworkingIDs.getResourceLocation(LoginNetworkingIDs.HANDSHAKE); + public static final ResourceLocation DATA = LoginNetworkingIDs.getResourceLocation(LoginNetworkingIDs.DATA); private static InetSocketAddress originalServerAddress; @@ -46,14 +46,14 @@ public static void registerS2CPackets() { } // Fires just after client go into login state and before any FML packet is sent. - public static void onReady(ServerLoginNetworkHandler handler, MinecraftServer server, ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender sender) { + public static void onReady(ServerLoginPacketListenerImpl handler, MinecraftServer server, ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender sender) { synchronizer.waitFor(server.submit(() -> { - PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); HandshakePacket handshakePacket = new HandshakePacket(serverConfig.acceptedLoaders, AM_VERSION, MC_VERSION); String jsonHandshakePacket = handshakePacket.toJson(); - buf.writeString(jsonHandshakePacket, Short.MAX_VALUE); + buf.writeUtf(jsonHandshakePacket, Short.MAX_VALUE); sender.sendPacket(HANDSHAKE, buf); })); } diff --git a/src/main/java/pl/skidam/automodpack/networking/PacketSender.java b/src/main/java/pl/skidam/automodpack/networking/PacketSender.java index 2b105eda5..a1bc268fc 100644 --- a/src/main/java/pl/skidam/automodpack/networking/PacketSender.java +++ b/src/main/java/pl/skidam/automodpack/networking/PacketSender.java @@ -1,10 +1,9 @@ package pl.skidam.automodpack.networking; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; -import net.minecraft.util.Identifier; - import java.util.Objects; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.resources.ResourceLocation; // credits to fabric api public interface PacketSender { @@ -15,14 +14,14 @@ public interface PacketSender { * @param channelName the id of the channel * @param buf the content of the packet */ - LoginQueryRequestS2CPacket createPacket(Identifier channelName, PacketByteBuf buf); + ClientboundCustomQueryPacket createPacket(ResourceLocation channelName, FriendlyByteBuf buf); /** * Sends a packet. * * @param packet the packet */ - void sendPacket(LoginQueryRequestS2CPacket packet); + void sendPacket(ClientboundCustomQueryPacket packet); /** * Sends a packet to a channel. @@ -30,7 +29,7 @@ public interface PacketSender { * @param channel the id of the channel * @param buf the content of the packet */ - default void sendPacket(Identifier channel, PacketByteBuf buf) { + default void sendPacket(ResourceLocation channel, FriendlyByteBuf buf) { Objects.requireNonNull(channel, "Channel cannot be null"); Objects.requireNonNull(buf, "Payload cannot be null"); diff --git a/src/main/java/pl/skidam/automodpack/networking/PayloadHelper.java b/src/main/java/pl/skidam/automodpack/networking/PayloadHelper.java index 9179492cf..b5da6d864 100644 --- a/src/main/java/pl/skidam/automodpack/networking/PayloadHelper.java +++ b/src/main/java/pl/skidam/automodpack/networking/PayloadHelper.java @@ -2,25 +2,25 @@ /*? if >=1.20.2 {*/ import io.netty.buffer.Unpooled; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.FriendlyByteBuf; // credits to fabric api public class PayloadHelper { - public static void write(PacketByteBuf byteBuf, PacketByteBuf data) { + public static void write(FriendlyByteBuf byteBuf, FriendlyByteBuf data) { byteBuf.writeBytes(data.copy()); } - public static PacketByteBuf read(PacketByteBuf byteBuf, int maxSize) { + public static FriendlyByteBuf read(FriendlyByteBuf byteBuf, int maxSize) { assertSize(byteBuf, maxSize); - PacketByteBuf newBuf = new PacketByteBuf(Unpooled.buffer()); + FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer()); newBuf.writeBytes(byteBuf.copy()); byteBuf.skipBytes(byteBuf.readableBytes()); return newBuf; } - private static void assertSize(PacketByteBuf buf, int maxSize) { + private static void assertSize(FriendlyByteBuf buf, int maxSize) { int size = buf.readableBytes(); if (size < 0 || size > maxSize) { diff --git a/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworkAddon.java b/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworkAddon.java index 80cda0100..b84b74d9b 100644 --- a/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworkAddon.java +++ b/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworkAddon.java @@ -1,25 +1,25 @@ package pl.skidam.automodpack.networking.client; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; -import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; import pl.skidam.automodpack.mixin.core.ClientLoginNetworkHandlerAccessor; import pl.skidam.automodpack.networking.LoginQueryParser; import java.util.concurrent.CompletableFuture; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.resources.ResourceLocation; import static pl.skidam.automodpack_core.GlobalVariables.LOGGER; // credits to fabric api public class ClientLoginNetworkAddon { - private final ClientLoginNetworkHandler handler; - private final MinecraftClient client; + private final ClientHandshakePacketListenerImpl handler; + private final Minecraft client; - public ClientLoginNetworkAddon(ClientLoginNetworkHandler clientLoginNetworkHandler, MinecraftClient client) { + public ClientLoginNetworkAddon(ClientHandshakePacketListenerImpl clientLoginNetworkHandler, Minecraft client) { this.handler = clientLoginNetworkHandler; this.client = client; } @@ -30,25 +30,25 @@ public ClientLoginNetworkAddon(ClientLoginNetworkHandler clientLoginNetworkHandl * @param packet the packet to handle * @return true if the packet was handled */ - public boolean handlePacket(LoginQueryRequestS2CPacket packet) { + public boolean handlePacket(ClientboundCustomQueryPacket packet) { LoginQueryParser loginQuery = new LoginQueryParser(packet); if (loginQuery.success) return handlePacket(loginQuery.queryId, loginQuery.channelName, loginQuery.buf); return false; } - private boolean handlePacket(int queryId, Identifier channelName, PacketByteBuf payload) { + private boolean handlePacket(int queryId, ResourceLocation channelName, FriendlyByteBuf payload) { @Nullable ClientLoginNetworking.LoginQueryRequestHandler handler = ClientLoginNetworking.getHandler(channelName); if (handler == null) { return false; } - PacketByteBuf buf = new PacketByteBuf(payload.slice()); + FriendlyByteBuf buf = new FriendlyByteBuf(payload.slice()); try { - CompletableFuture<@Nullable PacketByteBuf> future = handler.receive(this.client, this.handler, buf); + CompletableFuture future = handler.receive(this.client, this.handler, buf); future.thenAccept(resultBuf -> { - LoginQueryResponseC2SPacket packet = new LoginQueryResponseC2SPacket(queryId, /*? if <1.20.2 {*/ /*resultBuf *//*?} else {*/ new LoginResponsePayload(channelName, resultBuf) /*?}*/); + ServerboundCustomQueryAnswerPacket packet = new ServerboundCustomQueryAnswerPacket(queryId, /*? if <1.20.2 {*/ /*resultBuf *//*?} else {*/ new LoginResponsePayload(channelName, resultBuf) /*?}*/); ((ClientLoginNetworkHandlerAccessor) this.handler).getConnection().send(packet); }); } catch (Throwable e) { diff --git a/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworking.java b/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworking.java index 40ebfe8f2..401cd6d68 100644 --- a/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworking.java +++ b/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworking.java @@ -1,20 +1,18 @@ package pl.skidam.automodpack.networking.client; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.Nullable; - import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; // credits to fabric api public class ClientLoginNetworking { - private static final Map handlers = new HashMap<>(); + private static final Map handlers = new HashMap<>(); /** * Registers a handler to a query request channel. @@ -23,14 +21,14 @@ public class ClientLoginNetworking { * @param channelName the id of the channel * @param handler the handler */ - public static void registerGlobalReceiver(Identifier channelName, LoginQueryRequestHandler handler) { + public static void registerGlobalReceiver(ResourceLocation channelName, LoginQueryRequestHandler handler) { Objects.requireNonNull(channelName, "Channel name cannot be null"); Objects.requireNonNull(handler, "Channel handler cannot be null"); handlers.put(channelName, handler); } - public static LoginQueryRequestHandler getHandler(Identifier channelName) { + public static LoginQueryRequestHandler getHandler(ResourceLocation channelName) { return handlers.get(channelName); } @@ -40,7 +38,7 @@ public interface LoginQueryRequestHandler { * Handles an incoming query request from a server. * *

This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}. - * Modification to the game should be {@linkplain net.minecraft.util.thread.ThreadExecutor#submit(Runnable) scheduled} using the provided Minecraft client instance. + * Modification to the game should be {@linkplain net.minecraft.util.thread.BlockableEventLoop#submit(Runnable) scheduled} using the provided Minecraft client instance. * *

The return value of this method is a completable future that may be used to delay the login process to the server until a task {@link CompletableFuture#isDone() is done}. * The future should complete in reasonably time to prevent disconnection by the server. @@ -52,6 +50,6 @@ public interface LoginQueryRequestHandler { * @return a completable future which contains the payload to respond to the server with. * If the future contains {@code null}, then the server will be notified that the client did not understand the query. */ - CompletableFuture<@Nullable PacketByteBuf> receive(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf); + CompletableFuture receive(Minecraft client, ClientHandshakePacketListenerImpl handler, FriendlyByteBuf buf); } } diff --git a/src/main/java/pl/skidam/automodpack/networking/client/LoginResponsePayload.java b/src/main/java/pl/skidam/automodpack/networking/client/LoginResponsePayload.java index 785385564..75e76398c 100644 --- a/src/main/java/pl/skidam/automodpack/networking/client/LoginResponsePayload.java +++ b/src/main/java/pl/skidam/automodpack/networking/client/LoginResponsePayload.java @@ -1,18 +1,18 @@ package pl.skidam.automodpack.networking.client; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.util.Identifier; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; /*? if <1.20.2 {*/ -/*public record LoginResponsePayload(Identifier id, PacketByteBuf data) { } +/*public record LoginResponsePayload(ResourceLocation id, FriendlyByteBuf data) { } *//*?} else {*/ -import net.minecraft.network.packet.c2s.login.LoginQueryResponsePayload; +import net.minecraft.network.protocol.login.custom.CustomQueryAnswerPayload; import pl.skidam.automodpack.networking.PayloadHelper; -public record LoginResponsePayload(Identifier id, PacketByteBuf data) implements LoginQueryResponsePayload { +public record LoginResponsePayload(ResourceLocation id, FriendlyByteBuf data) implements CustomQueryAnswerPayload { @Override - public void write(PacketByteBuf buf) { + public void write(FriendlyByteBuf buf) { PayloadHelper.write(buf, data()); } } -/*}*/ \ No newline at end of file +/*?}*/ \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java index e97796c03..64dafbf0e 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java @@ -1,9 +1,6 @@ package pl.skidam.automodpack.networking.packet; import io.netty.buffer.Unpooled; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.PacketByteBuf; import pl.skidam.automodpack.mixin.core.ClientConnectionAccessor; import pl.skidam.automodpack.mixin.core.ClientLoginNetworkHandlerAccessor; import pl.skidam.automodpack.networking.ModPackets; @@ -20,16 +17,20 @@ import java.net.InetSocketAddress; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.FriendlyByteBuf; import static pl.skidam.automodpack_core.GlobalVariables.*; import static pl.skidam.automodpack_core.config.ConfigTools.GSON; public class DataC2SPacket { - public static CompletableFuture receive(MinecraftClient minecraftClient, ClientLoginNetworkHandler handler, PacketByteBuf buf) { + public static CompletableFuture receive(Minecraft Minecraft, ClientHandshakePacketListenerImpl handler, FriendlyByteBuf buf) { try { - String serverResponse = buf.readString(Short.MAX_VALUE); + String serverResponse = buf.readUtf(Short.MAX_VALUE); DataPacket dataPacket = DataPacket.fromJson(serverResponse); String packetAddress = dataPacket.address; @@ -49,11 +50,11 @@ public static CompletableFuture receive(MinecraftClient minecraft ModPackets.setOriginalServerAddress(null); // Reset for next server reconnection if (serverAddress == null) { LOGGER.error("Server address is null! Something gone very wrong! Please report this issue! https://github.com/Skidamek/AutoModpack/issues"); - return CompletableFuture.completedFuture(new PacketByteBuf(Unpooled.buffer())); + return CompletableFuture.completedFuture(new FriendlyByteBuf(Unpooled.buffer())); } // Get actual address of the server client have connected to and format it - InetSocketAddress connectedAddress = (InetSocketAddress) ((ClientLoginNetworkHandlerAccessor) handler).getConnection().getAddress(); + InetSocketAddress connectedAddress = (InetSocketAddress) ((ClientLoginNetworkHandlerAccessor) handler).getConnection().getRemoteAddress(); String effectiveHost; int effectivePort; @@ -76,7 +77,7 @@ public static CompletableFuture receive(MinecraftClient minecraft LOGGER.info("Modpack address: {}:{} Requires to follow magic protocol: {}", modpackAddress.getHostString(), modpackAddress.getPort(), requiresMagic); Boolean needsDisconnecting = null; - PacketByteBuf response = new PacketByteBuf(Unpooled.buffer()); + FriendlyByteBuf response = new FriendlyByteBuf(Unpooled.buffer()); Path modpackDir = ModpackUtils.getModpackPath(modpackAddress, modpackName); Jsons.ModpackAddresses modpackAddresses = new Jsons.ModpackAddresses(modpackAddress, serverAddress, requiresMagic); @@ -115,18 +116,18 @@ public static CompletableFuture receive(MinecraftClient minecraft SecretsStore.saveClientSecret(clientConfig.selectedModpack, secret); } - response.writeString(String.valueOf(needsDisconnecting), Short.MAX_VALUE); + response.writeUtf(String.valueOf(needsDisconnecting), Short.MAX_VALUE); return CompletableFuture.completedFuture(response); } catch (Exception e) { LOGGER.error("Error while handling data packet", e); - PacketByteBuf response = new PacketByteBuf(Unpooled.buffer()); - response.writeString("null", Short.MAX_VALUE); - return CompletableFuture.completedFuture(new PacketByteBuf(Unpooled.buffer())); + FriendlyByteBuf response = new FriendlyByteBuf(Unpooled.buffer()); + response.writeUtf("null", Short.MAX_VALUE); + return CompletableFuture.completedFuture(new FriendlyByteBuf(Unpooled.buffer())); } } - private static void disconnectImmediately(ClientLoginNetworkHandler clientLoginNetworkHandler) { + private static void disconnectImmediately(ClientHandshakePacketListenerImpl clientLoginNetworkHandler) { ((ClientConnectionAccessor) ((ClientLoginNetworkHandlerAccessor) clientLoginNetworkHandler).getConnection()).getChannel().disconnect(); } } diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java index aed073df4..09cb92d52 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java @@ -1,12 +1,12 @@ package pl.skidam.automodpack.networking.packet; import com.mojang.authlib.GameProfile; -import net.minecraft.network.ClientConnection; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.s2c.login.LoginDisconnectS2CPacket; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.text.Text; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import pl.skidam.automodpack.networking.PacketSender; import pl.skidam.automodpack.networking.server.ServerLoginNetworking; import pl.skidam.automodpack.client.ui.versioned.VersionedText; @@ -16,7 +16,7 @@ public class DataS2CPacket { - public static void receive(MinecraftServer server, ServerLoginNetworkHandler handler, boolean understood, PacketByteBuf buf, ServerLoginNetworking.LoginSynchronizer loginSynchronizer, PacketSender sender) { + public static void receive(MinecraftServer server, ServerLoginPacketListenerImpl handler, boolean understood, FriendlyByteBuf buf, ServerLoginNetworking.LoginSynchronizer loginSynchronizer, PacketSender sender) { try { GameProfile profile = ((ServerLoginNetworkHandlerAccessor) handler).getGameProfile(); @@ -28,20 +28,20 @@ public static void receive(MinecraftServer server, ServerLoginNetworkHandler han return; } - String clientHasUpdate = buf.readString(Short.MAX_VALUE); + String clientHasUpdate = buf.readUtf(Short.MAX_VALUE); if ("true".equals(clientHasUpdate)) { // disconnect LOGGER.warn("{} has not installed modpack. Certificate fingerprint: {}", profile.getName(), hostServer.getCertificateFingerprint()); - Text reason = VersionedText.literal("[AutoModpack] Install/Update modpack to join"); - ClientConnection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); - connection.send(new LoginDisconnectS2CPacket(reason)); + Component reason = VersionedText.literal("[AutoModpack] Install/Update modpack to join"); + Connection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); + connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); } else if ("false".equals(clientHasUpdate)) { LOGGER.info("{} has installed whole modpack", profile.getName()); } else { - Text reason = VersionedText.literal("[AutoModpack] Host server error. Please contact server administrator to check the server logs!"); - ClientConnection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); - connection.send(new LoginDisconnectS2CPacket(reason)); + Component reason = VersionedText.literal("[AutoModpack] Host server error. Please contact server administrator to check the server logs!"); + Connection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); + connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); LOGGER.error("Host server error. AutoModpack host server is down or server is not configured correctly"); diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java index d4ee10d20..99b8eb51f 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java @@ -1,9 +1,6 @@ package pl.skidam.automodpack.networking.packet; import io.netty.buffer.Unpooled; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.PacketByteBuf; import pl.skidam.automodpack.mixin.core.ClientConnectionAccessor; import pl.skidam.automodpack.mixin.core.ClientLoginNetworkHandlerAccessor; import pl.skidam.automodpack.networking.content.HandshakePacket; @@ -12,22 +9,25 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.FriendlyByteBuf; import static pl.skidam.automodpack_core.GlobalVariables.*; public class HandshakeC2SPacket { - public static CompletableFuture receive(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf) { + public static CompletableFuture receive(Minecraft client, ClientHandshakePacketListenerImpl handler, FriendlyByteBuf buf) { try { - String serverResponse = buf.readString(Short.MAX_VALUE); + String serverResponse = buf.readUtf(Short.MAX_VALUE); HandshakePacket serverHandshakePacket = HandshakePacket.fromJson(serverResponse); String loader = LOADER_MANAGER.getPlatformType().toString().toLowerCase(); - PacketByteBuf outBuf = new PacketByteBuf(Unpooled.buffer()); + FriendlyByteBuf outBuf = new FriendlyByteBuf(Unpooled.buffer()); HandshakePacket clientHandshakePacket = new HandshakePacket(List.of(loader), AM_VERSION, MC_VERSION); - outBuf.writeString(clientHandshakePacket.toJson(), Short.MAX_VALUE); + outBuf.writeUtf(clientHandshakePacket.toJson(), Short.MAX_VALUE); if (serverHandshakePacket.equals(clientHandshakePacket) || (serverHandshakePacket.loaders.contains(loader) && serverHandshakePacket.amVersion.equals(AM_VERSION))) { LOGGER.info("Versions match " + serverHandshakePacket.amVersion); @@ -40,11 +40,11 @@ public static CompletableFuture receive(MinecraftClient client, C return CompletableFuture.completedFuture(outBuf); } catch (Exception e) { LOGGER.error("Error while handling HandshakeC2SPacket", e); - return CompletableFuture.completedFuture(new PacketByteBuf(Unpooled.buffer())); + return CompletableFuture.completedFuture(new FriendlyByteBuf(Unpooled.buffer())); } } - private static void updateMod(ClientLoginNetworkHandler handler, String serverAMVersion, String serverMCVersion) { + private static void updateMod(ClientHandshakePacketListenerImpl handler, String serverAMVersion, String serverMCVersion) { if (!serverMCVersion.equals(MC_VERSION)) { return; } diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java index 8b6c92df6..6a0c1f26b 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java @@ -2,12 +2,12 @@ import com.mojang.authlib.GameProfile; import io.netty.buffer.Unpooled; -import net.minecraft.network.ClientConnection; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.s2c.login.LoginDisconnectS2CPacket; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; import net.minecraft.server.*; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.text.Text; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import pl.skidam.automodpack.client.ui.versioned.VersionedText; import pl.skidam.automodpack.init.Common; import pl.skidam.automodpack.mixin.core.ServerLoginNetworkHandlerAccessor; @@ -27,8 +27,8 @@ public class HandshakeS2CPacket { - public static void receive(MinecraftServer server, ServerLoginNetworkHandler handler, boolean understood, PacketByteBuf buf, ServerLoginNetworking.LoginSynchronizer loginSynchronizer, PacketSender sender) { - ClientConnection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); + public static void receive(MinecraftServer server, ServerLoginPacketListenerImpl handler, boolean understood, FriendlyByteBuf buf, ServerLoginNetworking.LoginSynchronizer loginSynchronizer, PacketSender sender) { + Connection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); GameProfile profile = ((ServerLoginNetworkHandlerAccessor) handler).getGameProfile(); String playerName = profile.getName(); @@ -51,7 +51,7 @@ public static void receive(MinecraftServer server, ServerLoginNetworkHandler han // LOGGER.warn("Connection is not encrypted for player: {}", playerName); // } - if (!GameHelpers.isPlayerAuthorized(connection.getAddress(), profile)) { + if (!GameHelpers.isPlayerAuthorized(connection.getRemoteAddress(), profile)) { return; } @@ -59,22 +59,22 @@ public static void receive(MinecraftServer server, ServerLoginNetworkHandler han Common.players.put(playerName, false); LOGGER.warn("{} has not installed AutoModpack.", playerName); if (serverConfig.requireAutoModpackOnClient) { - Text reason = VersionedText.literal("AutoModpack mod for " + LOADER_MANAGER.getPlatformType().toString().toLowerCase() + " modloader is required to play on this server!"); - connection.send(new LoginDisconnectS2CPacket(reason)); + Component reason = VersionedText.literal("AutoModpack mod for " + LOADER_MANAGER.getPlatformType().toString().toLowerCase() + " modloader is required to play on this server!"); + connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); } } else { Common.players.put(playerName, true); GameProfile finalProfile = profile; - loginSynchronizer.waitFor(server.submit(() -> handleHandshake(connection, finalProfile, server.getServerPort(), buf, sender))); + loginSynchronizer.waitFor(server.submit(() -> handleHandshake(connection, finalProfile, server.getPort(), buf, sender))); } } - public static void handleHandshake(ClientConnection connection, GameProfile profile, int minecraftServerPort, PacketByteBuf buf, PacketSender packetSender) { + public static void handleHandshake(Connection connection, GameProfile profile, int minecraftServerPort, FriendlyByteBuf buf, PacketSender packetSender) { try { LOGGER.info("{} has installed AutoModpack.", profile.getName()); - String clientResponse = buf.readString(Short.MAX_VALUE); + String clientResponse = buf.readUtf(Short.MAX_VALUE); HandshakePacket clientHandshakePacket = HandshakePacket.fromJson(clientResponse); boolean isAcceptedLoader = false; @@ -86,11 +86,11 @@ public static void handleHandshake(ClientConnection connection, GameProfile prof } if (!isAcceptedLoader || !clientHandshakePacket.amVersion.equals(AM_VERSION)) { - Text reason = VersionedText.literal("AutoModpack version mismatch! Install " + AM_VERSION + " version of AutoModpack mod for " + LOADER_MANAGER.getPlatformType().toString().toLowerCase() + " to play on this server!"); + Component reason = VersionedText.literal("AutoModpack version mismatch! Install " + AM_VERSION + " version of AutoModpack mod for " + LOADER_MANAGER.getPlatformType().toString().toLowerCase() + " to play on this server!"); if (isClientVersionHigher(clientHandshakePacket.amVersion)) { reason = VersionedText.literal("You are using a more recent version of AutoModpack than the server. Please contact the server administrator to update the AutoModpack mod."); } - connection.send(new LoginDisconnectS2CPacket(reason)); + connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); return; } @@ -101,8 +101,8 @@ public static void handleHandshake(ClientConnection connection, GameProfile prof } if (modpackExecutor.isGenerating()) { - Text reason = VersionedText.literal("AutoModapck is generating modpack. Please wait a moment and try again."); - connection.send(new LoginDisconnectS2CPacket(reason)); + Component reason = VersionedText.literal("AutoModapck is generating modpack. Please wait a moment and try again."); + connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); return; } @@ -121,8 +121,8 @@ public static void handleHandshake(ClientConnection connection, GameProfile prof DataPacket dataPacket = new DataPacket(addressToSend, portToSend, serverConfig.modpackName, secret, serverConfig.requireAutoModpackOnClient, requiresMagic); String packetContentJson = dataPacket.toJson(); - PacketByteBuf outBuf = new PacketByteBuf(Unpooled.buffer()); - outBuf.writeString(packetContentJson, Short.MAX_VALUE); + FriendlyByteBuf outBuf = new FriendlyByteBuf(Unpooled.buffer()); + outBuf.writeUtf(packetContentJson, Short.MAX_VALUE); packetSender.sendPacket(DATA, outBuf); } catch (Exception e) { LOGGER.error("Error while handling handshake for {}", profile.getName(), e); diff --git a/src/main/java/pl/skidam/automodpack/networking/server/LoginRequestPayload.java b/src/main/java/pl/skidam/automodpack/networking/server/LoginRequestPayload.java index 83d0b9d25..7bd3f928b 100644 --- a/src/main/java/pl/skidam/automodpack/networking/server/LoginRequestPayload.java +++ b/src/main/java/pl/skidam/automodpack/networking/server/LoginRequestPayload.java @@ -1,17 +1,16 @@ package pl.skidam.automodpack.networking.server; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.util.Identifier; - +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; /*? if <1.20.2 {*/ -/*public record LoginRequestPayload(Identifier id, PacketByteBuf data) { } +/*public record LoginRequestPayload(ResourceLocation id, FriendlyByteBuf data) { } *//*?} else {*/ -import net.minecraft.network.packet.s2c.login.LoginQueryRequestPayload; +import net.minecraft.network.protocol.login.custom.CustomQueryPayload; import pl.skidam.automodpack.networking.PayloadHelper; -public record LoginRequestPayload(Identifier id, PacketByteBuf data) implements LoginQueryRequestPayload { +public record LoginRequestPayload(ResourceLocation id, FriendlyByteBuf data) implements CustomQueryPayload { @Override - public void write(PacketByteBuf buf) { + public void write(FriendlyByteBuf buf) { PayloadHelper.write(buf, data()); } } diff --git a/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworkAddon.java b/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworkAddon.java index 58ce10d1a..9b9cfb8e1 100644 --- a/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworkAddon.java +++ b/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworkAddon.java @@ -1,13 +1,13 @@ package pl.skidam.automodpack.networking.server; import io.netty.buffer.Unpooled; -import net.minecraft.network.ClientConnection; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.util.Identifier; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import org.jetbrains.annotations.Nullable; import pl.skidam.automodpack.mixin.core.ServerLoginNetworkHandlerAccessor; import pl.skidam.automodpack.networking.LoginNetworkingIDs; @@ -25,14 +25,14 @@ // credits to fabric api public class ServerLoginNetworkAddon implements PacketSender { - private final ServerLoginNetworkHandler handler; - private final ClientConnection connection; + private final ServerLoginPacketListenerImpl handler; + private final Connection connection; private final MinecraftServer server; private final Collection> synchronizers = new ConcurrentLinkedQueue<>(); - public final Map channels = new ConcurrentHashMap<>(); + public final Map channels = new ConcurrentHashMap<>(); private boolean firstTick = true; - public ServerLoginNetworkAddon(ServerLoginNetworkHandler serverLoginNetworkHandler) { + public ServerLoginNetworkAddon(ServerLoginPacketListenerImpl serverLoginNetworkHandler) { this.handler = serverLoginNetworkHandler; this.connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); this.server = ((ServerLoginNetworkHandlerAccessor) handler).getServer(); @@ -70,15 +70,15 @@ public boolean queryTick() { * @param packet the packet to handle * @return true if the packet was handled */ - public boolean handle(LoginQueryResponseC2SPacket packet) { + public boolean handle(ServerboundCustomQueryAnswerPacket packet) { LoginQueryParser loginQuery = new LoginQueryParser(packet); if (loginQuery.success) return handle(loginQuery.queryId, loginQuery.buf); return false; } - private boolean handle(int queryId, @Nullable PacketByteBuf originalBuf) { + private boolean handle(int queryId, @Nullable FriendlyByteBuf originalBuf) { - Identifier channel = this.channels.remove(queryId); + ResourceLocation channel = this.channels.remove(queryId); if (channel == null) { // Not an AutoModpack packet. @@ -92,7 +92,7 @@ private boolean handle(int queryId, @Nullable PacketByteBuf originalBuf) { return false; } - PacketByteBuf buf = understood ? new PacketByteBuf(originalBuf.slice()) : new PacketByteBuf(Unpooled.EMPTY_BUFFER); + FriendlyByteBuf buf = understood ? new FriendlyByteBuf(originalBuf.slice()) : new FriendlyByteBuf(Unpooled.EMPTY_BUFFER); try { handler.receive(this.server, this.handler, understood, buf, this.synchronizers::add, this); @@ -105,18 +105,18 @@ private boolean handle(int queryId, @Nullable PacketByteBuf originalBuf) { } @Override - public LoginQueryRequestS2CPacket createPacket(Identifier channelName, PacketByteBuf buf) { + public ClientboundCustomQueryPacket createPacket(ResourceLocation channelName, FriendlyByteBuf buf) { Integer queryId = LoginNetworkingIDs.getByKey(channelName); if (queryId == null) { return null; } - return new LoginQueryRequestS2CPacket(queryId, /*? if <1.20.2 {*/ /*channelName, buf *//*?} else {*/ new LoginRequestPayload(channelName, buf) /*?}*/); + return new ClientboundCustomQueryPacket(queryId, /*? if <1.20.2 {*/ /*channelName, buf *//*?} else {*/ new LoginRequestPayload(channelName, buf) /*?}*/); } @Override - public void sendPacket(LoginQueryRequestS2CPacket packet) { + public void sendPacket(ClientboundCustomQueryPacket packet) { Objects.requireNonNull(packet, "Connection cannot be null"); LoginQueryParser loginQuery = new LoginQueryParser(packet); diff --git a/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworking.java b/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworking.java index 3a92a1086..2d6f41127 100644 --- a/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworking.java +++ b/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworking.java @@ -1,9 +1,9 @@ package pl.skidam.automodpack.networking.server; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.util.Identifier; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import pl.skidam.automodpack.networking.PacketSender; import java.util.HashMap; @@ -14,7 +14,7 @@ // credits to fabric api public class ServerLoginNetworking { - private static final Map handlers = new HashMap<>(); + private static final Map handlers = new HashMap<>(); /** * Registers a handler to a query response channel. @@ -23,14 +23,14 @@ public class ServerLoginNetworking { * @param channelName the id of the channel * @param handler the handler */ - public static void registerGlobalReceiver(Identifier channelName, LoginQueryResponseHandler handler) { + public static void registerGlobalReceiver(ResourceLocation channelName, LoginQueryResponseHandler handler) { Objects.requireNonNull(channelName, "Channel name cannot be null"); Objects.requireNonNull(handler, "Channel handler cannot be null"); handlers.put(channelName, handler); } - public static LoginQueryResponseHandler getHandler(Identifier channelName) { + public static LoginQueryResponseHandler getHandler(ResourceLocation channelName) { return handlers.get(channelName); } @@ -40,7 +40,7 @@ public interface LoginQueryResponseHandler { * Handles an incoming query response from a client. * *

This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}. - * Modification to the game should be {@linkplain net.minecraft.util.thread.ThreadExecutor#submit(Runnable) scheduled} using the provided Minecraft client instance. + * Modification to the game should be {@linkplain net.minecraft.util.thread.BlockableEventLoop#submit(Runnable) scheduled} using the provided Minecraft client instance. * *

Whether the client understood the query should be checked before reading from the payload of the packet. * @param server the server @@ -49,7 +49,7 @@ public interface LoginQueryResponseHandler { * @param buf the payload of the packet * @param synchronizer the synchronizer which may be used to delay log-in till a {@link Future} is completed. */ - void receive(MinecraftServer server, ServerLoginNetworkHandler handler, boolean understood, PacketByteBuf buf, LoginSynchronizer synchronizer, PacketSender responseSender); + void receive(MinecraftServer server, ServerLoginPacketListenerImpl handler, boolean understood, FriendlyByteBuf buf, LoginSynchronizer synchronizer, PacketSender responseSender); } @@ -92,7 +92,7 @@ public interface LoginSynchronizer { * })); * }); * } - * Usually it is enough to pass the return value for {@link net.minecraft.util.thread.ThreadExecutor#submit(Runnable)} for {@code future}.

+ * Usually it is enough to pass the return value for {@link net.minecraft.util.thread.BlockableEventLoop#submit(Runnable)} for {@code future}.

* * @param future the future that must be done before the player can log in */ diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 000000000..e67148695 --- /dev/null +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1 @@ +public net.minecraft.server.network.ServerLoginPacketListenerImpl$State \ No newline at end of file diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 45ab91744..411dc535c 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -8,15 +8,13 @@ version = "${version}" displayName = "${name}" displayURL = "https://modrinth.com/mod/automodpack" authors = "Skidam" -description = ''' - -''' +description = "${description}" logoFile = "icon.png" [[dependencies."${id}"]] modId = "minecraft" mandatory = true -versionRange = "${minecraft_dependency}" +versionRange = "${minecraft}" ordering = "NONE" side = "BOTH" diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index 45ab91744..411dc535c 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -8,15 +8,13 @@ version = "${version}" displayName = "${name}" displayURL = "https://modrinth.com/mod/automodpack" authors = "Skidam" -description = ''' - -''' +description = "${description}" logoFile = "icon.png" [[dependencies."${id}"]] modId = "minecraft" mandatory = true -versionRange = "${minecraft_dependency}" +versionRange = "${minecraft}" ordering = "NONE" side = "BOTH" diff --git a/src/main/resources/automodpack.accesswidener b/src/main/resources/automodpack.accesswidener index ba19d5e8e..020303a01 100644 --- a/src/main/resources/automodpack.accesswidener +++ b/src/main/resources/automodpack.accesswidener @@ -1,2 +1,2 @@ accessWidener v1 named -accessible class net/minecraft/server/network/ServerLoginNetworkHandler$State \ No newline at end of file +accessible class net/minecraft/server/network/ServerLoginPacketListenerImpl$State \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index e73143b53..690455eba 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -25,7 +25,7 @@ ], "accessWidener" : "automodpack.accesswidener", "depends": { - "minecraft": "${minecraft_dependency}", + "minecraft": "${minecraft}", "mixinextras": ">=0.3.5" } } diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 97cbbc0ee..4d24c0228 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -1,12 +1,11 @@ -import java.io.FileInputStream -import java.io.FileOutputStream -import java.util.concurrent.CompletableFuture -import java.util.zip.ZipEntry -import java.util.zip.ZipInputStream -import java.util.zip.ZipOutputStream - plugins { id("dev.kikugie.stonecutter") + kotlin("jvm") version "2.1.21" apply false + id("co.uzzu.dotenv.gradle") version "4.0.0" + id("fabric-loom") version "1.10-SNAPSHOT" apply false + id("net.neoforged.moddev") version "2.0.95" apply false + id("me.modmuss50.mod-publish-plugin") version "0.8.+" apply false + id("com.gradleup.shadow") version "8.3.6" apply false id("org.moddedmc.wiki.toolkit") version "0.2.7" } @@ -16,263 +15,22 @@ wiki { } } -stonecutter active "1.21.1-neoforge" /* [SC] DO NOT EDIT */ - -//stonecutter registerChiseled tasks.register("chiseledBuild", stonecutter.chiseled) { -// group = "project" -// ofTask("build") -// finalizedBy("mergeJars") -//} +stonecutter active "1.21.6-neoforge" /* [SC] DO NOT EDIT */ stonecutter.parameters { - constants { - val loader: String = metadata.project.substringAfter('-') - match(loader, "fabric", "forge", "neoforge") - } -} - -// Non stonecutter stuff -val mergedDir = File("${rootProject.projectDir}/merged") - -class MinecraftVersionData(private val name: String) { - fun greaterThan(other: String) : Boolean { - return stonecutter.eval(name, ">" + other.lowercase()) - } - - fun lessThan(other: String) : Boolean { - return stonecutter.eval(name, "<" + other.lowercase()) - } - - fun greaterOrEqual(other: String) : Boolean { - return stonecutter.eval(name, ">=" + other.lowercase()) - } - - fun lessOrEqual(other: String) : Boolean { - return stonecutter.eval(name, "<=" + other.lowercase()) - } - - override fun equals(other: Any?) : Boolean { - return name == other - } - - override fun toString(): String { - return name - } - - override fun hashCode(): Int { - return name.hashCode() - } -} - -val coreModules = getProperty("core_modules")!!.split(',').map { it.trim() } - -fun getProperty(key: String): String? { - return project.findProperty(key) as? String -} - -// TODO find better way to do it -// If you get Array Exception, run "clean" task -tasks.register("mergeJars") { - coreModules.forEach { module -> - dependsOn(":loader-$module:build") - } - - doLast { - mergedDir.mkdirs() - val jarsToMerge = File("$rootDir/versions").listFiles() - ?.flatMap { - File("$it/build/libs").listFiles() - ?.filter { file -> file.isFile && !file.name.endsWith("-sources.jar") && file.name.endsWith(".jar") } - ?: emptyList() - } - ?: emptyList() - - val tasks = mutableListOf>() - val time = System.currentTimeMillis() - val size = jarsToMerge.size - var current = 0 - - for (jarToMerge in jarsToMerge) { - val task = CompletableFuture.runAsync { - val minecraftVersionStr = jarToMerge.name.substringAfterLast("-mc").substringBefore("-") - val minecraftVersion = MinecraftVersionData(minecraftVersionStr) - - var loaderModule = "" - if (jarToMerge.name.contains("fabric")) { - loaderModule = "fabric/core" - } else if (jarToMerge.name.contains("neoforge")) { - loaderModule = if (minecraftVersion.greaterOrEqual("1.20.6")) { - "neoforge/fml4" - } else { - "neoforge/fml2" - } - } else if (jarToMerge.name.contains("forge")) { - loaderModule = if (minecraftVersion.lessOrEqual("1.18.2")) { - "forge/fml40" - } else { - "forge/fml47" - } - } - - val loaderFile = File("${rootProject.projectDir}/loader/$loaderModule/build/libs").listFiles() - ?.single { it.isFile && !it.name.endsWith("-sources.jar") && it.name.endsWith(".jar") } ?: return@runAsync - val finalJar = File("$mergedDir/${jarToMerge.name}") - - loaderFile.copyTo(finalJar, overwrite = true) - appendFileToZip(finalJar, jarToMerge, "automodpack-mod.jar") - - println("${++current}/$size - Merged: ${jarToMerge.name} into: ${finalJar.name} from: ${loaderFile.name}") - } - - tasks.add(task) - } - - tasks.forEach { it.join() } - - if (size == 0) { - error("No jars to merge!") - } else if (size != current) { - error("Not all jars were merged!") - } else { - println("All jars were merged! Took: ${System.currentTimeMillis() - time}ms") - } - } -} + constants.match(node.metadata.project.substringAfterLast('-'), "fabric", "neoforge", "forge") -fun appendFileToZip(zipFile: File, fileToAppend: File, entryName: String) { - val entries = ZipInputStream(FileInputStream(zipFile)).use { zipStream -> - generateSequence { zipStream.nextEntry } - .toList() - } - - val graph = mutableMapOf>() - - entries.forEach { entry -> - val children = entry.name.split("/") - var currentParent = "" - children.forEach { child -> - if (child.isNotEmpty()) { - currentParent = "$currentParent$child/" - val parent = graph.getOrPut(currentParent) { mutableListOf() } - parent.add(child) - } - } - } - -// graph.forEach { (parent, children) -> -// println("$parent -> $children") -// } - - val filteredGraph = filterEntries(entries, graph, zipFile) - - // Doing with temp file since for some reason just adding the file breaks the zip/jar file - val tempFile = File("$zipFile.temp") - tempFile.createNewFile() - - ZipOutputStream(FileOutputStream(tempFile)).use { zipStream -> - ZipInputStream(FileInputStream(zipFile)).use { existingZipStream -> - while (true) { - val entry = existingZipStream.nextEntry ?: break - if (filteredGraph.containsKey("${entry.name}/")) { - try { - val zipEntry = ZipEntry(entry.name) - zipStream.putNextEntry(zipEntry) - existingZipStream.copyTo(zipStream) - zipStream.closeEntry() - } catch (e: Exception) { - println("Error while copying entry: ${entry.name}") - } - } - } - } - - // Add the new entry - zipStream.putNextEntry(ZipEntry(entryName)) - FileInputStream(fileToAppend).use { fileInputStream -> - fileInputStream.copyTo(zipStream) + replacements { + string { + direction = eval(current.version, ">=1.20.2") + phase = "FIRST" + replace("ServerboundCustomQueryPacket", "ServerboundCustomQueryAnswerPacket") } - zipStream.closeEntry() - } - - // Replace the original zip file with the one containing the new entry - zipFile.delete() - tempFile.renameTo(zipFile) -} - -val dupesDir = File("${rootProject.projectDir}/dupes") -fun filterEntries(entries: List, graph: Map>, zipFile: File): Map> { - - val emptyDirs = mutableSetOf() - - // find empty directories - graph.forEach { (parent, children) -> - if (children.size == 0) { - emptyDirs.add(parent) + string { + direction = eval(current.version, ">=1.20.2") + phase = "FIRST" + replace(".SystemToastIds.", ".SystemToastId.") } } - - // dump duplicate entries - dupesDir.deleteRecursively() - dupesDir.mkdirs() - - dumpDupeEntries(zipFile, entries) - - // filter empty directories - // filter single duplicate files (leave only one) - val dupes = mutableSetOf() - - val filteredGraph = graph.filter { (parent, children) -> - if (emptyDirs.contains(parent)) { - println("Filtering empty dir: $parent -> $children") - return@filter false - } - - val count = graph.count { parent == it.key } - if (count > 1 && !dupes.add(children[0])) { - return@filter false - } - - return@filter true - } - - return filteredGraph -} - -fun dumpDupeEntries(zipFile: File, entries: List) { - // check for duplicates - val entryNames = mutableSetOf() - entries.forEach { duplicate -> - if (!entryNames.add(duplicate.name)) { - println("Duplicate entry: $duplicate") - - // write the entry to the file - ZipInputStream(FileInputStream(zipFile)).use { zipStream -> - var i = 0 - generateSequence { zipStream.nextEntry } - .filter { it.name == duplicate.name } - .forEach { _ -> - i++ - val dupeFile = File("$dupesDir/$i-$duplicate.dupe") - println("Extracting to: $dupeFile") - dupeFile.parentFile.mkdirs() - dupeFile.createNewFile() - FileOutputStream(dupeFile).use { fileOutputStream -> - zipStream.copyTo(fileOutputStream) - } - } - } - } - } -} - - -tasks.register("clean") { - dependsOn("cleanMerged") -} - -tasks.register("cleanMerged") { - doLast { - mergedDir.deleteRecursively() - } -} +} \ No newline at end of file diff --git a/versions/1.18.2-fabric/gradle.properties b/versions/1.18.2-fabric/gradle.properties index ceb436883..41fd22ab9 100644 --- a/versions/1.18.2-fabric/gradle.properties +++ b/versions/1.18.2-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.18.2 -yarn_mappings=1.18.2+build.4 -loom.platform=fabric -minecraft_dependency=1.18.x -fabric_version=0.76.0+1.18.2 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.18\n1.18.1\n1.18.2 \ No newline at end of file +deps.minecraft=1.18.2 +deps.parchment=1.18.2:2022.11.06 +deps.fabric-api=0.76.0+1.18.2 +meta.minecraft=1.18.x +publish.versions=1.18\n1.18.1\n1.18.2 \ No newline at end of file diff --git a/versions/1.18.2-forge/gradle.properties b/versions/1.18.2-forge/gradle.properties index 3d10c6025..d8bc61d89 100644 --- a/versions/1.18.2-forge/gradle.properties +++ b/versions/1.18.2-forge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.18.2 -yarn_mappings=1.18.2+build.4 -loom.platform=forge -minecraft_dependency=1.18.x -loader_forge=40.2.10 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.18\n1.18.1\n1.18.2 +deps.minecraft=1.18.2 +deps.parchment=1.18.2:2022.11.06 +deps.forge=1.18.2-40.2.10 +meta.minecraft=1.18.x +publish.versions=1.18\n1.18.1\n1.18.2 diff --git a/versions/1.19.2-fabric/gradle.properties b/versions/1.19.2-fabric/gradle.properties index 09e1b3394..dcb564715 100644 --- a/versions/1.19.2-fabric/gradle.properties +++ b/versions/1.19.2-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.19.2 -yarn_mappings=1.19.2+build.28 -loom.platform=fabric -minecraft_dependency=>=1.19 <=1.19.2 -fabric_version=0.76.1+1.19.2 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file +deps.minecraft=1.19.2 +deps.fabric-api=0.76.1+1.19.2 +deps.parchment=1.19.2:2022.11.27 +meta.minecraft=>=1.19 <=1.19.2 +publish.versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file diff --git a/versions/1.19.2-forge/gradle.properties b/versions/1.19.2-forge/gradle.properties index c0a58b92a..65887912f 100644 --- a/versions/1.19.2-forge/gradle.properties +++ b/versions/1.19.2-forge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.19.2 -yarn_mappings=1.19.2+build.28 -loom.platform=forge -minecraft_dependency=>=1.19 <=1.19.2 -loader_forge=43.3.0 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file +deps.minecraft=1.19.2 +deps.forge=1.19.2-43.3.0 +deps.parchment=1.19.2:2022.11.27 +meta.minecraft=>=1.19 <=1.19.2 +publish.versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file diff --git a/versions/1.19.4-fabric/gradle.properties b/versions/1.19.4-fabric/gradle.properties index 2f06d41f4..67d229280 100644 --- a/versions/1.19.4-fabric/gradle.properties +++ b/versions/1.19.4-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.19.4 -yarn_mappings=1.19.4+build.2 -loom.platform=fabric -minecraft_dependency=1.19.4 -fabric_version=0.87.2+1.19.4 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.19.4 \ No newline at end of file +deps.minecraft=1.19.4 +deps.fabric-api=0.87.2+1.19.4 +deps.parchment=1.19.4:2023.06.26 +meta.minecraft=1.19.4 +publish.versions=1.19.4 \ No newline at end of file diff --git a/versions/1.19.4-forge/gradle.properties b/versions/1.19.4-forge/gradle.properties index e579ae507..0e162dbe2 100644 --- a/versions/1.19.4-forge/gradle.properties +++ b/versions/1.19.4-forge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.19.4 -yarn_mappings=1.19.4+build.2 -loom.platform=forge -minecraft_dependency=1.19.4 -loader_forge=45.3.0 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.19.4 \ No newline at end of file +deps.minecraft=1.19.4 +deps.forge=1.19.4-45.3.0 +deps.parchment=1.19.4:2023.06.26 +meta.minecraft=1.19.4 +publish.versions=1.19.4 \ No newline at end of file diff --git a/versions/1.20.1-fabric/gradle.properties b/versions/1.20.1-fabric/gradle.properties index be0dcb541..4bfbe69c9 100644 --- a/versions/1.20.1-fabric/gradle.properties +++ b/versions/1.20.1-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=fabric -minecraft_dependency=>=1.20 <=1.20.1 -fabric_version=0.92.6+1.20.1 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20\n1.20.1 \ No newline at end of file +deps.minecraft=1.20.1 +deps.fabric-api=0.92.6+1.20.1 +deps.parchment=1.20.1:2023.09.03 +meta.minecraft=>=1.20 <=1.20.1 +publish.versions=1.20\n1.20.1 \ No newline at end of file diff --git a/versions/1.20.1-forge/gradle.properties b/versions/1.20.1-forge/gradle.properties index ddeefc921..44605f8d0 100644 --- a/versions/1.20.1-forge/gradle.properties +++ b/versions/1.20.1-forge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=forge -minecraft_dependency=>=1.20 <=1.20.1 -loader_forge=47.3.0 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20\n1.20.1 +deps.minecraft=1.20.1 +deps.forge=1.20.1-47.3.0 +deps.parchment=1.20.1:2023.09.03 +meta.minecraft=>=1.20 <=1.20.1 +publish.versions=1.20\n1.20.1 diff --git a/versions/1.20.4-fabric/gradle.properties b/versions/1.20.4-fabric/gradle.properties index fdb9ada15..f3926d995 100644 --- a/versions/1.20.4-fabric/gradle.properties +++ b/versions/1.20.4-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.4 -yarn_mappings=1.20.4+build.3 -loom.platform=fabric -minecraft_dependency=1.20.4 -fabric_version=0.97.2+1.20.4 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20.4 \ No newline at end of file +deps.minecraft=1.20.4 +deps.fabric-api=0.97.3+1.20.4 +deps.parchment=1.20.4:2024.04.14 +meta.minecraft=1.20.4 +publish.versions=1.20.4 \ No newline at end of file diff --git a/versions/1.20.4-neoforge/gradle.properties b/versions/1.20.4-neoforge/gradle.properties index bc596fdbb..edd185e52 100644 --- a/versions/1.20.4-neoforge/gradle.properties +++ b/versions/1.20.4-neoforge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.4 -yarn_mappings=1.20.4+build.3 -loom.platform=neoforge -minecraft_dependency=1.20.4 -loader_neoforge=20.4.237 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20.4 \ No newline at end of file +deps.minecraft=1.20.4 +deps.neoforge=20.4.248 +deps.parchment=1.20.4:2024.04.14 +meta.minecraft=1.20.4 +publish.versions=1.20.4 \ No newline at end of file diff --git a/versions/1.20.6-fabric/gradle.properties b/versions/1.20.6-fabric/gradle.properties index 0cf8f8e49..c235674f0 100644 --- a/versions/1.20.6-fabric/gradle.properties +++ b/versions/1.20.6-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.6 -yarn_mappings=1.20.6+build.3 -loom.platform=fabric -minecraft_dependency=1.20.6 -fabric_version=0.100.8+1.20.6 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20.6 \ No newline at end of file +deps.minecraft=1.20.6 +deps.fabric-api=0.100.8+1.20.6 +deps.parchment=1.20.6:2024.06.16 +meta.minecraft=1.20.6 +publish.versions=1.20.6 \ No newline at end of file diff --git a/versions/1.20.6-neoforge/gradle.properties b/versions/1.20.6-neoforge/gradle.properties index 5a5586036..f033883a0 100644 --- a/versions/1.20.6-neoforge/gradle.properties +++ b/versions/1.20.6-neoforge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.6 -yarn_mappings=1.20.6+build.3 -loom.platform=neoforge -minecraft_dependency=1.20.6 -loader_neoforge=20.6.117 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20.6 +deps.minecraft=1.20.6 +deps.neoforge=20.6.135 +deps.parchment=1.20.6:2024.06.16 +meta.minecraft=1.20.6 +publish.versions=1.20.6 diff --git a/versions/1.21.1-fabric/gradle.properties b/versions/1.21.1-fabric/gradle.properties index d76de054b..d372d5a81 100644 --- a/versions/1.21.1-fabric/gradle.properties +++ b/versions/1.21.1-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21.1 -yarn_mappings=1.21.1+build.3 -loom.platform=fabric -minecraft_dependency=>=1.21 <=1.21.1 -fabric_version=0.116.1+1.21.1 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21\n1.21.1 \ No newline at end of file +deps.minecraft=1.21.1 +deps.fabric-api=0.116.3+1.21.1 +deps.parchment=1.21.1:2024.11.17 +meta.minecraft=>=1.21 <=1.21.1 +publish.versions=1.21\n1.21.1 \ No newline at end of file diff --git a/versions/1.21.1-neoforge/gradle.properties b/versions/1.21.1-neoforge/gradle.properties index e7ffd7574..325c14d19 100644 --- a/versions/1.21.1-neoforge/gradle.properties +++ b/versions/1.21.1-neoforge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21 -yarn_mappings=1.21.1+build.3 -loom.platform=neoforge -minecraft_dependency=>=1.21 <=1.21.1 -loader_neoforge=21.1.169 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21\n1.21.1 \ No newline at end of file +deps.minecraft=1.21.1 +deps.neoforge=21.1.169 +deps.parchment=1.21.1:2024.11.17 +meta.minecraft=>=1.21 <=1.21.1 +publish.versions=1.21\n1.21.1 \ No newline at end of file diff --git a/versions/1.21.3-fabric/gradle.properties b/versions/1.21.3-fabric/gradle.properties index 8c1754d57..004917c0f 100644 --- a/versions/1.21.3-fabric/gradle.properties +++ b/versions/1.21.3-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21.3 -yarn_mappings=1.21.3+build.2 -loom.platform=fabric -minecraft_dependency=>=1.21.2 <=1.21.3 -fabric_version=0.114.1+1.21.3 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21.2\n1.21.3 \ No newline at end of file +deps.minecraft=1.21.3 +deps.fabric-api=0.114.1+1.21.3 +deps.parchment=1.21.3:2024.12.07 +meta.minecraft=>=1.21.2 <=1.21.3 +publish.versions=1.21.2\n1.21.3 \ No newline at end of file diff --git a/versions/1.21.3-neoforge/gradle.properties b/versions/1.21.3-neoforge/gradle.properties index cca94e4f2..afcfcb82a 100644 --- a/versions/1.21.3-neoforge/gradle.properties +++ b/versions/1.21.3-neoforge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21.3 -yarn_mappings=1.21.3+build.2 -loom.platform=neoforge -minecraft_dependency=>=1.21.2 <=1.21.3 -loader_neoforge=21.3.76 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21.2\n1.21.3 \ No newline at end of file +deps.minecraft=1.21.3 +deps.neoforge=21.3.76 +deps.parchment=1.21.3:2024.12.07 +meta.minecraft=>=1.21.2 <=1.21.3 +publish.versions=1.21.2\n1.21.3 \ No newline at end of file diff --git a/versions/1.21.4-fabric/gradle.properties b/versions/1.21.4-fabric/gradle.properties index 90b002725..fc03fe49f 100644 --- a/versions/1.21.4-fabric/gradle.properties +++ b/versions/1.21.4-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21.4 -yarn_mappings=1.21.4+build.8 -loom.platform=fabric -minecraft_dependency=>=1.21.4 -fabric_version=0.119.3+1.21.4 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21.4 \ No newline at end of file +deps.minecraft=1.21.4 +deps.fabric-api=0.119.3+1.21.4 +deps.parchment=1.21.4:2025.03.23 +meta.minecraft=>=1.21.4 +publish.versions=1.21.4 \ No newline at end of file diff --git a/versions/1.21.4-neoforge/gradle.properties b/versions/1.21.4-neoforge/gradle.properties index 2f7877b87..0643e524f 100644 --- a/versions/1.21.4-neoforge/gradle.properties +++ b/versions/1.21.4-neoforge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21.4 -yarn_mappings=1.21.4+build.8 -loom.platform=neoforge -minecraft_dependency=>=1.21.4 -loader_neoforge=21.4.135 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21.4 \ No newline at end of file +deps.minecraft=1.21.4 +deps.neoforge=21.4.135 +deps.parchment=1.21.4:2025.03.23 +meta.minecraft=>=1.21.4 +publish.versions=1.21.4 \ No newline at end of file diff --git a/versions/1.21.5-fabric/gradle.properties b/versions/1.21.5-fabric/gradle.properties new file mode 100644 index 000000000..3d99816b6 --- /dev/null +++ b/versions/1.21.5-fabric/gradle.properties @@ -0,0 +1,5 @@ +deps.minecraft=1.21.5 +deps.fabric-api=0.127.1+1.21.5 +deps.parchment=1.21.5:2025.06.15 +meta.minecraft=1.21.5 +publish.versions=1.21.5 \ No newline at end of file diff --git a/versions/1.21.5-neoforge/gradle.properties b/versions/1.21.5-neoforge/gradle.properties new file mode 100644 index 000000000..1c138eb6b --- /dev/null +++ b/versions/1.21.5-neoforge/gradle.properties @@ -0,0 +1,5 @@ +deps.minecraft=1.21.5 +deps.neoforge=21.5.75 +deps.parchment=1.21.5:2025.06.15 +meta.minecraft=1.21.5 +publish.versions=1.21.5 \ No newline at end of file diff --git a/versions/1.21.6-fabric/gradle.properties b/versions/1.21.6-fabric/gradle.properties new file mode 100644 index 000000000..36ac30131 --- /dev/null +++ b/versions/1.21.6-fabric/gradle.properties @@ -0,0 +1,4 @@ +deps.minecraft=1.21.6 +deps.fabric-api=0.127.1+1.21.6 +meta.minecraft=>=1.21.6 +publish.versions=1.21.6 \ No newline at end of file diff --git a/versions/1.21.6-neoforge/gradle.properties b/versions/1.21.6-neoforge/gradle.properties new file mode 100644 index 000000000..fc32650c1 --- /dev/null +++ b/versions/1.21.6-neoforge/gradle.properties @@ -0,0 +1,4 @@ +deps.minecraft=1.21.6 +deps.neoforge=21.6.11-beta +meta.minecraft=>=1.21.6 +publish.versions=1.21.6 \ No newline at end of file From 295f765a4f0c76b615a1c196bb4d095c244be5ac Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 24 Jun 2025 14:03:49 +0200 Subject: [PATCH 54/86] bump to beta 38 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index fb5cd261f..a9f168925 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,6 +11,6 @@ deps.mixin-extras = 0.5.0-rc.2 mod.id = automodpack mod.name = AutoModpack -mod.version = 4.0.0-beta37 +mod.version = 4.0.0-beta38 mod.group = pl.skidam.automodpack mod.description = Enjoy a seamless modpack installation process and effortless updates with a user-friendly solution that simplifies management, making your gaming experience a breeze. From 2614739566e2cc5b19c55c7ec1a10368b73a434c Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 24 Jun 2025 14:04:47 +0200 Subject: [PATCH 55/86] prevent downgrades to lower versions than 4.0.0-beta38 --- .../automodpack_loader_core/SelfUpdater.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java index 9a961a71d..8697549bb 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java @@ -83,21 +83,32 @@ public static boolean update(Jsons.ModpackContentFields serverModpackContent) { continue; } - boolean currentBeta = AM_VERSION.contains("-beta"); - boolean remoteBeta = fileVersion.contains("-beta"); + boolean currentIsBeta = AM_VERSION.contains("-beta"); + boolean remoteIsBeta = fileVersion.contains("-beta"); String[] currentVersionSplit = AM_VERSION.split("-beta"); String[] remoteVersionSplit = fileVersion.split("-beta"); + int remoteBeta = -1; + if (remoteIsBeta) { + remoteBeta = Integer.parseInt(remoteVersionSplit[1]); + } + // Removes '-betaX' if exists // Removes '.' - dots - to then parse it as number String OUR_VERSION = currentVersionSplit[0].replace(".", ""); - String LATEST_VERSION = remoteVersionSplit[0].replace(".", ""); + String REMOTE_VERSION = remoteVersionSplit[0].replace(".", ""); + + // Don't allow downgrades pass 4.0.0-beta38 + if (Integer.parseInt(REMOTE_VERSION) < 400 || (remoteIsBeta && remoteBeta < 38)) { + message = "Can't downgrade AutoModpack to version: " + automodpack.fileVersion() + ". Use newer version!"; + continue; + } // Compare versions as numbers if (!gettingServerVersion) { try { - if (Integer.parseInt(OUR_VERSION) > Integer.parseInt(LATEST_VERSION)) { + if (Integer.parseInt(OUR_VERSION) > Integer.parseInt(REMOTE_VERSION)) { message = "You are using pre-released or beta version of AutoModpack: " + AM_VERSION + " latest stable version is: " + automodpack.fileVersion(); break; // Break, checked version is lower than installed, meaning that higher version doesn't exist. } @@ -105,19 +116,19 @@ public static boolean update(Jsons.ModpackContentFields serverModpackContent) { LOGGER.error("Failed to parse version numbers: " + e); } - if (currentBeta && remoteBeta) { + if (currentIsBeta && remoteIsBeta) { if (Integer.parseInt(currentVersionSplit[1]) > Integer.parseInt(remoteVersionSplit[1])) { message = "You are using pre-released or beta version of AutoModpack: " + AM_VERSION + " latest stable version is: " + automodpack.fileVersion(); break; // Break, checked version is lower than installed, meaning that higher version doesn't exist. } } - if (!currentBeta && remoteBeta) { + if (!currentIsBeta && remoteIsBeta) { message = "You are using stable version of AutoModpack: " + AM_VERSION + " latest pre-released or beta version is: " + automodpack.fileVersion(); continue; } - if (currentBeta && !remoteBeta) { + if (currentIsBeta && !remoteIsBeta) { message = "You are using pre-released or beta version of AutoModpack: " + AM_VERSION + " latest stable version is: " + automodpack.fileVersion(); continue; } From a0d4995de2f68e030cec57249a22711c05f22970 Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 24 Jun 2025 14:09:17 +0200 Subject: [PATCH 56/86] update fabric-api --- versions/1.21.5-fabric/gradle.properties | 2 +- versions/1.21.6-fabric/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/versions/1.21.5-fabric/gradle.properties b/versions/1.21.5-fabric/gradle.properties index 3d99816b6..1f42df586 100644 --- a/versions/1.21.5-fabric/gradle.properties +++ b/versions/1.21.5-fabric/gradle.properties @@ -1,5 +1,5 @@ deps.minecraft=1.21.5 -deps.fabric-api=0.127.1+1.21.5 +deps.fabric-api=0.128.0+1.21.5 deps.parchment=1.21.5:2025.06.15 meta.minecraft=1.21.5 publish.versions=1.21.5 \ No newline at end of file diff --git a/versions/1.21.6-fabric/gradle.properties b/versions/1.21.6-fabric/gradle.properties index 36ac30131..fe15fb1f1 100644 --- a/versions/1.21.6-fabric/gradle.properties +++ b/versions/1.21.6-fabric/gradle.properties @@ -1,4 +1,4 @@ deps.minecraft=1.21.6 -deps.fabric-api=0.127.1+1.21.6 +deps.fabric-api=0.128.0+1.21.6 meta.minecraft=>=1.21.6 publish.versions=1.21.6 \ No newline at end of file From 520ef392a069452a7c1853d3e53c9bb7c0f51648 Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 24 Jun 2025 14:48:09 +0200 Subject: [PATCH 57/86] run tests just on windows --- .github/workflows/tests.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d667148fa..05b235732 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,10 +4,7 @@ on: [workflow_call] jobs: tests: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [windows-latest, ubuntu-latest] + runs-on: windows-latest steps: - uses: actions/checkout@v4 - name: Set up JDK 21 From 07ee8e2b1cc37aa22a72247b176a521ba6eea53e Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 24 Jun 2025 17:54:25 +0200 Subject: [PATCH 58/86] idk --- buildSrc/src/main/kotlin/automodpack.common.gradle.kts | 2 +- src/main/resources/pack.mcmeta | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 src/main/resources/pack.mcmeta diff --git a/buildSrc/src/main/kotlin/automodpack.common.gradle.kts b/buildSrc/src/main/kotlin/automodpack.common.gradle.kts index e94d35697..63ddbd474 100644 --- a/buildSrc/src/main/kotlin/automodpack.common.gradle.kts +++ b/buildSrc/src/main/kotlin/automodpack.common.gradle.kts @@ -35,7 +35,7 @@ tasks.named("processResources") { this["description"] = prop("mod.description") } - filesMatching(listOf("fabric.mod.json", "META-INF/neoforge.mods.toml", "META-INF/mods.toml")) { + filesMatching(listOf("pack.mcmeta", "fabric.mod.json", "META-INF/neoforge.mods.toml", "META-INF/mods.toml")) { expand(props) } } diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta new file mode 100644 index 000000000..bc086de01 --- /dev/null +++ b/src/main/resources/pack.mcmeta @@ -0,0 +1,7 @@ +{ + "pack": { + "description": "${name} Resources", + "pack_format": 15, + "supported_formats": [1, 80] + } +} \ No newline at end of file From 89c06fa737597524d2643534976862f835843a62 Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 24 Jun 2025 17:54:37 +0200 Subject: [PATCH 59/86] refactor: improve downgrade prevention logic in SelfUpdater --- .../automodpack_loader_core/SelfUpdater.java | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java index 8697549bb..53ef25e9e 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java @@ -99,12 +99,6 @@ public static boolean update(Jsons.ModpackContentFields serverModpackContent) { String OUR_VERSION = currentVersionSplit[0].replace(".", ""); String REMOTE_VERSION = remoteVersionSplit[0].replace(".", ""); - // Don't allow downgrades pass 4.0.0-beta38 - if (Integer.parseInt(REMOTE_VERSION) < 400 || (remoteIsBeta && remoteBeta < 38)) { - message = "Can't downgrade AutoModpack to version: " + automodpack.fileVersion() + ". Use newer version!"; - continue; - } - // Compare versions as numbers if (!gettingServerVersion) { try { @@ -154,7 +148,30 @@ public static boolean update(Jsons.ModpackContentFields serverModpackContent) { return false; } + public static boolean validUpdate(ModrinthAPI automodpack) { + String[] remoteVersionSplit = automodpack.fileVersion().split("-beta"); + + int remoteBeta = -1; + boolean remoteIsBeta = false; + if (remoteVersionSplit.length > 1) { + remoteIsBeta = true; + remoteBeta = Integer.parseInt(remoteVersionSplit[1]); + } + + // Removes '-betaX' if exists + // Removes '.' - dots - to then parse it as number + String REMOTE_VERSION = remoteVersionSplit[0].replace(".", ""); + + // Don't allow downgrades pass 4.0.0-beta38 + return Integer.parseInt(REMOTE_VERSION) >= 400 && (!remoteIsBeta || remoteBeta >= 38); + } + public static void installModVersion(ModrinthAPI automodpack) { + if (!validUpdate(automodpack)) { + LOGGER.error("Can't downgrade AutoModpack to version: " + automodpack.fileVersion() + ". Use newer version!"); + return; + } + Path automodpackUpdateJar = automodpackDir.resolve(automodpack.fileName()); Path newAutomodpackJar; From 6aa15d3b94e8c9c5f252cf2aa871120143e9954c Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 07:30:27 +0200 Subject: [PATCH 60/86] 1000 because why not --- src/main/resources/pack.mcmeta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index bc086de01..75c979a41 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -2,6 +2,6 @@ "pack": { "description": "${name} Resources", "pack_format": 15, - "supported_formats": [1, 80] + "supported_formats": [1, 1000] } } \ No newline at end of file From 2126b02c7d32f48fdf318d89955e1d95798b77ae Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 10:32:48 +0200 Subject: [PATCH 61/86] update merge log message and remove vcsVersion --- buildSrc/src/main/kotlin/automodpack.common.gradle.kts | 2 +- settings.gradle.kts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/buildSrc/src/main/kotlin/automodpack.common.gradle.kts b/buildSrc/src/main/kotlin/automodpack.common.gradle.kts index 63ddbd474..282912621 100644 --- a/buildSrc/src/main/kotlin/automodpack.common.gradle.kts +++ b/buildSrc/src/main/kotlin/automodpack.common.gradle.kts @@ -81,7 +81,7 @@ tasks.register("mergeJar") { ?: error("No jar found to merge in build/libs directory! ${buildDirLibs.absolutePath}") val time = System.currentTimeMillis() - println("Found ${jarToMerge} merge. Merging...") + println("Found $jarToMerge to merge. Merging...") val minecraftVersionStr = jarToMerge.name.substringAfterLast("-mc").substringBefore("-") if (minecraftVersionStr.isEmpty()) { diff --git a/settings.gradle.kts b/settings.gradle.kts index f1775351b..6327159e9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -56,10 +56,6 @@ stonecutter { match("1.19.4", "fabric", "forge") match("1.19.2", "fabric", "forge") match("1.18.2", "fabric", "forge") - - // This is the default target. - // https://stonecutter.kikugie.dev/stonecutter/guide/setup#settings-settings-gradle-kts - vcsVersion = "1.21.6-neoforge" } } From 4c8033ceea0b38c775c52934f32838504c73d7ef Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 10:33:36 +0200 Subject: [PATCH 62/86] handle mixins and refmaps correctly on legacy forge --- build.forge.gradle.kts | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/build.forge.gradle.kts b/build.forge.gradle.kts index a3c40af61..1b07b48d9 100644 --- a/build.forge.gradle.kts +++ b/build.forge.gradle.kts @@ -1,3 +1,7 @@ +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.databind.node.ObjectNode + plugins { kotlin("jvm") id("automodpack.common") @@ -26,9 +30,30 @@ dependencies { compileOnly("net.fabricmc.fabric-api:fabric-api:0.92.2+1.20.1") compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:${property("deps.mixin-extras")}")!!) implementation(jarJar("io.github.llamalad7:mixinextras-forge:${property("deps.mixin-extras")}")!!) + annotationProcessor("org.spongepowered:mixin:0.8.5:processor") // required to generate refmaps +} + +mixin { // Add mixins + add(sourceSets.main.get(), "automodpack-main.mixins.refmap.json") + config("automodpack-main.mixins.json") +} + +tasks.getByName("processResources") { + doLast { // Add refmap to the mixin config + val mixinConfigFile = File(destinationDir, "automodpack-main.mixins.json") + addRefmapToJsonFile(mixinConfigFile, "automodpack-main.mixins.refmap.json") + } } tasks { + jar { // add the mixin config to the jar + manifest { + attributes( + "MixinConfigs" to "automodpack-main.mixins.json" + ) + } + } + processResources { exclude("**/fabric.mod.json", "**/automodpack.accesswidener") } @@ -56,4 +81,26 @@ java { toolchain.languageVersion.set(JavaLanguageVersion.of(17)) } withSourcesJar() +} + +fun addRefmapToJsonFile(jsonFile: File, refmap: String) { + if (!jsonFile.exists()) { + error("JSON file not found: ${jsonFile.absolutePath}") + } + + val objectMapper = ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT) + try { + val jsonNode = objectMapper.readTree(jsonFile) + if (jsonNode.isObject) { + val objectNode = jsonNode as ObjectNode + objectNode.put("refmap", refmap) + objectMapper.writeValue(jsonFile, objectNode) + println("Added refmap ($refmap) to ${jsonFile.name}") + } else { + error("JSON file ${jsonFile.name} is not a JSON object, couldn't add refmap.") + } + } catch (e: Exception) { + println("Error processing JSON file ${jsonFile.absolutePath}: ${e.message}") + e.printStackTrace() + } } \ No newline at end of file From de318f65751f955603ed951066dd086c4c992592 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 10:39:38 +0200 Subject: [PATCH 63/86] fix icon path for neo/forge --- build.forge.gradle.kts | 4 ++++ build.neoforge.gradle.kts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/build.forge.gradle.kts b/build.forge.gradle.kts index 1b07b48d9..e59f54847 100644 --- a/build.forge.gradle.kts +++ b/build.forge.gradle.kts @@ -56,6 +56,10 @@ tasks { processResources { exclude("**/fabric.mod.json", "**/automodpack.accesswidener") + + filesMatching("assets/automodpack/icon.png") { + path = "icon.png" + } } named("createMinecraftArtifacts") { diff --git a/build.neoforge.gradle.kts b/build.neoforge.gradle.kts index d50727426..939c8f6d1 100644 --- a/build.neoforge.gradle.kts +++ b/build.neoforge.gradle.kts @@ -29,6 +29,10 @@ dependencies { tasks { processResources { exclude("**/fabric.mod.json", "**/automodpack.accesswidener", "**/forge.mods.toml") + + filesMatching("assets/automodpack/icon.png") { + path = "icon.png" + } } named("createMinecraftArtifacts") { From a0969e3b5474cb3b34cd0afb85ddb6646d900c45 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 11:06:39 +0200 Subject: [PATCH 64/86] remove unused stuff --- .imgbotconfig | 14 -------------- buildSrc/build.gradle.kts | 3 +-- stonecutter.gradle.kts | 2 -- 3 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 .imgbotconfig diff --git a/.imgbotconfig b/.imgbotconfig deleted file mode 100644 index 1828d9c0b..000000000 --- a/.imgbotconfig +++ /dev/null @@ -1,14 +0,0 @@ -{ - "schedule": "daily", // daily|weekly|monthly -// "ignoredFiles": [ -// "*.jpg", // ignore by extension -// "image1.png", // ignore by filename -// "public/special_images/*", // ignore by folderpath -// ], -// "aggressiveCompression": "true", // true|false -// "compressWiki": "true", // true|false - "minKBReduced": null, // delay new prs until size reduction meets this threshold (default to 10) -// "prTitle" : "Images are optimized!", -// "prBody" : " Text before optimization ratio {optimization_ratio} Text after optimization ratio -// Text before optimization details {optimization_details} Text after optimization details", -} \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index eaf927569..6083ada0a 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -4,11 +4,10 @@ plugins { } repositories { - google() mavenCentral() gradlePluginPortal() } dependencies { - + implementation("com.fasterxml.jackson.core:jackson-databind:2.19.1") // For JSON parsing e.g. in build.forge.gradle.kts } \ No newline at end of file diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 4d24c0228..1a0b552aa 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -1,10 +1,8 @@ plugins { id("dev.kikugie.stonecutter") kotlin("jvm") version "2.1.21" apply false - id("co.uzzu.dotenv.gradle") version "4.0.0" id("fabric-loom") version "1.10-SNAPSHOT" apply false id("net.neoforged.moddev") version "2.0.95" apply false - id("me.modmuss50.mod-publish-plugin") version "0.8.+" apply false id("com.gradleup.shadow") version "8.3.6" apply false id("org.moddedmc.wiki.toolkit") version "0.2.7" } From cb7af34dd1e56fe85ef5e3747fd59a80d8072f09 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 11:15:19 +0200 Subject: [PATCH 65/86] change inject point in MinecraftServerMixin to fix legacy forge --- .../skidam/automodpack/mixin/core/MinecraftServerMixin.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java index 756ff84ef..244195c71 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java @@ -10,11 +10,7 @@ @Mixin(MinecraftServer.class) public class MinecraftServerMixin { - /*? if >=1.19.3 {*/ - @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;loadStatusIcon()Ljava/util/Optional;"), method = "runServer") -/*?} else {*/ - /*@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;setFavicon(Lnet/minecraft/server/ServerMetadata;)V", ordinal = 0), method = "runServer") -*//*?}*/ + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;initServer()Z", shift = At.Shift.AFTER), method = "runServer") private void afterSetupServer(CallbackInfo info) { Common.server = (MinecraftServer) (Object) this; Common.afterSetupServer(); From b34918105ef626d61bd8f68768fa96b3da9c373d Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 12:22:26 +0200 Subject: [PATCH 66/86] neo/forge: just specify different icon path instead or processResources --- build.forge.gradle.kts | 4 ---- build.neoforge.gradle.kts | 4 ---- src/main/resources/META-INF/mods.toml | 2 +- src/main/resources/META-INF/neoforge.mods.toml | 2 +- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/build.forge.gradle.kts b/build.forge.gradle.kts index e59f54847..1b07b48d9 100644 --- a/build.forge.gradle.kts +++ b/build.forge.gradle.kts @@ -56,10 +56,6 @@ tasks { processResources { exclude("**/fabric.mod.json", "**/automodpack.accesswidener") - - filesMatching("assets/automodpack/icon.png") { - path = "icon.png" - } } named("createMinecraftArtifacts") { diff --git a/build.neoforge.gradle.kts b/build.neoforge.gradle.kts index 939c8f6d1..d50727426 100644 --- a/build.neoforge.gradle.kts +++ b/build.neoforge.gradle.kts @@ -29,10 +29,6 @@ dependencies { tasks { processResources { exclude("**/fabric.mod.json", "**/automodpack.accesswidener", "**/forge.mods.toml") - - filesMatching("assets/automodpack/icon.png") { - path = "icon.png" - } } named("createMinecraftArtifacts") { diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 411dc535c..c07f28b9b 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -9,7 +9,7 @@ displayName = "${name}" displayURL = "https://modrinth.com/mod/automodpack" authors = "Skidam" description = "${description}" -logoFile = "icon.png" +logoFile = "assets/automodpack/icon.png" [[dependencies."${id}"]] modId = "minecraft" diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index 411dc535c..c07f28b9b 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -9,7 +9,7 @@ displayName = "${name}" displayURL = "https://modrinth.com/mod/automodpack" authors = "Skidam" description = "${description}" -logoFile = "icon.png" +logoFile = "assets/automodpack/icon.png" [[dependencies."${id}"]] modId = "minecraft" From eca02b05e220bccf4c7cec5e172ef64693b4cb3f Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 12:32:06 +0200 Subject: [PATCH 67/86] add `forceCopyFilesToStandardLocation` --- .../pl/skidam/automodpack_core/Server.java | 2 +- .../skidam/automodpack_core/config/Jsons.java | 5 +- .../modpack/ModpackContent.java | 12 ++++- .../modpack/ModpackExecutor.java | 2 +- .../utils/WorkaroundUtil.java | 8 +-- .../automodpack_core/modpack/ModpackTest.java | 2 +- docs/compatibility/mods.mdx | 3 +- docs/configuration/server-config.mdx | 53 ++++++++++--------- .../client/ModpackUpdater.java | 22 +++++--- .../client/ModpackUtils.java | 9 ++-- 10 files changed, 71 insertions(+), 47 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/Server.java b/core/src/main/java/pl/skidam/automodpack_core/Server.java index 064883a4f..763e8d7cd 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/Server.java +++ b/core/src/main/java/pl/skidam/automodpack_core/Server.java @@ -60,7 +60,7 @@ public static void main(String[] args) { mainModpackDir.toFile().mkdirs(); ModpackExecutor modpackExecutor = new ModpackExecutor(); - ModpackContent modpackContent = new ModpackContent(serverConfig.modpackName, null, mainModpackDir, serverConfig.syncedFiles, serverConfig.allowEditsInFiles, modpackExecutor.getExecutor()); + ModpackContent modpackContent = new ModpackContent(serverConfig.modpackName, null, mainModpackDir, serverConfig.syncedFiles, serverConfig.allowEditsInFiles, serverConfig.forceCopyFilesToStandardLocation, modpackExecutor.getExecutor()); boolean generated = modpackExecutor.generateNew(modpackContent); if (generated) { diff --git a/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java b/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java index da1e958ce..d2123e17c 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java +++ b/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java @@ -91,6 +91,7 @@ public static class ServerConfigFieldsV2 { public boolean generateModpackOnStart = true; public List syncedFiles = List.of("/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"); public List allowEditsInFiles = List.of("/options.txt", "/config/**"); + public List forceCopyFilesToStandardLocation = List.of(); public boolean autoExcludeServerSideMods = true; public boolean autoExcludeUnnecessaryFiles = true; public boolean requireAutoModpackOnClient = true; @@ -147,14 +148,16 @@ public static class ModpackContentItem { public String size; public String type; public boolean editable; + public boolean forceCopy; public String sha1; public String murmur; - public ModpackContentItem(String file, String size, String type, boolean editable, String sha1, String murmur) { + public ModpackContentItem(String file, String size, String type, boolean editable, boolean forceCopy, String sha1, String murmur) { this.file = file; this.size = size; this.type = type; this.editable = editable; + this.forceCopy = forceCopy; this.sha1 = sha1; this.murmur = murmur; } diff --git a/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java b/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java index 3157579a3..059279e9d 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java +++ b/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java @@ -20,11 +20,12 @@ public class ModpackContent { private final String MODPACK_NAME; private final WildCards SYNCED_FILES_CARDS; private final WildCards EDITABLE_CARDS; + private final WildCards FORCE_COPY_FILES_TO_STANDARD_LOCATION; private final Path MODPACK_DIR; private final ThreadPoolExecutor CREATION_EXECUTOR; private final Map sha1MurmurMapPreviousContent = new HashMap<>(); - public ModpackContent(String modpackName, Path cwd, Path modpackDir, List syncedFiles, List allowEditsInFiles, ThreadPoolExecutor CREATION_EXECUTOR) { + public ModpackContent(String modpackName, Path cwd, Path modpackDir, List syncedFiles, List allowEditsInFiles, List forceCopyFilesToStandardLocation, ThreadPoolExecutor CREATION_EXECUTOR) { this.MODPACK_NAME = modpackName; this.MODPACK_DIR = modpackDir; Set directoriesToSearch = new HashSet<>(2); @@ -36,6 +37,7 @@ public ModpackContent(String modpackName, Path cwd, Path modpackDir, List getWorkaroundMods(Jsons.ModpackContentFields modpackContentFi return workaroundMods; } - for (Jsons.ModpackContentFields.ModpackContentItem mod : modpackContentFields.list) { - if (mod.type.equals("mod")) { - Path modPath = CustomFileUtils.getPath(modpackPath, mod.file); + for (Jsons.ModpackContentFields.ModpackContentItem item : modpackContentFields.list) { + if (item.type.equals("mod")) { + Path modPath = CustomFileUtils.getPath(modpackPath, item.file); if (FileInspection.hasSpecificServices(modPath)) { - workaroundMods.add(mod.file); + workaroundMods.add(item.file); } } } diff --git a/core/src/test/java/pl/skidam/automodpack_core/modpack/ModpackTest.java b/core/src/test/java/pl/skidam/automodpack_core/modpack/ModpackTest.java index ee5e2bf0d..e97b197fb 100644 --- a/core/src/test/java/pl/skidam/automodpack_core/modpack/ModpackTest.java +++ b/core/src/test/java/pl/skidam/automodpack_core/modpack/ModpackTest.java @@ -48,7 +48,7 @@ void modpackTest() { "ModpackContentItems(file=/mods/server-mod-1.20.jar, size=1, type=other, editable=false, sha1=86f7e437faa5a7fce15d1ddcb9eaeaea377667b8, murmur=null)" ); - ModpackContent content = new ModpackContent("TestPack", null, testFilesDir, new ArrayList<>(), new ArrayList<>(editable), new ModpackExecutor().getExecutor()); + ModpackContent content = new ModpackContent("TestPack", null, testFilesDir, new ArrayList<>(), new ArrayList<>(editable), new ArrayList<>(), new ModpackExecutor().getExecutor()); content.create(); boolean correct = true; diff --git a/docs/compatibility/mods.mdx b/docs/compatibility/mods.mdx index 9b32fb541..853c0d5a6 100644 --- a/docs/compatibility/mods.mdx +++ b/docs/compatibility/mods.mdx @@ -1,5 +1,6 @@ AutoModpack should work with all minecraft modifications (it would be a bit bad modpack updater if it didn't). However, there are some exceptions and limitations that you should be aware of: -- Majrusz's Mods: Require a modified Majrusz Library to work with AutoModpack. [Github Issue](https://github.com/Skidamek/AutoModpack/issues/268). +- Majrusz's Mods: Requires a modified Majrusz Library to work with AutoModpack. [Github Issue](https://github.com/Skidamek/AutoModpack/issues/268). Or `forceCopyFilesToStandardLocation` in server config. +- [CITResewnNeoPatcher](https://modrinth.com/mod/cit-resewn-neopatcher): Requires to specify fabric version of CITResewn in `forceCopyFilesToStandardLocation` in server config, like (`/mods/citresewn-1.2.1+1.21.jar`). - [ModSets](https://modrinth.com/mod/mod-sets): It's currently broken due to similarities in mod loading modification process. - [Cesium](https://modrinth.com/mod/cesium): Cesium is currently incompatible due to wrong way of zstd packaging on AutoModpack side. It will be fixed soon. [GitHub Issue](https://github.com/Skidamek/AutoModpack/issues/384) \ No newline at end of file diff --git a/docs/configuration/server-config.mdx b/docs/configuration/server-config.mdx index 09f840de5..5cb096b1d 100644 --- a/docs/configuration/server-config.mdx +++ b/docs/configuration/server-config.mdx @@ -1,29 +1,30 @@ ### Server Config File `~/automodpack/automodpack-server.json` -| Name | Default Value | Description | -|-------------------------------|--------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | -| `modpackName` | `""` | The name of the server modpack, shows while downloading modpack and that's how modpack directory gets called inside `~/automodpack/modpacks/`. | -| `modpackHost` | `true` | Starts modpack host server. | -| `generateModpackOnStart` | `true` | Automatically regenerate modpack metadata when the server starts. | -| `syncedFiles` | `"/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"` | A list of *relative paths* from the root folder of the Minecraft server that will be **synced to the modpack**. Use wildcards like `*` to include multiple files from this directory (e.g., `/mods/*.jar`). Or use `**` (double star) to recursively include all files and subdirectories from a folder (e.g., `/config/**`). Prefix a path with `!` to **exclude** it from syncing. **Note:** This does **not** copy the files; it only defines what will be available on the client side under `~/.minecraft/`. | -| `allowEditsInFiles` | `"/options.txt", "/config/**"` | A list of files that clients are allowed to edit. In other words, files that are downloaded only one time and then ignored from updating. Configuration works exactly the same way `syncedFiles` does. | -| `autoExcludeServerSideMods` | `true` | Automatically excludes server-side mods from the modpack. (Works only if mod developer specified environment type in mod metadata) | -| `autoExcludeUnnecessaryFiles` | `true` | Auto skip files which are: empty, hidden, temporary, disabled or backup. | -| `requireAutoModpackOnClient` | `true` | Whether or not this mod is optional for clients to join server. | -| `nagUnModdedClients` | `true` | If `true` clients without AutoModpack will be nagged with a chat message on join. To work requires `requireAutoModpackOnClient` to be `false`. | -| `nagMessage` | `"This server provides dedicated modpack through AutoModpack!"` | The message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | -| `nagClickableMessage` | `"Click here to get the AutoModpack!"` | The clickable part of the message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | -| `nagClickableLink` | `"https://modrinth.com/project/automodpack"` | The link that will be opened when the message is clicked. To work requires `nagUnModdedClients` to be `true`. | -| `bindAddress` | `""` | The address to which the modpack host server binds. Leave this empty to automatically determine a local address (typically `0.0.0.0` or `::0`). When `bindPort` is set to `-1`, this value is **ignored** and the modpack host will be bound to the same address as the Minecraft server, as specified in the `server.properties` file. | -| `bindPort` | `-1` | The port number on which the modpack host server listens. If set to `-1`, the modpack will be hosted directly on the port of your Minecraft server. | -| `addressToSend` | `""` | The address to the modpack host which will be used by clients to download the modpack. If empty, clients will use the same address they used to connect to the Minecraft server. (Do **not** include the port there, use `portToSend` instead) | -| `portToSend` | `-1` | The port number that is sent alongside the `addressToSend`. If set to `-1`, clients will use the same port they used to connect to the Minecraft server. | -| `disableInternalTLS` | `false` | Disables internal TLS management. If enabled, you must manage TLS externally, e.g. on a reverse proxy. When using this option, you have to use different `bindPort` than `-1` and you should consider setting `bindAddress` to a loopback address (e.g. `127.0.0.1`), so the unencrypted traffic between the modpack host and the reverse proxy isn't leaked. | -| `updateIpsOnEveryStart` | `false` | Updates `addressToSend` on every server start. Might be useful if you have dynamic IP address. (Use it only if you really need it!) | -| `bandwidthLimit` | `0` | Upload limit in Mbps that modpack host server is restricted to. (Value has to be an Integer, `0` - means unlimited) | -| `validateSecrets` | `true` | Makes modpack host validate and authorize modpack download attempts using unique secrets. | -| `secretLifetime` | `336` | Time in hours that a player's secret remains valid. | -| `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | -| `acceptedLoaders` | `""` | Allows players from different modloaders to connect to your server. (Use with caution, most mods work only on one loader) | +| Name | Default Value | Description | +|------------------------------------|----------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | +| `modpackName` | `""` | The name of the server modpack, shows while downloading modpack and that's how modpack directory gets called inside `~/automodpack/modpacks/`. | +| `modpackHost` | `true` | Starts modpack host server. | +| `generateModpackOnStart` | `true` | Automatically regenerate modpack metadata when the server starts. | +| `syncedFiles` | `["/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"]` | A list of *relative paths* from the root folder of the Minecraft server that will be **synced to the modpack**. Use wildcards like `*` to include multiple files from this directory (e.g., `/mods/*.jar`). Or use `**` (double star) to recursively include all files and subdirectories from a folder (e.g., `/config/**`). Prefix a path with `!` to **exclude** it from syncing. **Note:** This does **not** copy the files; it only defines what will be available on the client side under `~/.minecraft/`. | +| `allowEditsInFiles` | `["/options.txt", "/config/**"]` | A list of files that clients are allowed to edit. In other words, files that are downloaded only one time and then ignored from updating. Configuration works exactly the same way `syncedFiles` does. | +| `forceCopyFilesToStandardLocation` | `[]` | A list of files that client will be forced to load from standard directory. By default most mods are loaded from separate automodpack directory, this list specifies files which will be copied to e.g. standard mods folder. **This will break seamless updates!** Use only when you have really good reason for it. Configuration works exactly the same way `syncedFiles` does. | +| `autoExcludeServerSideMods` | `true` | Automatically excludes server-side mods from the modpack. (Works only if mod developer specified environment type in mod metadata) | +| `autoExcludeUnnecessaryFiles` | `true` | Auto skip files which are: empty, hidden, temporary, disabled or backup. | +| `requireAutoModpackOnClient` | `true` | Whether or not this mod is optional for clients to join server. | +| `nagUnModdedClients` | `true` | If `true` clients without AutoModpack will be nagged with a chat message on join. To work requires `requireAutoModpackOnClient` to be `false`. | +| `nagMessage` | `"This server provides dedicated modpack through AutoModpack!"` | The message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | +| `nagClickableMessage` | `"Click here to get the AutoModpack!"` | The clickable part of the message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | +| `nagClickableLink` | `"https://modrinth.com/project/automodpack"` | The link that will be opened when the message is clicked. To work requires `nagUnModdedClients` to be `true`. | +| `bindAddress` | `""` | The address to which the modpack host server binds. Leave this empty to automatically determine a local address (typically `0.0.0.0` or `::0`). When `bindPort` is set to `-1`, this value is **ignored** and the modpack host will be bound to the same address as the Minecraft server, as specified in the `server.properties` file. | +| `bindPort` | `-1` | The port number on which the modpack host server listens. If set to `-1`, the modpack will be hosted directly on the port of your Minecraft server. | +| `addressToSend` | `""` | The address to the modpack host which will be used by clients to download the modpack. If empty, clients will use the same address they used to connect to the Minecraft server. (Do **not** include the port there, use `portToSend` instead) | +| `portToSend` | `-1` | The port number that is sent alongside the `addressToSend`. If set to `-1`, clients will use the same port they used to connect to the Minecraft server. | +| `disableInternalTLS` | `false` | Disables internal TLS management. If enabled, you must manage TLS externally, e.g. on a reverse proxy. When using this option, you have to use different `bindPort` than `-1` and you should consider setting `bindAddress` to a loopback address (e.g. `127.0.0.1`), so the unencrypted traffic between the modpack host and the reverse proxy isn't leaked. | +| `updateIpsOnEveryStart` | `false` | Updates `addressToSend` on every server start. Might be useful if you have dynamic IP address. (Use it only if you really need it!) | +| `bandwidthLimit` | `0` | Upload limit in Mbps that modpack host server is restricted to. (Value has to be an Integer, `0` - means unlimited) | +| `validateSecrets` | `true` | Makes modpack host validate and authorize modpack download attempts using unique secrets. | +| `secretLifetime` | `336` | Time in hours that a player's secret remains valid. | +| `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | +| `acceptedLoaders` | `[""]` | Allows players from different modloaders to connect to your server. (Use with caution, most mods work only on one loader) | diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUpdater.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUpdater.java index 8e2145196..fe18cbc7c 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUpdater.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUpdater.java @@ -15,6 +15,7 @@ import java.net.SocketTimeoutException; import java.nio.file.*; import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; import static pl.skidam.automodpack_core.GlobalVariables.*; @@ -494,8 +495,13 @@ private boolean applyModpack() throws Exception { boolean needsRestart2 = ModpackUtils.fixNestedMods(conflictingNestedMods, standardModList); Set ignoredFiles = ModpackUtils.getIgnoredFiles(conflictingNestedMods, workaroundMods); + Set forceCopyFiles = modpackContent.list.stream() + .filter(item -> item.forceCopy) + .map(item -> item.file) + .collect(Collectors.toSet()); + // Remove duplicate mods - ModpackUtils.RemoveDupeModsResult removeDupeModsResult = ModpackUtils.removeDupeMods(modpackDir, standardModList, modpackModList, ignoredFiles, workaroundMods); + ModpackUtils.RemoveDupeModsResult removeDupeModsResult = ModpackUtils.removeDupeMods(modpackDir, standardModList, modpackModList, ignoredFiles, workaroundMods, forceCopyFiles); boolean needsRestart3 = removeDupeModsResult.requiresRestart(); // Remove rest of mods not for standard mods directory @@ -509,16 +515,20 @@ private Set getFilesNotToCopy(Set filesNotToCopy = new HashSet<>(); // Make list of files which we do not copy to the running directory - for (Jsons.ModpackContentFields.ModpackContentItem modpackContentItem : modpackContentItems) { + for (Jsons.ModpackContentFields.ModpackContentItem item : modpackContentItems) { + if (item.forceCopy) { + continue; + } + // We only want to copy editable file if its downloaded first time // So we add to ignored any other editable file - if (modpackContentItem.editable && !newDownloadedFiles.contains(modpackContentItem.file)) { - filesNotToCopy.add(modpackContentItem.file); + if (item.editable && !newDownloadedFiles.contains(item.file)) { + filesNotToCopy.add(item.file); } // We only want to copy mods which need a workaround - if (modpackContentItem.type.equals("mod") && !workaroundMods.contains(modpackContentItem.file)) { - filesNotToCopy.add(modpackContentItem.file); + if (item.type.equals("mod") && !workaroundMods.contains(item.file)) { + filesNotToCopy.add(item.file); } } diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java index b504f2249..d66b3fe9e 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java @@ -224,8 +224,7 @@ public record RemoveDupeModsResult(boolean requiresRestart, Set modsToKeep // Returns true if removed any mod from standard mods folder // If the client mod is a duplicate of what modpack contains then it removes it from client so that you dont need to restart game just when you launched it and modpack get updated - basically having these mods separately allows for seamless updates // If you have client mods which require specific mod which is also a duplicate of what modpack contains it should stay - public static RemoveDupeModsResult removeDupeMods(Path modpackDir, Collection standardModList, Collection modpackModList, Set ignoredMods, Set workaroundMods) throws IOException { - + public static RemoveDupeModsResult removeDupeMods(Path modpackDir, Collection standardModList, Collection modpackModList, Set ignoredMods, Set workaroundMods, Set forceCopyFiles) throws IOException { var dupeMods = ModpackUtils.getDupeMods(modpackDir, ignoredMods, standardModList, modpackModList); if (dupeMods.isEmpty()) { @@ -259,12 +258,14 @@ public static RemoveDupeModsResult removeDupeMods(Path modpackDir, Collection providesIDs = modpackMod.providesIDs(); List IDs = new ArrayList<>(providesIDs); IDs.add(modId); boolean isDependent = IDs.stream().anyMatch(idsToKeep::contains); - boolean isWorkaround = workaroundMods.contains(CustomFileUtils.formatPath(standardModPath, MODS_DIR.getParent())); + boolean isWorkaround = workaroundMods.contains(formatedPath); + boolean isForceCopy = forceCopyFiles.contains(formatedPath); if (isDependent) { Path newStandardModPath = standardModPath.getParent().resolve(modpackModPath.getFileName()); @@ -278,7 +279,7 @@ public static RemoveDupeModsResult removeDupeMods(Path modpackDir, Collection Date: Wed, 25 Jun 2025 12:54:35 +0200 Subject: [PATCH 68/86] enhance logging for version checks and update validation in HandshakeC2SPacket and SelfUpdater --- .../skidam/automodpack_loader_core/SelfUpdater.java | 2 +- .../networking/packet/HandshakeC2SPacket.java | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java index 53ef25e9e..0b2eb790d 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java @@ -168,7 +168,7 @@ public static boolean validUpdate(ModrinthAPI automodpack) { public static void installModVersion(ModrinthAPI automodpack) { if (!validUpdate(automodpack)) { - LOGGER.error("Can't downgrade AutoModpack to version: " + automodpack.fileVersion() + ". Use newer version!"); + LOGGER.error("Can't downgrade AutoModpack to version: {}. Server should use newer version! Please do not downgrade AutoModpack on client to prevent security vulnerabilities!", automodpack.fileVersion()); return; } diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java index 99b8eb51f..fab9e8827 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java @@ -14,6 +14,7 @@ import net.minecraft.network.FriendlyByteBuf; import static pl.skidam.automodpack_core.GlobalVariables.*; +import static pl.skidam.automodpack_loader_core.SelfUpdater.validUpdate; public class HandshakeC2SPacket { @@ -30,9 +31,9 @@ public static CompletableFuture receive(Minecraft client, Clien outBuf.writeUtf(clientHandshakePacket.toJson(), Short.MAX_VALUE); if (serverHandshakePacket.equals(clientHandshakePacket) || (serverHandshakePacket.loaders.contains(loader) && serverHandshakePacket.amVersion.equals(AM_VERSION))) { - LOGGER.info("Versions match " + serverHandshakePacket.amVersion); + LOGGER.info("Versions match {}", serverHandshakePacket.amVersion); } else { - LOGGER.warn("Versions mismatch " + serverHandshakePacket.amVersion); + LOGGER.warn("Versions mismatch. Server: {}: Client: {}", serverHandshakePacket.amVersion, AM_VERSION); LOGGER.info("Trying to change automodpack version to the version required by server..."); updateMod(handler, serverHandshakePacket.amVersion, serverHandshakePacket.mcVersion); } @@ -58,8 +59,12 @@ private static void updateMod(ClientHandshakePacketListenerImpl handler, String return; } - ((ClientConnectionAccessor) ((ClientLoginNetworkHandlerAccessor) handler).getConnection()).getChannel().disconnect(); + if (!validUpdate(automodpack)) { + LOGGER.error("Can't downgrade AutoModpack to version: {}. Server should use newer version! Please do not downgrade AutoModpack on client to prevent security vulnerabilities!", automodpack.fileVersion()); + return; + } + ((ClientConnectionAccessor) ((ClientLoginNetworkHandlerAccessor) handler).getConnection()).getChannel().disconnect(); SelfUpdater.installModVersion(automodpack); } } From dc06f3e857796ea0a4ca8e31ef3dc9baa52f7e60 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 13:23:27 +0200 Subject: [PATCH 69/86] improve downgrade handling in AutoModpack validation to enhance security warnings --- .../pl/skidam/automodpack_loader_core/SelfUpdater.java | 8 ++++++-- .../automodpack/networking/packet/HandshakeC2SPacket.java | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java index 0b2eb790d..f2e9ef8ee 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java @@ -163,12 +163,16 @@ public static boolean validUpdate(ModrinthAPI automodpack) { String REMOTE_VERSION = remoteVersionSplit[0].replace(".", ""); // Don't allow downgrades pass 4.0.0-beta38 - return Integer.parseInt(REMOTE_VERSION) >= 400 && (!remoteIsBeta || remoteBeta >= 38); + if (Integer.parseInt(REMOTE_VERSION) >= 400 && (!remoteIsBeta || remoteBeta >= 38)) { + return true; + } + + LOGGER.error("Downgrading AutoModpack to version {} is strongly discouraged and disabled from auto-server-syncing. To protect against potential security vulnerabilities, please use a newer version.", automodpack.fileVersion()); + return false; } public static void installModVersion(ModrinthAPI automodpack) { if (!validUpdate(automodpack)) { - LOGGER.error("Can't downgrade AutoModpack to version: {}. Server should use newer version! Please do not downgrade AutoModpack on client to prevent security vulnerabilities!", automodpack.fileVersion()); return; } diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java index fab9e8827..fa6735d06 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java @@ -60,7 +60,6 @@ private static void updateMod(ClientHandshakePacketListenerImpl handler, String } if (!validUpdate(automodpack)) { - LOGGER.error("Can't downgrade AutoModpack to version: {}. Server should use newer version! Please do not downgrade AutoModpack on client to prevent security vulnerabilities!", automodpack.fileVersion()); return; } From 5ef10517673ede2bf4e9040d0d28a6707af648f1 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 13:27:09 +0200 Subject: [PATCH 70/86] Add french docs translation Co-authored-by: @justeyanis --- docs/.translated/fr_fr/_homepage.mdx | 9 +++ docs/.translated/fr_fr/_meta.json | 34 +++++++++ docs/.translated/fr_fr/commands/_meta.json | 6 ++ docs/.translated/fr_fr/commands/commands.mdx | 11 +++ .../fr_fr/compatibility/_meta.json | 14 ++++ .../fr_fr/compatibility/launchers.mdx | 15 ++++ docs/.translated/fr_fr/compatibility/mods.mdx | 5 ++ .../fr_fr/compatibility/proxies.mdx | 14 ++++ .../fr_fr/configuration/_meta.json | 14 ++++ .../fr_fr/configuration/client-config.mdx | 10 +++ .../fr_fr/configuration/server-config.mdx | 29 ++++++++ .../fr_fr/configuration/troubleshooting.mdx | 6 ++ docs/.translated/fr_fr/faq.mdx | 49 ++++++++++++ docs/.translated/fr_fr/quick-start.mdx | 36 +++++++++ docs/.translated/fr_fr/technicals/_meta.json | 22 ++++++ .../fr_fr/technicals/certificate.mdx | 74 +++++++++++++++++++ .../fr_fr/technicals/connection.mdx | 35 +++++++++ .../fr_fr/technicals/file-structure.mdx | 24 ++++++ .../fr_fr/technicals/modpack-creation.mdx | 17 +++++ .../fr_fr/technicals/modpack-hosting.mdx | 2 + 20 files changed, 426 insertions(+) create mode 100644 docs/.translated/fr_fr/_homepage.mdx create mode 100644 docs/.translated/fr_fr/_meta.json create mode 100644 docs/.translated/fr_fr/commands/_meta.json create mode 100644 docs/.translated/fr_fr/commands/commands.mdx create mode 100644 docs/.translated/fr_fr/compatibility/_meta.json create mode 100644 docs/.translated/fr_fr/compatibility/launchers.mdx create mode 100644 docs/.translated/fr_fr/compatibility/mods.mdx create mode 100644 docs/.translated/fr_fr/compatibility/proxies.mdx create mode 100644 docs/.translated/fr_fr/configuration/_meta.json create mode 100644 docs/.translated/fr_fr/configuration/client-config.mdx create mode 100644 docs/.translated/fr_fr/configuration/server-config.mdx create mode 100644 docs/.translated/fr_fr/configuration/troubleshooting.mdx create mode 100644 docs/.translated/fr_fr/faq.mdx create mode 100644 docs/.translated/fr_fr/quick-start.mdx create mode 100644 docs/.translated/fr_fr/technicals/_meta.json create mode 100644 docs/.translated/fr_fr/technicals/certificate.mdx create mode 100644 docs/.translated/fr_fr/technicals/connection.mdx create mode 100644 docs/.translated/fr_fr/technicals/file-structure.mdx create mode 100644 docs/.translated/fr_fr/technicals/modpack-creation.mdx create mode 100644 docs/.translated/fr_fr/technicals/modpack-hosting.mdx diff --git a/docs/.translated/fr_fr/_homepage.mdx b/docs/.translated/fr_fr/_homepage.mdx new file mode 100644 index 000000000..3f7e2f1aa --- /dev/null +++ b/docs/.translated/fr_fr/_homepage.mdx @@ -0,0 +1,9 @@ +Bienvenue dans la documentation d'AutoModpack ! + +Si vous débutez avec AutoModpack, nous vous recommandons de commencer par le [Guide de démarrage rapide](docs/quick-start) pour vous familiariser avec les bases de l'utilisation d'AutoModpack. + +Si vous recherchez des informations plus détaillées, vous pouvez les trouver dans la section `👨‍💻 Détails techniques`. + +Vous avez des questions ou besoin d'aide ? Rejoignez notre [serveur Discord](https://discord.gg/hS6aMyeA9P). + +Vous trouvez ce projet utile ? Envisagez de soutenir son développement et sa maintenance continus en faisant un don à l'auteur sur [Ko-fi](https://ko-fi.com/skidam). \ No newline at end of file diff --git a/docs/.translated/fr_fr/_meta.json b/docs/.translated/fr_fr/_meta.json new file mode 100644 index 000000000..96177897b --- /dev/null +++ b/docs/.translated/fr_fr/_meta.json @@ -0,0 +1,34 @@ +{ + "quick-start.mdx": { + "name": "🚀 Démarrage rapide", + "icon": null + }, + "usage": { + "name": "Utilisation", + "icon": null + }, + "configuration": { + "name": "\uD83D\uDEE0\uFE0F Configuration", + "icon": null + }, + "commands": { + "name": "⌨\uFE0F Commandes", + "icon": null + }, + "examples": { + "name": "\uD83D\uDCF7 Exemples", + "icon": null + }, + "technicals": { + "name": "\uD83D\uDC68\u200D\uD83D\uDCBB Détails techniques", + "icon": null + }, + "compatibility": { + "name": "\uD83D\uDCDA Compatibilité", + "icon": null + }, + "faq.mdx": { + "name": "❔ FAQ", + "icon": null + } +} \ No newline at end of file diff --git a/docs/.translated/fr_fr/commands/_meta.json b/docs/.translated/fr_fr/commands/_meta.json new file mode 100644 index 000000000..5610f28c6 --- /dev/null +++ b/docs/.translated/fr_fr/commands/_meta.json @@ -0,0 +1,6 @@ +{ + "commands.mdx": { + "name": "Commandes", + "icon": null + } +} diff --git a/docs/.translated/fr_fr/commands/commands.mdx b/docs/.translated/fr_fr/commands/commands.mdx new file mode 100644 index 000000000..df07b58dc --- /dev/null +++ b/docs/.translated/fr_fr/commands/commands.mdx @@ -0,0 +1,11 @@ +`/amp` est un alias pour `/automodpack`. + +- `/automodpack` - Statut d'AutoModpack et aide générale. +- `/automodpack generate` - Générer le modpack. +- `/automodpack host` - Statut de l'hébergement du modpack. +- `/automodpack host start` - Démarrer l'hébergement du modpack. +- `/automodpack host stop` - Arrêter l'hébergement du modpack. +- `/automodpack host restart` - Redémarrer l'hébergement du modpack. +- `/automodpack host connections` - Liste toutes les connexions actives à l'hébergement du modpack. +- `/automodpack host fingerprint` - Obtenir le [certificat de l'empreinte](../technicals/certificate) de l'hôte du modpack. +- `/automodpack config reload` - Recharger les fichiers de configuration. \ No newline at end of file diff --git a/docs/.translated/fr_fr/compatibility/_meta.json b/docs/.translated/fr_fr/compatibility/_meta.json new file mode 100644 index 000000000..29d93bf90 --- /dev/null +++ b/docs/.translated/fr_fr/compatibility/_meta.json @@ -0,0 +1,14 @@ +{ + "launchers.mdx": { + "name": "Compatibilité des launchers", + "icon": null + }, + "mods.mdx": { + "name": "Modifications compatibility", + "icon": null + }, + "proxies.mdx": { + "name": "Proxies compatibility", + "icon": null + } +} diff --git a/docs/.translated/fr_fr/compatibility/launchers.mdx b/docs/.translated/fr_fr/compatibility/launchers.mdx new file mode 100644 index 000000000..33cea4460 --- /dev/null +++ b/docs/.translated/fr_fr/compatibility/launchers.mdx @@ -0,0 +1,15 @@ +Most of the launchers will work without any issues, but some modified custom clients won't work. Here is a list of known launchers and their compatibility with AutoModpack: + +| Launcher Name | Compatibility | Notes | +|---------------------------|---------------|----------------------------------------------------------------------------------------------------------------| +| Vanilla official Launcher | ✅ | Supported | +| Prism/MultiMC Launcher | ✅ | Prism is recommended - Perfect support, with additional features like modloader version synchronization | +| Modrinth Launcher | ✅ | Supported | +| CurseForge | ✅ | Supported | +| GDLauncher | ✅ | Supported | +| ATLauncher | ✅ | Supported | +| PojavLauncher (Android) | ⚠️ | Currently broken, worked on previous versions, [read more](https://github.com/Skidamek/AutoModpack/issues/379) | +| FCL (Android) | ⚠️ | Currently broken, worked on previous versions, [read more](https://github.com/Skidamek/AutoModpack/issues/379) | +| Feather Client | ❌ | Unsupported - breaks modpack loading | +| Lunar Client | ❌ | Unsupported - breaks modpack loading | +| PCL2 | ❌ | Unsupported - breaks modpack loading | diff --git a/docs/.translated/fr_fr/compatibility/mods.mdx b/docs/.translated/fr_fr/compatibility/mods.mdx new file mode 100644 index 000000000..94748cef5 --- /dev/null +++ b/docs/.translated/fr_fr/compatibility/mods.mdx @@ -0,0 +1,5 @@ +AutoModpack devrait fonctionner avec toutes les modifications Minecraft (sinon, ce ne serait pas un bon outil de mise à jour de modpack). Cependant, il existe quelques exceptions et limitations dont vous devez être conscient : + +- Mods de Majrusz : Nécessitent une version modifiée de la Majrusz Library pour fonctionner avec AutoModpack. [Github Issue](https://github.com/Skidamek/AutoModpack/issues/268). +- [ModSets](https://modrinth.com/mod/mod-sets) : Ne fonctionne actuellement pas en raison de similitudes dans le processus de modification du chargement des mods. +- [Cesium](https://modrinth.com/mod/cesium) : Cesium est actuellement incompatible en raison d'un mauvais empaquetage de zstd du côté d'AutoModpack. Cela sera bientôt corrigé. [GitHub Issue](https://github.com/Skidamek/AutoModpack/issues/384) \ No newline at end of file diff --git a/docs/.translated/fr_fr/compatibility/proxies.mdx b/docs/.translated/fr_fr/compatibility/proxies.mdx new file mode 100644 index 000000000..f4f6e370a --- /dev/null +++ b/docs/.translated/fr_fr/compatibility/proxies.mdx @@ -0,0 +1,14 @@ +La plupart des proxys Minecraft ne fonctionneront pas avec AutoModpack en raison des paquets de la phase de connexion précoce. Cependant, Gate Lite est supporté et fonctionne bien avec AutoModpack. Voici une liste des proxys connus et de leur compatibilité : + +| Nom du Proxy                                      | Compatibilité | Notes         | +|---------------------------------------------------|----------------|---------------| +| [Gate Lite](https://gate.minekube.com/guide/lite) | ✅             | Supporté      | +| Gate                                              | ❌             | Non supporté  | +| Velocity                                          | ❌             | Non supporté  | +| Waterfall                                         | ❌             | Non supporté  | +| BungeeCord                                        | ❌             | Non supporté  | + +Lorsque vous utilisez Gate Lite, il est crucial de configurer correctement `portToSend`. Par défaut, les clients tentent de se connecter à l'hôte du modpack en utilisant le même port que celui utilisé pour se connecter à votre serveur Minecraft. Cependant, lorsque vous utilisez un proxy comme Gate, ce port diffère du port réel de votre serveur Minecraft, empêchant les clients de se connecter pour télécharger le modpack. + +Par conséquent, si votre modpack est hébergé avec `bindPort` défini sur `-1`, `portToSend` doit être défini sur le port depuis lequel votre serveur Minecraft est accessible (généralement, ce sera le même port sur lequel le serveur Minecraft écoute). +De même, si `bindPort` est défini sur un port spécifique, `portToSend` doit être défini sur le port depuis lequel l'hôte du modpack est accessible (généralement, ce sera le même que `bindPort`). \ No newline at end of file diff --git a/docs/.translated/fr_fr/configuration/_meta.json b/docs/.translated/fr_fr/configuration/_meta.json new file mode 100644 index 000000000..aed4f3058 --- /dev/null +++ b/docs/.translated/fr_fr/configuration/_meta.json @@ -0,0 +1,14 @@ +{ + "server-config.mdx": { + "name": "Server Configuration", + "icon": null + }, + "client-config.mdx": { + "name": "Client Configuration", + "icon": null + }, + "troubleshooting.mdx": { + "name": "Troubleshooting", + "icon": null + } +} diff --git a/docs/.translated/fr_fr/configuration/client-config.mdx b/docs/.translated/fr_fr/configuration/client-config.mdx new file mode 100644 index 000000000..630f8c8f7 --- /dev/null +++ b/docs/.translated/fr_fr/configuration/client-config.mdx @@ -0,0 +1,10 @@ +### Fichier de Configuration Client + +`/automodpack/automodpack-client.json` + +| Nom                  | Valeur par Défaut | Description                                                                                                                                                        | +|---------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT`  | ``  | Numéro de version du fichier utilisé pour la conversion automatique des anciennes versions de configuration vers les nouvelles lors de la mise à jour d'AutoModpack.  | +| `selectedModpack`   |                  | Le dossier principal du modpack auquel vous voulez jouer. Taper le nom du modpack ici provoquera son chargement.                                            | +| `installedModpacks` |                  | Une liste des modpacks qui sont installés sur le client.                                                                                                       | +| `selfUpdater`       | `false`          | Active/désactive les mises à jour automatiques d'AutoModpack. Cela n'affecte pas l'activité du mod dans l'installation des modpacks. Cela ne désactive pas la synchronisation de la version d'AutoModpack avec le serveur. | diff --git a/docs/.translated/fr_fr/configuration/server-config.mdx b/docs/.translated/fr_fr/configuration/server-config.mdx new file mode 100644 index 000000000..d9f597138 --- /dev/null +++ b/docs/.translated/fr_fr/configuration/server-config.mdx @@ -0,0 +1,29 @@ +### Fichier de Configuration Serveur +`~/automodpack/automodpack-server.json` + +| Nom | Valeur par Défaut | Description | +|-------------------------------|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT` | `` | Numéro de version du fichier utilisé pour la conversion automatique des anciennes versions de configuration vers les nouvelles lors de la mise à jour d'AutoModpack. | +| `modpackName` | `""` | Le nom du modpack du serveur, s'affiche pendant le téléchargement du modpack et c'est ainsi que le répertoire du modpack est nommé dans `~/automodpack/modpacks/`. | +| `modpackHost` | `true` | Démarre le serveur d'hébergement du modpack. | +| `generateModpackOnStart` | `true` | Regénère automatiquement les métadonnées du modpack au démarrage du serveur. | +| `syncedFiles` | `"/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"` | Une liste de *chemins relatifs* depuis le dossier racine du serveur Minecraft qui seront **synchronisés avec le modpack**. Utilisez des jokers comme `*` pour inclure plusieurs fichiers de ce répertoire (ex: `/mods/*.jar`). Ou utilisez `**` (double étoile) pour inclure récursivement tous les fichiers et sous-dossiers d'un répertoire (ex: `/config/**`). Préfixez un chemin avec `!` pour l'**exclure** de la synchronisation. **Note :** Ceci ne **copie pas** les fichiers ; cela définit seulement ce qui sera disponible côté client sous `~/.minecraft/`. | +| `allowEditsInFiles` | `"/options.txt", "/config/**"` | Une liste de fichiers que les clients sont autorisés à modifier. En d'autres termes, des fichiers qui sont téléchargés une seule fois puis ignorés lors des mises à jour. La configuration fonctionne exactement de la même manière que `syncedFiles`. | +| `autoExcludeServerSideMods` | `true` | Exclut automatiquement les mods côté serveur du modpack. (Ne fonctionne que si le développeur du mod a spécifié le type d'environnement dans les métadonnées du mod) | +| `autoExcludeUnnecessaryFiles` | `true` | Ignore automatiquement les fichiers qui sont : vides, cachés, temporaires, désactivés ou de sauvegarde. | +| `requireAutoModpackOnClient` | `true` | Indique si ce mod est optionnel ou non pour que les clients rejoignent le serveur. | +| `nagUnModdedClients` | `true` | Si `true`, les clients sans AutoModpack recevront un message de rappel dans le chat à leur connexion. Pour fonctionner, `requireAutoModpackOnClient` doit être `false`. | +| `nagMessage` | `"This server provides dedicated modpack through AutoModpack!"` | Le message qui sera affiché aux clients sans AutoModpack. Pour fonctionner, `nagUnModdedClients` doit être `true`. | +| `nagClickableMessage` | `"Click here to get the AutoModpack!"` | La partie cliquable du message qui sera affichée aux clients sans AutoModpack. Pour fonctionner, `nagUnModdedClients` doit être `true`. | +| `nagClickableLink` | `"https://modrinth.com/project/automodpack"` | Le lien qui sera ouvert lorsque le message est cliqué. Pour fonctionner, `nagUnModdedClients` doit être `true`. | +| `bindAddress` | `""` | L'adresse à laquelle le serveur d'hébergement du modpack se lie. Laissez ce champ vide pour déterminer automatiquement une adresse locale (typiquement `0.0.0.0` ou `::0`). Lorsque `bindPort` est défini sur `-1`, cette valeur est **ignorée** et l'hôte du modpack sera lié à la même adresse que le serveur Minecraft, comme spécifié dans le fichier `server.properties`. | +| `bindPort` | `-1` | Le numéro de port sur lequel le serveur d'hébergement du modpack écoute. Si défini sur `-1`, le modpack sera hébergé directement sur le port de votre serveur Minecraft. | +| `addressToSend` | `""` | L'adresse de l'hôte du modpack qui sera utilisée par les clients pour télécharger le modpack. Si vide, les clients utiliseront la même adresse que celle utilisée pour se connecter au serveur Minecraft. (N'incluez **pas** le port ici, utilisez `portToSend` à la place) | +| `portToSend` | `-1` | Le numéro de port qui est envoyé avec `addressToSend`. Si défini sur `-1`, les clients utiliseront le même port que celui utilisé pour se connecter au serveur Minecraft. | +| `disableInternalTLS` | `false` | Désactive la gestion TLS interne. Si activé, vous devez gérer le TLS de manière externe, par ex. sur un reverse proxy. En utilisant cette option, vous devez utiliser un `bindPort` différent de `-1` et vous devriez envisager de définir `bindAddress` sur une adresse de loopback (ex: `127.0.0.1`), afin que le trafic non chiffré entre l'hôte du modpack et le reverse proxy ne fuite pas. | +| `updateIpsOnEveryStart` | `false` | Met à jour `addressToSend` à chaque démarrage du serveur. Peut être utile si vous avez une adresse IP dynamique. (Utilisez-le uniquement si vous en avez vraiment besoin !) | +| `bandwidthLimit` | `0` | Limite de téléversement en Mbps à laquelle le serveur d'hébergement du modpack est restreint. (La valeur doit être un entier, `0` - signifie illimité) | +| `validateSecrets` | `true` | Fait en sorte que l'hôte du modpack valide et autorise les tentatives de téléchargement du modpack à l'aide de secrets uniques. | +| `secretLifetime` | `336` | Durée en heures pendant laquelle le secret d'un joueur reste valide. | +| `selfUpdater` | `false` | Active/désactive toutes les mises à jour d'AutoModpack. Cela n'affecte pas l'activité du mod dans l'installation des modpacks. | +| `acceptedLoaders` | `""` | Permet aux joueurs de différents modloaders de se connecter à votre serveur. (À utiliser avec prudence, la plupart des mods ne fonctionnent que sur un seul loader) | \ No newline at end of file diff --git a/docs/.translated/fr_fr/configuration/troubleshooting.mdx b/docs/.translated/fr_fr/configuration/troubleshooting.mdx new file mode 100644 index 000000000..28592c062 --- /dev/null +++ b/docs/.translated/fr_fr/configuration/troubleshooting.mdx @@ -0,0 +1,6 @@ +## Dépannage des problèmes de configuration +Lorsque vous avez fini de modifier les fichiers de configuration, assurez-vous de les recharger en exécutant la commande `automodpack config reload` ou en redémarrant le serveur/jeu. + +Si vous ne trouvez pas les fichiers de configuration, assurez-vous que le client/serveur a été démarré au moins une fois, afin que les fichiers soient générés. + +Si les valeurs sont restaurées par défaut, cela peut signifier que votre fichier de configuration est malformé. Assurez-vous d'utiliser un éditeur avec un analyseur JSON intégré, ou vérifiez-le dans un outil en ligne, par ex. https://jsonformatter.org/json-parser. diff --git a/docs/.translated/fr_fr/faq.mdx b/docs/.translated/fr_fr/faq.mdx new file mode 100644 index 000000000..4735ef4a2 --- /dev/null +++ b/docs/.translated/fr_fr/faq.mdx @@ -0,0 +1,49 @@ +### Est-ce un mod côté serveur ? +Non, c'est les deux. Vous devez l'installer à la fois sur le serveur et sur le client. + +### Comment puis-je ajouter des mods uniquement client au modpack ? (ex: le serveur crash avec un certain mod mais je le veux pour les clients) +Jetez un œil à la [page de création de modpack](techinicals/modpack-creation). + +### Comment restreindre l'accès à mon modpack ? +Activez `validateSecrets` dans la [config](configuration/server-config), activez `online-mode` dans le fichier server.properties et activez la whitelist ou bannissez quelqu'un. + +### Est-ce que cela fonctionne en LAN (monde solo avec l'option pour que d'autres joueurs rejoignent) ? +Oui. N'oubliez pas de générer le modpack via la commande /`automodpack generate` et de démarrer l'hébergement du modpack avec /`automodpack host start`. + +### Est-ce que cela fonctionne avec un monde solo partagé via Essential, World Host, e4mc ? +Peu de chance, la plupart de ces types de mods ne transfèrent que les paquets Minecraft. Ces mods devraient prendre en charge le protocole d'AutoModpack ou simplement relayer tous les paquets TCP au lieu de se limiter au protocole Minecraft. + +### Puis-je utiliser des mods hors-modpack sur le client ? +Oui, vous pouvez ajouter n'importe quel mod que vous souhaitez utiliser en parallèle du modpack téléchargé en plaçant simplement le mod dans le dossier mods standard. + +### Comment puis-je supprimer des fichiers du modpack ? +Supprimez simplement le fichier du serveur, ou excluez-le de la synchronisation, lisez la section sur `syncedFiles` dans la [config](configuration/server-config). + +### Puis-je retirer/désactiver les fichiers/mods hors-modpack pour les joueurs ? +Pas encore, mais cela pourrait arriver dans le futur. + +### Dois-je ouvrir un port sur mon routeur ? +Non, ce n'est pas nécessaire. Par défaut, AutoModpack s'injecte dans les E/S réseau de Minecraft, ce qui réutilise le port de votre serveur Minecraft. (ex: 25565) + +Si vous voulez utiliser un port différent, désactivez `hostModpackOnMinecraftPort` et modifiez `hostPort` dans la [config](configuration/server-config). + +### Que se passe-t-il quand je mets à jour la version d'AutoModpack sur le serveur ? +Le client essaie toujours de se synchroniser avec la version d'AutoModpack du serveur. Il se met à jour ou se rétrograde à la version du serveur si celle-ci est trouvée sur Modrinth. + +### Pourquoi le chemin d'hébergement du modpack est-il si long ? +Il est prévu d'implémenter la possibilité d'ajouter plus d'un seul modpack, le modpack `main`. + +### Quelles versions de Minecraft sont supportées ? +Toutes les versions que vous voyez dans la [dernière version](https://github.com/Skidamek/AutoModpack/releases/latest). + +### Mon launcher est-il supporté ? +Jetez un œil à la [page de compatibilité](compatibility/launchers) pour plus d'informations sur les launchers supportés. + +### Est-ce que ce mod met à jour les mods de mon modpack aux dernières versions ? +Non, c'est hors du champ d'application de ce projet. Ce mod ne fait que synchroniser le modpack du serveur avec les clients. Tous vos mods sur le serveur devront toujours être mis à jour manuellement. AutoModpack s'assure simplement que les versions du client correspondent à celles du serveur. + +### J'ai trouvé un bug, que dois-je faire ? +Si vous avez trouvé un bug, veuillez le signaler sur le [tracker de bugs](https://github.com/Skidamek/AutoModpack/issues). + +### J'ai trouvé une faille de sécurité, que dois-je faire ? +Veuillez la signaler en privé à l'auteur sur [Discord](https://discordapp.com/users/464522287618457631), nous essaierons de la résoudre dès que possible. \ No newline at end of file diff --git a/docs/.translated/fr_fr/quick-start.mdx b/docs/.translated/fr_fr/quick-start.mdx new file mode 100644 index 000000000..c267ad965 --- /dev/null +++ b/docs/.translated/fr_fr/quick-start.mdx @@ -0,0 +1,36 @@ +Alors comme ça vous voulez commencer à utiliser AutoModpack ? Super ! C'est parti. + +## Installation + +1. **Télécharger AutoModpack** : Obtenez la dernière version d'AutoModpack pour votre version de Minecraft depuis [Modrinth](https://modrinth.com/mod/automodpack) ou [CurseForge](https://www.curseforge.com/minecraft/mc-mods/automodpack). +2. **Installer AutoModpack** : Placez le mod téléchargé dans le dossier `mods` de votre instance Minecraft. +3. **Démarrer votre serveur** : Lancez votre serveur Minecraft avec AutoModpack installé. Cela générera les métadonnées initiales du modpack. + +Vous êtes maintenant, dans la plupart des cas, prêt à démarrer. À ce stade, il est recommandé d'essayer de vous connecter à votre serveur avec uniquement AutoModpack d'installé sur le client pour voir si tout fonctionne. + +## Première Connexion +Lors de la première connexion, il vous sera demandé de vérifier l'empreinte du certificat du serveur. C'est une mesure de sécurité pour s'assurer que vous vous connectez au bon serveur et non à un serveur malveillant. En savoir plus sur la [certificate verification](technicals/certificate). +Après une vérification réussie, AutoModpack vous demandera de confirmer l'installation du modpack. Confirmez et le modpack sera téléchargé et installé automatiquement. + +Maintenant, redémarrez votre jeu et le modpack de votre serveur sera chargé. Vous devriez pouvoir vous connecter à nouveau à votre serveur, mais cette fois-ci en apparaissant dans l'overworld ! + + +Si vous ne voyez pas les mods de votre modpack dans le dossier mods, ne vous inquiétez pas. Les mods ne seront pas installés dans votre dossier mods standard, ils seront installés dans le dossier `~/.minecraft/automodpack/modapcks/{your-modpack}/mods/` à la place. Il y a de [bonnes raisons pour ça](technicals/file-structure). + + +## Dépannage +Si vous rencontrez des problèmes à ce stade, voici quelques étapes de dépannage courantes : +- Assurez-vous d'utiliser la même version d'AutoModpack sur le serveur et le client. +- Assurez-vous que votre serveur est en cours d'exécution et accessible sans AutoModpack. +- Vérifiez les logs et la console du serveur, il pourrait y avoir des indices sur ce qui n'a pas fonctionné. +- Lisez la [page faq](faq) pour les questions et réponses courantes. + +Si vous rencontrez toujours des problèmes, rejoignez notre [serveur Discord](https://discord.gg/hS6aMyeA9P) et créez une publication dans le salon `#support`, nous essaierons de vous aider ! + +## Prochaines Étapes + +Maintenant que vous avez tout installé et fait fonctionner AutoModpack, vous pouvez commencer à personnaliser votre modpack. Voici quelques choses que vous pouvez faire ensuite : + +- Lisez la [page de création de modpack](technicals/modpack-creation) pour apprendre à gérer votre modpack. +- Lisez la [page de configuration](configuration/server-config) pour apprendre à configurer AutoModpack selon vos besoins. +``` \ No newline at end of file diff --git a/docs/.translated/fr_fr/technicals/_meta.json b/docs/.translated/fr_fr/technicals/_meta.json new file mode 100644 index 000000000..69aabdfc4 --- /dev/null +++ b/docs/.translated/fr_fr/technicals/_meta.json @@ -0,0 +1,22 @@ +{ + "connection.mdx": { + "name": "Connection", + "icon": null + }, + "certificate.mdx": { + "name": "Gestion des certificats", + "icon": null + }, + "modpack-creation.mdx": { + "name": "Création du modpack", + "icon": null + }, + "file-structure.mdx": { + "name": "Structure des fichiers", + "icon": null + }, + "modpack-hosting.mdx": { + "name": "Hebergement du modpack", + "icon": null + } +} diff --git a/docs/.translated/fr_fr/technicals/certificate.mdx b/docs/.translated/fr_fr/technicals/certificate.mdx new file mode 100644 index 000000000..e60a753bd --- /dev/null +++ b/docs/.translated/fr_fr/technicals/certificate.mdx @@ -0,0 +1,74 @@ +### Comment vérifier l'empreinte du certificat ? +Pour vérifier, il suffit de **copier l’empreinte depuis la console de votre serveur dans le jeu.** Il s’agit d’une vérification unique : vous n’aurez pas à la refaire après la première connexion. Ce processus permet de prévenir les menaces de sécurité comme les attaques de type [Man-in-the-middle](https://fr.wikipedia.org/wiki/Attaque_de_l%27homme_du_milieu). L’empreinte est la même pour tous les joueurs de votre serveur et elle ne change jamais. Il est recommandé de la partager avec les joueurs à l’avance. + +Si vous ne trouvez pas l’empreinte dans la console, vous pouvez la récupérer à tout moment avec la commande `/automodpack host fingerprint` [commande](../commands/commands). + +Cependant, ne la considérez pas comme un mot de passe pour le modpack : ce n’en est pas un, et ce n’est pas non plus un secret. C’est une partie publique et vérifiable du certificat du serveur, **n’importe qui pourrait télécharger** votre modpack sans connaître cette empreinte, par exemple en contournant cette vérification avec `I AM INCREDIBLY STUPID`. (ne faites pas ça) + +Pour fournir le modpack uniquement aux joueurs autorisés/liste blanche, utilisez l’option `validateSecrets` dans la [configuration du serveur](../configuration/server-config) (activée par défaut). + + +Si vous ne souhaitez pas que les joueurs valident manuellement le certificat et que vous possédez un nom de domaine (requis), vous pouvez fournir votre propre certificat signé par une autorité de certification (CA). + +### Comment fournir votre propre certificat signé par une CA ? + +Vous pouvez utiliser des outils comme [Certbot](https://eff-certbot.readthedocs.io/en/stable/install.html). + +Une fois le certificat obtenu, sur le serveur dans le dossier `~/automodpack/.private/`, remplacez les fichiers `cert.crt` (avec le certificat complet en chaîne) et `key.pem` ([la clé doit être au format PKCS#8 (PEM)](https://netty.io/wiki/sslcontextbuilder-and-private-key.html)) – attention : l’interception de ces fichiers pourrait permettre l’usurpation de votre serveur, [en savoir plus](https://security.stackexchange.com/a/16694). +Si vous hébergez le modpack sur un sous-domaine ou domaine différent de celui de votre serveur Minecraft, assurez-vous que votre certificat couvre bien les deux. + +### Petit tutoriel pour obtenir un certificat avec Certbot + +Installez Certbot sur votre PC ou serveur, puis exécutez la commande suivante et suivez les instructions fournies par Certbot pour obtenir un certificat pour votre domaine : + +```bash +# certbot certonly --manual --preferred-challenges dns -d -d + + +✅ Si vous hébergez le modpack sur **le même port que le serveur Minecraft**, les domaines sont souvent identiques → dans ce cas, **le second `-d` n’est pas nécessaire**. + +> ⚠️ **Attention** : ne copiez-collez jamais une commande trouvée au hasard sur Internet. +> Lisez toujours la documentation de l’outil que vous utilisez ! + +Une fois le certificat obtenu : + +* `fullchain.pem` → renommez-le en `cert.crt` +* `privkey.pem` → renommez-le en `key.pem` + +Placez-les dans `~/automodpack/.private/` + +--- + +### Mais pourquoi ai-je besoin de tout ça ? C’est juste un modpack pour mes amis ! + +Si quelqu’un peut prendre le contrôle de la connexion Automodpack, +il pourrait **installer n’importe quoi sur votre PC (ou celui de vos amis)**. + +Minecraft **n’est pas sandboxé**, et les mods peuvent **exécuter n’importe quel code**. +Donc injecter du code malveillant, même si vous n’êtes pas une cible spécifique, est un énorme risque : + +* vol de mots de passe +* vol de comptes +* suppression de fichiers importants (comme votre dossier de mèmes de chats) + +➡️ **Voilà pourquoi la sécurité est essentielle**, même dans un petit contexte entre amis. + +--- + +### Hmm... ok, mais personne ne connaît mon serveur à part mes amis, pourquoi devrais-je m’en soucier ? + +Même si vous pensez que votre serveur est privé, **ce n’est probablement pas le cas**. +Des scanners de serveurs Minecraft analysent **tout Internet** pour détecter les serveurs accessibles. + +Si votre serveur est vulnérable et que vous ne vérifiez pas le certificat, vous devenez une cible potentielle. + +🔐 De plus, rien n’empêche une attaque via d’autres moyens : + +* accès SSH +* panneau d’administration de l’hébergeur + +👉 **En tant qu’administrateur**, prenez ces mesures sérieusement : + +* mots de passe robustes +* authentification à deux facteurs +* mises à jour régulières de votre logiciel serveur diff --git a/docs/.translated/fr_fr/technicals/connection.mdx b/docs/.translated/fr_fr/technicals/connection.mdx new file mode 100644 index 000000000..5ab855b00 --- /dev/null +++ b/docs/.translated/fr_fr/technicals/connection.mdx @@ -0,0 +1,35 @@ +### Chiffrement + +La connexion pour le téléchargement du modpack est sécurisée à l'aide de TLS 1.3. + +C'est la même norme de sécurité que celle utilisée par HTTPS, le protocole que vous utilisez tout le temps pour naviguer sur le web (même en ce moment \!). + +TLS dispose d'une méthode de vérification de l'identité du serveur à l'aide d'un certificat, ce qui est crucial pour prévenir des attaques telles que [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). + +C'est pourquoi AutoModpack vous demande de vérifier l'empreinte du certificat du serveur lors de la première connexion. Lisez [Comment vérifier l'empreinte du certificat](certificate) pour plus de détails. + +### Téléchargements Directs + +AutoModpack utilise des liens de téléchargement direct depuis les API de Modrinth et CurseForge pour télécharger la plupart des fichiers de votre modpack. +Cela signifie que les auteurs des mods sont crédités pour chaque téléchargement. + +Cependant, si un fichier que vous fournissez dans le modpack n'est pas disponible sur Modrinth ou CurseForge, AutoModpack le téléchargera (également directement) mais depuis votre serveur. + +### Compression + +Pour accélérer le téléchargement, AutoModpack compresse les fichiers du modpack à l'aide de l'algorithme de compression [Zstandard](https://facebook.github.io/zstd/). +Cet algorithme est très rapide et offre un excellent taux de compression, ce qui signifie que les fichiers du modpack sont plus petits et plus rapides à télécharger. +C'est particulièrement utile pour les modpacks plus volumineux ou les connexions Internet plus lentes. +Cependant, cela ne s'applique qu'aux fichiers téléchargés depuis votre serveur. + +### Autorisation + +\ +Ne confondez pas cela avec la [certificate fingerprint verification](certificate), qui est utilisée pour vérifier l'identité du serveur. +\ + +AutoModpack utilise un mécanisme d'autorisation simple pour empêcher l'accès non autorisé à votre modpack. +Lorsque vous vous connectez au serveur, en arrière-plan, AutoModpack génère un secret unique et aléatoire pour chaque joueur. Ce secret est ensuite envoyé au joueur et utilisé pour l'autoriser à télécharger les fichiers du modpack. +Chaque fois que vous vous reconnectez au serveur, l'ancien secret est invalidé et un nouveau est généré. +Cela empêche l'accès non autorisé aux fichiers de votre modpack et garantit que seuls les joueurs sur liste blanche et non bannis peuvent télécharger le modpack. +Si vous ne souhaitez pas utiliser cette fonctionnalité, vous pouvez la désactiver (`validateSecrets`) dans la [server config](../configuration/server-config). \ No newline at end of file diff --git a/docs/.translated/fr_fr/technicals/file-structure.mdx b/docs/.translated/fr_fr/technicals/file-structure.mdx new file mode 100644 index 000000000..ca7e4c979 --- /dev/null +++ b/docs/.translated/fr_fr/technicals/file-structure.mdx @@ -0,0 +1,24 @@ +Tous les fichiers d'AutoModpack sont conservés dans le dossier `~/automodpack/`. + +### Serveur + +Le modpack est généré à partir des fichiers se trouvant dans `~/automodpack/host-modpack/main/` et/ou `syncedFiles` (lisez la [config](https://github.com/Skidamek/AutoModpack/blob/main/docs/configuration/server-config)) ce qui se traduit par le dossier `~/.minecraft/` (dossier racine de l'instance minecraft) sur le client. + +La génération du modpack signifie la création de `~/automodpack/host-modpack/automodpack-content.json` qui contient les métadonnées de chaque fichier du modpack. Vous **ne devriez PAS** toucher ce fichier manuellement, modifiez plutôt la configuration. + +### Client + +Tous les modpacks téléchargés sont conservés dans `~/automodpack/modpacks/`. + +Tous les fichiers du modpack sélectionné sont copiés dans `~/.minecraft/` à l'exception des **mods** (avec quelques exceptions, en raison des limitations du mod loader). + +S'il les détecte, AutoModpack essaie également de se débarrasser de la plupart, sinon de la totalité, des mods dupliqués de votre dossier mods standard en faveur du dossier mods d'AutoModpack. +\ +Cela ne devrait pas causer de crashs, mais si vous remarquez qu'AutoModpack supprime une dépendance causant un crash du jeu, veuillez le signaler \! +\ + +Les mods sont séparés pour plusieurs raisons. Une telle approche atténue les inconvénients courants liés au placement de tous les mods du modpack dans le dossier mods standard, tels que : + + - **Crashs dus aux conflits de mods** : Cela ne les résoudra pas, mais cela donne au moins au propriétaire du serveur la possibilité de supprimer ou de corriger à distance les mods en conflit sans intervention manuelle du côté client. + - **Débogage** : Il est plus facile de voir quels mods font partie du modpack et lesquels n'en font pas partie. Comme les clients peuvent ajouter leurs propres mods au dossier mods standard, il est plus facile de déboguer lorsque quelque chose se casse ou entre en conflit. + - **Redémarrages** : Si tous les mods du modpack étaient placés dans le dossier mods standard, il faudrait un redémarrage pour mettre à jour les mods du modpack. Maintenant, cela peut être fait de manière transparente au démarrage sans aucun redémarrage \! \ No newline at end of file diff --git a/docs/.translated/fr_fr/technicals/modpack-creation.mdx b/docs/.translated/fr_fr/technicals/modpack-creation.mdx new file mode 100644 index 000000000..ff49f89cd --- /dev/null +++ b/docs/.translated/fr_fr/technicals/modpack-creation.mdx @@ -0,0 +1,17 @@ +**Oui, vous pouvez ajouter *n'importe quoi* à votre modpack.** + +#### Deux manières d'ajouter du contenu au modpack. + + - Pour réutiliser des fichiers déjà existants sur le serveur, utilisez `syncedFiles`. Pour en savoir plus, lisez la [config](https://github.com/Skidamek/AutoModpack/blob/main/docs/configuration/server-config). Par exemple, avec la configuration par défaut, cela crée un modpack contenant *chaque* mod (fichier se terminant par .jar) du dossier `~/mods/` du serveur. + + - Pour ajouter plus de fichiers au modpack, utilisez le répertoire séparé `~/automodpack/host-modpack/main/`. + Par exemple, pour ajouter plus de mods, créez un dossier `mods` et placez-y les mods, ce qui donnera `~/automodpack/host-modpack/main/mods/`**`votre-mod.jar`**. + Ou pour ajouter des packs de ressources, de manière analogue, créez un dossier `resourcepacks` et placez-y les packs de ressources, ce qui donnera `~/automodpack/host-modpack/main/resourcepacks/`**`votre-packderessources.zip`**. + +\ +Ne placez pas de fichiers de cache dans le modpack, comme le répertoire `.connector` d'un connecteur. Il n'est pas censé être fourni dans le modpack et cela casse les mises à jour transparentes/sans redémarrage. +\ + +Ces deux méthodes se traduisent par le répertoire `~/.minecraft/` du côté client (répertoire racine de l'instance Minecraft). + +N'oubliez pas de regénérer le modpack en utilisant `/automodpack generate` (voir les [commands](https://github.com/Skidamek/AutoModpack/blob/main/docs/commands/commands)) \ No newline at end of file diff --git a/docs/.translated/fr_fr/technicals/modpack-hosting.mdx b/docs/.translated/fr_fr/technicals/modpack-hosting.mdx new file mode 100644 index 000000000..0c8bfeb61 --- /dev/null +++ b/docs/.translated/fr_fr/technicals/modpack-hosting.mdx @@ -0,0 +1,2 @@ +Par défaut, AutoModpack héberge votre modpack sur le port de votre serveur Minecraft (par ex. 25565). Cela signifie que vous n'avez pas besoin d'ouvrir d'autre port sur votre routeur/pare-feu. +Si vous souhaitez utiliser un port différent, modifiez `bindPort` dans la [config](https://github.com/Skidamek/AutoModpack/blob/main/docs/configuration/server-config). \ No newline at end of file From f50aaad4527a647685f1935ac339474aa12e7e49 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 13:48:03 +0200 Subject: [PATCH 71/86] change id to comply with github action restrictions lets hope it works --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b4a4ef76b..8b3315d31 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -117,7 +117,7 @@ jobs: properties: 'publish.versions' - name: Fix game version - id: publish.versions + id: publish_versions run: | # Fixed \n in publish.versions isn't parsed by christian-draeger/read-properties as a line separator echo 'value<> $GITHUB_OUTPUT From df44545a12a8b655d97e9c53cf2092ce94535595 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 14:25:07 +0200 Subject: [PATCH 72/86] aaAAaaAAaAaAaaaaaaaaAa forgor --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8b3315d31..60f57595e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -171,7 +171,7 @@ jobs: version-type: release loaders: ${{ matrix.mod_brand }} - game-versions: ${{ steps.publish.versions.outputs.value }} + game-versions: ${{ steps.publish_versions.outputs.value }} game-version-filter: any dependencies: '' # declare the dependencies explicitly, so mc-publish won't try to load from fabric.mod.json From a7dd481a8428cb20364ca58a2434a7aaf6feeaec Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 14:53:36 +0200 Subject: [PATCH 73/86] try this --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 60f57595e..79f1dbeaf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -166,8 +166,8 @@ jobs: files: ${{ steps.file_info.outputs.path }} - name: ${{ format('{0} {1} {2}', steps.properties_c.outputs.mod.name, steps.properties_c.outputs.mod.version, matrix.mod_brand) }} - version: ${{ steps.properties_c.outputs.mod.version }} + name: ${{ format('{0} {1} {2}', steps.properties_c.outputs.mod_name, steps.properties_c.outputs.mod_version, matrix.mod_brand) }} + version: ${{ steps.properties_c.outputs.mod_version }} version-type: release loaders: ${{ matrix.mod_brand }} From d87f15b7168c5b98d9e86742eb5ec1d634b66ad6 Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 14:54:53 +0200 Subject: [PATCH 74/86] and here --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 79f1dbeaf..3799e72e6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -121,7 +121,7 @@ jobs: run: | # Fixed \n in publish.versions isn't parsed by christian-draeger/read-properties as a line separator echo 'value<> $GITHUB_OUTPUT - echo -e "${{ steps.properties_v.outputs.publish.versions }}" >> $GITHUB_OUTPUT + echo -e "${{ steps.properties_v.outputs.publish_versions }}" >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT - name: Prepare file information From c3e489bf9c16c4b74e2684d01be97d56a2f88d9e Mon Sep 17 00:00:00 2001 From: skidam Date: Wed, 25 Jun 2025 15:12:55 +0200 Subject: [PATCH 75/86] i hate ci/cd --- .github/workflows/release.yml | 6 +++--- build.fabric.gradle.kts | 6 +++--- build.forge.gradle.kts | 6 +++--- build.neoforge.gradle.kts | 6 +++--- buildSrc/src/main/kotlin/automodpack.common.gradle.kts | 4 ++-- core/build.gradle.kts | 2 +- gradle.properties | 4 ++-- loader/loader-core.gradle.kts | 2 +- loader/loader-fabric-core.gradle.kts | 2 +- loader/loader-fabric.gradle.kts | 2 +- loader/loader-forge.gradle.kts | 4 ++-- loader/loader-neoforge.gradle.kts | 4 ++-- versions/1.18.2-fabric/gradle.properties | 2 +- versions/1.18.2-forge/gradle.properties | 2 +- versions/1.19.2-fabric/gradle.properties | 2 +- versions/1.19.2-forge/gradle.properties | 2 +- versions/1.19.4-fabric/gradle.properties | 2 +- versions/1.19.4-forge/gradle.properties | 2 +- versions/1.20.1-fabric/gradle.properties | 2 +- versions/1.20.1-forge/gradle.properties | 2 +- versions/1.20.4-fabric/gradle.properties | 2 +- versions/1.20.4-neoforge/gradle.properties | 2 +- versions/1.20.6-fabric/gradle.properties | 2 +- versions/1.20.6-neoforge/gradle.properties | 2 +- versions/1.21.1-fabric/gradle.properties | 2 +- versions/1.21.1-neoforge/gradle.properties | 2 +- versions/1.21.3-fabric/gradle.properties | 2 +- versions/1.21.3-neoforge/gradle.properties | 2 +- versions/1.21.4-fabric/gradle.properties | 2 +- versions/1.21.4-neoforge/gradle.properties | 2 +- versions/1.21.5-fabric/gradle.properties | 2 +- versions/1.21.5-neoforge/gradle.properties | 2 +- versions/1.21.6-fabric/gradle.properties | 2 +- versions/1.21.6-neoforge/gradle.properties | 2 +- 34 files changed, 46 insertions(+), 46 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3799e72e6..cf483ef71 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -107,19 +107,19 @@ jobs: uses: christian-draeger/read-properties@1.1.1 with: path: gradle.properties - properties: 'mod.name mod.version' + properties: 'mod_name mod_version' - name: Read version-specific properties id: properties_v uses: christian-draeger/read-properties@1.1.1 with: path: ${{ format('versions/{0}/gradle.properties', matrix.subproject) }} - properties: 'publish.versions' + properties: 'publish_versions' - name: Fix game version id: publish_versions run: | - # Fixed \n in publish.versions isn't parsed by christian-draeger/read-properties as a line separator + # Fixed \n in publish_versions isn't parsed by christian-draeger/read-properties as a line separator echo 'value<> $GITHUB_OUTPUT echo -e "${{ steps.properties_v.outputs.publish_versions }}" >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT diff --git a/build.fabric.gradle.kts b/build.fabric.gradle.kts index 5cd6ed502..fbc31398e 100644 --- a/build.fabric.gradle.kts +++ b/build.fabric.gradle.kts @@ -6,9 +6,9 @@ plugins { id("fabric-loom") } -version = "${property("mod.version")}" +version = "${property("mod_version")}" group = "${property("mod.group")}" -base.archivesName.set("${property("mod.name")}-mc${property("deps.minecraft")}-fabric".lowercase()) +base.archivesName.set("${property("mod_name")}-mc${property("deps.minecraft")}-fabric".lowercase()) loom { accessWidenerPath = rootProject.file("src/main/resources/automodpack.accesswidener") @@ -67,7 +67,7 @@ tasks { register("buildAndCollect") { group = "build" from(remapJar.map { it.archiveFile }) - into(rootProject.layout.buildDirectory.file("libs/${project.property("mod.version")}")) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod_version")}")) dependsOn("build") } } \ No newline at end of file diff --git a/build.forge.gradle.kts b/build.forge.gradle.kts index 1b07b48d9..332229a0f 100644 --- a/build.forge.gradle.kts +++ b/build.forge.gradle.kts @@ -8,9 +8,9 @@ plugins { id("net.neoforged.moddev.legacyforge") } -version = "${property("mod.version")}" +version = "${property("mod_version")}" group = "${property("mod.group")}" -base.archivesName.set("${property("mod.name")}-mc${property("deps.minecraft")}-forge".lowercase()) +base.archivesName.set("${property("mod_name")}-mc${property("deps.minecraft")}-forge".lowercase()) legacyForge { version = property("deps.forge") as String @@ -65,7 +65,7 @@ tasks { register("buildAndCollect") { group = "build" from(jar.map { it.archiveFile }) - into(rootProject.layout.buildDirectory.file("libs/${project.property("mod.version")}")) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod_version")}")) dependsOn("build") } } diff --git a/build.neoforge.gradle.kts b/build.neoforge.gradle.kts index d50727426..7e1bba8fd 100644 --- a/build.neoforge.gradle.kts +++ b/build.neoforge.gradle.kts @@ -4,9 +4,9 @@ plugins { id("net.neoforged.moddev") } -version = "${property("mod.version")}" +version = "${property("mod_version")}" group = "${property("mod.group")}" -base.archivesName.set("${property("mod.name")}-mc${property("deps.minecraft")}-neoforge".lowercase()) +base.archivesName.set("${property("mod_name")}-mc${property("deps.minecraft")}-neoforge".lowercase()) neoForge { version = property("deps.neoforge") as String @@ -38,7 +38,7 @@ tasks { register("buildAndCollect") { group = "build" from(jar.map { it.archiveFile }) - into(rootProject.layout.buildDirectory.file("libs/${project.property("mod.version")}")) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod_version")}")) dependsOn("build") } } diff --git a/buildSrc/src/main/kotlin/automodpack.common.gradle.kts b/buildSrc/src/main/kotlin/automodpack.common.gradle.kts index 282912621..774da674e 100644 --- a/buildSrc/src/main/kotlin/automodpack.common.gradle.kts +++ b/buildSrc/src/main/kotlin/automodpack.common.gradle.kts @@ -28,10 +28,10 @@ tasks.named("processResources") { fun prop(name: String) = project.property(name) as String val props = HashMap().apply { - this["version"] = prop("mod.version") + this["version"] = prop("mod_version") this["minecraft"] = prop("meta.minecraft") this["id"] = prop("mod.id") - this["name"] = prop("mod.name") + this["name"] = prop("mod_name") this["description"] = prop("mod.description") } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 87cad8110..ce131a635 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -7,7 +7,7 @@ plugins { base { archivesName = property("mod.id") as String + "-" + project.name - version = property("mod.version") as String + version = property("mod_version") as String group = property("mod.group") as String } diff --git a/gradle.properties b/gradle.properties index a9f168925..34af6929e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ deps.fabric-loader = 0.15.11 deps.mixin-extras = 0.5.0-rc.2 mod.id = automodpack -mod.name = AutoModpack -mod.version = 4.0.0-beta38 +mod_name = AutoModpack +mod_version = 4.0.0-beta38 mod.group = pl.skidam.automodpack mod.description = Enjoy a seamless modpack installation process and effortless updates with a user-friendly solution that simplifies management, making your gaming experience a breeze. diff --git a/loader/loader-core.gradle.kts b/loader/loader-core.gradle.kts index 224b81892..60824d979 100644 --- a/loader/loader-core.gradle.kts +++ b/loader/loader-core.gradle.kts @@ -4,7 +4,7 @@ plugins { base { archivesName = property("mod.id") as String + "-" + project.name - version = property("mod.version") as String + version = property("mod_version") as String group = property("mod.group") as String } diff --git a/loader/loader-fabric-core.gradle.kts b/loader/loader-fabric-core.gradle.kts index c23286541..8f0a1c3a0 100644 --- a/loader/loader-fabric-core.gradle.kts +++ b/loader/loader-fabric-core.gradle.kts @@ -7,7 +7,7 @@ plugins { base { archivesName = property("mod.id") as String + "-" + project.name - version = property("mod.version") as String + version = property("mod_version") as String group = property("mod.group") as String } diff --git a/loader/loader-fabric.gradle.kts b/loader/loader-fabric.gradle.kts index ef77b834d..8c58c3c38 100644 --- a/loader/loader-fabric.gradle.kts +++ b/loader/loader-fabric.gradle.kts @@ -4,7 +4,7 @@ plugins { base { archivesName = property("mod.id") as String + "-" + project.name - version = property("mod.version") as String + version = property("mod_version") as String group = property("mod.group") as String } diff --git a/loader/loader-forge.gradle.kts b/loader/loader-forge.gradle.kts index d5b0170a6..a6399dfbd 100644 --- a/loader/loader-forge.gradle.kts +++ b/loader/loader-forge.gradle.kts @@ -8,7 +8,7 @@ plugins { base { archivesName = property("mod.id") as String + "-" + project.name - version = property("mod.version") as String + version = property("mod_version") as String group = property("mod.group") as String } @@ -37,7 +37,7 @@ tasks { register("buildAndCollect") { group = "build" from(jar.map { it.archiveFile }) - into(rootProject.layout.buildDirectory.file("libs/${project.property("mod.version")}")) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod_version")}")) dependsOn("build") } } diff --git a/loader/loader-neoforge.gradle.kts b/loader/loader-neoforge.gradle.kts index 37ccd3e92..955b9c902 100644 --- a/loader/loader-neoforge.gradle.kts +++ b/loader/loader-neoforge.gradle.kts @@ -8,7 +8,7 @@ plugins { base { archivesName = property("mod.id") as String + "-" + project.name - version = property("mod.version") as String + version = property("mod_version") as String group = property("mod.group") as String } @@ -37,7 +37,7 @@ tasks { register("buildAndCollect") { group = "build" from(jar.map { it.archiveFile }) - into(rootProject.layout.buildDirectory.file("libs/${project.property("mod.version")}")) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod_version")}")) dependsOn("build") } } diff --git a/versions/1.18.2-fabric/gradle.properties b/versions/1.18.2-fabric/gradle.properties index 41fd22ab9..fbe2a23dd 100644 --- a/versions/1.18.2-fabric/gradle.properties +++ b/versions/1.18.2-fabric/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.18.2 deps.parchment=1.18.2:2022.11.06 deps.fabric-api=0.76.0+1.18.2 meta.minecraft=1.18.x -publish.versions=1.18\n1.18.1\n1.18.2 \ No newline at end of file +publish_versions=1.18\n1.18.1\n1.18.2 \ No newline at end of file diff --git a/versions/1.18.2-forge/gradle.properties b/versions/1.18.2-forge/gradle.properties index d8bc61d89..062b54532 100644 --- a/versions/1.18.2-forge/gradle.properties +++ b/versions/1.18.2-forge/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.18.2 deps.parchment=1.18.2:2022.11.06 deps.forge=1.18.2-40.2.10 meta.minecraft=1.18.x -publish.versions=1.18\n1.18.1\n1.18.2 +publish_versions=1.18\n1.18.1\n1.18.2 diff --git a/versions/1.19.2-fabric/gradle.properties b/versions/1.19.2-fabric/gradle.properties index dcb564715..d6edf72fa 100644 --- a/versions/1.19.2-fabric/gradle.properties +++ b/versions/1.19.2-fabric/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.19.2 deps.fabric-api=0.76.1+1.19.2 deps.parchment=1.19.2:2022.11.27 meta.minecraft=>=1.19 <=1.19.2 -publish.versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file +publish_versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file diff --git a/versions/1.19.2-forge/gradle.properties b/versions/1.19.2-forge/gradle.properties index 65887912f..d4ec7ed84 100644 --- a/versions/1.19.2-forge/gradle.properties +++ b/versions/1.19.2-forge/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.19.2 deps.forge=1.19.2-43.3.0 deps.parchment=1.19.2:2022.11.27 meta.minecraft=>=1.19 <=1.19.2 -publish.versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file +publish_versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file diff --git a/versions/1.19.4-fabric/gradle.properties b/versions/1.19.4-fabric/gradle.properties index 67d229280..8f72b9e20 100644 --- a/versions/1.19.4-fabric/gradle.properties +++ b/versions/1.19.4-fabric/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.19.4 deps.fabric-api=0.87.2+1.19.4 deps.parchment=1.19.4:2023.06.26 meta.minecraft=1.19.4 -publish.versions=1.19.4 \ No newline at end of file +publish_versions=1.19.4 \ No newline at end of file diff --git a/versions/1.19.4-forge/gradle.properties b/versions/1.19.4-forge/gradle.properties index 0e162dbe2..297fbf226 100644 --- a/versions/1.19.4-forge/gradle.properties +++ b/versions/1.19.4-forge/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.19.4 deps.forge=1.19.4-45.3.0 deps.parchment=1.19.4:2023.06.26 meta.minecraft=1.19.4 -publish.versions=1.19.4 \ No newline at end of file +publish_versions=1.19.4 \ No newline at end of file diff --git a/versions/1.20.1-fabric/gradle.properties b/versions/1.20.1-fabric/gradle.properties index 4bfbe69c9..e12481046 100644 --- a/versions/1.20.1-fabric/gradle.properties +++ b/versions/1.20.1-fabric/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.20.1 deps.fabric-api=0.92.6+1.20.1 deps.parchment=1.20.1:2023.09.03 meta.minecraft=>=1.20 <=1.20.1 -publish.versions=1.20\n1.20.1 \ No newline at end of file +publish_versions=1.20\n1.20.1 \ No newline at end of file diff --git a/versions/1.20.1-forge/gradle.properties b/versions/1.20.1-forge/gradle.properties index 44605f8d0..93985f5f1 100644 --- a/versions/1.20.1-forge/gradle.properties +++ b/versions/1.20.1-forge/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.20.1 deps.forge=1.20.1-47.3.0 deps.parchment=1.20.1:2023.09.03 meta.minecraft=>=1.20 <=1.20.1 -publish.versions=1.20\n1.20.1 +publish_versions=1.20\n1.20.1 diff --git a/versions/1.20.4-fabric/gradle.properties b/versions/1.20.4-fabric/gradle.properties index f3926d995..8723a8ce9 100644 --- a/versions/1.20.4-fabric/gradle.properties +++ b/versions/1.20.4-fabric/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.20.4 deps.fabric-api=0.97.3+1.20.4 deps.parchment=1.20.4:2024.04.14 meta.minecraft=1.20.4 -publish.versions=1.20.4 \ No newline at end of file +publish_versions=1.20.4 \ No newline at end of file diff --git a/versions/1.20.4-neoforge/gradle.properties b/versions/1.20.4-neoforge/gradle.properties index edd185e52..4899839a0 100644 --- a/versions/1.20.4-neoforge/gradle.properties +++ b/versions/1.20.4-neoforge/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.20.4 deps.neoforge=20.4.248 deps.parchment=1.20.4:2024.04.14 meta.minecraft=1.20.4 -publish.versions=1.20.4 \ No newline at end of file +publish_versions=1.20.4 \ No newline at end of file diff --git a/versions/1.20.6-fabric/gradle.properties b/versions/1.20.6-fabric/gradle.properties index c235674f0..b13737fbf 100644 --- a/versions/1.20.6-fabric/gradle.properties +++ b/versions/1.20.6-fabric/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.20.6 deps.fabric-api=0.100.8+1.20.6 deps.parchment=1.20.6:2024.06.16 meta.minecraft=1.20.6 -publish.versions=1.20.6 \ No newline at end of file +publish_versions=1.20.6 \ No newline at end of file diff --git a/versions/1.20.6-neoforge/gradle.properties b/versions/1.20.6-neoforge/gradle.properties index f033883a0..1d286b9d0 100644 --- a/versions/1.20.6-neoforge/gradle.properties +++ b/versions/1.20.6-neoforge/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.20.6 deps.neoforge=20.6.135 deps.parchment=1.20.6:2024.06.16 meta.minecraft=1.20.6 -publish.versions=1.20.6 +publish_versions=1.20.6 diff --git a/versions/1.21.1-fabric/gradle.properties b/versions/1.21.1-fabric/gradle.properties index d372d5a81..664fdb11f 100644 --- a/versions/1.21.1-fabric/gradle.properties +++ b/versions/1.21.1-fabric/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.21.1 deps.fabric-api=0.116.3+1.21.1 deps.parchment=1.21.1:2024.11.17 meta.minecraft=>=1.21 <=1.21.1 -publish.versions=1.21\n1.21.1 \ No newline at end of file +publish_versions=1.21\n1.21.1 \ No newline at end of file diff --git a/versions/1.21.1-neoforge/gradle.properties b/versions/1.21.1-neoforge/gradle.properties index 325c14d19..8b0a89942 100644 --- a/versions/1.21.1-neoforge/gradle.properties +++ b/versions/1.21.1-neoforge/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.21.1 deps.neoforge=21.1.169 deps.parchment=1.21.1:2024.11.17 meta.minecraft=>=1.21 <=1.21.1 -publish.versions=1.21\n1.21.1 \ No newline at end of file +publish_versions=1.21\n1.21.1 \ No newline at end of file diff --git a/versions/1.21.3-fabric/gradle.properties b/versions/1.21.3-fabric/gradle.properties index 004917c0f..62015b1bb 100644 --- a/versions/1.21.3-fabric/gradle.properties +++ b/versions/1.21.3-fabric/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.21.3 deps.fabric-api=0.114.1+1.21.3 deps.parchment=1.21.3:2024.12.07 meta.minecraft=>=1.21.2 <=1.21.3 -publish.versions=1.21.2\n1.21.3 \ No newline at end of file +publish_versions=1.21.2\n1.21.3 \ No newline at end of file diff --git a/versions/1.21.3-neoforge/gradle.properties b/versions/1.21.3-neoforge/gradle.properties index afcfcb82a..1a3adccf5 100644 --- a/versions/1.21.3-neoforge/gradle.properties +++ b/versions/1.21.3-neoforge/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.21.3 deps.neoforge=21.3.76 deps.parchment=1.21.3:2024.12.07 meta.minecraft=>=1.21.2 <=1.21.3 -publish.versions=1.21.2\n1.21.3 \ No newline at end of file +publish_versions=1.21.2\n1.21.3 \ No newline at end of file diff --git a/versions/1.21.4-fabric/gradle.properties b/versions/1.21.4-fabric/gradle.properties index fc03fe49f..32a9524c8 100644 --- a/versions/1.21.4-fabric/gradle.properties +++ b/versions/1.21.4-fabric/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.21.4 deps.fabric-api=0.119.3+1.21.4 deps.parchment=1.21.4:2025.03.23 meta.minecraft=>=1.21.4 -publish.versions=1.21.4 \ No newline at end of file +publish_versions=1.21.4 \ No newline at end of file diff --git a/versions/1.21.4-neoforge/gradle.properties b/versions/1.21.4-neoforge/gradle.properties index 0643e524f..e31bf34fa 100644 --- a/versions/1.21.4-neoforge/gradle.properties +++ b/versions/1.21.4-neoforge/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.21.4 deps.neoforge=21.4.135 deps.parchment=1.21.4:2025.03.23 meta.minecraft=>=1.21.4 -publish.versions=1.21.4 \ No newline at end of file +publish_versions=1.21.4 \ No newline at end of file diff --git a/versions/1.21.5-fabric/gradle.properties b/versions/1.21.5-fabric/gradle.properties index 1f42df586..509c61a89 100644 --- a/versions/1.21.5-fabric/gradle.properties +++ b/versions/1.21.5-fabric/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.21.5 deps.fabric-api=0.128.0+1.21.5 deps.parchment=1.21.5:2025.06.15 meta.minecraft=1.21.5 -publish.versions=1.21.5 \ No newline at end of file +publish_versions=1.21.5 \ No newline at end of file diff --git a/versions/1.21.5-neoforge/gradle.properties b/versions/1.21.5-neoforge/gradle.properties index 1c138eb6b..7a0f88d7a 100644 --- a/versions/1.21.5-neoforge/gradle.properties +++ b/versions/1.21.5-neoforge/gradle.properties @@ -2,4 +2,4 @@ deps.minecraft=1.21.5 deps.neoforge=21.5.75 deps.parchment=1.21.5:2025.06.15 meta.minecraft=1.21.5 -publish.versions=1.21.5 \ No newline at end of file +publish_versions=1.21.5 \ No newline at end of file diff --git a/versions/1.21.6-fabric/gradle.properties b/versions/1.21.6-fabric/gradle.properties index fe15fb1f1..0c926b8c5 100644 --- a/versions/1.21.6-fabric/gradle.properties +++ b/versions/1.21.6-fabric/gradle.properties @@ -1,4 +1,4 @@ deps.minecraft=1.21.6 deps.fabric-api=0.128.0+1.21.6 meta.minecraft=>=1.21.6 -publish.versions=1.21.6 \ No newline at end of file +publish_versions=1.21.6 \ No newline at end of file diff --git a/versions/1.21.6-neoforge/gradle.properties b/versions/1.21.6-neoforge/gradle.properties index fc32650c1..dfe3f829e 100644 --- a/versions/1.21.6-neoforge/gradle.properties +++ b/versions/1.21.6-neoforge/gradle.properties @@ -1,4 +1,4 @@ deps.minecraft=1.21.6 deps.neoforge=21.6.11-beta meta.minecraft=>=1.21.6 -publish.versions=1.21.6 \ No newline at end of file +publish_versions=1.21.6 \ No newline at end of file From ae1624e7ba8e1d782d1c60ce0b775b68fda01d9e Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 27 Jun 2025 00:14:04 +0200 Subject: [PATCH 76/86] WildCards: add error handling for file traversal to skip problematic files --- .../java/pl/skidam/automodpack_core/utils/WildCards.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java b/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java index fe6cd8051..e188a696f 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java +++ b/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java @@ -46,7 +46,12 @@ public WildCards(List rules, Set startDirectories) { for (Path startDirectory : startDirectories) { try (Stream paths = Files.walk(startDirectory)) { - paths.forEach(node -> matchWhiteRules(node, startDirectory, composedWhiteRules)); + try { // Fixes some wierd edge cases + paths.filter(Files::isRegularFile) + .forEach(node -> matchWhiteRules(node, startDirectory, composedWhiteRules)); + } catch (Exception e) { + LOGGER.error("Error processing files in directory: {}", startDirectory, e); + } } matchBlackRules(startDirectory, composedBlackRules); From aaf68cc2eb06a08e673ae84971aa576dee5ffe1e Mon Sep 17 00:00:00 2001 From: gesterom Date: Fri, 18 Jul 2025 14:55:25 +0200 Subject: [PATCH 77/86] Improve NetUtils.loadCertificate() method --- .../automodpack_core/protocol/NetUtils.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/NetUtils.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/NetUtils.java index 89adb20af..cc4a0994d 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/NetUtils.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/NetUtils.java @@ -8,7 +8,7 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import pl.skidam.automodpack_core.utils.CustomFileUtils; -import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; @@ -85,15 +85,10 @@ public static void saveCertificate(X509Certificate cert, Path path) throws Excep public static X509Certificate loadCertificate(Path path) throws Exception { if (!Files.exists(path)) return null; - String certPem = Files.readString(path).replaceAll("\n", ""); - String beginMarker = "-----BEGIN CERTIFICATE-----"; - String endMarker = "-----END CERTIFICATE-----"; - int begin = certPem.indexOf(beginMarker); - int end = certPem.indexOf(endMarker); - if (begin == -1 || end == -1) return null; - String cert = certPem.substring(begin + beginMarker.length(), end).trim(); - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(Base64.getDecoder().decode(cert))); + try (InputStream in = Files.newInputStream(path)) { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + return (X509Certificate) cf.generateCertificate(in); + } } public static void savePrivateKey(PrivateKey key, Path path) throws Exception { From 51ab95ec7e2e136c161237b5a7cb8d740a03fe93 Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 18 Jul 2025 13:44:07 +0200 Subject: [PATCH 78/86] change ID from hyphen to underscore --- .../java/pl/skidam/automodpack_core/modpack/ModpackContent.java | 2 +- loader/fabric/core/src/main/resources/fabric.mod.json | 2 +- loader/forge/fml40/src/main/resources/META-INF/mods.toml | 2 +- loader/forge/fml47/src/main/resources/META-INF/mods.toml | 2 +- loader/neoforge/fml2/src/main/resources/META-INF/mods.toml | 2 +- .../fml4/src/main/resources/META-INF/neoforge.mods.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java b/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java index 059279e9d..222366dd9 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java +++ b/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java @@ -277,7 +277,7 @@ private Jsons.ModpackContentFields.ModpackContentItem generateContent(final Path } // Exclude automodpack mod - if (type.equals("mod") && (MOD_ID + "-bootstrap").equals(FileInspection.getModID(file))) { + if (type.equals("mod") && (MOD_ID + "_bootstrap").equals(FileInspection.getModID(file))) { return null; } diff --git a/loader/fabric/core/src/main/resources/fabric.mod.json b/loader/fabric/core/src/main/resources/fabric.mod.json index d52bd437a..591e99aa8 100644 --- a/loader/fabric/core/src/main/resources/fabric.mod.json +++ b/loader/fabric/core/src/main/resources/fabric.mod.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "id": "automodpack-bootstrap", + "id": "automodpack_bootstrap", "version": "1.0.0", "name": "AutoModpack bootstrap", "description": "", diff --git a/loader/forge/fml40/src/main/resources/META-INF/mods.toml b/loader/forge/fml40/src/main/resources/META-INF/mods.toml index 9ba75c843..a8fef7558 100644 --- a/loader/forge/fml40/src/main/resources/META-INF/mods.toml +++ b/loader/forge/fml40/src/main/resources/META-INF/mods.toml @@ -3,7 +3,7 @@ loaderVersion = "[1,)" license = "LGPLv3" [[mods]] -modId = "automodpack-bootstrap" +modId = "automodpack_bootstrap" version = "1.0.0" displayName = "AutoModpack bootstrap" displayURL = "https://modrinth.com/mod/automodpack" diff --git a/loader/forge/fml47/src/main/resources/META-INF/mods.toml b/loader/forge/fml47/src/main/resources/META-INF/mods.toml index 9ba75c843..a8fef7558 100644 --- a/loader/forge/fml47/src/main/resources/META-INF/mods.toml +++ b/loader/forge/fml47/src/main/resources/META-INF/mods.toml @@ -3,7 +3,7 @@ loaderVersion = "[1,)" license = "LGPLv3" [[mods]] -modId = "automodpack-bootstrap" +modId = "automodpack_bootstrap" version = "1.0.0" displayName = "AutoModpack bootstrap" displayURL = "https://modrinth.com/mod/automodpack" diff --git a/loader/neoforge/fml2/src/main/resources/META-INF/mods.toml b/loader/neoforge/fml2/src/main/resources/META-INF/mods.toml index 9ba75c843..a8fef7558 100644 --- a/loader/neoforge/fml2/src/main/resources/META-INF/mods.toml +++ b/loader/neoforge/fml2/src/main/resources/META-INF/mods.toml @@ -3,7 +3,7 @@ loaderVersion = "[1,)" license = "LGPLv3" [[mods]] -modId = "automodpack-bootstrap" +modId = "automodpack_bootstrap" version = "1.0.0" displayName = "AutoModpack bootstrap" displayURL = "https://modrinth.com/mod/automodpack" diff --git a/loader/neoforge/fml4/src/main/resources/META-INF/neoforge.mods.toml b/loader/neoforge/fml4/src/main/resources/META-INF/neoforge.mods.toml index 9ba75c843..a8fef7558 100644 --- a/loader/neoforge/fml4/src/main/resources/META-INF/neoforge.mods.toml +++ b/loader/neoforge/fml4/src/main/resources/META-INF/neoforge.mods.toml @@ -3,7 +3,7 @@ loaderVersion = "[1,)" license = "LGPLv3" [[mods]] -modId = "automodpack-bootstrap" +modId = "automodpack_bootstrap" version = "1.0.0" displayName = "AutoModpack bootstrap" displayURL = "https://modrinth.com/mod/automodpack" From 7a7856708025d597266d17d072c16227a8a3bb58 Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 18 Jul 2025 15:42:56 +0200 Subject: [PATCH 79/86] Update stonecutter, fabric loom and neoforge moddev --- settings.gradle.kts | 2 +- stonecutter.gradle.kts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 6327159e9..15dfb351f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { plugins { // For some reason, this plugin is crucial - do not remove id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" - id("dev.kikugie.stonecutter") version "0.7-beta.4" + id("dev.kikugie.stonecutter") version "0.7+" } include(":core") diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 1a0b552aa..45bea7b03 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -1,8 +1,8 @@ plugins { id("dev.kikugie.stonecutter") kotlin("jvm") version "2.1.21" apply false - id("fabric-loom") version "1.10-SNAPSHOT" apply false - id("net.neoforged.moddev") version "2.0.95" apply false + id("fabric-loom") version "1.11-SNAPSHOT" apply false + id("net.neoforged.moddev") version "2.0.103" apply false id("com.gradleup.shadow") version "8.3.6" apply false id("org.moddedmc.wiki.toolkit") version "0.2.7" } From 8e4b35351783165430f3e3090f84d3656a57a239 Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 18 Jul 2025 15:45:09 +0200 Subject: [PATCH 80/86] fix 1.21.4 mussictrackermixin #401 #399 --- .../pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java index 21f9f97a2..dd65e1043 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java @@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import pl.skidam.automodpack.client.audio.AudioManager; -/*? if >1.21.4 {*/ +/*? if >=1.21.4 {*/ import net.minecraft.client.sounds.MusicInfo; /*?} else {*/ /*import net.minecraft.sounds.Music; @@ -22,7 +22,7 @@ public class MusicTrackerMixin { at = @At("HEAD"), cancellable = true ) - /*? if >1.21.4 {*/ + /*? if >=1.21.4 {*/ private void play(MusicInfo p_383115_, CallbackInfo ci) { /*?} else {*/ /*private void play(Music type, CallbackInfo ci) { From 08cd856c83a6c256ec4d33d03564ce2eb847c649 Mon Sep 17 00:00:00 2001 From: skidam Date: Sun, 20 Jul 2025 17:31:07 +0200 Subject: [PATCH 81/86] Add NotSslRecordException handling to ErrorPrinter and update pipeline setup to catch it --- .../automodpack_core/protocol/netty/handler/ErrorPrinter.java | 3 ++- .../protocol/netty/handler/ProtocolServerHandler.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ErrorPrinter.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ErrorPrinter.java index f0276aa4b..5f2da1aa0 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ErrorPrinter.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ErrorPrinter.java @@ -3,6 +3,7 @@ import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.DecoderException; +import io.netty.handler.ssl.NotSslRecordException; import javax.net.ssl.SSLHandshakeException; @@ -15,7 +16,7 @@ public class ErrorPrinter extends ChannelDuplexHandler { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - if (cause instanceof DecoderException && cause.getCause() != null && cause.getCause() instanceof SSLHandshakeException) { + if (cause instanceof DecoderException && cause.getCause() != null && (cause.getCause() instanceof SSLHandshakeException || cause.getCause() instanceof NotSslRecordException)) { // Probably the client rejecting the server certificate. Omit stack trace to reduce log output. LOGGER.debug("Error occurred in connection to client at address {}: {}", ctx.channel().remoteAddress(), cause.getMessage()); } else { diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java index 04f0cc2c8..622a40048 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java @@ -55,6 +55,8 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { private void setupPipeline(ChannelHandlerContext ctx) { ctx.pipeline().channel().attr(NettyServer.USE_COMPRESSION).set(true); + // add error handler pipeline + ctx.pipeline().addLast("error-printer", new ErrorPrinter()); ctx.pipeline().addLast("traffic-shaper", TrafficShaper.trafficShaper.getTrafficShapingHandler()); if (sslCtx != null) { // If SSL context is provided, add TLS handler ctx.pipeline().addLast("tls", sslCtx.newHandler(ctx.alloc())); From b5331b7278cfd66cac46a28e9ec28824ecd5ef09 Mon Sep 17 00:00:00 2001 From: skidam Date: Sun, 20 Jul 2025 18:02:26 +0200 Subject: [PATCH 82/86] Rename error-printer handlers to avoid conflicts in ProtocolServerHandler --- .../protocol/netty/handler/ProtocolServerHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java index 622a40048..3776ac3a6 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java @@ -56,7 +56,7 @@ private void setupPipeline(ChannelHandlerContext ctx) { ctx.pipeline().channel().attr(NettyServer.USE_COMPRESSION).set(true); // add error handler pipeline - ctx.pipeline().addLast("error-printer", new ErrorPrinter()); + ctx.pipeline().addLast("error-printer-first", new ErrorPrinter()); ctx.pipeline().addLast("traffic-shaper", TrafficShaper.trafficShaper.getTrafficShapingHandler()); if (sslCtx != null) { // If SSL context is provided, add TLS handler ctx.pipeline().addLast("tls", sslCtx.newHandler(ctx.alloc())); @@ -67,6 +67,6 @@ private void setupPipeline(ChannelHandlerContext ctx) { .addLast("chunked-write", new ChunkedWriteHandler()) .addLast("protocol-msg-decoder", new ProtocolMessageDecoder()) .addLast("msg-handler", new ServerMessageHandler()) - .addLast("error-printer", new ErrorPrinter()); + .addLast("error-printer-last", new ErrorPrinter()); } } \ No newline at end of file From 9de73d9e7bd662f4cf62ef9f7a76ec00df864f49 Mon Sep 17 00:00:00 2001 From: skidam Date: Mon, 21 Jul 2025 00:11:30 +0200 Subject: [PATCH 83/86] Refactor isPlayerAuthorized to use server thread --- .../automodpack/modpack/GameHelpers.java | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java b/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java index f7661911c..4d8ac6c5d 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java +++ b/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java @@ -3,8 +3,9 @@ import com.mojang.authlib.GameProfile; import java.net.SocketAddress; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + import net.minecraft.server.players.GameProfileCache; -import net.minecraft.server.players.PlayerList; import static pl.skidam.automodpack.init.Common.server; @@ -12,18 +13,23 @@ public class GameHelpers { // Simpler version of `PlayerManager.checkCanJoin` public static boolean isPlayerAuthorized(SocketAddress address, GameProfile profile) { - var playerManager = server.getPlayerList(); - if (playerManager.getBans().isBanned(profile)) { - return false; - } - if (!playerManager.isWhiteListed(profile)) { - return false; - } - if (playerManager.getIpBans().isBanned(address)) { - return false; - } - - return true; + AtomicBoolean isAuthorized = new AtomicBoolean(false); + server.submit(() -> { + var playerManager = server.getPlayerList(); + if (playerManager.getBans().isBanned(profile)) { + return; + } + if (!playerManager.isWhiteListed(profile)) { + return; + } + if (playerManager.getIpBans().isBanned(address)) { + return; + } + + isAuthorized.set(true); + }).join(); + + return isAuthorized.get(); } // Method to get GameProfile from UUID with accounting for a fact that this player may not be on the server right now From 35a98c398bc97a7fa977f9569709b87498324656 Mon Sep 17 00:00:00 2001 From: skidam Date: Tue, 22 Jul 2025 08:23:47 +0200 Subject: [PATCH 84/86] Make sound instance source MUSIC instead of MASTER fixes #397 --- .../automodpack/client/audio/CustomSoundInstance.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java b/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java index 3570affa2..5f871c7fd 100644 --- a/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java +++ b/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java @@ -12,11 +12,11 @@ public class CustomSoundInstance extends AbstractSoundInstance { public CustomSoundInstance(Supplier event) { /*? if >=1.21.2 {*/ - super(event.get().location(), SoundSource.MASTER, RandomSource.create()); + super(event.get().location(), SoundSource.MUSIC, RandomSource.create()); /*?} else if >=1.19.1 {*/ - /*super(event.get().getLocation(), SoundSource.MASTER, RandomSource.create()); + /*super(event.get().getLocation(), SoundSource.MUSIC, RandomSource.create()); *//*?} else {*/ - /*super(event.get().getLocation(), SoundSource.MASTER); + /*super(event.get().getLocation(), SoundSource.MUSIC); *//*?}*/ this.attenuation = Attenuation.NONE; } From cfeffee2f8d718927d6d307fa956972c0eb81a0a Mon Sep 17 00:00:00 2001 From: skidam Date: Fri, 25 Jul 2025 21:30:59 +0200 Subject: [PATCH 85/86] Fix typos in AutoModpack references in HandshakeS2CPacket and SelfUpdater --- .../java/pl/skidam/automodpack_loader_core/SelfUpdater.java | 2 +- .../automodpack/networking/packet/HandshakeS2CPacket.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java index f2e9ef8ee..889efee8b 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java @@ -182,7 +182,7 @@ public static void installModVersion(ModrinthAPI automodpack) { try { DownloadManager downloadManager = new DownloadManager(); - new ScreenManager().download(downloadManager, "AutoModapck " + automodpack.fileVersion()); + new ScreenManager().download(downloadManager, "AutoModpack " + automodpack.fileVersion()); // Download it downloadManager.download( diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java index 6a0c1f26b..d88c49e75 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java @@ -101,7 +101,7 @@ public static void handleHandshake(Connection connection, GameProfile profile, i } if (modpackExecutor.isGenerating()) { - Component reason = VersionedText.literal("AutoModapck is generating modpack. Please wait a moment and try again."); + Component reason = VersionedText.literal("AutoModpack is generating modpack. Please wait a moment and try again."); connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); return; From 2942104d5025bbab500429e8dd942eacc092ba9c Mon Sep 17 00:00:00 2001 From: Suerion Date: Sun, 27 Jul 2025 13:36:31 +0200 Subject: [PATCH 86/86] update to latest beta --- .github/ISSUE_TEMPLATE/bug_report.yml | 3 +- .github/workflows/build.yml | 55 +--- .github/workflows/gradle.yml | 7 +- .github/workflows/release.yml | 18 +- .github/workflows/scripts/summary.py | 73 ----- .github/workflows/tests.yml | 2 +- .imgbotconfig | 14 - CONTRIBUTING.md | 2 +- README.md | 22 +- build.fabric.gradle.kts | 73 +++++ build.forge.gradle.kts | 106 +++++++ build.gradle.kts | 260 ---------------- build.neoforge.gradle.kts | 51 ++++ buildSrc/build.gradle.kts | 13 + .../main/kotlin/automodpack.common.gradle.kts | 247 ++++++++++++++++ core/build.gradle.kts | 14 +- .../automodpack_core/GlobalVariables.java | 2 +- .../pl/skidam/automodpack_core/Server.java | 26 +- .../automodpack_core/config/ConfigTools.java | 21 +- .../skidam/automodpack_core/config/Jsons.java | 48 ++- .../modpack/ModpackContent.java | 14 +- .../modpack/ModpackExecutor.java | 6 +- .../protocol/DownloadClient.java | 39 ++- .../automodpack_core/protocol/NetUtils.java | 15 +- .../protocol/netty/NettyServer.java | 112 +++---- .../protocol/netty/handler/ErrorPrinter.java | 3 +- .../netty/handler/ProtocolServerHandler.java | 42 +-- .../utils/AddressHelpers.java | 5 +- .../utils/CustomFileUtils.java | 8 +- .../automodpack_core/utils/WildCards.java | 15 +- .../utils/WorkaroundUtil.java | 8 +- .../automodpack_core/modpack/ModpackTest.java | 2 +- docs/.translated/fr_fr/_homepage.mdx | 9 + docs/.translated/fr_fr/_meta.json | 34 +++ docs/.translated/fr_fr/commands/_meta.json | 6 + docs/.translated/fr_fr/commands/commands.mdx | 11 + .../fr_fr/compatibility/_meta.json | 14 + .../fr_fr/compatibility/launchers.mdx | 15 + docs/.translated/fr_fr/compatibility/mods.mdx | 5 + .../fr_fr/compatibility/proxies.mdx | 14 + .../fr_fr/configuration/_meta.json | 14 + .../fr_fr/configuration/client-config.mdx | 10 + .../fr_fr/configuration/server-config.mdx | 29 ++ .../fr_fr/configuration/troubleshooting.mdx | 6 + docs/.translated/fr_fr/faq.mdx | 49 ++++ docs/.translated/fr_fr/quick-start.mdx | 36 +++ docs/.translated/fr_fr/technicals/_meta.json | 22 ++ .../fr_fr/technicals/certificate.mdx | 74 +++++ .../fr_fr/technicals/connection.mdx | 35 +++ .../fr_fr/technicals/file-structure.mdx | 24 ++ .../fr_fr/technicals/modpack-creation.mdx | 17 ++ .../fr_fr/technicals/modpack-hosting.mdx | 2 + docs/.translated/pl_pl/_homepage.mdx | 7 + docs/_homepage.mdx | 9 + docs/_meta.json | 34 +++ docs/commands/_meta.json | 6 + docs/commands/commands.mdx | 11 + docs/compatibility/_meta.json | 14 + docs/compatibility/launchers.mdx | 15 + docs/compatibility/mods.mdx | 6 + docs/compatibility/proxies.mdx | 14 + docs/configuration/_meta.json | 14 + docs/configuration/client-config.mdx | 10 + docs/configuration/server-config.mdx | 30 ++ docs/configuration/troubleshooting.mdx | 6 + docs/faq.mdx | 49 ++++ docs/quick-start.mdx | 35 +++ docs/sinytra-wiki.json | 7 + docs/technicals/_meta.json | 22 ++ docs/technicals/certificate.mdx | 45 +++ docs/technicals/connection.mdx | 35 +++ docs/technicals/file-structure.mdx | 21 ++ docs/technicals/modpack-creation.mdx | 17 ++ docs/technicals/modpack-hosting.mdx | 2 + gradle.properties | 19 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../automodpack_loader_core/Preload.java | 53 +++- .../automodpack_loader_core/SelfUpdater.java | 48 ++- .../client/ModpackUpdater.java | 22 +- .../client/ModpackUtils.java | 14 +- .../platforms/CurseForgeAPI.java | 2 +- .../platforms/ModrinthAPI.java | 27 +- .../utils/DownloadManager.java | 9 +- .../utils/FetchManager.java | 2 +- .../utils/SpeedMeter.java | 31 +- loader/fabric/15/gradle.properties | 5 +- loader/fabric/16/gradle.properties | 5 +- loader/fabric/core/gradle.properties | 5 +- .../core/src/main/resources/fabric.mod.json | 2 +- loader/forge/fml40/gradle.properties | 5 +- .../src/main/resources/META-INF/mods.toml | 2 +- loader/forge/fml47/gradle.properties | 5 +- .../src/main/resources/META-INF/mods.toml | 2 +- loader/loader-core.gradle.kts | 11 +- loader/loader-fabric-core.gradle.kts | 14 +- loader/loader-fabric.gradle.kts | 27 +- loader/loader-forge.gradle.kts | 67 ++--- loader/loader-neoforge.gradle.kts | 100 +++++++ loader/neoforge/fml2/gradle.properties | 5 +- .../src/main/resources/META-INF/mods.toml | 2 +- loader/neoforge/fml4/gradle.properties | 5 +- .../resources/META-INF/neoforge.mods.toml | 2 +- settings.gradle.kts | 67 ++--- .../skidam/automodpack/client/ScreenImpl.java | 36 +-- .../client/audio/AudioManager.java | 49 ++-- .../client/audio/CustomSoundInstance.java | 24 +- .../client/ui/ChangelogScreen.java | 85 +++--- .../automodpack/client/ui/DangerScreen.java | 52 +--- .../automodpack/client/ui/DownloadScreen.java | 98 +++---- .../automodpack/client/ui/ErrorScreen.java | 16 +- .../automodpack/client/ui/FetchScreen.java | 15 +- .../automodpack/client/ui/RestartScreen.java | 29 +- .../automodpack/client/ui/TextColors.java | 63 ++++ .../client/ui/ValidationScreen.java | 63 ++-- .../ui/versioned/VersionedCommandSource.java | 32 +- .../ui/versioned/VersionedMatrices.java | 48 ++- .../client/ui/versioned/VersionedScreen.java | 96 +++--- .../client/ui/versioned/VersionedText.java | 26 +- .../automodpack/client/ui/widget/Badge.java | 7 +- .../client/ui/widget/ListEntry.java | 53 ++-- .../client/ui/widget/ListEntryWidget.java | 56 ++-- .../pl/skidam/automodpack/init/Common.java | 8 +- .../mixin/core/ClientConnectionAccessor.java | 4 +- .../ClientLoginNetworkHandlerAccessor.java | 8 +- .../core/ClientLoginNetworkHandlerMixin.java | 16 +- .../mixin/core/ConnectScreenMixin.java | 32 +- .../mixin/core/DrawContextAccessor.java | 21 -- .../mixin/core/FabricLoginMixin.java | 4 +- .../core/LoginQueryRequestS2CPacketMixin.java | 12 +- .../LoginQueryResponseC2SPacketMixin.java | 14 +- .../mixin/core/MinecraftServerMixin.java | 8 +- .../mixin/core/MusicTrackerMixin.java | 27 +- .../mixin/core/PlayerManagerMixin.java | 55 ++-- .../ServerLoginNetworkHandlerAccessor.java | 14 +- .../core/ServerLoginNetworkHandlerMixin.java | 18 +- .../mixin/core/ServerNetworkIoMixin.java | 4 +- .../automodpack/mixin/dev/TestButton.java | 11 +- .../skidam/automodpack/modpack/Commands.java | 173 ++++++----- .../automodpack/modpack/GameHelpers.java | 38 ++- .../networking/LoginNetworkingIDs.java | 11 +- .../networking/LoginQueryParser.java | 45 ++- .../automodpack/networking/ModPackets.java | 16 +- .../automodpack/networking/PacketSender.java | 13 +- .../automodpack/networking/PayloadHelper.java | 10 +- .../client/ClientLoginNetworkAddon.java | 28 +- .../client/ClientLoginNetworking.java | 20 +- .../client/LoginResponsePayload.java | 14 +- .../networking/content/DataPacket.java | 6 +- .../networking/packet/DataC2SPacket.java | 57 ++-- .../networking/packet/DataS2CPacket.java | 53 ++-- .../networking/packet/HandshakeC2SPacket.java | 28 +- .../networking/packet/HandshakeS2CPacket.java | 87 ++---- .../server/LoginRequestPayload.java | 13 +- .../server/ServerLoginNetworkAddon.java | 34 +-- .../server/ServerLoginNetworking.java | 18 +- .../resources/META-INF/accesstransformer.cfg | 1 + src/main/resources/META-INF/mods.toml | 8 +- .../resources/META-INF/neoforge.mods.toml | 8 +- .../assets/automodpack/lang/fr_fr.json | Bin 5450 -> 3433 bytes .../assets/automodpack/lang/zh_cn.json | 10 +- .../resources/automodpack-main.mixins.json | 1 - src/main/resources/automodpack.accesswidener | 2 +- src/main/resources/fabric.mod.json | 2 +- src/main/resources/pack.mcmeta | 3 +- stonecutter.gradle.kts | 277 ++---------------- versions/1.18.2-fabric/gradle.properties | 13 +- versions/1.18.2-forge/gradle.properties | 13 +- versions/1.19.2-fabric/gradle.properties | 13 +- versions/1.19.2-forge/gradle.properties | 13 +- versions/1.19.4-fabric/gradle.properties | 13 +- versions/1.19.4-forge/gradle.properties | 13 +- versions/1.20.1-fabric/gradle.properties | 13 +- versions/1.20.1-forge/gradle.properties | 13 +- versions/1.20.4-fabric/gradle.properties | 13 +- versions/1.20.4-neoforge/gradle.properties | 13 +- versions/1.20.6-fabric/gradle.properties | 13 +- versions/1.20.6-neoforge/gradle.properties | 13 +- versions/1.21.1-fabric/gradle.properties | 13 +- versions/1.21.1-neoforge/gradle.properties | 13 +- versions/1.21.3-fabric/gradle.properties | 13 +- versions/1.21.3-neoforge/gradle.properties | 13 +- versions/1.21.4-fabric/gradle.properties | 13 +- versions/1.21.4-neoforge/gradle.properties | 13 +- versions/1.21.5-fabric/gradle.properties | 5 + versions/1.21.5-neoforge/gradle.properties | 5 + versions/1.21.6-fabric/gradle.properties | 4 + versions/1.21.6-neoforge/gradle.properties | 4 + 187 files changed, 2927 insertions(+), 2126 deletions(-) delete mode 100644 .github/workflows/scripts/summary.py delete mode 100644 .imgbotconfig create mode 100644 build.fabric.gradle.kts create mode 100644 build.forge.gradle.kts delete mode 100644 build.gradle.kts create mode 100644 build.neoforge.gradle.kts create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/automodpack.common.gradle.kts create mode 100644 docs/.translated/fr_fr/_homepage.mdx create mode 100644 docs/.translated/fr_fr/_meta.json create mode 100644 docs/.translated/fr_fr/commands/_meta.json create mode 100644 docs/.translated/fr_fr/commands/commands.mdx create mode 100644 docs/.translated/fr_fr/compatibility/_meta.json create mode 100644 docs/.translated/fr_fr/compatibility/launchers.mdx create mode 100644 docs/.translated/fr_fr/compatibility/mods.mdx create mode 100644 docs/.translated/fr_fr/compatibility/proxies.mdx create mode 100644 docs/.translated/fr_fr/configuration/_meta.json create mode 100644 docs/.translated/fr_fr/configuration/client-config.mdx create mode 100644 docs/.translated/fr_fr/configuration/server-config.mdx create mode 100644 docs/.translated/fr_fr/configuration/troubleshooting.mdx create mode 100644 docs/.translated/fr_fr/faq.mdx create mode 100644 docs/.translated/fr_fr/quick-start.mdx create mode 100644 docs/.translated/fr_fr/technicals/_meta.json create mode 100644 docs/.translated/fr_fr/technicals/certificate.mdx create mode 100644 docs/.translated/fr_fr/technicals/connection.mdx create mode 100644 docs/.translated/fr_fr/technicals/file-structure.mdx create mode 100644 docs/.translated/fr_fr/technicals/modpack-creation.mdx create mode 100644 docs/.translated/fr_fr/technicals/modpack-hosting.mdx create mode 100644 docs/.translated/pl_pl/_homepage.mdx create mode 100644 docs/_homepage.mdx create mode 100644 docs/_meta.json create mode 100644 docs/commands/_meta.json create mode 100644 docs/commands/commands.mdx create mode 100644 docs/compatibility/_meta.json create mode 100644 docs/compatibility/launchers.mdx create mode 100644 docs/compatibility/mods.mdx create mode 100644 docs/compatibility/proxies.mdx create mode 100644 docs/configuration/_meta.json create mode 100644 docs/configuration/client-config.mdx create mode 100644 docs/configuration/server-config.mdx create mode 100644 docs/configuration/troubleshooting.mdx create mode 100644 docs/faq.mdx create mode 100644 docs/quick-start.mdx create mode 100644 docs/sinytra-wiki.json create mode 100644 docs/technicals/_meta.json create mode 100644 docs/technicals/certificate.mdx create mode 100644 docs/technicals/connection.mdx create mode 100644 docs/technicals/file-structure.mdx create mode 100644 docs/technicals/modpack-creation.mdx create mode 100644 docs/technicals/modpack-hosting.mdx create mode 100644 loader/loader-neoforge.gradle.kts create mode 100644 src/main/java/pl/skidam/automodpack/client/ui/TextColors.java delete mode 100644 src/main/java/pl/skidam/automodpack/mixin/core/DrawContextAccessor.java create mode 100644 src/main/resources/META-INF/accesstransformer.cfg create mode 100644 versions/1.21.5-fabric/gradle.properties create mode 100644 versions/1.21.5-neoforge/gradle.properties create mode 100644 versions/1.21.6-fabric/gradle.properties create mode 100644 versions/1.21.6-neoforge/gradle.properties diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c44455092..755175316 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -44,7 +44,6 @@ body: Please send the corresponding Minecraft log from the `logs` folder here. Please upload the log file as an attachment, or upload the log to [mclo.gs](https://mclo.gs/) and paste the url here - mclo.gs sanitizes sensitive information such as ip addresses. Please refrain from pasting the entire log file directly. - Leave empty if there is none. placeholder: https://mclo.gs/ - type: input id: versions @@ -93,5 +92,5 @@ body: required: true - label: I have provided all the necessary information to reproduce the issue. required: true - - label: I have verified that the issue does not occur without the AutoModpack - Ignore you are reporting a mod conflict issue. + - label: I have verified that the issue does not occur without the AutoModpack - Ignore if you are reporting a mod conflict issue. required: false diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b2a39e938..73ab596fc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,12 +12,6 @@ on: type: string required: false default: '' -# # [FEATURE] MIXIN_AUDITOR -# mixin_audit: -# description: run mixin audit for Minecraft server after build -# type: boolean -# required: false -# default: false jobs: build: @@ -48,8 +42,7 @@ jobs: if [ -z "${{ inputs.target_subproject }}" ]; then echo "Building all subprojects" ./gradlew clean - ./gradlew chiseledBuild -x mergeJars - ./gradlew mergeJars + ./gradlew build else args=$(echo "${{ inputs.target_subproject }}" | tr ',' '\n' | sed 's/$/:build/' | paste -sd ' ') echo "Building with arguments=$args" @@ -59,52 +52,8 @@ jobs: BUILD_ID: ${{ github.run_number }} BUILD_RELEASE: ${{ inputs.release }} - # # [FEATURE] MIXIN_AUDITOR - # - name: Run mixin audit check for Minecraft server - # if: ${{ inputs.mixin_audit }} - # timeout-minutes: 10 - # run: | - # mkdir -p ./run - # echo eula=true > ./run/eula.txt - # ./gradlew runServerMixinAudit - - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: build-artifacts - path: merged - - # # [FEATURE] FALLENS_MAVEN - # - name: Publish with gradle - # if: inputs.release || github.ref == 'refs/heads/dev' - # run: | - # if [ -z "${{ inputs.target_subproject }}" ]; then - # echo "Publishing all subprojects" - # ./gradlew publish - # else - # args=$(echo "${{ inputs.target_subproject }}" | tr ',' '\n' | sed 's/$/:publish/' | paste -sd ' ') - # echo "Publishing with arguments=$args" - # ./gradlew $args - # fi - # env: - # BUILD_RELEASE: ${{ inputs.release }} - # FALLENS_MAVEN_TOKEN: ${{ secrets.FALLENS_MAVEN_TOKEN }} - -# summary: -# runs-on: ubuntu-22.04 -# needs: -# - build -# -# steps: -# - uses: actions/checkout@v4 -# -# - name: Download build artifacts -# uses: actions/download-artifact@v4 -# with: -# name: build-artifacts -# path: build-artifacts -# -# - name: Make build summary -# run: python3 .github/workflows/scripts/summary.py # ubuntu-22.04 uses Python 3.10.6 -# env: -# TARGET_SUBPROJECT: ${{ inputs.target_subproject }} \ No newline at end of file + path: merged \ No newline at end of file diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index bc9ef67e2..0595316a3 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,6 +1,6 @@ name: Dev Builds -on: [workflow_dispatch, pull_request, push] +on: [workflow_dispatch, push] jobs: tests: # runs on linux and windows @@ -8,7 +8,4 @@ jobs: secrets: inherit build: # runs on linux uses: ./.github/workflows/build.yml - secrets: inherit -# # [FEATURE] MIXIN_AUDITOR -# with: -# mixin_audit: true \ No newline at end of file + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cff308c48..cf483ef71 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -103,25 +103,25 @@ jobs: fi - name: Read common properties - id: properties_common + id: properties_c uses: christian-draeger/read-properties@1.1.1 with: path: gradle.properties properties: 'mod_name mod_version' - name: Read version-specific properties - id: properties_versioned + id: properties_v uses: christian-draeger/read-properties@1.1.1 with: path: ${{ format('versions/{0}/gradle.properties', matrix.subproject) }} - properties: 'minecraft_version game_versions' + properties: 'publish_versions' - name: Fix game version - id: game_versions + id: publish_versions run: | - # Fixed \n in game_versions isn't parsed by christian-draeger/read-properties as a line separator + # Fixed \n in publish_versions isn't parsed by christian-draeger/read-properties as a line separator echo 'value<> $GITHUB_OUTPUT - echo -e "${{ steps.properties_versioned.outputs.game_versions }}" >> $GITHUB_OUTPUT + echo -e "${{ steps.properties_v.outputs.publish_versions }}" >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT - name: Prepare file information @@ -166,12 +166,12 @@ jobs: files: ${{ steps.file_info.outputs.path }} - name: ${{ format('{0} {1} {2}', steps.properties_common.outputs.mod_name, steps.properties_common.outputs.mod_version, matrix.mod_brand) }} - version: ${{ steps.properties_common.outputs.mod_version }} + name: ${{ format('{0} {1} {2}', steps.properties_c.outputs.mod_name, steps.properties_c.outputs.mod_version, matrix.mod_brand) }} + version: ${{ steps.properties_c.outputs.mod_version }} version-type: release loaders: ${{ matrix.mod_brand }} - game-versions: ${{ steps.game_versions.outputs.value }} + game-versions: ${{ steps.publish_versions.outputs.value }} game-version-filter: any dependencies: '' # declare the dependencies explicitly, so mc-publish won't try to load from fabric.mod.json diff --git a/.github/workflows/scripts/summary.py b/.github/workflows/scripts/summary.py deleted file mode 100644 index 3da4c2f93..000000000 --- a/.github/workflows/scripts/summary.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -A script to scan through all valid mod jars in build-artifacts.zip/$version/build/libs, -and generate an artifact summary table for that to GitHub action step summary -""" -__author__ = 'Fallen_Breath' - -import functools -import glob -import hashlib -import json -import os - - -def read_prop(file_name: str, key: str) -> str: - with open(file_name) as prop: - return next(filter( - lambda l: l.split('=', 1)[0].strip() == key, - prop.readlines() - )).split('=', 1)[1].lstrip() - - -def get_sha256_hash(file_path: str) -> str: - sha256_hash = hashlib.sha256() - - with open(file_path, 'rb') as f: - for buf in iter(functools.partial(f.read, 4096), b''): - sha256_hash.update(buf) - - return sha256_hash.hexdigest() - - -def main(): - target_subproject_env = os.environ.get('TARGET_SUBPROJECT', '') - target_subprojects = list(filter(None, target_subproject_env.split(',') if target_subproject_env != '' else [])) - print('target_subprojects: {}'.format(target_subprojects)) - - with open('settings.json') as f: - settings: dict = json.load(f) - - with open(os.environ['GITHUB_STEP_SUMMARY'], 'w') as f: - f.write('## Build Artifacts Summary\n\n') - f.write('| Subproject | for Minecraft | File | Size | SHA-256 |\n') - f.write('| --- | --- | --- | --- | --- |\n') - - warnings = [] - for subproject in settings['versions']: - if len(target_subprojects) > 0 and subproject not in target_subprojects: - print('skipping {}'.format(subproject)) - continue - game_versions = read_prop('versions/{}/gradle.properties'.format(subproject), 'game_versions') - game_versions = game_versions.strip().replace('\\n', ', ') - file_paths = glob.glob('build-artifacts/{}/build/libs/*.jar'.format(subproject)) - file_paths = list(filter(lambda fp: not fp.endswith('-sources.jar') and not fp.endswith('-dev.jar') and not fp.endswith('-shadow.jar'), file_paths)) - if len(file_paths) == 0: - file_name = '*not found*' - sha256 = '*N/A*' - else: - file_name = '`{}`'.format(os.path.basename(file_paths[0])) - file_size = '{} B'.format(os.path.getsize(file_paths[0])) - sha256 = '`{}`'.format(get_sha256_hash(file_paths[0])) - if len(file_paths) > 1: - warnings.append('Found too many build files in subproject {}: {}'.format(subproject, ', '.join(file_paths))) - - f.write('| {} | {} | {} | {} | {} |\n'.format(subproject, game_versions, file_name, file_size, sha256)) - - if len(warnings) > 0: - f.write('\n### Warnings\n\n') - for warning in warnings: - f.write('- {}\n'.format(warning)) - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e822e1f87..05b235732 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,7 @@ on: [workflow_call] jobs: tests: - runs-on: windows-2019 + runs-on: windows-latest steps: - uses: actions/checkout@v4 - name: Set up JDK 21 diff --git a/.imgbotconfig b/.imgbotconfig deleted file mode 100644 index 1828d9c0b..000000000 --- a/.imgbotconfig +++ /dev/null @@ -1,14 +0,0 @@ -{ - "schedule": "daily", // daily|weekly|monthly -// "ignoredFiles": [ -// "*.jpg", // ignore by extension -// "image1.png", // ignore by filename -// "public/special_images/*", // ignore by folderpath -// ], -// "aggressiveCompression": "true", // true|false -// "compressWiki": "true", // true|false - "minKBReduced": null, // delay new prs until size reduction meets this threshold (default to 10) -// "prTitle" : "Images are optimized!", -// "prBody" : " Text before optimization ratio {optimization_ratio} Text after optimization ratio -// Text before optimization details {optimization_details} Text after optimization details", -} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83226ad34..91803241c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ If you're a developer, you can contribute to the AutoModpack project by writing 1. Discuss the changes you want to make. By e.g. creating an issue. 2. Fork the repository and create a new branch for your feature or bug fix. 3. Make your code changes. -4. Build code with `./gradlew chiseledBuild` command. +4. Build code with `./gradlew build` command. 5. Test your changes thoroughly to prevent regressions. 6. Submit a pull request, describing your changes and providing relevant context. diff --git a/README.md b/README.md index 0925a4972..ed2c14c85 100644 --- a/README.md +++ b/README.md @@ -40,36 +40,38 @@ This isn't just another mod; it's a game-changer for private servers. Here's why ## 🛠️ How the Magic Happens -AutoModpack works by generating a modpack (**metadata file**) on the server, containing all the files of your modpack. The server then hosts this file and the modpack files. +AutoModpack works by generating a modpack (**metadata file**) on the server, which contains all the files of your modpack. The server then hosts this file and the modpack files. -When a client connects to server (simplified): +When a client connects to the server: -1. AutoModpack securely connects to the modpack host server and fetches the metadata. -2. It uses Modrinth and CurseForge APIs to download directly your modpack's files where possible. -3. All files are downloaded to the client's automodpack folder. -4. Restart the game, AutoModpack loads the modpack and the client is perfectly synced and ready to play! +1. Connection: AutoModpack establishes a secure connection and prompts you to [verify the server's certificate fingerprint](https://moddedmc.wiki/en/project/automodpack/docs/technicals/certificate). +2. Direct links: Fetches the APIs for direct downloads of your modpack's files from Modrinth and CurseForge, where possible (mods, resource packs, shaders). +3. Modpack download: All files are downloaded to the client's automodpack folder. +4. Game restart: AutoModpack loads the modpack, and the client is perfectly synced and ready to play! -On subsequent game boots, AutoModpack checks for updates. If changes are detected, it updates the modpack in the background, no additional restarts required! +On subsequent game launches, AutoModpack checks for updates. If changes are detected, it updates the modpack in the background—no additional restarts are required! ## ⚠️ Security and Trust - Read This! > With great power comes great responsibility. -Because it downloads files directly into your game folder, it's crucial to **only use it on servers you absolutely trust**. A malicious server (administrator) *can* include harmful files. +Be aware that this mod allows remote servers to download *arbitrary executable* files directly into your game folder. It's crucial to **only use it on servers you absolutely trust**. A malicious server (administrator) *can* include malicious/harmful files. While AutoModpack itself tries to be as secure as possible, due to the nature of the internet, the creators and contributors of AutoModpack are not responsible for any harm, damage, loss, or issues that may result from files downloaded from a server you connect to using the mod. **By using AutoModpack, you acknowledge and accept this risk.** -**If you have valuable security insights or concerns, please reach out!** You can contact privately on [Discord](https://discordapp.com/users/464522287618457631) or open an issue on GitHub. +**If you have valuable security insights or concerns, please reach out!** You can contact privately on [Discord](https://discordapp.com/users/464522287618457631) or publicly on [Discord server](https://discord.gg/hS6aMyeA9P) or just open an issue on [GitHub](https://github.com/Skidamek/AutoModpack/issues). ## 🚀 Getting Started is a Breeze! Installing AutoModpack is as simple as installing any other mod. 1. Download the AutoModpack from the releases page on [GitHub](https://github.com/Skidamek/AutoModpack/releases), [CurseForge](https://www.curseforge.com/minecraft/mc-mods/automodpack), or [Modrinth](https://modrinth.com/mod/automodpack). 2. Place the downloaded file into the `/mods/` folder of both your server and client Minecraft installations. +3. Start your server and let AutoModpack generate the initial modpack metadata. +4. Connect to your server with the mod installed on your client. That's typically all you need to do! AutoModpack will automatically create the modpack from your server's mods. -**Want to customize your modpack further?** Add configs, client-side-only mods, and more? **Check out the [wiki](https://github.com/Skidamek/AutoModpack/wiki)!** It *hopefully* has all the details you need to tailor AutoModpack to your specific needs. +**Want to customize your modpack further?** Add configs, client-side-only mods, and more? **Check out the [documentation](https://moddedmc.wiki/en/project/automodpack/docs)!** There's also a start guide covering more stuff. If you encounter any issues or have questions, feel free to join [Discord server](https://discord.gg/hS6aMyeA9P) or open an issue on [GitHub](https://github.com/Skidamek/AutoModpack/issues). Prefer an all-in-one solution? You can also use our [modified Fabric installer](https://github.com/Skidamek/AutoModpack-Installer/releases/tag/Latest) which downloads AutoModpack alongside the Fabric loader. diff --git a/build.fabric.gradle.kts b/build.fabric.gradle.kts new file mode 100644 index 000000000..fbc31398e --- /dev/null +++ b/build.fabric.gradle.kts @@ -0,0 +1,73 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + kotlin("jvm") + id("automodpack.common") + id("fabric-loom") +} + +version = "${property("mod_version")}" +group = "${property("mod.group")}" +base.archivesName.set("${property("mod_name")}-mc${property("deps.minecraft")}-fabric".lowercase()) + +loom { + accessWidenerPath = rootProject.file("src/main/resources/automodpack.accesswidener") +} + +dependencies { + implementation(project(":core")) + implementation(project(":loader-core")) + + minecraft("com.mojang:minecraft:${property("deps.minecraft")}") + mappings(loom.layered { + officialMojangMappings() + if (hasProperty("deps.parchment")) + parchment("org.parchmentmc.data:parchment-${property("deps.parchment")}@zip") + }) + + modImplementation("net.fabricmc:fabric-loader:${property("deps.fabric-loader")}") + + // TODO transitive false + setOf( + "api-base", // Required by modules below + "resource-loader-v0", // Required for translatable texts + "registry-sync-v0", // Required for custom sounds + "networking-api-v1" // Required by registry sync module + ).forEach { + include(modImplementation(fabricApi.module("fabric-$it", property("deps.fabric-api") as String))!!) + } + + if (stonecutter.eval(stonecutter.current.version, "<1.19.2")) { + include(modImplementation(fabricApi.module("fabric-command-api-v1", property("deps.fabric-api") as String))!!) // TODO transitive false + } else { + include(modImplementation(fabricApi.module("fabric-command-api-v2", property("deps.fabric-api") as String))!!) // TODO transitive false + } + + include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:${property("deps.mixin-extras")}")!!)!!) +} + +java { + if (stonecutter.eval(stonecutter.current.version, ">=1.20.5")) { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + } else { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) + } + withSourcesJar() +} + +tasks { + processResources { + exclude("**/neoforge.mods.toml", "**/mods.toml", "**/accesstransformer.cfg") + } + + register("buildAndCollect") { + group = "build" + from(remapJar.map { it.archiveFile }) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod_version")}")) + dependsOn("build") + } +} \ No newline at end of file diff --git a/build.forge.gradle.kts b/build.forge.gradle.kts new file mode 100644 index 000000000..332229a0f --- /dev/null +++ b/build.forge.gradle.kts @@ -0,0 +1,106 @@ +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.databind.node.ObjectNode + +plugins { + kotlin("jvm") + id("automodpack.common") + id("net.neoforged.moddev.legacyforge") +} + +version = "${property("mod_version")}" +group = "${property("mod.group")}" +base.archivesName.set("${property("mod_name")}-mc${property("deps.minecraft")}-forge".lowercase()) + +legacyForge { + version = property("deps.forge") as String + validateAccessTransformers = true + + if (hasProperty("deps.parchment")) parchment { + val (mc, ver) = (property("deps.parchment") as String).split(':') + mappingsVersion = ver + minecraftVersion = mc + } +} + +dependencies { + implementation(project(":core")) + implementation(project(":loader-core")) + + compileOnly("net.fabricmc.fabric-api:fabric-api:0.92.2+1.20.1") + compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:${property("deps.mixin-extras")}")!!) + implementation(jarJar("io.github.llamalad7:mixinextras-forge:${property("deps.mixin-extras")}")!!) + annotationProcessor("org.spongepowered:mixin:0.8.5:processor") // required to generate refmaps +} + +mixin { // Add mixins + add(sourceSets.main.get(), "automodpack-main.mixins.refmap.json") + config("automodpack-main.mixins.json") +} + +tasks.getByName("processResources") { + doLast { // Add refmap to the mixin config + val mixinConfigFile = File(destinationDir, "automodpack-main.mixins.json") + addRefmapToJsonFile(mixinConfigFile, "automodpack-main.mixins.refmap.json") + } +} + +tasks { + jar { // add the mixin config to the jar + manifest { + attributes( + "MixinConfigs" to "automodpack-main.mixins.json" + ) + } + } + + processResources { + exclude("**/fabric.mod.json", "**/automodpack.accesswidener") + } + + named("createMinecraftArtifacts") { + dependsOn("stonecutterGenerate") + } + + register("buildAndCollect") { + group = "build" + from(jar.map { it.archiveFile }) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod_version")}")) + dependsOn("build") + } +} + +java { + if (stonecutter.eval(stonecutter.current.version, ">=1.20.5")) { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + } else { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) + } + withSourcesJar() +} + +fun addRefmapToJsonFile(jsonFile: File, refmap: String) { + if (!jsonFile.exists()) { + error("JSON file not found: ${jsonFile.absolutePath}") + } + + val objectMapper = ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT) + try { + val jsonNode = objectMapper.readTree(jsonFile) + if (jsonNode.isObject) { + val objectNode = jsonNode as ObjectNode + objectNode.put("refmap", refmap) + objectMapper.writeValue(jsonFile, objectNode) + println("Added refmap ($refmap) to ${jsonFile.name}") + } else { + error("JSON file ${jsonFile.name} is not a JSON object, couldn't add refmap.") + } + } catch (e: Exception) { + println("Error processing JSON file ${jsonFile.absolutePath}: ${e.message}") + e.printStackTrace() + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 351d40fc0..000000000 --- a/build.gradle.kts +++ /dev/null @@ -1,260 +0,0 @@ -import java.util.* - -plugins { - id("dev.architectury.loom") -} - -class ModData { - val id = property("mod_id").toString() - val name = property("mod_name").toString() - val version = property("mod_version").toString() - val group = property("mod_group").toString() - val minecraftDependency = property("minecraft_dependency").toString() - val description = property("mod_description").toString() -} - -class LoaderData { - private val name = loom.platform.get().name.lowercase() - val isFabric = name == "fabric" - val isForge = name == "forge" - val isNeoForge = name == "neoforge" - - fun getVersion() : String? { - return if (isForge) { - property("loader_forge")?.toString() - } else if (isNeoForge) { - property("loader_neoforge")?.toString() - } else if (isFabric) { - property("loader_fabric")?.toString() - } else { - null - } - } - - override fun toString(): String { - return name - } -} - -class MinecraftVersionData { - private val name = stonecutter.current.version.substringBeforeLast("-") - val needsJava21 = greaterOrEqual("1.20.5") - - fun greaterThan(other: String) : Boolean { - return stonecutter.compare(name, other.lowercase()) > 0 - } - - fun lessThan(other: String) : Boolean { - return stonecutter.compare(name, other.lowercase()) < 0 - } - - fun greaterOrEqual(other: String) : Boolean { - return stonecutter.compare(name, other.lowercase()) >= 0 - } - - fun lessOrEqual(other: String) : Boolean { - return stonecutter.compare(name, other.lowercase()) <= 0 - } - - override fun equals(other: Any?) : Boolean { - return name == other - } - - override fun toString(): String { - return name - } - - override fun hashCode(): Int { - var result = name.hashCode() - result = 31 * result + needsJava21.hashCode() - return result - } -} - -val mod = ModData() -val loader = LoaderData() -val minecraftVersion = MinecraftVersionData() - - -version = mod.version -group = mod.group -base.archivesName.set("${mod.name}-mc$minecraftVersion-$loader".lowercase(Locale.getDefault())) - -repositories { - mavenCentral() - mavenLocal() - maven { url = uri("https://api.modrinth.com/maven") } - maven { url = uri("https://maven.neoforged.net/releases") } - maven { url = uri("https://files.minecraftforge.net/maven/") } - maven { url = uri("https://libraries.minecraft.net/") } -} - -dependencies { - // just for IDE not complaining (its already included in the shadow jar of core-${mod_brand}) - // + needed for runtime in dev env - implementation(project(":core")) - implementation(project(":loader-core")) - - testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2") - - // TODO fix dev env launch - - minecraft("com.mojang:minecraft:$minecraftVersion") - - // Mappings have to be patched for new neoforge - if (loader.isNeoForge && minecraftVersion.equals("1.20.6")) { - mappings( - loom.layered { - mappings("net.fabricmc:yarn:${property("yarn_mappings")}:v2") - mappings("dev.architectury:yarn-mappings-patch-neoforge:1.20.6+build.4") - } - ) - } else if (loader.isNeoForge && minecraftVersion.greaterOrEqual("1.21")) { - mappings( - loom.layered { - mappings("net.fabricmc:yarn:${property("yarn_mappings")}:v2") - mappings("dev.architectury:yarn-mappings-patch-neoforge:1.21+build.4") - } - ) - } else { - mappings("net.fabricmc:yarn:${property("yarn_mappings")}:v2") - } - - if (loader.isFabric) { - setOf( - "fabric-api-base", // Required by modules below - "fabric-resource-loader-v0", // Required for translatable texts - "fabric-registry-sync-v0", // Required for custom sounds - "fabric-networking-api-v1" // Required by registry sync module - ).forEach { - include(modImplementation(fabricApi.module(it, property("fabric_version") as String))!!) // TODO transitive false - } - if (minecraftVersion.lessThan("1.19.2")) { - include(modImplementation(fabricApi.module("fabric-command-api-v1", property("fabric_version") as String))!!) // TODO transitive false - } else { - include(modImplementation(fabricApi.module("fabric-command-api-v2", property("fabric_version") as String))!!) // TODO transitive false - } - - // JiJ lastest version of mixin extras so all mods work (workaround) - remove when we detect such incompatibilities and copy the jij mod to the mods folder, currently the stable loader version would be loaded instead of the lastest required by some mods - include(implementation(annotationProcessor("io.github.llamalad7:mixinextras-fabric:${property("mixin_extras")}")!!)!!) - } - - if (loader.isFabric) { - modImplementation("net.fabricmc:fabric-loader:${loader.getVersion()}") - } else if (loader.isForge) { - "forge"("net.minecraftforge:forge:$minecraftVersion-${loader.getVersion()}") - - // For pseudo mixin (compat with Forgified Fabric API) - compileOnly(fabricApi.module("fabric-networking-api-v1", "0.92.2+1.20.1")) // version is not important - - implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:${property("mixin_extras")}")!!) - implementation(include("io.github.llamalad7:mixinextras-forge:${property("mixin_extras")}")!!) - } else if (loader.isNeoForge) { - - // For pseudo mixin (compat with Forgified Fabric API) - compileOnly(fabricApi.module("fabric-networking-api-v1", "0.92.2+1.20.1")) // version is not important - - "neoForge"("net.neoforged:neoforge:${loader.getVersion()}") - - // Bundle mixin extras to 1.20.2 since it has too old version of it - if (minecraftVersion.equals("1.20.2")) { - implementation(include("io.github.llamalad7:mixinextras-neoforge:${property("mixin_extras")}")!!) - } - } -} - -loom { - runConfigs["client"].apply { - ideConfigGenerated(true) - vmArgs("-Dmixin.debug.export=true") - runDir = "../../run" - } - - runConfigs["server"].apply { - ideConfigGenerated(true) - vmArgs("-Dmixin.debug.export=true") - runDir = "../../run" - } - - if (loader.isForge) { - forge { - convertAccessWideners = true - mixinConfigs = listOf("automodpack-main.mixins.json") - } - } - - accessWidenerPath = file("../../src/main/resources/automodpack.accesswidener") -} - -if (stonecutter.current.isActive) { - rootProject.tasks.register("buildActive") { - group = "project" - dependsOn(tasks.named("build")) - finalizedBy("mergeJars") - } -} - -tasks.processResources { - val map = mapOf( - "id" to mod.id, - "name" to mod.name, - "version" to mod.version, - "minecraft_dependency" to mod.minecraftDependency, - "description" to mod.description - ) - - inputs.properties(map) - - if (loader.isFabric) { - exclude("META-INF/neoforge.mods.toml") - exclude("META-INF/mods.toml") - filesMatching("fabric.mod.json") { - expand(map) - } - } else if (loader.isNeoForge) { - exclude("fabric.mod.json") - exclude("META-INF/mods.toml") - filesMatching("META-INF/neoforge.mods.toml") { - expand(map) - } - } else if (loader.isForge) { - exclude("fabric.mod.json") - exclude("META-INF/neoforge-mods.toml") - filesMatching("META-INF/mods.toml") { - expand(map) - } - } - - if (loader.isForge || loader.isNeoForge) { - filesMatching("assets/automodpack/icon.png") { - path = "icon.png" - } - } - - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - from("../../src/main/resources") { - include("META-INF/services/**") - } -} - -java { - if (minecraftVersion.needsJava21) { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 - } else { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } - - withSourcesJar() -} - -tasks.withType { - options.encoding = "UTF-8" -} - -tasks.named("jar") { - from(rootProject.file("LICENSE")) { - rename { "${it}_${mod.id}" } - } -} \ No newline at end of file diff --git a/build.neoforge.gradle.kts b/build.neoforge.gradle.kts new file mode 100644 index 000000000..7e1bba8fd --- /dev/null +++ b/build.neoforge.gradle.kts @@ -0,0 +1,51 @@ +plugins { + kotlin("jvm") + id("automodpack.common") + id("net.neoforged.moddev") +} + +version = "${property("mod_version")}" +group = "${property("mod.group")}" +base.archivesName.set("${property("mod_name")}-mc${property("deps.minecraft")}-neoforge".lowercase()) + +neoForge { + version = property("deps.neoforge") as String + validateAccessTransformers = true + + if (hasProperty("deps.parchment")) parchment { + val (mc, ver) = (property("deps.parchment") as String).split(':') + mappingsVersion = ver + minecraftVersion = mc + } +} + +dependencies { + implementation(project(":core")) + implementation(project(":loader-core")) + + compileOnly("net.fabricmc.fabric-api:fabric-api:0.92.2+1.20.1") +} + +tasks { + processResources { + exclude("**/fabric.mod.json", "**/automodpack.accesswidener", "**/forge.mods.toml") + } + + named("createMinecraftArtifacts") { + dependsOn("stonecutterGenerate") + } + + register("buildAndCollect") { + group = "build" + from(jar.map { it.archiveFile }) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod_version")}")) + dependsOn("build") + } +} + +java { + withSourcesJar() + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) +} \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 000000000..6083ada0a --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + `kotlin-dsl` + kotlin("jvm") version "2.1.21" +} + +repositories { + mavenCentral() + gradlePluginPortal() +} + +dependencies { + implementation("com.fasterxml.jackson.core:jackson-databind:2.19.1") // For JSON parsing e.g. in build.forge.gradle.kts +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/automodpack.common.gradle.kts b/buildSrc/src/main/kotlin/automodpack.common.gradle.kts new file mode 100644 index 000000000..774da674e --- /dev/null +++ b/buildSrc/src/main/kotlin/automodpack.common.gradle.kts @@ -0,0 +1,247 @@ +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream + +plugins { + idea +} + +idea { + module { + isDownloadJavadoc = true + isDownloadSources = true + } +} + +repositories { + fun strictMaven(url: String, vararg groups: String) = exclusiveContent { + forRepository { maven(url) } + filter { groups.forEach(::includeGroup) } + } + strictMaven("https://maven.parchmentmc.org", "org.parchmentmc.data") + maven("https://maven.fabricmc.net/") +} + +tasks.named("processResources") { + fun prop(name: String) = project.property(name) as String + + val props = HashMap().apply { + this["version"] = prop("mod_version") + this["minecraft"] = prop("meta.minecraft") + this["id"] = prop("mod.id") + this["name"] = prop("mod_name") + this["description"] = prop("mod.description") + } + + filesMatching(listOf("pack.mcmeta", "fabric.mod.json", "META-INF/neoforge.mods.toml", "META-INF/mods.toml")) { + expand(props) + } +} + +tasks.named("build") { + // We need to build the loader modules first, so that we can merge the jars later + dependsOn(":core:build", ":loader-core:build") + when { + project.name.contains("fabric") -> { + dependsOn(":loader-fabric-core:build", ":loader-fabric-15:build", ":loader-fabric-16:build") + } + project.name.contains("neoforge") -> { + dependsOn(":loader-neoforge-fml2:build", ":loader-neoforge-fml4:build") + } + project.name.contains("forge") -> { + dependsOn(":loader-forge-fml40:build", ":loader-forge-fml47:build") + } + } + + finalizedBy(tasks.named("mergeJar")) +} + +val mergedDir = File("${rootProject.projectDir}/merged") + +tasks.named("clean") { + finalizedBy("cleanMerged") +} + +tasks.register("cleanMerged") { + doLast { + mergedDir.deleteRecursively() + } +} + +// TODO make it faster +// If something goes wrong, try to run the "clean" task +tasks.register("mergeJar") { + doLast { + mergedDir.mkdirs() + val buildDirLibs = project.layout.buildDirectory.dir("libs").get().asFile + val jarToMerge = buildDirLibs.listFiles() + ?.firstOrNull { file -> file.isFile && !file.name.endsWith("-sources.jar") && file.name.endsWith(".jar") } + ?: error("No jar found to merge in build/libs directory! ${buildDirLibs.absolutePath}") + + val time = System.currentTimeMillis() + println("Found $jarToMerge to merge. Merging...") + + val minecraftVersionStr = jarToMerge.name.substringAfterLast("-mc").substringBefore("-") + if (minecraftVersionStr.isEmpty()) { + error("Could not identify Minecraft version in the jar name: ${jarToMerge.name}") + } + + var loaderModule = "" + if (jarToMerge.name.contains("fabric")) { + loaderModule = "fabric-core" + } else if (jarToMerge.name.contains("neoforge")) { + loaderModule = when (minecraftVersionStr) { + "1.20.6", "1.20.4", "1.20.1", "1.19.4", "1.19.2", "1.18.2" -> "neoforge-fml2" + else -> "neoforge-fml4" + } + } else if (jarToMerge.name.contains("forge")) { + loaderModule = if (minecraftVersionStr == "1.18.2") { + "forge-fml40" + } else { + "forge-fml47" + } + } + + val loaderModuleProject = rootProject.findProject("loader-$loaderModule") + ?: error("Loader module '$loaderModule' not found in the project.") + + val loaderFile = loaderModuleProject.layout.buildDirectory.dir("libs").get().asFile.listFiles() + ?.single { it.isFile && !it.name.endsWith("-sources.jar") && it.name.endsWith(".jar") } + ?: error("No loader jar found in loader/$loaderModule/build/libs directory! Make sure to build the loader module first.") + + val finalJar = File("$mergedDir/${jarToMerge.name}") + loaderFile.copyTo(finalJar, overwrite = true) + appendFileToZip(finalJar, jarToMerge, "automodpack-mod.jar") + + println("Merged: ${jarToMerge.name} into: ${finalJar.name} from: ${loaderFile.name} Took: ${System.currentTimeMillis() - time}ms") + } +} + +fun appendFileToZip(zipFile: File, fileToAppend: File, entryName: String) { + val entries = ZipInputStream(FileInputStream(zipFile)).use { zipStream -> + generateSequence { zipStream.nextEntry } + .toList() + } + + val graph = mutableMapOf>() + + entries.forEach { entry -> + val children = entry.name.split("/") + var currentParent = "" + children.forEach { child -> + if (child.isNotEmpty()) { + currentParent = "$currentParent$child/" + val parent = graph.getOrPut(currentParent) { mutableListOf() } + parent.add(child) + } + } + } + +// graph.forEach { (parent, children) -> +// println("$parent -> $children") +// } + + val filteredGraph = filterEntries(entries, graph, zipFile) + + // Doing with temp file since for some reason just adding the file breaks the zip/jar file + val tempFile = File("$zipFile.temp") + tempFile.createNewFile() + + ZipOutputStream(FileOutputStream(tempFile)).use { zipStream -> + ZipInputStream(FileInputStream(zipFile)).use { existingZipStream -> + while (true) { + val entry = existingZipStream.nextEntry ?: break + if (filteredGraph.containsKey("${entry.name}/")) { + try { + val zipEntry = ZipEntry(entry.name) + zipStream.putNextEntry(zipEntry) + existingZipStream.copyTo(zipStream) + zipStream.closeEntry() + } catch (_: Exception) { + println("Error while copying entry: ${entry.name}") + } + } + } + } + + // Add the new entry + zipStream.putNextEntry(ZipEntry(entryName)) + FileInputStream(fileToAppend).use { fileInputStream -> + fileInputStream.copyTo(zipStream) + } + zipStream.closeEntry() + } + + // Replace the original zip file with the one containing the new entry + zipFile.delete() + tempFile.renameTo(zipFile) +} + +val dupesDir = File("${rootProject.projectDir}/dupes") + +fun filterEntries(entries: List, graph: Map>, zipFile: File): Map> { + + val emptyDirs = mutableSetOf() + + // find empty directories + graph.forEach { (parent, children) -> + if (children.size == 0) { + emptyDirs.add(parent) + } + } + + // dump duplicate entries + dupesDir.deleteRecursively() + dupesDir.mkdirs() + + dumpDupeEntries(zipFile, entries) + + // filter empty directories + // filter single duplicate files (leave only one) + val dupes = mutableSetOf() + + val filteredGraph = graph.filter { (parent, children) -> + if (emptyDirs.contains(parent)) { + println("Filtering empty dir: $parent -> $children") + return@filter false + } + + val count = graph.count { parent == it.key } + if (count > 1 && !dupes.add(children[0])) { + return@filter false + } + + return@filter true + } + + return filteredGraph +} + +fun dumpDupeEntries(zipFile: File, entries: List) { + // check for duplicates + val entryNames = mutableSetOf() + entries.forEach { duplicate -> + if (!entryNames.add(duplicate.name)) { + println("Duplicate entry: $duplicate") + + // write the entry to the file + ZipInputStream(FileInputStream(zipFile)).use { zipStream -> + var i = 0 + generateSequence { zipStream.nextEntry } + .filter { it.name == duplicate.name } + .forEach { _ -> + i++ + val dupeFile = File("$dupesDir/$i-$duplicate.dupe") + println("Extracting to: $dupeFile") + dupeFile.parentFile.mkdirs() + dupeFile.createNewFile() + FileOutputStream(dupeFile).use { fileOutputStream -> + zipStream.copyTo(fileOutputStream) + } + } + } + } + } +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 965cd2955..ce131a635 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -2,13 +2,13 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { java - id("com.github.johnrengelman.shadow") + id("com.gradleup.shadow") } base { - archivesName = property("mod_id") as String + "-" + project.name + archivesName = property("mod.id") as String + "-" + project.name version = property("mod_version") as String - group = property("mod_group") as String + group = property("mod.group") as String } repositories { @@ -16,13 +16,13 @@ repositories { } dependencies { - implementation("org.apache.logging.log4j:log4j-core:2.20.0") + implementation("org.apache.logging.log4j:log4j-core:2.19.0") implementation("com.google.code.gson:gson:2.10.1") - implementation("io.netty:netty-all:4.1.118.Final") + compileOnly("io.netty:netty-all:4.1.118.Final") implementation("org.bouncycastle:bcpkix-jdk18on:1.80") implementation("com.github.luben:zstd-jni:1.5.7-3") implementation("org.tomlj:tomlj:1.1.1") - implementation("org.apache.httpcomponents.client5:httpclient5:5.4.4") + implementation("org.apache.httpcomponents.client5:httpclient5:5.5") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2") } @@ -31,7 +31,7 @@ java { // leave it on java 17 to be compatible with older versions and we dont really need 21 there anyway sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 - + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) withSourcesJar() } diff --git a/core/src/main/java/pl/skidam/automodpack_core/GlobalVariables.java b/core/src/main/java/pl/skidam/automodpack_core/GlobalVariables.java index 24c7bcc0c..4b4df2ef8 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/GlobalVariables.java +++ b/core/src/main/java/pl/skidam/automodpack_core/GlobalVariables.java @@ -29,7 +29,7 @@ public class GlobalVariables { public static Modpack modpack; public static FullServerPack fullpacks; public static NettyServer hostServer; - public static Jsons.ServerConfigFields serverConfig; + public static Jsons.ServerConfigFieldsV2 serverConfig; public static Jsons.ClientConfigFieldsV2 clientConfig; public static Jsons.KnownHostsFields knownHosts; public static final Path automodpackDir = Path.of("automodpack"); diff --git a/core/src/main/java/pl/skidam/automodpack_core/Server.java b/core/src/main/java/pl/skidam/automodpack_core/Server.java index fb7cbf9ac..763e8d7cd 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/Server.java +++ b/core/src/main/java/pl/skidam/automodpack_core/Server.java @@ -3,10 +3,7 @@ import pl.skidam.automodpack_core.config.ConfigTools; import pl.skidam.automodpack_core.config.Jsons; import pl.skidam.automodpack_core.modpack.ModpackExecutor; -import pl.skidam.automodpack_core.modpack.FullServerPack; -import pl.skidam.automodpack_core.modpack.Modpack; import pl.skidam.automodpack_core.modpack.ModpackContent; -import pl.skidam.automodpack_core.modpack.FullServerPackContent; import pl.skidam.automodpack_core.protocol.netty.NettyServer; import java.nio.file.Path; @@ -38,14 +35,13 @@ public static void main(String[] args) { serverConfigFile = modpackDir.resolve("automodpack-server.json"); serverCoreConfigFile = modpackDir.resolve("automodpack-core.json"); - serverConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFields.class); + serverConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFieldsV2.class); if (serverConfig != null) { serverConfig.syncedFiles = new ArrayList<>(); - serverConfig.hostModpackOnMinecraftPort = false; serverConfig.validateSecrets = false; ConfigTools.save(serverConfigFile, serverConfig); - if (serverConfig.hostPort == -1) { + if (serverConfig.bindPort == -1) { LOGGER.error("Host port not set in config!"); return; } @@ -64,7 +60,7 @@ public static void main(String[] args) { mainModpackDir.toFile().mkdirs(); ModpackExecutor modpackExecutor = new ModpackExecutor(); - ModpackContent modpackContent = new ModpackContent(serverConfig.modpackName, null, mainModpackDir, serverConfig.syncedFiles, serverConfig.allowEditsInFiles, modpackExecutor.getExecutor()); + ModpackContent modpackContent = new ModpackContent(serverConfig.modpackName, null, mainModpackDir, serverConfig.syncedFiles, serverConfig.allowEditsInFiles, serverConfig.forceCopyFilesToStandardLocation, modpackExecutor.getExecutor()); boolean generated = modpackExecutor.generateNew(modpackContent); if (generated) { @@ -72,24 +68,10 @@ public static void main(String[] args) { } else { LOGGER.error("Failed to generate modpack!"); } - LOGGER.info("Start FullServerPack generation!"); modpackExecutor.stop(); - FullServerPack fullserverpack = new FullServerPack(); - FullServerPackContent fullServerPackContent = new FullServerPackContent(serverConfig.modpackName, hostContentModpackDir, fullserverpack.CREATION_EXECUTOR); - boolean fullpackgenerated = fullserverpack.generateNew(fullServerPackContent); - - - if (fullpackgenerated) { - LOGGER.info("FullServerPack generated!"); - } else { - LOGGER.error("Failed to generate serverpack!"); - } - - modpack.shutdownExecutor(); - fullserverpack.shutdownExecutor(); - LOGGER.info("Starting server on port {}", serverConfig.hostPort); + LOGGER.info("Starting server on port {}", serverConfig.bindPort); server.start(); // wait for server to stop while (server.isRunning()) { diff --git a/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java b/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java index 8fa495954..0aae21193 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java +++ b/core/src/main/java/pl/skidam/automodpack_core/config/ConfigTools.java @@ -27,7 +27,6 @@ public class ConfigTools { public static Gson GSON = new GsonBuilder() - .serializeNulls() .disableHtmlEscaping() .setPrettyPrinting() .registerTypeAdapter(InetSocketAddress.class, new InetSocketAddressTypeAdapter()) @@ -57,27 +56,13 @@ public static T getConfigObject(Class configClass) { } // Config stuff - public static T loadCheck(Path configFile, Class configClass) { + public static T softLoad(Path configFile, Class configClass) { try { if (Files.isRegularFile(configFile)) { String json = Files.readString(configFile); - T obj = GSON.fromJson(json, configClass); - if (obj == null) { - LOGGER.error("Parsed object is null. Possible JSON syntax error in file: " + configFile); - return null; - } - - return obj; + return GSON.fromJson(json, configClass); } - } catch (JsonSyntaxException e) { - LOGGER.error("JSON syntax error while loading config! {} {}", configClass, e.getMessage()); - LOGGER.error("This error most often happens when you e.g. forget to put a comma between fields in JSON file. Check the file: " + configFile.toAbsolutePath().normalize()); - return null; - } catch (Exception e) { - LOGGER.error("Couldn't load config! " + configClass); - e.printStackTrace(); - } - + } catch (Exception ignored) { } return null; } diff --git a/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java b/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java index 6d89e047b..d2123e17c 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java +++ b/core/src/main/java/pl/skidam/automodpack_core/config/Jsons.java @@ -33,6 +33,7 @@ public static class ClientConfigFieldsV2 { public static class ModpackAddresses { public InetSocketAddress hostAddress; // modpack host address public InetSocketAddress serverAddress; // minecraft server address + public boolean requiresMagic; // if true, client will use magic packets to connect to the modpack host public ModpackAddresses() { // Default constructor for Gson @@ -45,9 +46,10 @@ public ModpackAddresses() { * @param serverAddress minecraft server address that represents the target address * which client uses to connect. This value CANNOT be manipulated by the server. */ - public ModpackAddresses(InetSocketAddress hostAddress, InetSocketAddress serverAddress) { + public ModpackAddresses(InetSocketAddress hostAddress, InetSocketAddress serverAddress, boolean requiresMagic) { this.hostAddress = hostAddress; this.serverAddress = serverAddress; + this.requiresMagic = requiresMagic; } public boolean isAnyEmpty() { @@ -55,24 +57,20 @@ public boolean isAnyEmpty() { } } - public static class ServerConfigFields { + public static class ServerConfigFieldsV1 { public int DO_NOT_CHANGE_IT = 1; // file version public String modpackName = ""; public boolean modpackHost = true; public boolean generateModpackOnStart = true; - public List syncedFiles = List.of("/mods/*.jar", "!/mods/iDontWantThisModInModpack.jar", "!/config/andThisConfigToo.json", "!/mods/andAllTheseMods-*.jar", "!/mods/server-*.jar"); - public List allowEditsInFiles = List.of("/options.txt", "/config/*", "!/config/excludeThisFile"); + public List syncedFiles = List.of("/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"); + public List allowEditsInFiles = List.of("/options.txt", "/config/**"); public boolean autoExcludeUnnecessaryFiles = true; -// public List forceLoad = List.of("/resourcepacks/someResourcePack.zip", "/shaderpacks/someShaderPack.zip"); -// public List> forceLoad = new ArrayList<>(); public boolean requireAutoModpackOnClient = true; public boolean nagUnModdedClients = true; public String nagMessage = "This server provides dedicated modpack through AutoModpack!"; public String nagClickableMessage = "Click here to get the AutoModpack!"; public String nagClickableLink = "https://modrinth.com/project/automodpack"; public boolean autoExcludeServerSideMods = true; -// public boolean velocityMode = false; compat plugin... someday I hope -// public boolean forceToDisableAllOtherModsOnClients = false; public boolean hostModpackOnMinecraftPort = true; public String hostIp = ""; public String hostLocalIp = ""; @@ -86,8 +84,36 @@ public static class ServerConfigFields { public List acceptedLoaders; } + public static class ServerConfigFieldsV2 { + public int DO_NOT_CHANGE_IT = 2; // file version + public String modpackName = ""; + public boolean modpackHost = true; + public boolean generateModpackOnStart = true; + public List syncedFiles = List.of("/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"); + public List allowEditsInFiles = List.of("/options.txt", "/config/**"); + public List forceCopyFilesToStandardLocation = List.of(); + public boolean autoExcludeServerSideMods = true; + public boolean autoExcludeUnnecessaryFiles = true; + public boolean requireAutoModpackOnClient = true; + public boolean nagUnModdedClients = true; + public String nagMessage = "This server provides dedicated modpack through AutoModpack!"; + public String nagClickableMessage = "Click here to get the AutoModpack!"; + public String nagClickableLink = "https://modrinth.com/project/automodpack"; + public String bindAddress = ""; + public int bindPort = -1; + public String addressToSend = ""; + public int portToSend = -1; + public boolean disableInternalTLS = false; + public boolean updateIpsOnEveryStart = false; + public int bandwidthLimit = 0; + public boolean validateSecrets = true; + public long secretLifetime = 336; // 336 hours = 14 days + public boolean selfUpdater = false; + public List acceptedLoaders; + } + public static class ServerCoreConfigFields { - public String automodpackVersion = "4.0.0-beta35"; // TODO: dont hardcode it + public String automodpackVersion = "4.0.0-beta37"; // TODO: dont hardcode it public String loader = "fabric"; public String loaderVersion = "0.16.14"; public String mcVersion = "1.21.1"; @@ -122,14 +148,16 @@ public static class ModpackContentItem { public String size; public String type; public boolean editable; + public boolean forceCopy; public String sha1; public String murmur; - public ModpackContentItem(String file, String size, String type, boolean editable, String sha1, String murmur) { + public ModpackContentItem(String file, String size, String type, boolean editable, boolean forceCopy, String sha1, String murmur) { this.file = file; this.size = size; this.type = type; this.editable = editable; + this.forceCopy = forceCopy; this.sha1 = sha1; this.murmur = murmur; } diff --git a/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java b/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java index 350ebeeed..2bcc22c8c 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java +++ b/core/src/main/java/pl/skidam/automodpack_core/modpack/ModpackContent.java @@ -20,11 +20,12 @@ public class ModpackContent { private final String MODPACK_NAME; private final WildCards SYNCED_FILES_CARDS; private final WildCards EDITABLE_CARDS; + private final WildCards FORCE_COPY_FILES_TO_STANDARD_LOCATION; private final Path MODPACK_DIR; private final ThreadPoolExecutor CREATION_EXECUTOR; private final Map sha1MurmurMapPreviousContent = new HashMap<>(); - public ModpackContent(String modpackName, Path cwd, Path modpackDir, List syncedFiles, List allowEditsInFiles, ThreadPoolExecutor CREATION_EXECUTOR) { + public ModpackContent(String modpackName, Path cwd, Path modpackDir, List syncedFiles, List allowEditsInFiles, List forceCopyFilesToStandardLocation, ThreadPoolExecutor CREATION_EXECUTOR) { this.MODPACK_NAME = modpackName; this.MODPACK_DIR = modpackDir; Set directoriesToSearch = new HashSet<>(2); @@ -36,6 +37,7 @@ public ModpackContent(String modpackName, Path cwd, Path modpackDir, List start() { } try { - if (!Files.exists(serverCertFile) || !Files.exists(serverPrivateKeyFile)) { - // Create a self-signed certificate - KeyPair keyPair = NetUtils.generateKeyPair(); - X509Certificate cert = NetUtils.selfSign(keyPair); - - // save it to the file - NetUtils.saveCertificate(cert, serverCertFile); - NetUtils.savePrivateKey(keyPair.getPrivate(), serverPrivateKeyFile); - } + if (serverConfig.disableInternalTLS && serverConfig.bindPort != -1) { + LOGGER.warn("Internal TLS is disabled. Clients will not be able to connect directly; you must use e.g. a reverse proxy with TLS."); + } else { + if (serverConfig.disableInternalTLS) { + LOGGER.error("Internal TLS cannot be disabled. You have to bind modpack host on a separate port, preferably also on a loopback address or atleast some private one."); + } - X509Certificate cert = NetUtils.loadCertificate(serverCertFile); + if (!Files.exists(serverCertFile) || !Files.exists(serverPrivateKeyFile)) { + // Create a self-signed certificate + KeyPair keyPair = NetUtils.generateKeyPair(); + X509Certificate cert = NetUtils.selfSign(keyPair); - if (cert == null) { - throw new IllegalStateException("Server certificate couldn't be loaded"); - } + // save it to the file + NetUtils.saveCertificate(cert, serverCertFile); + NetUtils.savePrivateKey(keyPair.getPrivate(), serverPrivateKeyFile); + } - // Shiny TLS 1.3 - sslCtx = SslContextBuilder.forServer(serverCertFile.toFile(), serverPrivateKeyFile.toFile()) - .sslProvider(SslProvider.JDK) - .protocols("TLSv1.3") - .ciphers(Arrays.asList( - "TLS_AES_128_GCM_SHA256", - "TLS_AES_256_GCM_SHA384", - "TLS_CHACHA20_POLY1305_SHA256")) - .build(); + X509Certificate cert = NetUtils.loadCertificate(serverCertFile); - // generate sha256 from cert as a fingerprint - certificateFingerprint = NetUtils.getFingerprint(cert); - LOGGER.warn("Certificate fingerprint for client validation: {}", certificateFingerprint); + if (cert == null) { + throw new IllegalStateException("Server certificate couldn't be loaded"); + } + + // Shiny TLS 1.3 + sslCtx = SslContextBuilder.forServer(serverCertFile.toFile(), serverPrivateKeyFile.toFile()) + .sslProvider(SslProvider.JDK) + .protocols("TLSv1.3") + .ciphers(Arrays.asList( + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256")) + .build(); + + // generate sha256 from cert as a fingerprint + certificateFingerprint = NetUtils.getFingerprint(cert); + if (certificateFingerprint != null) { + LOGGER.warn("Certificate fingerprint: {}", certificateFingerprint); + } + } if (!canStart()) { new TrafficShaper(null); return Optional.empty(); } - int port = serverConfig.hostPort; - InetSocketAddress bindAddress = new InetSocketAddress("0.0.0.0", port); + String address = serverConfig.bindAddress; + int port = serverConfig.bindPort; + InetSocketAddress bindAddress = null; + if (port != -1) { + if (address == null || address.isBlank()) { + bindAddress = new InetSocketAddress(port); + } else { + bindAddress = new InetSocketAddress(address, port); + } + } + LOGGER.info("Starting modpack host server on {}", bindAddress); Class socketChannelClass; @@ -157,14 +176,13 @@ public boolean shouldHost() { // Returns true if stopped successfully public boolean stop() { try { - if (serverChannel == null) { - if (shouldHost) { - shouldHost = false; - } - } else { + if (serverChannel != null) { serverChannel.channel().close().sync(); + serverChannel = null; } + shouldHost = false; + TrafficShaper.close(); if (eventLoopGroup != null) { @@ -200,35 +218,29 @@ private boolean canStart() { return false; } - if (serverConfig.hostModpackOnMinecraftPort) { - shouldHost = true; - LOGGER.info("Hosting modpack on Minecraft port"); - return false; - } - - if (serverConfig.updateIpsOnEveryStart || (serverConfig.hostIp == null || serverConfig.hostIp.isEmpty())) { + if (serverConfig.updateIpsOnEveryStart) { String publicIp = AddressHelpers.getPublicIp(); if (publicIp != null) { - serverConfig.hostIp = publicIp; - ConfigTools.save(serverConfigFile, serverConfig); - LOGGER.warn("Setting Host IP to {}", serverConfig.hostIp); + serverConfig.addressToSend = publicIp; + LOGGER.warn("Setting Host IP to {}", serverConfig.addressToSend); } else { - LOGGER.error("Host IP isn't set in config, please change it manually! Couldn't get public IP"); - return false; + LOGGER.error("Couldn't get public IP, please change it manually! "); } - } - if (serverConfig.updateIpsOnEveryStart || (serverConfig.hostLocalIp == null || serverConfig.hostLocalIp.isEmpty())) { try { - serverConfig.hostLocalIp = AddressHelpers.getLocalIp(); ConfigTools.save(serverConfigFile, serverConfig); - LOGGER.warn("Setting Host local IP to {}", serverConfig.hostLocalIp); } catch (Exception e) { e.printStackTrace(); } } - shouldHost = true; - return true; + shouldHost = true; // At this point we know that we want to host the modpack + + if (serverConfig.bindPort == -1) { + LOGGER.info("Hosting modpack on Minecraft port"); + return false; // Dont start separate server for modpack hosting, use minecraft port instead + } else { + return true; // Start separate server for modpack hosting + } } } diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ErrorPrinter.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ErrorPrinter.java index f0276aa4b..5f2da1aa0 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ErrorPrinter.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ErrorPrinter.java @@ -3,6 +3,7 @@ import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.DecoderException; +import io.netty.handler.ssl.NotSslRecordException; import javax.net.ssl.SSLHandshakeException; @@ -15,7 +16,7 @@ public class ErrorPrinter extends ChannelDuplexHandler { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - if (cause instanceof DecoderException && cause.getCause() != null && cause.getCause() instanceof SSLHandshakeException) { + if (cause instanceof DecoderException && cause.getCause() != null && (cause.getCause() instanceof SSLHandshakeException || cause.getCause() instanceof NotSslRecordException)) { // Probably the client rejecting the server certificate. Omit stack trace to reduce log output. LOGGER.debug("Error occurred in connection to client at address {}: {}", ctx.channel().remoteAddress(), cause.getMessage()); } else { diff --git a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java index 94cd0b1fc..3776ac3a6 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java +++ b/core/src/main/java/pl/skidam/automodpack_core/protocol/netty/handler/ProtocolServerHandler.java @@ -10,6 +10,7 @@ import java.util.List; +import static pl.skidam.automodpack_core.GlobalVariables.serverConfig; import static pl.skidam.automodpack_core.protocol.NetUtils.*; public class ProtocolServerHandler extends ByteToMessageDecoder { @@ -27,7 +28,7 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { } int magic = in.getInt(0); - if (magic == MAGIC_AMMC) { + if (magic == MAGIC_AMMC) { // Server should always support AMMC protocol (magic packets) (preferred way to connect, required for hosting on Minecraft port and good for backwards compatibility) // Consume the packet in.skipBytes(in.readableBytes()); @@ -40,24 +41,9 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { var handlers = ctx.pipeline().toMap(); handlers.forEach((name, handler) -> ctx.pipeline().remove(handler)); -// InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress(); -// boolean isLocalConnection = AddressHelpers.isLocal(address); -// -// // Use compression only for non-local connections -// ctx.pipeline().channel().attr(NetUtils.USE_COMPRESSION).set(!isLocalConnection); - - ctx.pipeline().channel().attr(NettyServer.USE_COMPRESSION).set(true); - - // Set up the pipeline for our protocol - ctx.pipeline() - .addLast("traffic-shaper", TrafficShaper.trafficShaper.getTrafficShapingHandler()) - .addLast("tls", sslCtx.newHandler(ctx.alloc())) - .addLast("zstd-encoder", new ZstdEncoder()) - .addLast("zstd-decoder", new ZstdDecoder()) - .addLast("chunked-write", new ChunkedWriteHandler()) - .addLast("protocol-msg-decoder", new ProtocolMessageDecoder()) - .addLast("msg-handler", new ServerMessageHandler()) - .addLast("error-printer", new ErrorPrinter()); + setupPipeline(ctx); + } else if (sslCtx == null || serverConfig.bindPort != -1) { // However if there's no magic packet and we don't use internal TLS or we are hosting on a separate port, we have to try to connect anyway, for use with reverse proxy setups + setupPipeline(ctx); } // Always remove this handler after processing if its still there @@ -65,4 +51,22 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { ctx.pipeline().remove(this); } } + + private void setupPipeline(ChannelHandlerContext ctx) { + ctx.pipeline().channel().attr(NettyServer.USE_COMPRESSION).set(true); + + // add error handler pipeline + ctx.pipeline().addLast("error-printer-first", new ErrorPrinter()); + ctx.pipeline().addLast("traffic-shaper", TrafficShaper.trafficShaper.getTrafficShapingHandler()); + if (sslCtx != null) { // If SSL context is provided, add TLS handler + ctx.pipeline().addLast("tls", sslCtx.newHandler(ctx.alloc())); + } + ctx.pipeline() // Add the rest + .addLast("zstd-encoder", new ZstdEncoder()) + .addLast("zstd-decoder", new ZstdDecoder()) + .addLast("chunked-write", new ChunkedWriteHandler()) + .addLast("protocol-msg-decoder", new ProtocolMessageDecoder()) + .addLast("msg-handler", new ServerMessageHandler()) + .addLast("error-printer-last", new ErrorPrinter()); + } } \ No newline at end of file diff --git a/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java b/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java index f135ba31f..28f19dd66 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java +++ b/core/src/main/java/pl/skidam/automodpack_core/utils/AddressHelpers.java @@ -91,6 +91,7 @@ public static InetSocketAddress format(String host, int port) { if (host.endsWith(".")) { // It breaks our checks and looks ugly, but its a valid domain... host = host.substring(0, host.length() - 1); } + host = host.toLowerCase(); // #382 return InetSocketAddress.createUnresolved(host, port); } @@ -103,11 +104,11 @@ public static InetSocketAddress parse(String address) { String host = address.substring(0, portIndex); String port = address.substring(portIndex + 1); if (port.matches("\\d+")) { - socketAddress = InetSocketAddress.createUnresolved(host, Integer.parseInt(port)); + socketAddress = format(host, Integer.parseInt(port)); } } if (socketAddress == null) { - socketAddress = InetSocketAddress.createUnresolved(address, 0); + socketAddress = format(address, 0); } } catch (Exception e) { LOGGER.error("Error while parsing address", e); diff --git a/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java b/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java index 116eb7531..8fa727290 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java +++ b/core/src/main/java/pl/skidam/automodpack_core/utils/CustomFileUtils.java @@ -144,10 +144,10 @@ public static String formatPath(final Path modpackFile, final Path modpackPath) String formattedFile = modpackFileStr; // Checks if in file parents paths (absolute path) there is modpack directory (absolute path) - if (modpackFileStrAbs.contains(modpackPathStrAbs)) { - formattedFile = modpackFileStrAbs.replace(modpackPathStrAbs, ""); - } else if (modpackFileStrAbs.contains(cwdStrAbs)) { - formattedFile = modpackFileStrAbs.replace(cwdStrAbs, ""); + if (modpackFileStrAbs.startsWith(modpackPathStrAbs)) { + formattedFile = modpackFileStrAbs.substring(modpackPathStrAbs.length()); + } else if (modpackFileStrAbs.startsWith(cwdStrAbs)) { + formattedFile = modpackFileStrAbs.substring(cwdStrAbs.length()); } else if (!modpackFileStrAbs.equals(modpackFileStr)) { // possible in e.g. docker LOGGER.error("File: {} ({}) is not in modpack directory: {} ({}) or current working directory: {}", modpackFileStr, modpackFileStrAbs, modpackPath, modpackPathStrAbs, cwdStrAbs); } diff --git a/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java b/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java index 75a16e4ea..e188a696f 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java +++ b/core/src/main/java/pl/skidam/automodpack_core/utils/WildCards.java @@ -26,6 +26,10 @@ public Map getWildcardMatches() { public void separateRules(List rules) { for (String rule : rules) { + if (rule == null || rule.isBlank()) { + continue; + } + if (rule.startsWith("!")) { blackListRules.add(rule.substring(1)); } else { @@ -42,7 +46,12 @@ public WildCards(List rules, Set startDirectories) { for (Path startDirectory : startDirectories) { try (Stream paths = Files.walk(startDirectory)) { - paths.forEach(node -> matchWhiteRules(node, startDirectory, composedWhiteRules)); + try { // Fixes some wierd edge cases + paths.filter(Files::isRegularFile) + .forEach(node -> matchWhiteRules(node, startDirectory, composedWhiteRules)); + } catch (Exception e) { + LOGGER.error("Error processing files in directory: {}", startDirectory, e); + } } matchBlackRules(startDirectory, composedBlackRules); @@ -187,6 +196,10 @@ public Map> composeRules(List rules) { Map> directoryRulePathsMap = new HashMap<>(rules.size()); for (String rule : rules) { + if (rule == null || rule.isBlank()) { + continue; + } + int lastSlashIndex = rule.lastIndexOf("/"); if (lastSlashIndex == -1) { continue; diff --git a/core/src/main/java/pl/skidam/automodpack_core/utils/WorkaroundUtil.java b/core/src/main/java/pl/skidam/automodpack_core/utils/WorkaroundUtil.java index d207e0543..931fab77f 100644 --- a/core/src/main/java/pl/skidam/automodpack_core/utils/WorkaroundUtil.java +++ b/core/src/main/java/pl/skidam/automodpack_core/utils/WorkaroundUtil.java @@ -25,11 +25,11 @@ public Set getWorkaroundMods(Jsons.ModpackContentFields modpackContentFi return workaroundMods; } - for (Jsons.ModpackContentFields.ModpackContentItem mod : modpackContentFields.list) { - if (mod.type.equals("mod")) { - Path modPath = CustomFileUtils.getPath(modpackPath, mod.file); + for (Jsons.ModpackContentFields.ModpackContentItem item : modpackContentFields.list) { + if (item.type.equals("mod")) { + Path modPath = CustomFileUtils.getPath(modpackPath, item.file); if (FileInspection.hasSpecificServices(modPath)) { - workaroundMods.add(mod.file); + workaroundMods.add(item.file); } } } diff --git a/core/src/test/java/pl/skidam/automodpack_core/modpack/ModpackTest.java b/core/src/test/java/pl/skidam/automodpack_core/modpack/ModpackTest.java index ee5e2bf0d..e97b197fb 100644 --- a/core/src/test/java/pl/skidam/automodpack_core/modpack/ModpackTest.java +++ b/core/src/test/java/pl/skidam/automodpack_core/modpack/ModpackTest.java @@ -48,7 +48,7 @@ void modpackTest() { "ModpackContentItems(file=/mods/server-mod-1.20.jar, size=1, type=other, editable=false, sha1=86f7e437faa5a7fce15d1ddcb9eaeaea377667b8, murmur=null)" ); - ModpackContent content = new ModpackContent("TestPack", null, testFilesDir, new ArrayList<>(), new ArrayList<>(editable), new ModpackExecutor().getExecutor()); + ModpackContent content = new ModpackContent("TestPack", null, testFilesDir, new ArrayList<>(), new ArrayList<>(editable), new ArrayList<>(), new ModpackExecutor().getExecutor()); content.create(); boolean correct = true; diff --git a/docs/.translated/fr_fr/_homepage.mdx b/docs/.translated/fr_fr/_homepage.mdx new file mode 100644 index 000000000..3f7e2f1aa --- /dev/null +++ b/docs/.translated/fr_fr/_homepage.mdx @@ -0,0 +1,9 @@ +Bienvenue dans la documentation d'AutoModpack ! + +Si vous débutez avec AutoModpack, nous vous recommandons de commencer par le [Guide de démarrage rapide](docs/quick-start) pour vous familiariser avec les bases de l'utilisation d'AutoModpack. + +Si vous recherchez des informations plus détaillées, vous pouvez les trouver dans la section `👨‍💻 Détails techniques`. + +Vous avez des questions ou besoin d'aide ? Rejoignez notre [serveur Discord](https://discord.gg/hS6aMyeA9P). + +Vous trouvez ce projet utile ? Envisagez de soutenir son développement et sa maintenance continus en faisant un don à l'auteur sur [Ko-fi](https://ko-fi.com/skidam). \ No newline at end of file diff --git a/docs/.translated/fr_fr/_meta.json b/docs/.translated/fr_fr/_meta.json new file mode 100644 index 000000000..96177897b --- /dev/null +++ b/docs/.translated/fr_fr/_meta.json @@ -0,0 +1,34 @@ +{ + "quick-start.mdx": { + "name": "🚀 Démarrage rapide", + "icon": null + }, + "usage": { + "name": "Utilisation", + "icon": null + }, + "configuration": { + "name": "\uD83D\uDEE0\uFE0F Configuration", + "icon": null + }, + "commands": { + "name": "⌨\uFE0F Commandes", + "icon": null + }, + "examples": { + "name": "\uD83D\uDCF7 Exemples", + "icon": null + }, + "technicals": { + "name": "\uD83D\uDC68\u200D\uD83D\uDCBB Détails techniques", + "icon": null + }, + "compatibility": { + "name": "\uD83D\uDCDA Compatibilité", + "icon": null + }, + "faq.mdx": { + "name": "❔ FAQ", + "icon": null + } +} \ No newline at end of file diff --git a/docs/.translated/fr_fr/commands/_meta.json b/docs/.translated/fr_fr/commands/_meta.json new file mode 100644 index 000000000..5610f28c6 --- /dev/null +++ b/docs/.translated/fr_fr/commands/_meta.json @@ -0,0 +1,6 @@ +{ + "commands.mdx": { + "name": "Commandes", + "icon": null + } +} diff --git a/docs/.translated/fr_fr/commands/commands.mdx b/docs/.translated/fr_fr/commands/commands.mdx new file mode 100644 index 000000000..df07b58dc --- /dev/null +++ b/docs/.translated/fr_fr/commands/commands.mdx @@ -0,0 +1,11 @@ +`/amp` est un alias pour `/automodpack`. + +- `/automodpack` - Statut d'AutoModpack et aide générale. +- `/automodpack generate` - Générer le modpack. +- `/automodpack host` - Statut de l'hébergement du modpack. +- `/automodpack host start` - Démarrer l'hébergement du modpack. +- `/automodpack host stop` - Arrêter l'hébergement du modpack. +- `/automodpack host restart` - Redémarrer l'hébergement du modpack. +- `/automodpack host connections` - Liste toutes les connexions actives à l'hébergement du modpack. +- `/automodpack host fingerprint` - Obtenir le [certificat de l'empreinte](../technicals/certificate) de l'hôte du modpack. +- `/automodpack config reload` - Recharger les fichiers de configuration. \ No newline at end of file diff --git a/docs/.translated/fr_fr/compatibility/_meta.json b/docs/.translated/fr_fr/compatibility/_meta.json new file mode 100644 index 000000000..29d93bf90 --- /dev/null +++ b/docs/.translated/fr_fr/compatibility/_meta.json @@ -0,0 +1,14 @@ +{ + "launchers.mdx": { + "name": "Compatibilité des launchers", + "icon": null + }, + "mods.mdx": { + "name": "Modifications compatibility", + "icon": null + }, + "proxies.mdx": { + "name": "Proxies compatibility", + "icon": null + } +} diff --git a/docs/.translated/fr_fr/compatibility/launchers.mdx b/docs/.translated/fr_fr/compatibility/launchers.mdx new file mode 100644 index 000000000..33cea4460 --- /dev/null +++ b/docs/.translated/fr_fr/compatibility/launchers.mdx @@ -0,0 +1,15 @@ +Most of the launchers will work without any issues, but some modified custom clients won't work. Here is a list of known launchers and their compatibility with AutoModpack: + +| Launcher Name | Compatibility | Notes | +|---------------------------|---------------|----------------------------------------------------------------------------------------------------------------| +| Vanilla official Launcher | ✅ | Supported | +| Prism/MultiMC Launcher | ✅ | Prism is recommended - Perfect support, with additional features like modloader version synchronization | +| Modrinth Launcher | ✅ | Supported | +| CurseForge | ✅ | Supported | +| GDLauncher | ✅ | Supported | +| ATLauncher | ✅ | Supported | +| PojavLauncher (Android) | ⚠️ | Currently broken, worked on previous versions, [read more](https://github.com/Skidamek/AutoModpack/issues/379) | +| FCL (Android) | ⚠️ | Currently broken, worked on previous versions, [read more](https://github.com/Skidamek/AutoModpack/issues/379) | +| Feather Client | ❌ | Unsupported - breaks modpack loading | +| Lunar Client | ❌ | Unsupported - breaks modpack loading | +| PCL2 | ❌ | Unsupported - breaks modpack loading | diff --git a/docs/.translated/fr_fr/compatibility/mods.mdx b/docs/.translated/fr_fr/compatibility/mods.mdx new file mode 100644 index 000000000..94748cef5 --- /dev/null +++ b/docs/.translated/fr_fr/compatibility/mods.mdx @@ -0,0 +1,5 @@ +AutoModpack devrait fonctionner avec toutes les modifications Minecraft (sinon, ce ne serait pas un bon outil de mise à jour de modpack). Cependant, il existe quelques exceptions et limitations dont vous devez être conscient : + +- Mods de Majrusz : Nécessitent une version modifiée de la Majrusz Library pour fonctionner avec AutoModpack. [Github Issue](https://github.com/Skidamek/AutoModpack/issues/268). +- [ModSets](https://modrinth.com/mod/mod-sets) : Ne fonctionne actuellement pas en raison de similitudes dans le processus de modification du chargement des mods. +- [Cesium](https://modrinth.com/mod/cesium) : Cesium est actuellement incompatible en raison d'un mauvais empaquetage de zstd du côté d'AutoModpack. Cela sera bientôt corrigé. [GitHub Issue](https://github.com/Skidamek/AutoModpack/issues/384) \ No newline at end of file diff --git a/docs/.translated/fr_fr/compatibility/proxies.mdx b/docs/.translated/fr_fr/compatibility/proxies.mdx new file mode 100644 index 000000000..f4f6e370a --- /dev/null +++ b/docs/.translated/fr_fr/compatibility/proxies.mdx @@ -0,0 +1,14 @@ +La plupart des proxys Minecraft ne fonctionneront pas avec AutoModpack en raison des paquets de la phase de connexion précoce. Cependant, Gate Lite est supporté et fonctionne bien avec AutoModpack. Voici une liste des proxys connus et de leur compatibilité : + +| Nom du Proxy                                      | Compatibilité | Notes         | +|---------------------------------------------------|----------------|---------------| +| [Gate Lite](https://gate.minekube.com/guide/lite) | ✅             | Supporté      | +| Gate                                              | ❌             | Non supporté  | +| Velocity                                          | ❌             | Non supporté  | +| Waterfall                                         | ❌             | Non supporté  | +| BungeeCord                                        | ❌             | Non supporté  | + +Lorsque vous utilisez Gate Lite, il est crucial de configurer correctement `portToSend`. Par défaut, les clients tentent de se connecter à l'hôte du modpack en utilisant le même port que celui utilisé pour se connecter à votre serveur Minecraft. Cependant, lorsque vous utilisez un proxy comme Gate, ce port diffère du port réel de votre serveur Minecraft, empêchant les clients de se connecter pour télécharger le modpack. + +Par conséquent, si votre modpack est hébergé avec `bindPort` défini sur `-1`, `portToSend` doit être défini sur le port depuis lequel votre serveur Minecraft est accessible (généralement, ce sera le même port sur lequel le serveur Minecraft écoute). +De même, si `bindPort` est défini sur un port spécifique, `portToSend` doit être défini sur le port depuis lequel l'hôte du modpack est accessible (généralement, ce sera le même que `bindPort`). \ No newline at end of file diff --git a/docs/.translated/fr_fr/configuration/_meta.json b/docs/.translated/fr_fr/configuration/_meta.json new file mode 100644 index 000000000..aed4f3058 --- /dev/null +++ b/docs/.translated/fr_fr/configuration/_meta.json @@ -0,0 +1,14 @@ +{ + "server-config.mdx": { + "name": "Server Configuration", + "icon": null + }, + "client-config.mdx": { + "name": "Client Configuration", + "icon": null + }, + "troubleshooting.mdx": { + "name": "Troubleshooting", + "icon": null + } +} diff --git a/docs/.translated/fr_fr/configuration/client-config.mdx b/docs/.translated/fr_fr/configuration/client-config.mdx new file mode 100644 index 000000000..630f8c8f7 --- /dev/null +++ b/docs/.translated/fr_fr/configuration/client-config.mdx @@ -0,0 +1,10 @@ +### Fichier de Configuration Client + +`/automodpack/automodpack-client.json` + +| Nom                  | Valeur par Défaut | Description                                                                                                                                                        | +|---------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT`  | ``  | Numéro de version du fichier utilisé pour la conversion automatique des anciennes versions de configuration vers les nouvelles lors de la mise à jour d'AutoModpack.  | +| `selectedModpack`   |                  | Le dossier principal du modpack auquel vous voulez jouer. Taper le nom du modpack ici provoquera son chargement.                                            | +| `installedModpacks` |                  | Une liste des modpacks qui sont installés sur le client.                                                                                                       | +| `selfUpdater`       | `false`          | Active/désactive les mises à jour automatiques d'AutoModpack. Cela n'affecte pas l'activité du mod dans l'installation des modpacks. Cela ne désactive pas la synchronisation de la version d'AutoModpack avec le serveur. | diff --git a/docs/.translated/fr_fr/configuration/server-config.mdx b/docs/.translated/fr_fr/configuration/server-config.mdx new file mode 100644 index 000000000..d9f597138 --- /dev/null +++ b/docs/.translated/fr_fr/configuration/server-config.mdx @@ -0,0 +1,29 @@ +### Fichier de Configuration Serveur +`~/automodpack/automodpack-server.json` + +| Nom | Valeur par Défaut | Description | +|-------------------------------|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT` | `` | Numéro de version du fichier utilisé pour la conversion automatique des anciennes versions de configuration vers les nouvelles lors de la mise à jour d'AutoModpack. | +| `modpackName` | `""` | Le nom du modpack du serveur, s'affiche pendant le téléchargement du modpack et c'est ainsi que le répertoire du modpack est nommé dans `~/automodpack/modpacks/`. | +| `modpackHost` | `true` | Démarre le serveur d'hébergement du modpack. | +| `generateModpackOnStart` | `true` | Regénère automatiquement les métadonnées du modpack au démarrage du serveur. | +| `syncedFiles` | `"/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"` | Une liste de *chemins relatifs* depuis le dossier racine du serveur Minecraft qui seront **synchronisés avec le modpack**. Utilisez des jokers comme `*` pour inclure plusieurs fichiers de ce répertoire (ex: `/mods/*.jar`). Ou utilisez `**` (double étoile) pour inclure récursivement tous les fichiers et sous-dossiers d'un répertoire (ex: `/config/**`). Préfixez un chemin avec `!` pour l'**exclure** de la synchronisation. **Note :** Ceci ne **copie pas** les fichiers ; cela définit seulement ce qui sera disponible côté client sous `~/.minecraft/`. | +| `allowEditsInFiles` | `"/options.txt", "/config/**"` | Une liste de fichiers que les clients sont autorisés à modifier. En d'autres termes, des fichiers qui sont téléchargés une seule fois puis ignorés lors des mises à jour. La configuration fonctionne exactement de la même manière que `syncedFiles`. | +| `autoExcludeServerSideMods` | `true` | Exclut automatiquement les mods côté serveur du modpack. (Ne fonctionne que si le développeur du mod a spécifié le type d'environnement dans les métadonnées du mod) | +| `autoExcludeUnnecessaryFiles` | `true` | Ignore automatiquement les fichiers qui sont : vides, cachés, temporaires, désactivés ou de sauvegarde. | +| `requireAutoModpackOnClient` | `true` | Indique si ce mod est optionnel ou non pour que les clients rejoignent le serveur. | +| `nagUnModdedClients` | `true` | Si `true`, les clients sans AutoModpack recevront un message de rappel dans le chat à leur connexion. Pour fonctionner, `requireAutoModpackOnClient` doit être `false`. | +| `nagMessage` | `"This server provides dedicated modpack through AutoModpack!"` | Le message qui sera affiché aux clients sans AutoModpack. Pour fonctionner, `nagUnModdedClients` doit être `true`. | +| `nagClickableMessage` | `"Click here to get the AutoModpack!"` | La partie cliquable du message qui sera affichée aux clients sans AutoModpack. Pour fonctionner, `nagUnModdedClients` doit être `true`. | +| `nagClickableLink` | `"https://modrinth.com/project/automodpack"` | Le lien qui sera ouvert lorsque le message est cliqué. Pour fonctionner, `nagUnModdedClients` doit être `true`. | +| `bindAddress` | `""` | L'adresse à laquelle le serveur d'hébergement du modpack se lie. Laissez ce champ vide pour déterminer automatiquement une adresse locale (typiquement `0.0.0.0` ou `::0`). Lorsque `bindPort` est défini sur `-1`, cette valeur est **ignorée** et l'hôte du modpack sera lié à la même adresse que le serveur Minecraft, comme spécifié dans le fichier `server.properties`. | +| `bindPort` | `-1` | Le numéro de port sur lequel le serveur d'hébergement du modpack écoute. Si défini sur `-1`, le modpack sera hébergé directement sur le port de votre serveur Minecraft. | +| `addressToSend` | `""` | L'adresse de l'hôte du modpack qui sera utilisée par les clients pour télécharger le modpack. Si vide, les clients utiliseront la même adresse que celle utilisée pour se connecter au serveur Minecraft. (N'incluez **pas** le port ici, utilisez `portToSend` à la place) | +| `portToSend` | `-1` | Le numéro de port qui est envoyé avec `addressToSend`. Si défini sur `-1`, les clients utiliseront le même port que celui utilisé pour se connecter au serveur Minecraft. | +| `disableInternalTLS` | `false` | Désactive la gestion TLS interne. Si activé, vous devez gérer le TLS de manière externe, par ex. sur un reverse proxy. En utilisant cette option, vous devez utiliser un `bindPort` différent de `-1` et vous devriez envisager de définir `bindAddress` sur une adresse de loopback (ex: `127.0.0.1`), afin que le trafic non chiffré entre l'hôte du modpack et le reverse proxy ne fuite pas. | +| `updateIpsOnEveryStart` | `false` | Met à jour `addressToSend` à chaque démarrage du serveur. Peut être utile si vous avez une adresse IP dynamique. (Utilisez-le uniquement si vous en avez vraiment besoin !) | +| `bandwidthLimit` | `0` | Limite de téléversement en Mbps à laquelle le serveur d'hébergement du modpack est restreint. (La valeur doit être un entier, `0` - signifie illimité) | +| `validateSecrets` | `true` | Fait en sorte que l'hôte du modpack valide et autorise les tentatives de téléchargement du modpack à l'aide de secrets uniques. | +| `secretLifetime` | `336` | Durée en heures pendant laquelle le secret d'un joueur reste valide. | +| `selfUpdater` | `false` | Active/désactive toutes les mises à jour d'AutoModpack. Cela n'affecte pas l'activité du mod dans l'installation des modpacks. | +| `acceptedLoaders` | `""` | Permet aux joueurs de différents modloaders de se connecter à votre serveur. (À utiliser avec prudence, la plupart des mods ne fonctionnent que sur un seul loader) | \ No newline at end of file diff --git a/docs/.translated/fr_fr/configuration/troubleshooting.mdx b/docs/.translated/fr_fr/configuration/troubleshooting.mdx new file mode 100644 index 000000000..28592c062 --- /dev/null +++ b/docs/.translated/fr_fr/configuration/troubleshooting.mdx @@ -0,0 +1,6 @@ +## Dépannage des problèmes de configuration +Lorsque vous avez fini de modifier les fichiers de configuration, assurez-vous de les recharger en exécutant la commande `automodpack config reload` ou en redémarrant le serveur/jeu. + +Si vous ne trouvez pas les fichiers de configuration, assurez-vous que le client/serveur a été démarré au moins une fois, afin que les fichiers soient générés. + +Si les valeurs sont restaurées par défaut, cela peut signifier que votre fichier de configuration est malformé. Assurez-vous d'utiliser un éditeur avec un analyseur JSON intégré, ou vérifiez-le dans un outil en ligne, par ex. https://jsonformatter.org/json-parser. diff --git a/docs/.translated/fr_fr/faq.mdx b/docs/.translated/fr_fr/faq.mdx new file mode 100644 index 000000000..4735ef4a2 --- /dev/null +++ b/docs/.translated/fr_fr/faq.mdx @@ -0,0 +1,49 @@ +### Est-ce un mod côté serveur ? +Non, c'est les deux. Vous devez l'installer à la fois sur le serveur et sur le client. + +### Comment puis-je ajouter des mods uniquement client au modpack ? (ex: le serveur crash avec un certain mod mais je le veux pour les clients) +Jetez un œil à la [page de création de modpack](techinicals/modpack-creation). + +### Comment restreindre l'accès à mon modpack ? +Activez `validateSecrets` dans la [config](configuration/server-config), activez `online-mode` dans le fichier server.properties et activez la whitelist ou bannissez quelqu'un. + +### Est-ce que cela fonctionne en LAN (monde solo avec l'option pour que d'autres joueurs rejoignent) ? +Oui. N'oubliez pas de générer le modpack via la commande /`automodpack generate` et de démarrer l'hébergement du modpack avec /`automodpack host start`. + +### Est-ce que cela fonctionne avec un monde solo partagé via Essential, World Host, e4mc ? +Peu de chance, la plupart de ces types de mods ne transfèrent que les paquets Minecraft. Ces mods devraient prendre en charge le protocole d'AutoModpack ou simplement relayer tous les paquets TCP au lieu de se limiter au protocole Minecraft. + +### Puis-je utiliser des mods hors-modpack sur le client ? +Oui, vous pouvez ajouter n'importe quel mod que vous souhaitez utiliser en parallèle du modpack téléchargé en plaçant simplement le mod dans le dossier mods standard. + +### Comment puis-je supprimer des fichiers du modpack ? +Supprimez simplement le fichier du serveur, ou excluez-le de la synchronisation, lisez la section sur `syncedFiles` dans la [config](configuration/server-config). + +### Puis-je retirer/désactiver les fichiers/mods hors-modpack pour les joueurs ? +Pas encore, mais cela pourrait arriver dans le futur. + +### Dois-je ouvrir un port sur mon routeur ? +Non, ce n'est pas nécessaire. Par défaut, AutoModpack s'injecte dans les E/S réseau de Minecraft, ce qui réutilise le port de votre serveur Minecraft. (ex: 25565) + +Si vous voulez utiliser un port différent, désactivez `hostModpackOnMinecraftPort` et modifiez `hostPort` dans la [config](configuration/server-config). + +### Que se passe-t-il quand je mets à jour la version d'AutoModpack sur le serveur ? +Le client essaie toujours de se synchroniser avec la version d'AutoModpack du serveur. Il se met à jour ou se rétrograde à la version du serveur si celle-ci est trouvée sur Modrinth. + +### Pourquoi le chemin d'hébergement du modpack est-il si long ? +Il est prévu d'implémenter la possibilité d'ajouter plus d'un seul modpack, le modpack `main`. + +### Quelles versions de Minecraft sont supportées ? +Toutes les versions que vous voyez dans la [dernière version](https://github.com/Skidamek/AutoModpack/releases/latest). + +### Mon launcher est-il supporté ? +Jetez un œil à la [page de compatibilité](compatibility/launchers) pour plus d'informations sur les launchers supportés. + +### Est-ce que ce mod met à jour les mods de mon modpack aux dernières versions ? +Non, c'est hors du champ d'application de ce projet. Ce mod ne fait que synchroniser le modpack du serveur avec les clients. Tous vos mods sur le serveur devront toujours être mis à jour manuellement. AutoModpack s'assure simplement que les versions du client correspondent à celles du serveur. + +### J'ai trouvé un bug, que dois-je faire ? +Si vous avez trouvé un bug, veuillez le signaler sur le [tracker de bugs](https://github.com/Skidamek/AutoModpack/issues). + +### J'ai trouvé une faille de sécurité, que dois-je faire ? +Veuillez la signaler en privé à l'auteur sur [Discord](https://discordapp.com/users/464522287618457631), nous essaierons de la résoudre dès que possible. \ No newline at end of file diff --git a/docs/.translated/fr_fr/quick-start.mdx b/docs/.translated/fr_fr/quick-start.mdx new file mode 100644 index 000000000..c267ad965 --- /dev/null +++ b/docs/.translated/fr_fr/quick-start.mdx @@ -0,0 +1,36 @@ +Alors comme ça vous voulez commencer à utiliser AutoModpack ? Super ! C'est parti. + +## Installation + +1. **Télécharger AutoModpack** : Obtenez la dernière version d'AutoModpack pour votre version de Minecraft depuis [Modrinth](https://modrinth.com/mod/automodpack) ou [CurseForge](https://www.curseforge.com/minecraft/mc-mods/automodpack). +2. **Installer AutoModpack** : Placez le mod téléchargé dans le dossier `mods` de votre instance Minecraft. +3. **Démarrer votre serveur** : Lancez votre serveur Minecraft avec AutoModpack installé. Cela générera les métadonnées initiales du modpack. + +Vous êtes maintenant, dans la plupart des cas, prêt à démarrer. À ce stade, il est recommandé d'essayer de vous connecter à votre serveur avec uniquement AutoModpack d'installé sur le client pour voir si tout fonctionne. + +## Première Connexion +Lors de la première connexion, il vous sera demandé de vérifier l'empreinte du certificat du serveur. C'est une mesure de sécurité pour s'assurer que vous vous connectez au bon serveur et non à un serveur malveillant. En savoir plus sur la [certificate verification](technicals/certificate). +Après une vérification réussie, AutoModpack vous demandera de confirmer l'installation du modpack. Confirmez et le modpack sera téléchargé et installé automatiquement. + +Maintenant, redémarrez votre jeu et le modpack de votre serveur sera chargé. Vous devriez pouvoir vous connecter à nouveau à votre serveur, mais cette fois-ci en apparaissant dans l'overworld ! + + +Si vous ne voyez pas les mods de votre modpack dans le dossier mods, ne vous inquiétez pas. Les mods ne seront pas installés dans votre dossier mods standard, ils seront installés dans le dossier `~/.minecraft/automodpack/modapcks/{your-modpack}/mods/` à la place. Il y a de [bonnes raisons pour ça](technicals/file-structure). + + +## Dépannage +Si vous rencontrez des problèmes à ce stade, voici quelques étapes de dépannage courantes : +- Assurez-vous d'utiliser la même version d'AutoModpack sur le serveur et le client. +- Assurez-vous que votre serveur est en cours d'exécution et accessible sans AutoModpack. +- Vérifiez les logs et la console du serveur, il pourrait y avoir des indices sur ce qui n'a pas fonctionné. +- Lisez la [page faq](faq) pour les questions et réponses courantes. + +Si vous rencontrez toujours des problèmes, rejoignez notre [serveur Discord](https://discord.gg/hS6aMyeA9P) et créez une publication dans le salon `#support`, nous essaierons de vous aider ! + +## Prochaines Étapes + +Maintenant que vous avez tout installé et fait fonctionner AutoModpack, vous pouvez commencer à personnaliser votre modpack. Voici quelques choses que vous pouvez faire ensuite : + +- Lisez la [page de création de modpack](technicals/modpack-creation) pour apprendre à gérer votre modpack. +- Lisez la [page de configuration](configuration/server-config) pour apprendre à configurer AutoModpack selon vos besoins. +``` \ No newline at end of file diff --git a/docs/.translated/fr_fr/technicals/_meta.json b/docs/.translated/fr_fr/technicals/_meta.json new file mode 100644 index 000000000..69aabdfc4 --- /dev/null +++ b/docs/.translated/fr_fr/technicals/_meta.json @@ -0,0 +1,22 @@ +{ + "connection.mdx": { + "name": "Connection", + "icon": null + }, + "certificate.mdx": { + "name": "Gestion des certificats", + "icon": null + }, + "modpack-creation.mdx": { + "name": "Création du modpack", + "icon": null + }, + "file-structure.mdx": { + "name": "Structure des fichiers", + "icon": null + }, + "modpack-hosting.mdx": { + "name": "Hebergement du modpack", + "icon": null + } +} diff --git a/docs/.translated/fr_fr/technicals/certificate.mdx b/docs/.translated/fr_fr/technicals/certificate.mdx new file mode 100644 index 000000000..e60a753bd --- /dev/null +++ b/docs/.translated/fr_fr/technicals/certificate.mdx @@ -0,0 +1,74 @@ +### Comment vérifier l'empreinte du certificat ? +Pour vérifier, il suffit de **copier l’empreinte depuis la console de votre serveur dans le jeu.** Il s’agit d’une vérification unique : vous n’aurez pas à la refaire après la première connexion. Ce processus permet de prévenir les menaces de sécurité comme les attaques de type [Man-in-the-middle](https://fr.wikipedia.org/wiki/Attaque_de_l%27homme_du_milieu). L’empreinte est la même pour tous les joueurs de votre serveur et elle ne change jamais. Il est recommandé de la partager avec les joueurs à l’avance. + +Si vous ne trouvez pas l’empreinte dans la console, vous pouvez la récupérer à tout moment avec la commande `/automodpack host fingerprint` [commande](../commands/commands). + +Cependant, ne la considérez pas comme un mot de passe pour le modpack : ce n’en est pas un, et ce n’est pas non plus un secret. C’est une partie publique et vérifiable du certificat du serveur, **n’importe qui pourrait télécharger** votre modpack sans connaître cette empreinte, par exemple en contournant cette vérification avec `I AM INCREDIBLY STUPID`. (ne faites pas ça) + +Pour fournir le modpack uniquement aux joueurs autorisés/liste blanche, utilisez l’option `validateSecrets` dans la [configuration du serveur](../configuration/server-config) (activée par défaut). + + +Si vous ne souhaitez pas que les joueurs valident manuellement le certificat et que vous possédez un nom de domaine (requis), vous pouvez fournir votre propre certificat signé par une autorité de certification (CA). + +### Comment fournir votre propre certificat signé par une CA ? + +Vous pouvez utiliser des outils comme [Certbot](https://eff-certbot.readthedocs.io/en/stable/install.html). + +Une fois le certificat obtenu, sur le serveur dans le dossier `~/automodpack/.private/`, remplacez les fichiers `cert.crt` (avec le certificat complet en chaîne) et `key.pem` ([la clé doit être au format PKCS#8 (PEM)](https://netty.io/wiki/sslcontextbuilder-and-private-key.html)) – attention : l’interception de ces fichiers pourrait permettre l’usurpation de votre serveur, [en savoir plus](https://security.stackexchange.com/a/16694). +Si vous hébergez le modpack sur un sous-domaine ou domaine différent de celui de votre serveur Minecraft, assurez-vous que votre certificat couvre bien les deux. + +### Petit tutoriel pour obtenir un certificat avec Certbot + +Installez Certbot sur votre PC ou serveur, puis exécutez la commande suivante et suivez les instructions fournies par Certbot pour obtenir un certificat pour votre domaine : + +```bash +# certbot certonly --manual --preferred-challenges dns -d -d + + +✅ Si vous hébergez le modpack sur **le même port que le serveur Minecraft**, les domaines sont souvent identiques → dans ce cas, **le second `-d` n’est pas nécessaire**. + +> ⚠️ **Attention** : ne copiez-collez jamais une commande trouvée au hasard sur Internet. +> Lisez toujours la documentation de l’outil que vous utilisez ! + +Une fois le certificat obtenu : + +* `fullchain.pem` → renommez-le en `cert.crt` +* `privkey.pem` → renommez-le en `key.pem` + +Placez-les dans `~/automodpack/.private/` + +--- + +### Mais pourquoi ai-je besoin de tout ça ? C’est juste un modpack pour mes amis ! + +Si quelqu’un peut prendre le contrôle de la connexion Automodpack, +il pourrait **installer n’importe quoi sur votre PC (ou celui de vos amis)**. + +Minecraft **n’est pas sandboxé**, et les mods peuvent **exécuter n’importe quel code**. +Donc injecter du code malveillant, même si vous n’êtes pas une cible spécifique, est un énorme risque : + +* vol de mots de passe +* vol de comptes +* suppression de fichiers importants (comme votre dossier de mèmes de chats) + +➡️ **Voilà pourquoi la sécurité est essentielle**, même dans un petit contexte entre amis. + +--- + +### Hmm... ok, mais personne ne connaît mon serveur à part mes amis, pourquoi devrais-je m’en soucier ? + +Même si vous pensez que votre serveur est privé, **ce n’est probablement pas le cas**. +Des scanners de serveurs Minecraft analysent **tout Internet** pour détecter les serveurs accessibles. + +Si votre serveur est vulnérable et que vous ne vérifiez pas le certificat, vous devenez une cible potentielle. + +🔐 De plus, rien n’empêche une attaque via d’autres moyens : + +* accès SSH +* panneau d’administration de l’hébergeur + +👉 **En tant qu’administrateur**, prenez ces mesures sérieusement : + +* mots de passe robustes +* authentification à deux facteurs +* mises à jour régulières de votre logiciel serveur diff --git a/docs/.translated/fr_fr/technicals/connection.mdx b/docs/.translated/fr_fr/technicals/connection.mdx new file mode 100644 index 000000000..5ab855b00 --- /dev/null +++ b/docs/.translated/fr_fr/technicals/connection.mdx @@ -0,0 +1,35 @@ +### Chiffrement + +La connexion pour le téléchargement du modpack est sécurisée à l'aide de TLS 1.3. + +C'est la même norme de sécurité que celle utilisée par HTTPS, le protocole que vous utilisez tout le temps pour naviguer sur le web (même en ce moment \!). + +TLS dispose d'une méthode de vérification de l'identité du serveur à l'aide d'un certificat, ce qui est crucial pour prévenir des attaques telles que [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). + +C'est pourquoi AutoModpack vous demande de vérifier l'empreinte du certificat du serveur lors de la première connexion. Lisez [Comment vérifier l'empreinte du certificat](certificate) pour plus de détails. + +### Téléchargements Directs + +AutoModpack utilise des liens de téléchargement direct depuis les API de Modrinth et CurseForge pour télécharger la plupart des fichiers de votre modpack. +Cela signifie que les auteurs des mods sont crédités pour chaque téléchargement. + +Cependant, si un fichier que vous fournissez dans le modpack n'est pas disponible sur Modrinth ou CurseForge, AutoModpack le téléchargera (également directement) mais depuis votre serveur. + +### Compression + +Pour accélérer le téléchargement, AutoModpack compresse les fichiers du modpack à l'aide de l'algorithme de compression [Zstandard](https://facebook.github.io/zstd/). +Cet algorithme est très rapide et offre un excellent taux de compression, ce qui signifie que les fichiers du modpack sont plus petits et plus rapides à télécharger. +C'est particulièrement utile pour les modpacks plus volumineux ou les connexions Internet plus lentes. +Cependant, cela ne s'applique qu'aux fichiers téléchargés depuis votre serveur. + +### Autorisation + +\ +Ne confondez pas cela avec la [certificate fingerprint verification](certificate), qui est utilisée pour vérifier l'identité du serveur. +\ + +AutoModpack utilise un mécanisme d'autorisation simple pour empêcher l'accès non autorisé à votre modpack. +Lorsque vous vous connectez au serveur, en arrière-plan, AutoModpack génère un secret unique et aléatoire pour chaque joueur. Ce secret est ensuite envoyé au joueur et utilisé pour l'autoriser à télécharger les fichiers du modpack. +Chaque fois que vous vous reconnectez au serveur, l'ancien secret est invalidé et un nouveau est généré. +Cela empêche l'accès non autorisé aux fichiers de votre modpack et garantit que seuls les joueurs sur liste blanche et non bannis peuvent télécharger le modpack. +Si vous ne souhaitez pas utiliser cette fonctionnalité, vous pouvez la désactiver (`validateSecrets`) dans la [server config](../configuration/server-config). \ No newline at end of file diff --git a/docs/.translated/fr_fr/technicals/file-structure.mdx b/docs/.translated/fr_fr/technicals/file-structure.mdx new file mode 100644 index 000000000..ca7e4c979 --- /dev/null +++ b/docs/.translated/fr_fr/technicals/file-structure.mdx @@ -0,0 +1,24 @@ +Tous les fichiers d'AutoModpack sont conservés dans le dossier `~/automodpack/`. + +### Serveur + +Le modpack est généré à partir des fichiers se trouvant dans `~/automodpack/host-modpack/main/` et/ou `syncedFiles` (lisez la [config](https://github.com/Skidamek/AutoModpack/blob/main/docs/configuration/server-config)) ce qui se traduit par le dossier `~/.minecraft/` (dossier racine de l'instance minecraft) sur le client. + +La génération du modpack signifie la création de `~/automodpack/host-modpack/automodpack-content.json` qui contient les métadonnées de chaque fichier du modpack. Vous **ne devriez PAS** toucher ce fichier manuellement, modifiez plutôt la configuration. + +### Client + +Tous les modpacks téléchargés sont conservés dans `~/automodpack/modpacks/`. + +Tous les fichiers du modpack sélectionné sont copiés dans `~/.minecraft/` à l'exception des **mods** (avec quelques exceptions, en raison des limitations du mod loader). + +S'il les détecte, AutoModpack essaie également de se débarrasser de la plupart, sinon de la totalité, des mods dupliqués de votre dossier mods standard en faveur du dossier mods d'AutoModpack. +\ +Cela ne devrait pas causer de crashs, mais si vous remarquez qu'AutoModpack supprime une dépendance causant un crash du jeu, veuillez le signaler \! +\ + +Les mods sont séparés pour plusieurs raisons. Une telle approche atténue les inconvénients courants liés au placement de tous les mods du modpack dans le dossier mods standard, tels que : + + - **Crashs dus aux conflits de mods** : Cela ne les résoudra pas, mais cela donne au moins au propriétaire du serveur la possibilité de supprimer ou de corriger à distance les mods en conflit sans intervention manuelle du côté client. + - **Débogage** : Il est plus facile de voir quels mods font partie du modpack et lesquels n'en font pas partie. Comme les clients peuvent ajouter leurs propres mods au dossier mods standard, il est plus facile de déboguer lorsque quelque chose se casse ou entre en conflit. + - **Redémarrages** : Si tous les mods du modpack étaient placés dans le dossier mods standard, il faudrait un redémarrage pour mettre à jour les mods du modpack. Maintenant, cela peut être fait de manière transparente au démarrage sans aucun redémarrage \! \ No newline at end of file diff --git a/docs/.translated/fr_fr/technicals/modpack-creation.mdx b/docs/.translated/fr_fr/technicals/modpack-creation.mdx new file mode 100644 index 000000000..ff49f89cd --- /dev/null +++ b/docs/.translated/fr_fr/technicals/modpack-creation.mdx @@ -0,0 +1,17 @@ +**Oui, vous pouvez ajouter *n'importe quoi* à votre modpack.** + +#### Deux manières d'ajouter du contenu au modpack. + + - Pour réutiliser des fichiers déjà existants sur le serveur, utilisez `syncedFiles`. Pour en savoir plus, lisez la [config](https://github.com/Skidamek/AutoModpack/blob/main/docs/configuration/server-config). Par exemple, avec la configuration par défaut, cela crée un modpack contenant *chaque* mod (fichier se terminant par .jar) du dossier `~/mods/` du serveur. + + - Pour ajouter plus de fichiers au modpack, utilisez le répertoire séparé `~/automodpack/host-modpack/main/`. + Par exemple, pour ajouter plus de mods, créez un dossier `mods` et placez-y les mods, ce qui donnera `~/automodpack/host-modpack/main/mods/`**`votre-mod.jar`**. + Ou pour ajouter des packs de ressources, de manière analogue, créez un dossier `resourcepacks` et placez-y les packs de ressources, ce qui donnera `~/automodpack/host-modpack/main/resourcepacks/`**`votre-packderessources.zip`**. + +\ +Ne placez pas de fichiers de cache dans le modpack, comme le répertoire `.connector` d'un connecteur. Il n'est pas censé être fourni dans le modpack et cela casse les mises à jour transparentes/sans redémarrage. +\ + +Ces deux méthodes se traduisent par le répertoire `~/.minecraft/` du côté client (répertoire racine de l'instance Minecraft). + +N'oubliez pas de regénérer le modpack en utilisant `/automodpack generate` (voir les [commands](https://github.com/Skidamek/AutoModpack/blob/main/docs/commands/commands)) \ No newline at end of file diff --git a/docs/.translated/fr_fr/technicals/modpack-hosting.mdx b/docs/.translated/fr_fr/technicals/modpack-hosting.mdx new file mode 100644 index 000000000..0c8bfeb61 --- /dev/null +++ b/docs/.translated/fr_fr/technicals/modpack-hosting.mdx @@ -0,0 +1,2 @@ +Par défaut, AutoModpack héberge votre modpack sur le port de votre serveur Minecraft (par ex. 25565). Cela signifie que vous n'avez pas besoin d'ouvrir d'autre port sur votre routeur/pare-feu. +Si vous souhaitez utiliser un port différent, modifiez `bindPort` dans la [config](https://github.com/Skidamek/AutoModpack/blob/main/docs/configuration/server-config). \ No newline at end of file diff --git a/docs/.translated/pl_pl/_homepage.mdx b/docs/.translated/pl_pl/_homepage.mdx new file mode 100644 index 000000000..0adcf7818 --- /dev/null +++ b/docs/.translated/pl_pl/_homepage.mdx @@ -0,0 +1,7 @@ +Witaj w dokumentacji AutoModpack'a! + +Jeśli jesteś nowy do AutoModpack'a, zalecamy rozpoczęcie od [Przewodnika Szybkiego Startu](docs/quick-start), aby zapoznać się z podstawami korzystania z AutoModpack'a. + +Jeśli szukasz bardziej szczegółowych informacji, znajdziesz je w sekcji `👨‍💻 Szczegóły Techniczne`. + +Masz pytania lub potrzebujesz pomocy? Dołącz na nasz [serwer Discord](https://discord.gg/hS6aMyeA9P). \ No newline at end of file diff --git a/docs/_homepage.mdx b/docs/_homepage.mdx new file mode 100644 index 000000000..d2945a29e --- /dev/null +++ b/docs/_homepage.mdx @@ -0,0 +1,9 @@ +Welcome to the AutoModpack documentation! + +If you are new to AutoModpack, we recommend starting with the [Quick Start Guide](docs/quick-start) to get familiar with the basics of using AutoModpack. + +If you are looking for more detailed information, you can find it in the `👨‍💻 Technical Details` section. + +Got any questions or need help? Join our [Discord server](https://discord.gg/hS6aMyeA9P). + +Found this project useful? Consider supporting its ongoing development and maintenance by donating to the author on [Ko-fi](https://ko-fi.com/skidam). diff --git a/docs/_meta.json b/docs/_meta.json new file mode 100644 index 000000000..d9751bf7f --- /dev/null +++ b/docs/_meta.json @@ -0,0 +1,34 @@ +{ + "quick-start.mdx": { + "name": "🚀 Quick Start", + "icon": null + }, + "usage": { + "name": "Usage", + "icon": null + }, + "configuration": { + "name": "\uD83D\uDEE0\uFE0F Configuration", + "icon": null + }, + "commands": { + "name": "⌨\uFE0F Commands", + "icon": null + }, + "examples": { + "name": "\uD83D\uDCF7 Examples", + "icon": null + }, + "technicals": { + "name": "\uD83D\uDC68\u200D\uD83D\uDCBB Technical Details", + "icon": null + }, + "compatibility": { + "name": "\uD83D\uDCDA Compatibility", + "icon": null + }, + "faq.mdx": { + "name": "❔ FAQ", + "icon": null + } +} \ No newline at end of file diff --git a/docs/commands/_meta.json b/docs/commands/_meta.json new file mode 100644 index 000000000..cd9c084df --- /dev/null +++ b/docs/commands/_meta.json @@ -0,0 +1,6 @@ +{ + "commands.mdx": { + "name": "Commands", + "icon": null + } +} diff --git a/docs/commands/commands.mdx b/docs/commands/commands.mdx new file mode 100644 index 000000000..579e83233 --- /dev/null +++ b/docs/commands/commands.mdx @@ -0,0 +1,11 @@ +`/amp` is an alias for `/automodpack`. + +- `/automodpack` - Status of automodpack and general help. +- `/automodpack generate` - Generate modpack. +- `/automodpack host` - Status of modpack hosting. +- `/automodpack host start` - Start modpack hosting. +- `/automodpack host stop` - Stop modpack hosting. +- `/automodpack host restart` - Restart modpack hosting. +- `/automodpack host connections` - Lists all currently active modpack host connections. +- `/automodpack host fingerprint` - Get the [certificate fingerprint](../technicals/certificate) of the modpack host. +- `/automodpack config reload` - Reload config files. \ No newline at end of file diff --git a/docs/compatibility/_meta.json b/docs/compatibility/_meta.json new file mode 100644 index 000000000..d3e204510 --- /dev/null +++ b/docs/compatibility/_meta.json @@ -0,0 +1,14 @@ +{ + "launchers.mdx": { + "name": "Launchers compatibility", + "icon": null + }, + "mods.mdx": { + "name": "Modifications compatibility", + "icon": null + }, + "proxies.mdx": { + "name": "Proxies compatibility", + "icon": null + } +} diff --git a/docs/compatibility/launchers.mdx b/docs/compatibility/launchers.mdx new file mode 100644 index 000000000..33cea4460 --- /dev/null +++ b/docs/compatibility/launchers.mdx @@ -0,0 +1,15 @@ +Most of the launchers will work without any issues, but some modified custom clients won't work. Here is a list of known launchers and their compatibility with AutoModpack: + +| Launcher Name | Compatibility | Notes | +|---------------------------|---------------|----------------------------------------------------------------------------------------------------------------| +| Vanilla official Launcher | ✅ | Supported | +| Prism/MultiMC Launcher | ✅ | Prism is recommended - Perfect support, with additional features like modloader version synchronization | +| Modrinth Launcher | ✅ | Supported | +| CurseForge | ✅ | Supported | +| GDLauncher | ✅ | Supported | +| ATLauncher | ✅ | Supported | +| PojavLauncher (Android) | ⚠️ | Currently broken, worked on previous versions, [read more](https://github.com/Skidamek/AutoModpack/issues/379) | +| FCL (Android) | ⚠️ | Currently broken, worked on previous versions, [read more](https://github.com/Skidamek/AutoModpack/issues/379) | +| Feather Client | ❌ | Unsupported - breaks modpack loading | +| Lunar Client | ❌ | Unsupported - breaks modpack loading | +| PCL2 | ❌ | Unsupported - breaks modpack loading | diff --git a/docs/compatibility/mods.mdx b/docs/compatibility/mods.mdx new file mode 100644 index 000000000..853c0d5a6 --- /dev/null +++ b/docs/compatibility/mods.mdx @@ -0,0 +1,6 @@ +AutoModpack should work with all minecraft modifications (it would be a bit bad modpack updater if it didn't). However, there are some exceptions and limitations that you should be aware of: + +- Majrusz's Mods: Requires a modified Majrusz Library to work with AutoModpack. [Github Issue](https://github.com/Skidamek/AutoModpack/issues/268). Or `forceCopyFilesToStandardLocation` in server config. +- [CITResewnNeoPatcher](https://modrinth.com/mod/cit-resewn-neopatcher): Requires to specify fabric version of CITResewn in `forceCopyFilesToStandardLocation` in server config, like (`/mods/citresewn-1.2.1+1.21.jar`). +- [ModSets](https://modrinth.com/mod/mod-sets): It's currently broken due to similarities in mod loading modification process. +- [Cesium](https://modrinth.com/mod/cesium): Cesium is currently incompatible due to wrong way of zstd packaging on AutoModpack side. It will be fixed soon. [GitHub Issue](https://github.com/Skidamek/AutoModpack/issues/384) \ No newline at end of file diff --git a/docs/compatibility/proxies.mdx b/docs/compatibility/proxies.mdx new file mode 100644 index 000000000..12b17a994 --- /dev/null +++ b/docs/compatibility/proxies.mdx @@ -0,0 +1,14 @@ +Most minecraft proxies won't work with AutoModpack due to early login stage packets. However Gate Lite is supported and works well with AutoModpack. Here is a list of known proxies and their compatibility: + +| Proxy Name | Compatibility | Notes | +|---------------------------------------------------|---------------|-------------| +| [Gate Lite](https://gate.minekube.com/guide/lite) | ✅ | Supported | +| Gate | ❌ | Unsupported | +| Velocity | ❌ | Unsupported | +| Waterfall | ❌ | Unsupported | +| BungeeCord | ❌ | Unsupported | + +When using Gate Lite, it's crucial to correctly configure `portToSend`. By default, clients attempt to connect to the modpack host using the same port they used to connect to your Minecraft server. However, when using a proxy like Gate, this port differs from your actual Minecraft server's port, preventing clients from connecting to download the modpack. + +Therefore, if your modpack is hosted with `bindPort` set to `-1`, `portToSend` must be set to the port from which your Minecraft server is accessible from (typically, this will be the same port on which Minecraft server listens). +Similarly, if `bindPort` is set to a specific port, `portToSend` must be set to the port from which modpack host is accessible from (typically, this will be the same as `bindPort`). \ No newline at end of file diff --git a/docs/configuration/_meta.json b/docs/configuration/_meta.json new file mode 100644 index 000000000..aed4f3058 --- /dev/null +++ b/docs/configuration/_meta.json @@ -0,0 +1,14 @@ +{ + "server-config.mdx": { + "name": "Server Configuration", + "icon": null + }, + "client-config.mdx": { + "name": "Client Configuration", + "icon": null + }, + "troubleshooting.mdx": { + "name": "Troubleshooting", + "icon": null + } +} diff --git a/docs/configuration/client-config.mdx b/docs/configuration/client-config.mdx new file mode 100644 index 000000000..ce86efb50 --- /dev/null +++ b/docs/configuration/client-config.mdx @@ -0,0 +1,10 @@ +### Client Config File + +`/automodpack/automodpack-client.json` + +| Name | Default Value | Description | +|---------------------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | +| `selectedModpack` | | The main folder of the modpack that you want to play. Typing the name of the modpack here will cause it to be loaded. | +| `installedModpacks` | | A list of modpacks that are installed on the client. | +| `selfUpdater` | `false` | Turn on/off automodpack self-updates. This does not affect the mod's activity in installing modpacks. This does not disable AutoModpack version syncing with server | diff --git a/docs/configuration/server-config.mdx b/docs/configuration/server-config.mdx new file mode 100644 index 000000000..5cb096b1d --- /dev/null +++ b/docs/configuration/server-config.mdx @@ -0,0 +1,30 @@ +### Server Config File +`~/automodpack/automodpack-server.json` + +| Name | Default Value | Description | +|------------------------------------|----------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DO_NOT_CHANGE_IT` | `` | File version number used for auto conversion old config versions to new ones with automodpack update. | +| `modpackName` | `""` | The name of the server modpack, shows while downloading modpack and that's how modpack directory gets called inside `~/automodpack/modpacks/`. | +| `modpackHost` | `true` | Starts modpack host server. | +| `generateModpackOnStart` | `true` | Automatically regenerate modpack metadata when the server starts. | +| `syncedFiles` | `["/mods/*.jar", "/kubejs/**", "!/kubejs/server_scripts/**", "/emotes/*"]` | A list of *relative paths* from the root folder of the Minecraft server that will be **synced to the modpack**. Use wildcards like `*` to include multiple files from this directory (e.g., `/mods/*.jar`). Or use `**` (double star) to recursively include all files and subdirectories from a folder (e.g., `/config/**`). Prefix a path with `!` to **exclude** it from syncing. **Note:** This does **not** copy the files; it only defines what will be available on the client side under `~/.minecraft/`. | +| `allowEditsInFiles` | `["/options.txt", "/config/**"]` | A list of files that clients are allowed to edit. In other words, files that are downloaded only one time and then ignored from updating. Configuration works exactly the same way `syncedFiles` does. | +| `forceCopyFilesToStandardLocation` | `[]` | A list of files that client will be forced to load from standard directory. By default most mods are loaded from separate automodpack directory, this list specifies files which will be copied to e.g. standard mods folder. **This will break seamless updates!** Use only when you have really good reason for it. Configuration works exactly the same way `syncedFiles` does. | +| `autoExcludeServerSideMods` | `true` | Automatically excludes server-side mods from the modpack. (Works only if mod developer specified environment type in mod metadata) | +| `autoExcludeUnnecessaryFiles` | `true` | Auto skip files which are: empty, hidden, temporary, disabled or backup. | +| `requireAutoModpackOnClient` | `true` | Whether or not this mod is optional for clients to join server. | +| `nagUnModdedClients` | `true` | If `true` clients without AutoModpack will be nagged with a chat message on join. To work requires `requireAutoModpackOnClient` to be `false`. | +| `nagMessage` | `"This server provides dedicated modpack through AutoModpack!"` | The message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | +| `nagClickableMessage` | `"Click here to get the AutoModpack!"` | The clickable part of the message that will be displayed to clients without AutoModpack. To work requires `nagUnModdedClients` to be `true`. | +| `nagClickableLink` | `"https://modrinth.com/project/automodpack"` | The link that will be opened when the message is clicked. To work requires `nagUnModdedClients` to be `true`. | +| `bindAddress` | `""` | The address to which the modpack host server binds. Leave this empty to automatically determine a local address (typically `0.0.0.0` or `::0`). When `bindPort` is set to `-1`, this value is **ignored** and the modpack host will be bound to the same address as the Minecraft server, as specified in the `server.properties` file. | +| `bindPort` | `-1` | The port number on which the modpack host server listens. If set to `-1`, the modpack will be hosted directly on the port of your Minecraft server. | +| `addressToSend` | `""` | The address to the modpack host which will be used by clients to download the modpack. If empty, clients will use the same address they used to connect to the Minecraft server. (Do **not** include the port there, use `portToSend` instead) | +| `portToSend` | `-1` | The port number that is sent alongside the `addressToSend`. If set to `-1`, clients will use the same port they used to connect to the Minecraft server. | +| `disableInternalTLS` | `false` | Disables internal TLS management. If enabled, you must manage TLS externally, e.g. on a reverse proxy. When using this option, you have to use different `bindPort` than `-1` and you should consider setting `bindAddress` to a loopback address (e.g. `127.0.0.1`), so the unencrypted traffic between the modpack host and the reverse proxy isn't leaked. | +| `updateIpsOnEveryStart` | `false` | Updates `addressToSend` on every server start. Might be useful if you have dynamic IP address. (Use it only if you really need it!) | +| `bandwidthLimit` | `0` | Upload limit in Mbps that modpack host server is restricted to. (Value has to be an Integer, `0` - means unlimited) | +| `validateSecrets` | `true` | Makes modpack host validate and authorize modpack download attempts using unique secrets. | +| `secretLifetime` | `336` | Time in hours that a player's secret remains valid. | +| `selfUpdater` | `false` | Turn on/off all automodpack updates. This does not affect the mod's activity in installing modpacks. | +| `acceptedLoaders` | `[""]` | Allows players from different modloaders to connect to your server. (Use with caution, most mods work only on one loader) | diff --git a/docs/configuration/troubleshooting.mdx b/docs/configuration/troubleshooting.mdx new file mode 100644 index 000000000..d0c8b88a4 --- /dev/null +++ b/docs/configuration/troubleshooting.mdx @@ -0,0 +1,6 @@ +## Troubleshooting Config Issues +When finished editing configuration files, make sure to reload them by executing command `automodpack config reload` or restart the server/game. + +If you can't find the config files, make sure the client/server was started at least once, so that the files are generated. + +If values get restored to default it might mean that your config file is malformed make sure to use editor with JSON parser build-in. Or check in some online one e.g. https://jsonformatter.org/json-parser. diff --git a/docs/faq.mdx b/docs/faq.mdx new file mode 100644 index 000000000..48e7f5dae --- /dev/null +++ b/docs/faq.mdx @@ -0,0 +1,49 @@ +### Is it server side mod? +No, it's both. You need to have it installed on server as well as on client. + +### How do i add client only mods to the modpack? (e.g. server crashes with some mod but i want it for clients) +Take a look at [modpack creation page](techinicals/modpack-creation). + +### How to restrict access to my modpack? +Enable `validateSecrets` in the [config](configuration/server-config), turn on `online-mode` in the server.properties and enable whitelist or ban someone. + +### Does it work on lan (single-player world with option for other players to join)? +Yes. Just remember to generate modpack via command /`automodpack generate` and start modpack host /`automodpack host start`. + +### Does it work with single-player world shared via essential, world host, e4mc? +Likely no, most of these type of mods forward only minecraft packets. These mods would need to support automodpack protocol or just relay all tcp packets instead of limiting to minecraft protocol. + +### Can I use non modpack mods on client? +Yes, you can add any mod you want to use alongside downloaded modpack by just placing the mod into the standard mods folder. + +### How can I delete files from modpack? +Just delete file from server, or exclude it from sync, read about `syncedFiles` in [config](configuration/server-config) + +### Can I remove/disable non modpack files/mods from player use? +Not yet, it may come in the future. + +### Do I have to open any port on my router? +No, you don't need to. By default AutoModpack injects into minecraft networking I/O which reuses your minecraft server port. (e.g. 25565) + +If you want to use different port. Disable `hostModpackOnMinecraftPort` and change `hostPort` in [config](configuration/server-config). + +### What happens when I update AutoModpack version on server? +Client always try to sync to the server's version of AutoModpack. It updates or downgrades itself to the server's version if such is found on the Modrinth. + +### Why is the host modpack path so long? +There are plans to implement ability of adding more than just one, `main` modpack. + +### Which minecraft versions are supported? +All of the versions you see in the [latest release](https://github.com/Skidamek/AutoModpack/releases/latest). + +### Is my launcher supported? +Take a look at the [compatibility page](compatibility/launchers) for more information about supported launchers. + +### Does this mod updates my modpack mods to the latest versions? +No, it's out of scope for this project. This mod only synchronizes server modpack to the clients. All your mods on the server would have to still be updated manually. AutoModpack just makes sure client versions matches the server ones. + +### I found a bug, what should I do? +If you found a bug, please report it on the [issue tracker](https://github.com/Skidamek/AutoModpack/issues). + +### I found a security vulnerability, what should I do? +Please report it privately to the author on [Discord](https://discordapp.com/users/464522287618457631), we will try to resolve it ASAP. \ No newline at end of file diff --git a/docs/quick-start.mdx b/docs/quick-start.mdx new file mode 100644 index 000000000..f1f490499 --- /dev/null +++ b/docs/quick-start.mdx @@ -0,0 +1,35 @@ +So you want to start using AutoModpack? Great! Let's get you started. + +## Installation + +1. **Download AutoModpack**: Get the latest version of AutoModpack for your minecraft version from [Modrinth](https://modrinth.com/mod/automodpack) or [CurseForge](https://www.curseforge.com/minecraft/mc-mods/automodpack). +2. **Install AutoModpack**: Place the downloaded mod into your `mods` folder of your minecraft instance. +3. **Start your server**: Launch your minecraft server with AutoModpack installed. This will generate the initial modpack metadata. + +Now you are in most cases ready to go, at this point, its recommended to try connecting to your server with just the automodpack installed on the client and see if everything works. + +## First Connection +On first connection you will be prompted to verify the server's certificate fingerprint. This is a security measure to ensure that you are connecting to the correct server and not a malicious one. Read more about [certificate verification](technicals/certificate). +After successful verification, AutoModpack will asks you to confirm the modpack installation. Confirm and modpack should be downloaded and installed automatically. + +Now restart your game and your servers' modpack should be loaded and you should be able to connect to your server again but this time spawning in the overworld! + + +If you don't see your modpack mods in the mods folder, don't worry. Mods won't be installed to your standard mods folder, they will be installed in the `~/.minecraft/automodpack/modapcks/{your-modpack}/mods/` folder instead. There are some [good reasons for that](technicals/file-structure). + + +## Troubleshooting +If you encounter any issues at this point, here are some common troubleshooting steps: +- Make sure you are using the same version of AutoModpack on both server and client. +- Ensure that your server is running and accessible without AutoModpack. +- Check the server logs there might be some nudge about what went wrong. +- Read the [faq page](faq) for common questions and answers. + +If you are still having issues, join our [Discord server](https://discord.gg/hS6aMyeA9P) and create a post in the `#support` channel, we will try to help! + +## Next Steps + +Now that you have AutoModpack installed and working, you can start customizing your modpack. Here are some things you can do next: + +- Read the [modpack creation page](technicals/modpack-creation) to learn how to manage your modpack. +- Read the [configuration page](configuration/server-config) to learn how to configure AutoModpack to your needs. diff --git a/docs/sinytra-wiki.json b/docs/sinytra-wiki.json new file mode 100644 index 000000000..6f1d73b11 --- /dev/null +++ b/docs/sinytra-wiki.json @@ -0,0 +1,7 @@ +{ + "id": "automodpack", + "platforms": { + "modrinth": "automodpack", + "curseforge": "automodpack" + } +} \ No newline at end of file diff --git a/docs/technicals/_meta.json b/docs/technicals/_meta.json new file mode 100644 index 000000000..8d68a11b8 --- /dev/null +++ b/docs/technicals/_meta.json @@ -0,0 +1,22 @@ +{ + "connection.mdx": { + "name": "Connection", + "icon": null + }, + "certificate.mdx": { + "name": "Certificate Handling", + "icon": null + }, + "modpack-creation.mdx": { + "name": "Modpack Creation", + "icon": null + }, + "file-structure.mdx": { + "name": "File Structure", + "icon": null + }, + "modpack-hosting.mdx": { + "name": "Modpack Hosting", + "icon": null + } +} diff --git a/docs/technicals/certificate.mdx b/docs/technicals/certificate.mdx new file mode 100644 index 000000000..ecde1d062 --- /dev/null +++ b/docs/technicals/certificate.mdx @@ -0,0 +1,45 @@ +### How to verify the certificate fingerprint? +To verify, simply **copy the fingerprint from your server console to the game.** This is a one-time verification, meaning you won't need to repeat it after the initial connection. This process helps prevent security threats like [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). The fingerprint is the same for everyone on your server and it never changes, its recommended to share it with players ahead of time. + +If you can't find the fingerprint in the console, you can retrieve it at any time using the `/automodpack host fingerprint` [command](../commands/commands). + +However please do not think of it like a password to the modpack, its not a password or any secret, its a public verifiable piece of server certificate, **anyone could download** your modpack without previous knowledge of such fingerprint e.g. by bypassing this check with `I AM INCREDIBLY STUPID`. (don't do it) + +To provide modpack for only authorized/whitelisted players, use `validateSecrets` option in the [server config](../configuration/server-config) (its enabled by default). + + +If you don't want players to explicitly verify the certificate manually and you own a domain (required), you can provide your own CA signed certificate. + +### How to provide your own CA signed certificate? + +You can use tools like [Certbot](https://eff-certbot.readthedocs.io/en/stable/install.html). + +After obtaining the certificate, on the server in the `~/automodpack/.private/` directory, replace the `cert.crt` (with the full chain certificate) and `key.pem` files ([key has to be in PKCS#8 (PEM) format](https://netty.io/wiki/sslcontextbuilder-and-private-key.html)) - here be cautious as interception of these files may result in impersonation of the server, [read more](https://security.stackexchange.com/a/16694). +If you're hosting modpack on different sub/domain than your minecraft server, make sure that your certificate verifies both of the sub/domains. + +### Little tutorial on how to obtain a certificate using Certbot + +Install Certbot on your pc or server, then run the example command and follow further certbot instructions to obtain a certificate for your domain: + +```bash +# certbot certonly --manual --preferred-challenges dns -d -d +``` +If you're hosting modpack on the same port as your minecraft server, in most cases these domains will match, if so you don't need to provide the second domain. + + +Never copy-paste random commands from the internet, always read the documentation of the tool you're using! + + +After obtaining the certificate, the files you need are `fullchain.pem` (the full chain certificate, copy and rename to `cert.crt`) and `privkey.pem` (the private key, copy and rename to `key.pem`). + +### Wait but why do I need all this? Its just a modpack for my friends! + +If someone could easily take control over the automodpack connection, they could download on your (or your friends) computers anything they want. +Minecraft isn't sandboxed in any way and mods **can run any arbitrary code** so if someone could easily inject some malicious piece of code to any automodpack modpack no matter who the victim is, that would be a huge win for them anyway, they could steal passwords from your browser, accounts to various services or even delete your very important folder with funny cat memes, there are many possibilities. +That's why i believe it's important to have some security measures. + +### Hmm... ok but nobody knows about my server besides my friends, why should I care? + +Even if you think that nobody knows about your server, it may not be true. There are many ways how someone could find out about your server. +For example there are many minecraft server scanners which scan the entire internet for minecraft servers, and you could be a victim if your server would be found vulnerable and we wouldn't verify the certificate. +However nothing we do here stops someone from taking control over your server in different ways, (e.g. gaining access to it via ssh or your server hosting panel) that's why it's very important that you as a server owner take your security seriously and use strong passwords, enable two-factor authentication and keep your server software up to date. diff --git a/docs/technicals/connection.mdx b/docs/technicals/connection.mdx new file mode 100644 index 000000000..3ca995fb3 --- /dev/null +++ b/docs/technicals/connection.mdx @@ -0,0 +1,35 @@ +### Encryption + +Modpack download connection is secured using TLS 1.3. + +This is the same security standard used by HTTPS, the protocol which you're using all the time to browse the web (even now!). + +TLS has method of verifying the server's identity using a certificate which is crucial to prevent attacks such as [Man-in-the-middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack). + +That's why AutoModpack requires you to verify the server's certificate fingerprint when connecting to the server for the first time. Read [how to verify the certificate fingerprint](certificate) for more details. + +### Direct Downloads + +AutoModpack uses direct download links from Modrinth and CurseForge APIs to download most of your modpack files. +This means that the mod authors get credit for every download. + +However if file you are providing in the modpack is not available on Modrinth or CurseForge, AutoModpack will download it (also directly) but from your server. + +### Compression + +To speed up the download, AutoModpack compresses the modpack files using [Zstandard](https://facebook.github.io/zstd/) compression algorithm. +This algorithm is very fast and provides great compression ratio, which means that the modpack files are smaller and faster to download. +This is especially useful for larger modpacks or slower internet connections. +However this only applies to files downloaded from your server. + +### Authorization + + +Do not confuse this with [certificate fingerprint verification](certificate), which is used to verify the server's identity. + + +AutoModpack uses a simple authorization mechanism to prevent unauthorized access to your modpack. +When you connect to the server, in the background AutoModpack generates a random unique secret for each player. This secret is then send to the player and used to authorize the player to download the modpack files. +Every time you reconnect to the server, the old secret is invalidated and a new one is generated. +This prevents unauthorized access to your modpack files and ensures that only whitelisted and not banned players can download the modpack. +If you don't want to use this feature, you can disable it (`validateSecrets`) in the [server config](../configuration/server-config). diff --git a/docs/technicals/file-structure.mdx b/docs/technicals/file-structure.mdx new file mode 100644 index 000000000..d22282b7e --- /dev/null +++ b/docs/technicals/file-structure.mdx @@ -0,0 +1,21 @@ +All of the AutoModpack files are kept inside `~/automodpack/` folder. + +### Server +Modpack is generated from files inside `~/automodpack/host-modpack/main/` and/or `syncedFiles` (read [config](../configuration/server-config)) which translates to `~/.minecraft/` (root minecraft instance) folder on client. + +Modpack generation means creation of `~/automodpack/host-modpack/automodpack-content.json` which contains metadata of every single file of the modapck. You **should NOT** touch this file manually, edit config instead. + +### Client +All of downloaded modpacks are kept inside `~/automodpack/modpacks/`. + +All of the selected modpack files are copied to `~/.minecraft/` besides **mods** (with some exceptions, due to mod loader limitations) + +If detected, AutoModpack also tries to get rid of most of if not all of duplicated mods from your standard mods folder in favor of AutoModpack's mods folder. + +It shouldn't cause any crashes, but if you notice that automodpack removes some dependency causing game to crash, please report it! + + +Mods are separated for reasons. Such approach mitigates common drawbacks of placing all modpack mods into the standard mods folder, like: +- **Mod conflict crashes**: It won't resolve them, but it atleast gives the server owner ability to remotely remove or fix conflicting mods without manual intervention on the client side. +- **Debugging**: Its easier to see what mods are part of the modpack and what are not. Since clients can add their own mods to the standard mods folder, it's easier to debug when something breaks or conflicts. +- **Reboots**: If all of the modpack mods were placed into the standard mods folder, it would require a reboot to update the modpack mods. Now it can be done seamlessly on boot without any restart! \ No newline at end of file diff --git a/docs/technicals/modpack-creation.mdx b/docs/technicals/modpack-creation.mdx new file mode 100644 index 000000000..a95f58ae9 --- /dev/null +++ b/docs/technicals/modpack-creation.mdx @@ -0,0 +1,17 @@ +**Yes, you can add *anything* to your modpack.** + +#### Two ways of adding content to the modpack. + +- To re-use already existing files on server use `syncedFiles` read more in [config](../configuration/server-config). As an example with default config it creates modpack containing *every* mod (file ending with .jar) from server `~/mods/` folder. + +- To add more files to modpack use separate directory `~/automodpack/host-modpack/main/`. +For example to add more mods create folder `mods` and place there mods so that will be `~/automodpack/host-modpack/main/mods/`**`your-mod.jar`**. +Or to add resourcepacks analogically create folder `resourcepacks` and place there resourcepacks so that will be `~/automodpack/host-modpack/main/resourcepacks/`**`your-resourcepack.zip`**. + + +Do not place in the modpack cache files, like connector's `.connector` directory, its not supposed to be provided within modpack and it break seamless/reboot-less updates. + + +Both of these methods translate to the `~/.minecraft/` on client side (root directory of minecraft instance). + +Remember to regenerate the modpack using `/automodpack generate` (see [commands](../commands/commands)) \ No newline at end of file diff --git a/docs/technicals/modpack-hosting.mdx b/docs/technicals/modpack-hosting.mdx new file mode 100644 index 000000000..5e019b673 --- /dev/null +++ b/docs/technicals/modpack-hosting.mdx @@ -0,0 +1,2 @@ +By default AutoModpack hosts your modpack on your minecraft server port (e.g. 25565). This means that you don't need to open any other port on your router/firewall. +If you want to use a different port, change `bindPort` in [config](../configuration/server-config). diff --git a/gradle.properties b/gradle.properties index 8076bac0c..34af6929e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,21 +1,16 @@ org.gradle.jvmargs = -Xmx8G # Run clean task before building if doesn't work -org.gradle.parallel = false +org.gradle.parallel = true org.gradle.caching = true -org.gradle.caching.debug = false org.gradle.configureondemand = true -fabric_versions = 1.18.2, 1.19.2, 1.19.4, 1.20.1, 1.20.4, 1.20.6, 1.21.1, 1.21.3, 1.21.4 -neoforge_versions = 1.20.4, 1.20.6, 1.21.1, 1.21.3, 1.21.4 -forge_versions = 1.18.2, 1.19.2, 1.19.4, 1.20.1 - core_modules = core, fabric-core, fabric-15, fabric-16, forge-fml40, forge-fml47, neoforge-fml2, neoforge-fml4 -loader_fabric = 0.15.11 -mixin_extras = 0.5.0-rc.2 +deps.fabric-loader = 0.15.11 +deps.mixin-extras = 0.5.0-rc.2 -mod_id = automodpack +mod.id = automodpack mod_name = AutoModpack -mod_version = 4.0.0-beta36 -mod_group = pl.skidam.automodpack -mod_description = Enjoy a seamless modpack installation process and effortless updates with a user-friendly solution that simplifies management, making your gaming experience a breeze. +mod_version = 4.0.0-beta38 +mod.group = pl.skidam.automodpack +mod.description = Enjoy a seamless modpack installation process and effortless updates with a user-friendly solution that simplifies management, making your gaming experience a breeze. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a793a..ff23a68d7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java index 9137ccd74..f5471d0e5 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/Preload.java @@ -52,19 +52,22 @@ private void updateAll() { selectedModpackDir = optionalSelectedModpackDir.get(); InetSocketAddress selectedModpackAddress = null; InetSocketAddress selectedServerAddress = null; + boolean requiresMagic = true; // Default to true if (!clientConfig.selectedModpack.isBlank() && clientConfig.installedModpacks.containsKey(clientConfig.selectedModpack)) { var entry = clientConfig.installedModpacks.get(clientConfig.selectedModpack); selectedModpackAddress = entry.hostAddress; selectedServerAddress = entry.serverAddress; + requiresMagic = entry.requiresMagic; } // Only selfupdate if no modpack is selected if (selectedModpackAddress == null) { SelfUpdater.update(); + CustomFileUtils.deleteDummyFiles(Path.of(System.getProperty("user.dir")), null); } else { Secrets.Secret secret = SecretsStore.getClientSecret(clientConfig.selectedModpack); - Jsons.ModpackAddresses modpackAddresses = new Jsons.ModpackAddresses(selectedModpackAddress, selectedServerAddress); + Jsons.ModpackAddresses modpackAddresses = new Jsons.ModpackAddresses(selectedModpackAddress, selectedServerAddress, requiresMagic); var optionalLatestModpackContent = ModpackUtils.requestServerModpackContent(modpackAddresses, secret, false); var latestModpackContent = ConfigTools.loadModpackContent(selectedModpackDir.resolve(hostModpackContentFile.getFileName())); @@ -115,24 +118,20 @@ private void loadConfigs() { // load client config if (clientConfigOverride == null) { - var clientConfigVersion = ConfigTools.loadCheck(clientConfigFile, Jsons.VersionConfigField.class); + var clientConfigVersion = ConfigTools.softLoad(clientConfigFile, Jsons.VersionConfigField.class); if (clientConfigVersion != null) { - // Update the configs schemes to not crash the game if loaded with old config! if (clientConfigVersion.DO_NOT_CHANGE_IT == 1) { + // Update the configs schemes to not crash the game if loaded with old config! var clientConfigV1 = ConfigTools.load(clientConfigFile, Jsons.ClientConfigFieldsV1.class); - // update to v2 - just delete the installedModpacks - if (clientConfigV1 != null) { - clientConfigV1.installedModpacks = null; - clientConfigV1.DO_NOT_CHANGE_IT = 2; + if (clientConfigV1 != null) { // update to V2 - just delete the installedModpacks clientConfigVersion.DO_NOT_CHANGE_IT = 2; + clientConfigV1.DO_NOT_CHANGE_IT = 2; + clientConfigV1.installedModpacks = null; } + ConfigTools.save(clientConfigFile, clientConfigV1); LOGGER.info("Updated client config version to {}", clientConfigVersion.DO_NOT_CHANGE_IT); } - -// if (clientConfigVersion.DO_NOT_CHANGE_IT == 2) { -// // Noice! -// } } clientConfig = ConfigTools.load(clientConfigFile, Jsons.ClientConfigFieldsV2.class); @@ -144,8 +143,38 @@ private void loadConfigs() { clientConfig = ConfigTools.load(clientConfigOverride, Jsons.ClientConfigFieldsV2.class); } + var serverConfigVersion = ConfigTools.softLoad(serverConfigFile, Jsons.VersionConfigField.class); + if (serverConfigVersion != null) { + if (serverConfigVersion.DO_NOT_CHANGE_IT == 1) { + // Update the configs schemes to make this update not as breaking as it could be + var serverConfigV1 = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFieldsV1.class); + var serverConfigV2 = ConfigTools.softLoad(serverConfigFile, Jsons.ServerConfigFieldsV2.class); + if (serverConfigV1 != null && serverConfigV2 != null) { + serverConfigVersion.DO_NOT_CHANGE_IT = 2; + serverConfigV2.DO_NOT_CHANGE_IT = 2; + + if (serverConfigV1.hostIp.isBlank()) { + serverConfigV2.addressToSend = ""; + } else { + serverConfigV2.addressToSend = AddressHelpers.parse(serverConfigV1.hostIp).getHostString(); + } + + if (serverConfigV1.hostModpackOnMinecraftPort) { + serverConfigV2.bindPort = -1; + serverConfigV2.portToSend = -1; + } else { + serverConfigV2.bindPort = serverConfigV1.hostPort; + serverConfigV2.portToSend = serverConfigV1.hostPort; + } + } + + ConfigTools.save(serverConfigFile, serverConfigV2); + LOGGER.info("Updated server config version to {}", serverConfigVersion.DO_NOT_CHANGE_IT); + } + } + // load server config - serverConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFields.class); + serverConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFieldsV2.class); if (serverConfig != null) { // Add current loader to the list diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java index 9a961a71d..889efee8b 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/SelfUpdater.java @@ -83,21 +83,26 @@ public static boolean update(Jsons.ModpackContentFields serverModpackContent) { continue; } - boolean currentBeta = AM_VERSION.contains("-beta"); - boolean remoteBeta = fileVersion.contains("-beta"); + boolean currentIsBeta = AM_VERSION.contains("-beta"); + boolean remoteIsBeta = fileVersion.contains("-beta"); String[] currentVersionSplit = AM_VERSION.split("-beta"); String[] remoteVersionSplit = fileVersion.split("-beta"); + int remoteBeta = -1; + if (remoteIsBeta) { + remoteBeta = Integer.parseInt(remoteVersionSplit[1]); + } + // Removes '-betaX' if exists // Removes '.' - dots - to then parse it as number String OUR_VERSION = currentVersionSplit[0].replace(".", ""); - String LATEST_VERSION = remoteVersionSplit[0].replace(".", ""); + String REMOTE_VERSION = remoteVersionSplit[0].replace(".", ""); // Compare versions as numbers if (!gettingServerVersion) { try { - if (Integer.parseInt(OUR_VERSION) > Integer.parseInt(LATEST_VERSION)) { + if (Integer.parseInt(OUR_VERSION) > Integer.parseInt(REMOTE_VERSION)) { message = "You are using pre-released or beta version of AutoModpack: " + AM_VERSION + " latest stable version is: " + automodpack.fileVersion(); break; // Break, checked version is lower than installed, meaning that higher version doesn't exist. } @@ -105,19 +110,19 @@ public static boolean update(Jsons.ModpackContentFields serverModpackContent) { LOGGER.error("Failed to parse version numbers: " + e); } - if (currentBeta && remoteBeta) { + if (currentIsBeta && remoteIsBeta) { if (Integer.parseInt(currentVersionSplit[1]) > Integer.parseInt(remoteVersionSplit[1])) { message = "You are using pre-released or beta version of AutoModpack: " + AM_VERSION + " latest stable version is: " + automodpack.fileVersion(); break; // Break, checked version is lower than installed, meaning that higher version doesn't exist. } } - if (!currentBeta && remoteBeta) { + if (!currentIsBeta && remoteIsBeta) { message = "You are using stable version of AutoModpack: " + AM_VERSION + " latest pre-released or beta version is: " + automodpack.fileVersion(); continue; } - if (currentBeta && !remoteBeta) { + if (currentIsBeta && !remoteIsBeta) { message = "You are using pre-released or beta version of AutoModpack: " + AM_VERSION + " latest stable version is: " + automodpack.fileVersion(); continue; } @@ -143,14 +148,41 @@ public static boolean update(Jsons.ModpackContentFields serverModpackContent) { return false; } + public static boolean validUpdate(ModrinthAPI automodpack) { + String[] remoteVersionSplit = automodpack.fileVersion().split("-beta"); + + int remoteBeta = -1; + boolean remoteIsBeta = false; + if (remoteVersionSplit.length > 1) { + remoteIsBeta = true; + remoteBeta = Integer.parseInt(remoteVersionSplit[1]); + } + + // Removes '-betaX' if exists + // Removes '.' - dots - to then parse it as number + String REMOTE_VERSION = remoteVersionSplit[0].replace(".", ""); + + // Don't allow downgrades pass 4.0.0-beta38 + if (Integer.parseInt(REMOTE_VERSION) >= 400 && (!remoteIsBeta || remoteBeta >= 38)) { + return true; + } + + LOGGER.error("Downgrading AutoModpack to version {} is strongly discouraged and disabled from auto-server-syncing. To protect against potential security vulnerabilities, please use a newer version.", automodpack.fileVersion()); + return false; + } + public static void installModVersion(ModrinthAPI automodpack) { + if (!validUpdate(automodpack)) { + return; + } + Path automodpackUpdateJar = automodpackDir.resolve(automodpack.fileName()); Path newAutomodpackJar; try { DownloadManager downloadManager = new DownloadManager(); - new ScreenManager().download(downloadManager, "AutoModapck " + automodpack.fileVersion()); + new ScreenManager().download(downloadManager, "AutoModpack " + automodpack.fileVersion()); // Download it downloadManager.download( diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUpdater.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUpdater.java index 0087785fa..1fdb63a78 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUpdater.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUpdater.java @@ -15,6 +15,7 @@ import java.net.SocketTimeoutException; import java.nio.file.*; import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; import static pl.skidam.automodpack_core.GlobalVariables.*; @@ -518,8 +519,13 @@ private boolean applyModpack() throws Exception { boolean needsRestart2 = ModpackUtils.fixNestedMods(conflictingNestedMods, standardModList); Set ignoredFiles = ModpackUtils.getIgnoredFiles(conflictingNestedMods, workaroundMods); + Set forceCopyFiles = modpackContent.list.stream() + .filter(item -> item.forceCopy) + .map(item -> item.file) + .collect(Collectors.toSet()); + // Remove duplicate mods - ModpackUtils.RemoveDupeModsResult removeDupeModsResult = ModpackUtils.removeDupeMods(modpackDir, standardModList, modpackModList, ignoredFiles, workaroundMods); + ModpackUtils.RemoveDupeModsResult removeDupeModsResult = ModpackUtils.removeDupeMods(modpackDir, standardModList, modpackModList, ignoredFiles, workaroundMods, forceCopyFiles); boolean needsRestart3 = removeDupeModsResult.requiresRestart(); // Remove rest of mods not for standard mods directory @@ -533,16 +539,20 @@ private Set getFilesNotToCopy(Set filesNotToCopy = new HashSet<>(); // Make list of files which we do not copy to the running directory - for (Jsons.ModpackContentFields.ModpackContentItem modpackContentItem : modpackContentItems) { + for (Jsons.ModpackContentFields.ModpackContentItem item : modpackContentItems) { + if (item.forceCopy) { + continue; + } + // We only want to copy editable file if its downloaded first time // So we add to ignored any other editable file - if (modpackContentItem.editable && !newDownloadedFiles.contains(modpackContentItem.file)) { - filesNotToCopy.add(modpackContentItem.file); + if (item.editable && !newDownloadedFiles.contains(item.file)) { + filesNotToCopy.add(item.file); } // We only want to copy mods which need a workaround - if (modpackContentItem.type.equals("mod") && !workaroundMods.contains(modpackContentItem.file)) { - filesNotToCopy.add(modpackContentItem.file); + if (item.type.equals("mod") && !workaroundMods.contains(item.file)) { + filesNotToCopy.add(item.file); } } diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java index 6b91cf8ff..c909efa09 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/client/ModpackUtils.java @@ -225,8 +225,7 @@ public record RemoveDupeModsResult(boolean requiresRestart, Set modsToKeep // Returns true if removed any mod from standard mods folder // If the client mod is a duplicate of what modpack contains then it removes it from client so that you dont need to restart game just when you launched it and modpack get updated - basically having these mods separately allows for seamless updates // If you have client mods which require specific mod which is also a duplicate of what modpack contains it should stay - public static RemoveDupeModsResult removeDupeMods(Path modpackDir, Collection standardModList, Collection modpackModList, Set ignoredMods, Set workaroundMods) throws IOException { - + public static RemoveDupeModsResult removeDupeMods(Path modpackDir, Collection standardModList, Collection modpackModList, Set ignoredMods, Set workaroundMods, Set forceCopyFiles) throws IOException { var dupeMods = ModpackUtils.getDupeMods(modpackDir, ignoredMods, standardModList, modpackModList); if (dupeMods.isEmpty()) { @@ -260,12 +259,14 @@ public static RemoveDupeModsResult removeDupeMods(Path modpackDir, Collection providesIDs = modpackMod.providesIDs(); List IDs = new ArrayList<>(providesIDs); IDs.add(modId); boolean isDependent = IDs.stream().anyMatch(idsToKeep::contains); - boolean isWorkaround = workaroundMods.contains(CustomFileUtils.formatPath(standardModPath, MODS_DIR.getParent())); + boolean isWorkaround = workaroundMods.contains(formatedPath); + boolean isForceCopy = forceCopyFiles.contains(formatedPath); if (isDependent) { Path newStandardModPath = standardModPath.getParent().resolve(modpackModPath.getFileName()); @@ -279,7 +280,7 @@ public static RemoveDupeModsResult removeDupeMods(Path modpackDir, Collection getModInfosFromFingerPrints(Map hashes) { + private static CurseForgeAPI parseJsonObject(JsonObject JSONObject, Map hashes) { if (JSONObject == null) { LOGGER.error("CurseForgeAPI Can't parse null object"); return null; diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/ModrinthAPI.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/ModrinthAPI.java index 2aa98d1d1..bbaf5b50b 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/ModrinthAPI.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/platforms/ModrinthAPI.java @@ -125,32 +125,7 @@ public static List getModsInfosFromListOfSHA1(List listOfSh return modrinthAPIList; } - // https://docs.modrinth.com/#tag/version-files - public static ModrinthAPI getModInfoFromSHA1(String sha1) { - if (sha1 == null || sha1.isEmpty()) { - return null; - } - - String requestUrl = BASE_URL + "/version_file/" + sha1 + "?algorithm=sha1"; - requestUrl = requestUrl.replaceAll("\"", "%22"); // so important! - - try { - JsonObject JSONObject = Json.fromUrl(requestUrl); - return parseJsonObject(JSONObject, List.of(sha1)); - } catch (IndexOutOfBoundsException e) { - LOGGER.warn("Something gone wrong while getting info from Modrinth API: {}", requestUrl); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - -// public static void main(String[] args) { -// var ww = getModsInfosFromListOfSHA1(List.of("2ff512a70c437c20523de01ea95b6fc9b164a5c0")); -// ww.forEach(System.out::println); -// } - - public static ModrinthAPI parseJsonObject(JsonObject JSONObject, List listOfSha1) { + private static ModrinthAPI parseJsonObject(JsonObject JSONObject, List listOfSha1) { if (JSONObject == null) { return null; } diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/DownloadManager.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/DownloadManager.java index 6db5cc58b..e063d6edb 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/DownloadManager.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/DownloadManager.java @@ -230,8 +230,13 @@ public long getTotalBytesRemaining() { return bytesToDownload - bytesDownloaded; } - public float getTotalPercentageOfFileSizeDownloaded() { - return (float) bytesDownloaded / bytesToDownload * 100; + public int getTotalPercentageOfFileSizeDownloaded() { + if (bytesDownloaded == 0 || bytesToDownload == 0) { + return 0; + } + + int percentage = (int) (bytesDownloaded * 100 / bytesToDownload); + return Math.max(0, Math.min(100, percentage)); } public String getStage() { diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/FetchManager.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/FetchManager.java index 04f22b4e1..3d19baf6d 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/FetchManager.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/FetchManager.java @@ -40,7 +40,7 @@ public void fetch() { List mo = new ArrayList<>(); for (Map.Entry entry : fetchDatas.entrySet()) { FetchData fetchData = entry.getValue().fetchData(); - if (fetchData.murmur != null) { + if (fetchData.murmur != null && !fetchData.murmur.isBlank()) { cf.put(fetchData.sha1, fetchData.murmur); } diff --git a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/SpeedMeter.java b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/SpeedMeter.java index 8289164ab..8867e461e 100644 --- a/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/SpeedMeter.java +++ b/loader/core/src/main/java/pl/skidam/automodpack_loader_core/utils/SpeedMeter.java @@ -6,7 +6,7 @@ public class SpeedMeter { private final DownloadManager downloadManager; private final ConcurrentSkipListMap bytesDownloadedPerSec = new ConcurrentSkipListMap<>(); - private static final int MAX_ENTRIES = 5; + private static final int MAX_ENTRIES = 3; public SpeedMeter(DownloadManager downloadManager) { this.downloadManager = downloadManager; @@ -26,20 +26,18 @@ public synchronized void addDownloadedBytes(long newBytes) { } /** - * Get the download speed in bytes per second. + * Get the average download speed in bytes per second from the last few seconds. */ - public synchronized long getCurrentSpeedInBytes() { - long lastTimeBucket = System.currentTimeMillis() / 1000 * 1000 - 1000; + public long getAverageSpeedOfLastFewSeconds(int seconds) { + long totalBytes = 0; + int count = 0; - Long value = -1L; - - if (bytesDownloadedPerSec.containsKey(lastTimeBucket)) { - value = bytesDownloadedPerSec.get(lastTimeBucket); - } else if (bytesDownloadedPerSec.containsKey(lastTimeBucket - 1000)) { - value = bytesDownloadedPerSec.get(lastTimeBucket - 1000); + for (Long bytes : bytesDownloadedPerSec.values()) { + totalBytes += bytes; + count++; } - return value != null ? value : -1; + return count >= seconds ? totalBytes / count : -1; } /** @@ -47,7 +45,7 @@ public synchronized long getCurrentSpeedInBytes() { */ public long getETAInSeconds() { long totalBytesRemaining = downloadManager.getTotalBytesRemaining(); - long speed = getCurrentSpeedInBytes(); + long speed = getAverageSpeedOfLastFewSeconds(3); if (speed <= 0) { return -1; @@ -70,13 +68,18 @@ public static String formatDownloadSpeedToMbps(long currentSpeedInBytes) { } /** - * Format ETA into HH:MM:SS. + * Format ETA into MM:SS format or HH:MM:SS format. */ public static String formatETAToSeconds(long seconds) { - if (seconds < 0) { + seconds++; // Increment by 1 to avoid showing 00:00:00 for 0 seconds + if (seconds < 1) { return "-1"; } + if (seconds < 3600) { + return String.format("%02d:%02d", seconds / 60, seconds % 60); + } + return String.format("%02d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, seconds % 60); } } diff --git a/loader/fabric/15/gradle.properties b/loader/fabric/15/gradle.properties index 83f11cd8c..146492225 100644 --- a/loader/fabric/15/gradle.properties +++ b/loader/fabric/15/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=fabric -loader_fabric=0.15.11 \ No newline at end of file +deps.fabric=0.15.11 \ No newline at end of file diff --git a/loader/fabric/16/gradle.properties b/loader/fabric/16/gradle.properties index 117c56d7a..5ebca380c 100644 --- a/loader/fabric/16/gradle.properties +++ b/loader/fabric/16/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=fabric -loader_fabric=0.16.9 \ No newline at end of file +deps.fabric=0.16.14 \ No newline at end of file diff --git a/loader/fabric/core/gradle.properties b/loader/fabric/core/gradle.properties index 83f11cd8c..146492225 100644 --- a/loader/fabric/core/gradle.properties +++ b/loader/fabric/core/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=fabric -loader_fabric=0.15.11 \ No newline at end of file +deps.fabric=0.15.11 \ No newline at end of file diff --git a/loader/fabric/core/src/main/resources/fabric.mod.json b/loader/fabric/core/src/main/resources/fabric.mod.json index d52bd437a..591e99aa8 100644 --- a/loader/fabric/core/src/main/resources/fabric.mod.json +++ b/loader/fabric/core/src/main/resources/fabric.mod.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "id": "automodpack-bootstrap", + "id": "automodpack_bootstrap", "version": "1.0.0", "name": "AutoModpack bootstrap", "description": "", diff --git a/loader/forge/fml40/gradle.properties b/loader/forge/fml40/gradle.properties index adf61db65..b6ad2295f 100644 --- a/loader/forge/fml40/gradle.properties +++ b/loader/forge/fml40/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.18.2 -yarn_mappings=1.18.2+build.4 -loom.platform=forge -loader_forge=40.2.10 \ No newline at end of file +deps.forge=1.18.2-40.3.10 \ No newline at end of file diff --git a/loader/forge/fml40/src/main/resources/META-INF/mods.toml b/loader/forge/fml40/src/main/resources/META-INF/mods.toml index 9ba75c843..a8fef7558 100644 --- a/loader/forge/fml40/src/main/resources/META-INF/mods.toml +++ b/loader/forge/fml40/src/main/resources/META-INF/mods.toml @@ -3,7 +3,7 @@ loaderVersion = "[1,)" license = "LGPLv3" [[mods]] -modId = "automodpack-bootstrap" +modId = "automodpack_bootstrap" version = "1.0.0" displayName = "AutoModpack bootstrap" displayURL = "https://modrinth.com/mod/automodpack" diff --git a/loader/forge/fml47/gradle.properties b/loader/forge/fml47/gradle.properties index 5371a7bc7..bde194594 100644 --- a/loader/forge/fml47/gradle.properties +++ b/loader/forge/fml47/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=forge -loader_forge=47.1.3 \ No newline at end of file +deps.forge=1.20.1-47.1.3 diff --git a/loader/forge/fml47/src/main/resources/META-INF/mods.toml b/loader/forge/fml47/src/main/resources/META-INF/mods.toml index 9ba75c843..a8fef7558 100644 --- a/loader/forge/fml47/src/main/resources/META-INF/mods.toml +++ b/loader/forge/fml47/src/main/resources/META-INF/mods.toml @@ -3,7 +3,7 @@ loaderVersion = "[1,)" license = "LGPLv3" [[mods]] -modId = "automodpack-bootstrap" +modId = "automodpack_bootstrap" version = "1.0.0" displayName = "AutoModpack bootstrap" displayURL = "https://modrinth.com/mod/automodpack" diff --git a/loader/loader-core.gradle.kts b/loader/loader-core.gradle.kts index 8b88d1d45..60824d979 100644 --- a/loader/loader-core.gradle.kts +++ b/loader/loader-core.gradle.kts @@ -3,9 +3,9 @@ plugins { } base { - archivesName = property("mod_id") as String + "-" + project.name + archivesName = property("mod.id") as String + "-" + project.name version = property("mod_version") as String - group = property("mod_group") as String + group = property("mod.group") as String } repositories { @@ -17,15 +17,14 @@ dependencies { // our needed dependencies implementation("com.google.code.gson:gson:2.10.1") - implementation("org.apache.logging.log4j:log4j-core:2.20.0") + implementation("org.apache.logging.log4j:log4j-core:2.19.0") implementation("org.tomlj:tomlj:1.1.1") } -java { - // leave it on java 17 to be compatible with older versions and we dont really need 21 there anyway +java { // leave it on java 17 to be compatible with older versions and we dont really need 21 there anyway sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 - + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) withSourcesJar() } diff --git a/loader/loader-fabric-core.gradle.kts b/loader/loader-fabric-core.gradle.kts index 273276445..8f0a1c3a0 100644 --- a/loader/loader-fabric-core.gradle.kts +++ b/loader/loader-fabric-core.gradle.kts @@ -2,13 +2,13 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar plugins { java - id("com.github.johnrengelman.shadow") + id("com.gradleup.shadow") } base { - archivesName = property("mod_id") as String + "-" + project.name + archivesName = property("mod.id") as String + "-" + project.name version = property("mod_version") as String - group = property("mod_group") as String + group = property("mod.group") as String } repositories { @@ -23,14 +23,14 @@ dependencies { compileOnly(project(":loader-fabric-16")) compileOnly("com.google.code.gson:gson:2.10.1") - compileOnly("org.apache.logging.log4j:log4j-core:2.20.0") + compileOnly("org.apache.logging.log4j:log4j-core:2.8.1") implementation("org.tomlj:tomlj:1.1.1") implementation("org.bouncycastle:bcpkix-jdk18on:1.80") implementation("com.github.luben:zstd-jni:1.5.7-3") - implementation("org.apache.httpcomponents.client5:httpclient5:5.4.4") + implementation("org.apache.httpcomponents.client5:httpclient5:5.5") - compileOnly("net.fabricmc:fabric-loader:${property("loader_fabric")}") + compileOnly("net.fabricmc:fabric-loader:${property("deps.fabric")}") } configurations { @@ -82,7 +82,7 @@ tasks.named("shadowJar") { java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 - + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) withSourcesJar() } diff --git a/loader/loader-fabric.gradle.kts b/loader/loader-fabric.gradle.kts index 50d47b0dc..8c58c3c38 100644 --- a/loader/loader-fabric.gradle.kts +++ b/loader/loader-fabric.gradle.kts @@ -1,21 +1,11 @@ -import net.fabricmc.loom.task.RemapJarTask - plugins { - id("dev.architectury.loom") - id("com.github.johnrengelman.shadow") -} - -val loader = property("loom.platform") as String -var mcVer = (property("minecraft_version") as String).replace(".", "").toInt() -// make from 3 char release 4 char release e.g. 1.21 -> 1.21.0 == 1210 -if (mcVer < 1000) { - mcVer *= 10 + java } base { - archivesName = property("mod_id") as String + "-" + project.name + archivesName = property("mod.id") as String + "-" + project.name version = property("mod_version") as String - group = property("mod_group") as String + group = property("mod.group") as String } repositories { @@ -28,27 +18,20 @@ dependencies { compileOnly(project(":core")) compileOnly(project(":loader-core")) - minecraft("com.mojang:minecraft:${property("minecraft_version")}") - mappings(loom.officialMojangMappings()) // We dont really use minecraft code there so we can use mojang mappings - compileOnly("com.google.code.gson:gson:2.10.1") compileOnly("org.apache.logging.log4j:log4j-core:2.20.0") implementation("org.tomlj:tomlj:1.1.1") - modImplementation("net.fabricmc:fabric-loader:${property("loader_fabric")}") + implementation("net.fabricmc:fabric-loader:${property("deps.fabric")}") } java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 - + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) withSourcesJar() } tasks.withType { options.encoding = "UTF-8" } - -tasks.named("remapJar") { - isEnabled = false -} \ No newline at end of file diff --git a/loader/loader-forge.gradle.kts b/loader/loader-forge.gradle.kts index f4de4a140..a6399dfbd 100644 --- a/loader/loader-forge.gradle.kts +++ b/loader/loader-forge.gradle.kts @@ -1,51 +1,44 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import net.fabricmc.loom.task.RemapJarTask plugins { - id("dev.architectury.loom") - id("com.github.johnrengelman.shadow") -} - -val loader = property("loom.platform") as String -var mcVer = (property("minecraft_version") as String).replace(".", "").toInt() -// make from 3 char release 4 char release e.g. 1.21 -> 1.21.0 == 1210 -if (mcVer < 1000) { - mcVer *= 10 + java + id("net.neoforged.moddev.legacyforge") + id("com.gradleup.shadow") } base { - archivesName = property("mod_id") as String + "-" + project.name + archivesName = property("mod.id") as String + "-" + project.name version = property("mod_version") as String - group = property("mod_group") as String + group = property("mod.group") as String } -repositories { - mavenCentral() - maven { url = uri("https://maven.fabricmc.net/") } - maven { url = uri("https://maven.neoforged.net/releases") } - maven { url = uri("https://files.minecraftforge.net/maven/") } - maven { url = uri("https://libraries.minecraft.net/") } +legacyForge { + version = property("deps.forge") as String } dependencies { compileOnly(project(":core")) compileOnly(project(":loader-core")) - minecraft("com.mojang:minecraft:${property("minecraft_version")}") - mappings(loom.officialMojangMappings()) // We dont really use minecraft code there so we can use mojang mappings - compileOnly("com.google.code.gson:gson:2.10.1") - compileOnly("org.apache.logging.log4j:log4j-core:2.20.0") + compileOnly("org.apache.logging.log4j:log4j-core:2.8.1") implementation("org.tomlj:tomlj:1.1.1") implementation("org.bouncycastle:bcpkix-jdk18on:1.80") implementation("com.github.luben:zstd-jni:1.5.7-3") - implementation("org.apache.httpcomponents.client5:httpclient5:5.4.4") + implementation("org.apache.httpcomponents.client5:httpclient5:5.5") +} - if (project.name.contains("neoforge")) { - "neoForge"("net.neoforged:neoforge:${property("loader_neoforge")}") - } else { - "forge"("net.minecraftforge:forge:${property("minecraft_version")}-${property("loader_forge")}") +tasks { + processResources { + exclude("**/fabric.mod.json", "**/automodpack.accesswidener") + } + + register("buildAndCollect") { + group = "build" + from(jar.map { it.archiveFile }) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod_version")}")) + dependsOn("build") } } @@ -92,14 +85,9 @@ tasks.named("shadowJar") { } java { - if (mcVer >= 1206) { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 - } else { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } - + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) withSourcesJar() } @@ -107,15 +95,6 @@ tasks.withType { options.encoding = "UTF-8" } -tasks.named("remapJar") { - isEnabled = false - -} - -tasks.named("jar") { - isEnabled = false -} - tasks.named("assemble") { dependsOn("shadowJar") } \ No newline at end of file diff --git a/loader/loader-neoforge.gradle.kts b/loader/loader-neoforge.gradle.kts new file mode 100644 index 000000000..955b9c902 --- /dev/null +++ b/loader/loader-neoforge.gradle.kts @@ -0,0 +1,100 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + java + id("net.neoforged.moddev") + id("com.gradleup.shadow") +} + +base { + archivesName = property("mod.id") as String + "-" + project.name + version = property("mod_version") as String + group = property("mod.group") as String +} + +neoForge { + version = property("deps.neoforge") as String +} + +dependencies { + compileOnly(project(":core")) + compileOnly(project(":loader-core")) + + compileOnly("com.google.code.gson:gson:2.10.1") + compileOnly("org.apache.logging.log4j:log4j-core:2.8.1") + + implementation("org.tomlj:tomlj:1.1.1") + implementation("org.bouncycastle:bcpkix-jdk18on:1.80") + implementation("com.github.luben:zstd-jni:1.5.7-3") + implementation("org.apache.httpcomponents.client5:httpclient5:5.5") +} + +tasks { + processResources { + exclude("**/fabric.mod.json", "**/automodpack.accesswidener") + } + + register("buildAndCollect") { + group = "build" + from(jar.map { it.archiveFile }) + into(rootProject.layout.buildDirectory.file("libs/${project.property("mod_version")}")) + dependsOn("build") + } +} + +configurations { + create("shadowImplementation") { + extendsFrom(configurations.getByName("implementation")) + isCanBeResolved = true + } +} + +tasks.named("shadowJar") { + archiveClassifier.set("") + + from(project(":core").sourceSets.main.get().output) + from(project(":loader-core").sourceSets.main.get().output) + + // Include the tomlj dependency in the shadow jar + configurations = listOf(project.configurations.getByName("shadowImplementation")) + + val reloc = "am_libs" + relocate("org.antlr", "${reloc}.org.antlr") + relocate("org.tomlj", "${reloc}.org.tomlj") + relocate("org.apache.hc", "${reloc}.org.apache.hc") + relocate("org.checkerframework", "${reloc}.org.checkerframework") + relocate("org.slf4j", "${reloc}.org.slf4j") +// relocate("com.github.luben", "${reloc}.com.github.luben") // cant relocate - natives + relocate("org.bouncycastle", "${reloc}.org.bouncycastle") + + if (project.name.contains("neoforge")) { + relocate("pl.skidam.automodpack_loader_core_neoforge", "pl.skidam.automodpack_loader_core") + } else { + relocate("pl.skidam.automodpack_loader_core_forge", "pl.skidam.automodpack_loader_core") + } + + exclude("pl/skidam/automodpack_loader_core/loader/LoaderManager.class") + exclude("pl/skidam/automodpack_loader_core/mods/ModpackLoader.class") + exclude("log4j2.xml") + + manifest { + attributes["AutoModpack-Version"] = version + } + + mergeServiceFiles() +} + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) + withSourcesJar() +} + +tasks.withType { + options.encoding = "UTF-8" +} + +tasks.named("assemble") { + dependsOn("shadowJar") +} \ No newline at end of file diff --git a/loader/neoforge/fml2/gradle.properties b/loader/neoforge/fml2/gradle.properties index b55507b93..5cf92ca53 100644 --- a/loader/neoforge/fml2/gradle.properties +++ b/loader/neoforge/fml2/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.20.4 -yarn_mappings=1.20.4+build.3 -loom.platform=neoforge -loader_neoforge=20.4.237 \ No newline at end of file +deps.neoforge=20.4.248 \ No newline at end of file diff --git a/loader/neoforge/fml2/src/main/resources/META-INF/mods.toml b/loader/neoforge/fml2/src/main/resources/META-INF/mods.toml index 9ba75c843..a8fef7558 100644 --- a/loader/neoforge/fml2/src/main/resources/META-INF/mods.toml +++ b/loader/neoforge/fml2/src/main/resources/META-INF/mods.toml @@ -3,7 +3,7 @@ loaderVersion = "[1,)" license = "LGPLv3" [[mods]] -modId = "automodpack-bootstrap" +modId = "automodpack_bootstrap" version = "1.0.0" displayName = "AutoModpack bootstrap" displayURL = "https://modrinth.com/mod/automodpack" diff --git a/loader/neoforge/fml4/gradle.properties b/loader/neoforge/fml4/gradle.properties index 0574b0042..cc919d069 100644 --- a/loader/neoforge/fml4/gradle.properties +++ b/loader/neoforge/fml4/gradle.properties @@ -1,4 +1 @@ -minecraft_version=1.21.1 -yarn_mappings=1.21.1+build.3 -loom.platform=neoforge -loader_neoforge=21.1.6 \ No newline at end of file +deps.neoforge=21.1.182 \ No newline at end of file diff --git a/loader/neoforge/fml4/src/main/resources/META-INF/neoforge.mods.toml b/loader/neoforge/fml4/src/main/resources/META-INF/neoforge.mods.toml index 9ba75c843..a8fef7558 100644 --- a/loader/neoforge/fml4/src/main/resources/META-INF/neoforge.mods.toml +++ b/loader/neoforge/fml4/src/main/resources/META-INF/neoforge.mods.toml @@ -3,7 +3,7 @@ loaderVersion = "[1,)" license = "LGPLv3" [[mods]] -modId = "automodpack-bootstrap" +modId = "automodpack_bootstrap" version = "1.0.0" displayName = "AutoModpack bootstrap" displayURL = "https://modrinth.com/mod/automodpack" diff --git a/settings.gradle.kts b/settings.gradle.kts index cf90ae829..15dfb351f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,23 +1,27 @@ pluginManagement { repositories { + mavenLocal() mavenCentral() gradlePluginPortal() - maven { url = uri("https://maven.architectury.dev/") } - maven { url = uri("https://maven.fabricmc.net/") } - maven { url = uri("https://maven.neoforged.net/releases") } - maven { url = uri("https://files.minecraftforge.net/maven/") } - mavenLocal() + maven("https://maven.architectury.dev/") { name = "Architectury" } + maven("https://maven.fabricmc.net/") { name = "Fabric" } + maven("https://maven.neoforged.net/releases/") { name = "NeoForged" } + maven("https://maven.kikugie.dev/snapshots") { name = "KikuGie" } } } plugins { - id("com.github.johnrengelman.shadow") version "8.1.1" apply false - id("dev.architectury.loom") version "1.9-SNAPSHOT" apply false // with 1.10 wait for remap fixes on neo/forge - id("dev.kikugie.stonecutter") version "0.6" + // For some reason, this plugin is crucial - do not remove + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" + id("dev.kikugie.stonecutter") version "0.7+" } include(":core") +fun getProperty(key: String): String? { + return settings.extra[key] as? String +} + val coreModules = getProperty("core_modules")!!.split(',').map { it.trim() } coreModules.forEach { module -> @@ -30,36 +34,29 @@ coreModules.forEach { module -> "core" -> project.buildFileName = "../loader-core.gradle.kts" "fabric-core" -> project.buildFileName = "../../loader-fabric-core.gradle.kts" "fabric-15", "fabric-16" -> project.buildFileName = "../../loader-fabric.gradle.kts" - "forge-fml40", "forge-fml47", "neoforge-fml2", "neoforge-fml4" -> project.buildFileName = "../../loader-forge.gradle.kts" + "forge-fml40", "forge-fml47" -> project.buildFileName = "../../loader-forge.gradle.kts" + "neoforge-fml2", "neoforge-fml4" -> project.buildFileName = "../../loader-neoforge.gradle.kts" } } -fun getProperty(key: String): String? { - return settings.extra[key] as? String -} - -fun getVersions(key: String): Set { - return getProperty(key)!!.split(',').map { it.trim() }.toSet() -} - -val versions = mapOf( - "forge" to getVersions("forge_versions"), - "fabric" to getVersions("fabric_versions"), - "neoforge" to getVersions("neoforge_versions") -) - -val sharedVersions = versions.map { entry -> - val loader = entry.key - entry.value.map { "$it-$loader" } -}.flatten().toSet() - stonecutter { - kotlinController = true - centralScript = "build.gradle.kts" - - shared { - versions(sharedVersions) + create(rootProject) { + fun match(version: String, vararg loaders: String) = loaders + .forEach { vers("$version-$it", version).buildscript = "build.$it.gradle.kts" } + + // Configure your targets here! + match("1.21.6", "fabric", "neoforge") + match("1.21.5", "fabric", "neoforge") + match("1.21.4", "fabric", "neoforge") + match("1.21.3", "fabric", "neoforge") + match("1.21.1", "fabric", "neoforge") + match("1.20.6", "fabric", "neoforge") + match("1.20.4", "fabric", "neoforge") + match("1.20.1", "fabric", "forge") + match("1.19.4", "fabric", "forge") + match("1.19.2", "fabric", "forge") + match("1.18.2", "fabric", "forge") } - - create(rootProject) } + +rootProject.name = "AutoModpack" \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/client/ScreenImpl.java b/src/main/java/pl/skidam/automodpack/client/ScreenImpl.java index 62fdff775..08b2e45cb 100644 --- a/src/main/java/pl/skidam/automodpack/client/ScreenImpl.java +++ b/src/main/java/pl/skidam/automodpack/client/ScreenImpl.java @@ -1,28 +1,22 @@ package pl.skidam.automodpack.client; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.TitleScreen; -import net.minecraft.util.Util; import pl.skidam.automodpack.client.ui.*; import pl.skidam.automodpack_loader_core.client.Changelogs; import pl.skidam.automodpack_loader_core.client.ModpackUpdater; import pl.skidam.automodpack_loader_core.screen.ScreenService; import pl.skidam.automodpack_loader_core.utils.DownloadManager; import pl.skidam.automodpack_loader_core.utils.FetchManager; -import pl.skidam.automodpack_loader_core.utils.SelectionManager; import pl.skidam.automodpack_loader_core.utils.UpdateType; import java.nio.file.Path; import java.util.Optional; +import net.minecraft.Util; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.TitleScreen; public class ScreenImpl implements ScreenService { - @Override - public void downloadselection(Object... args) { - Screens.downloadselection(args[0], args[1]); - } - @Override public void download(Object... args) { Screens.download(args[0], args[1]); @@ -45,7 +39,7 @@ public void restart(Object... args) { @Override public void danger(Object... args) { - Screens.danger(args[0], args[1], args[2]); + Screens.danger(args[0], args[1]); } @Override @@ -81,17 +75,14 @@ public Optional getScreen() { private static class Screens { private static Screen getScreen() { - return MinecraftClient.getInstance().currentScreen; + return Minecraft.getInstance().screen; } public static void setScreen(Screen screen) { // required for forge to handle it properly - Util.getMainWorkerExecutor().execute(() -> MinecraftClient.getInstance().execute(() -> MinecraftClient.getInstance().setScreen(screen))); - - } - public static void downloadselection(Object parent, Object modpackUpdaterInstance) { - Screens.setScreen(new DownloadSelectionScreen((Screen) parent, (ModpackUpdater) modpackUpdaterInstance)); + Util.backgroundExecutor().execute(() -> Minecraft.getInstance().execute(() -> Minecraft.getInstance().setScreen(screen))); } + public static void download(Object downloadManager, Object header) { Screens.setScreen(new DownloadScreen((DownloadManager) downloadManager, (String) header)); } @@ -103,20 +94,23 @@ public static void fetch(Object fetchManager) { public static void changelog(Object parent, Object modpackDir, Object changelog) { Screens.setScreen(new ChangelogScreen((Screen) parent, (Path) modpackDir, (Changelogs) changelog)); } + public static void restart(Object modpackDir, Object updateType, Object changelogs) { Screens.setScreen(new RestartScreen((Path) modpackDir, (UpdateType) updateType, (Changelogs) changelogs)); } - //only updated danger screen for selection Manager - public static void danger(Object parent, Object modpackUpdaterInstance, Object selectionManagerInstance) { - Screens.setScreen(new DangerScreen((Screen) parent, (ModpackUpdater) modpackUpdaterInstance, (SelectionManager) selectionManagerInstance)); + + public static void danger(Object parent, Object modpackUpdaterInstance) { + Screens.setScreen(new DangerScreen((Screen) parent, (ModpackUpdater) modpackUpdaterInstance)); } - // changed error Screen to ne Errors + public static void error(String... errors) { Screens.setScreen(new ErrorScreen(errors)); } + public static void title() { Screens.setScreen(new TitleScreen()); } + public static void menu() { // Screens.setScreen(new MenuScreen()); } diff --git a/src/main/java/pl/skidam/automodpack/client/audio/AudioManager.java b/src/main/java/pl/skidam/automodpack/client/audio/AudioManager.java index ea478e461..631ea2985 100644 --- a/src/main/java/pl/skidam/automodpack/client/audio/AudioManager.java +++ b/src/main/java/pl/skidam/automodpack/client/audio/AudioManager.java @@ -1,44 +1,43 @@ package pl.skidam.automodpack.client.audio; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.sound.SoundManager; -import net.minecraft.sound.SoundEvent; -import net.minecraft.util.Identifier; import java.util.function.Supplier; -import pl.skidam.automodpack.init.Common; +/*? if >=1.19.3 && !forge {*/ +import net.minecraft.core.registries.BuiltInRegistries; +/*?}*/ +import pl.skidam.automodpack.init.Common; +import net.minecraft.client.Minecraft; +import net.minecraft.client.sounds.SoundManager; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; /*? if neoforge {*/ import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.registries.DeferredRegister; import static pl.skidam.automodpack_core.GlobalVariables.MOD_ID; -/*?} elif forge {*/ +/*?} else if forge {*/ /*import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import static pl.skidam.automodpack_core.GlobalVariables.MOD_ID; *//*?} else {*/ /*/^? if >=1.19.3 {^/ -import net.minecraft.registry.Registry; -/^?} else {^/ -/^import net.minecraft.util.registry.Registry; +import net.minecraft.core.Registry; + /^?} else {^/ +/^import net.minecraft.core.Registry; ^//^?}^/ *//*?}*/ -/*? if >=1.19.3 && !forge {*/ -import net.minecraft.registry.Registries; -/*?}*/ - public class AudioManager { private static CustomSoundInstance SOUND_INSTANCE; private static SoundManager soundManager; private static boolean playing = false; - private static final Identifier WAITING_MUSIC_ID = Common.id("waiting_music"); + private static final ResourceLocation WAITING_MUSIC_ID = Common.id("waiting_music"); -/*? if >=1.19.3 {*/ - public static final SoundEvent WAITING_MUSIC_EVENT = SoundEvent.of(WAITING_MUSIC_ID); -/*?} else {*/ + /*? if >= 1.19.3 {*/ + public static final SoundEvent WAITING_MUSIC_EVENT = SoundEvent.createVariableRangeEvent(WAITING_MUSIC_ID); + /*?} else {*/ /*private static final SoundEvent WAITING_MUSIC_EVENT = new SoundEvent(WAITING_MUSIC_ID); *//*?}*/ @@ -54,23 +53,23 @@ public class AudioManager { /*? if neoforge {*/ public AudioManager(IEventBus eventBus) { - DeferredRegister SOUND_REGISTER = DeferredRegister.create(Registries.SOUND_EVENT, MOD_ID); + DeferredRegister SOUND_REGISTER = DeferredRegister.create(BuiltInRegistries.SOUND_EVENT, MOD_ID); SOUND_REGISTER.register(eventBus); WAITING_MUSIC = SOUND_REGISTER.register(WAITING_MUSIC_ID.getPath(),()-> WAITING_MUSIC_EVENT); } /*?}*/ -/*? if fabric {*/ + /*? if fabric {*/ /*public AudioManager() { SoundEvent waiting_music = register(); WAITING_MUSIC = () -> waiting_music; } private SoundEvent register() { - Identifier id = Common.id("waiting_music"); + ResourceLocation id = Common.id("waiting_music"); /^? if >=1.19.3 {^/ - Registry register = Registries.SOUND_EVENT; -/^?} else {^/ + Registry register = BuiltInRegistries.SOUND_EVENT; + /^?} else {^/ /^Registry register = Registry.SOUND_EVENT; ^//^?}^/ @@ -85,7 +84,7 @@ public static void playMusic() { SOUND_INSTANCE = new CustomSoundInstance(WAITING_MUSIC); } - getSoundManager().stopAll(); + getSoundManager().stop(); getSoundManager().play(SOUND_INSTANCE); playing = true; } @@ -93,13 +92,13 @@ public static void playMusic() { public static void stopMusic() { if (!playing || SOUND_INSTANCE == null) return; - getSoundManager().stopAll(); + getSoundManager().stop(); playing = false; } private static SoundManager getSoundManager() { if(soundManager == null) { - soundManager = MinecraftClient.getInstance().getSoundManager(); + soundManager = Minecraft.getInstance().getSoundManager(); } return soundManager; } diff --git a/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java b/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java index 6ea9ea881..5f871c7fd 100644 --- a/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java +++ b/src/main/java/pl/skidam/automodpack/client/audio/CustomSoundInstance.java @@ -1,26 +1,24 @@ package pl.skidam.automodpack.client.audio; -import net.minecraft.client.sound.AbstractSoundInstance; -import net.minecraft.sound.SoundCategory; -import net.minecraft.sound.SoundEvent; - import java.util.function.Supplier; - +import net.minecraft.client.resources.sounds.AbstractSoundInstance; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; /*? if >=1.19.1 {*/ -import net.minecraft.util.math.random.Random; +import net.minecraft.util.RandomSource; /*?}*/ public class CustomSoundInstance extends AbstractSoundInstance { public CustomSoundInstance(Supplier event) { /*? if >=1.21.2 {*/ - /*super(event.get().id(), SoundCategory.MASTER, Random.create()); - *//*?} elif >=1.19.1 {*/ - super(event.get().getId(), SoundCategory.MASTER, Random.create()); - /*?} else {*/ - /*super(event.get().getId(), SoundCategory.MASTER); + super(event.get().location(), SoundSource.MUSIC, RandomSource.create()); + /*?} else if >=1.19.1 {*/ + /*super(event.get().getLocation(), SoundSource.MUSIC, RandomSource.create()); + *//*?} else {*/ + /*super(event.get().getLocation(), SoundSource.MUSIC); *//*?}*/ - this.attenuationType = AttenuationType.NONE; + this.attenuation = Attenuation.NONE; } @Override @@ -34,7 +32,7 @@ public float getPitch() { } @Override - public boolean isRepeatable() { + public boolean isLooping() { return true; } } \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java index f0d63e985..a20a6fc1c 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ChangelogScreen.java @@ -1,9 +1,5 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.util.Util; import pl.skidam.automodpack_loader_core.client.Changelogs; import pl.skidam.automodpack.client.audio.AudioManager; import pl.skidam.automodpack.client.ui.versioned.VersionedText; @@ -18,6 +14,12 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import net.minecraft.Util; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; public class ChangelogScreen extends VersionedScreen { private final Screen parent; @@ -25,9 +27,9 @@ public class ChangelogScreen extends VersionedScreen { private final Changelogs changelogs; private static Map formattedChanges; private ListEntryWidget listEntryWidget; - private TextFieldWidget searchField; - private ButtonWidget backButton; - private ButtonWidget openMainPageButton; + private EditBox searchField; + private Button backButton; + private Button openMainPageButton; public ChangelogScreen(Screen parent, Path modpackDir, Changelogs changelogs) { super(VersionedText.literal("ChangelogScreen")); @@ -48,37 +50,37 @@ protected void init() { initWidgets(); - this.addDrawableChild(this.listEntryWidget); - this.addDrawableChild(this.searchField); - this.addDrawableChild(this.backButton); - this.addDrawableChild(this.openMainPageButton); + this.addRenderableWidget(this.listEntryWidget); + this.addRenderableWidget(this.searchField); + this.addRenderableWidget(this.backButton); + this.addRenderableWidget(this.openMainPageButton); this.setInitialFocus(this.searchField); } private void initWidgets() { - this.listEntryWidget = new ListEntryWidget(formattedChanges, this.client, this.width, this.height, 48, this.height - 50, 20); // 38 + this.listEntryWidget = new ListEntryWidget(formattedChanges, this.minecraft, this.width, this.height, 48, this.height - 50, 20); // 38 - this.searchField = new TextFieldWidget(this.textRenderer, this.width / 2 - 100, 20, 200, 20, + this.searchField = new EditBox(this.font, this.width / 2 - 100, 20, 200, 20, VersionedText.literal("") ); - this.searchField.setChangedListener((textField) -> updateChangelogs()); // Update the changelogs display based on the search query + this.searchField.setResponder((textField) -> updateChangelogs()); // Update the changelogs display based on the search query this.backButton = buttonWidget(this.width / 2 - 140, this.height - 30, 140, 20, VersionedText.translatable("automodpack.back"), - button -> this.client.setScreen(this.parent) + button -> this.minecraft.setScreen(this.parent) ); this.openMainPageButton = buttonWidget(this.width / 2 + 20, this.height - 30, 140, 20, VersionedText.translatable("automodpack.changelog.openPage"), button -> { - ListEntry selectedEntry = listEntryWidget.getSelectedOrNull(); + ListEntry selectedEntry = listEntryWidget.getSelected(); if (selectedEntry == null) { return; } String mainPageUrl = selectedEntry.getMainPageUrl(); - Util.getOperatingSystem().open(mainPageUrl); + Util.getPlatform().openUri(mainPageUrl); } ); @@ -86,9 +88,9 @@ private void initWidgets() { @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - this.listEntryWidget.render(matrices, mouseX, mouseY, delta); + this.listEntryWidget.render(matrices.getContext(), mouseX, mouseY, delta); - ListEntry selectedEntry = listEntryWidget.getSelectedOrNull(); + ListEntry selectedEntry = listEntryWidget.getSelected(); if (selectedEntry != null) { this.openMainPageButton.active = selectedEntry.getMainPageUrl() != null; } else { @@ -109,37 +111,24 @@ private void drawSummaryOfChanges(VersionedMatrices matrices) { modpackContent = ConfigTools.loadModpackContent(optionalModpackContentFile.get()); } - int modsAdded = 0; - int modsRemoved = 0; if (modpackContent == null) return; - for (var changelog : changelogs.changesAddedList.entrySet()) { - String fileType = ModpackContentTools.getFileType(changelog.getKey(), modpackContent); - if (fileType.equals("mod")) { - modsAdded++; - } - } - - for (var changelog : changelogs.changesDeletedList.entrySet()) { - String fileType = ModpackContentTools.getFileType(changelog.getKey(), modpackContent); - if (fileType.equals("mod")) { - modsRemoved++; - } - } + int filesAdded = changelogs.changesAddedList.size(); + int filesRemoved = changelogs.changesDeletedList.size(); - String summary = "Mods + " + modsAdded + " | - " + modsRemoved; + String summary = "+ " + filesAdded + " | - " + filesRemoved; - drawCenteredTextWithShadow(matrices, textRenderer, VersionedText.literal(summary), this.width / 2, 5, 16777215); + drawCenteredTextWithShadow(matrices, font, VersionedText.literal(summary), this.width / 2, 5, TextColors.WHITE); } private void updateChangelogs() { // If the search field is empty, reset the changelogs to the original list - if (this.searchField.getText().isEmpty()) { + if (this.searchField.getValue().isEmpty()) { formattedChanges = reFormatChanges(); } else { // Filter the changelogs based on the search query using a case-insensitive search Map filteredChangelogs = new HashMap<>(); for (Map.Entry changelog : reFormatChanges().entrySet()) { - if (changelog.getKey().toLowerCase().contains(this.searchField.getText().toLowerCase())) { + if (changelog.getKey().toLowerCase().contains(this.searchField.getValue().toLowerCase())) { filteredChangelogs.put(changelog.getKey(), changelog.getValue()); } } @@ -148,17 +137,17 @@ private void updateChangelogs() { // remove method is only available in 1.17+ /*? if >=1.17 {*/ - this.remove(this.listEntryWidget); - this.remove(this.backButton); - this.remove(this.openMainPageButton); + this.removeWidget(this.listEntryWidget); + this.removeWidget(this.backButton); + this.removeWidget(this.openMainPageButton); /*?}*/ - this.listEntryWidget = new ListEntryWidget(formattedChanges, this.client, this.width, this.height, 48, this.height - 50, 20); // 38 + this.listEntryWidget = new ListEntryWidget(formattedChanges, this.minecraft, this.width, this.height, 48, this.height - 50, 20); // 38 - this.addDrawableChild(this.listEntryWidget); - this.addDrawableChild(this.searchField); - this.addDrawableChild(this.backButton); - this.addDrawableChild(this.openMainPageButton); + this.addRenderableWidget(this.listEntryWidget); + this.addRenderableWidget(this.searchField); + this.addRenderableWidget(this.backButton); + this.addRenderableWidget(this.openMainPageButton); } private Map reFormatChanges() { @@ -182,8 +171,8 @@ private Map reFormatChanges() { @Override public boolean shouldCloseOnEsc() { - assert this.client != null; - this.client.setScreen(this.parent); + assert this.minecraft != null; + this.minecraft.setScreen(this.parent); return false; } } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java index 6495ec7b0..b2ac9e950 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/DangerScreen.java @@ -1,32 +1,23 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.util.Formatting; -import net.minecraft.util.Util; import pl.skidam.automodpack_loader_core.client.ModpackUpdater; +import net.minecraft.ChatFormatting; +import net.minecraft.Util; +import net.minecraft.client.gui.screens.Screen; import pl.skidam.automodpack.client.audio.AudioManager; -import java.nio.file.Files; -import java.nio.file.Path; -import pl.skidam.automodpack_core.config.ConfigTools; -import pl.skidam.automodpack_core.config.Jsons; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; import pl.skidam.automodpack.client.ui.versioned.VersionedText; -import pl.skidam.automodpack_loader_core.client.ModpackUtils; - -import pl.skidam.automodpack_loader_core.utils.SelectionManager; public class DangerScreen extends VersionedScreen { private final Screen parent; private final ModpackUpdater modpackUpdaterInstance; - private final SelectionManager selectionManagerInstance; - public DangerScreen(Screen parent, ModpackUpdater modpackUpdaterInstance, SelectionManager selectionManagerInstance) { + public DangerScreen(Screen parent, ModpackUpdater modpackUpdaterInstance) { super(VersionedText.literal("DangerScreen")); this.parent = parent; this.modpackUpdaterInstance = modpackUpdaterInstance; - this.selectionManagerInstance = selectionManagerInstance; if (AudioManager.isMusicPlaying()) { AudioManager.stopMusic(); @@ -36,39 +27,22 @@ public DangerScreen(Screen parent, ModpackUpdater modpackUpdaterInstance, Select @Override protected void init() { super.init(); - assert this.client != null; + assert this.minecraft != null; - this.addDrawableChild(buttonWidget(this.width / 2 - 115, this.height / 2 + 50, 120, 20, VersionedText.translatable("automodpack.danger.cancel"), button -> { - this.client.setScreen(parent); + this.addRenderableWidget(buttonWidget(this.width / 2 - 115, this.height / 2 + 50, 120, 20, VersionedText.translatable("automodpack.danger.cancel"), button -> { + this.minecraft.setScreen(parent); })); - if (selectionManagerInstance != null) { - this.addDrawableChild(buttonWidget(this.width / 2 + 15, this.height / 2 + 50, 120, 20, VersionedText.translatable("automodpack.danger.selection").formatted(Formatting.BOLD), button -> { - this.client.setScreen(new DownloadSelectionScreen(parent, modpackUpdaterInstance)); - })); - } else { - this.addDrawableChild(buttonWidget(this.width / 2 + 15, this.height / 2 + 50, 120, 20, VersionedText.translatable("automodpack.danger.confirm").formatted(Formatting.BOLD), button -> { - Util.getMainWorkerExecutor().execute(modpackUpdaterInstance::startUpdate); - })); - } - Path serverConfigPath = ModpackUtils.getMinecraftPath().resolve("mods/automodpack/automodpack-server.json"); - if (Files.exists(serverConfigPath)) { - Jsons.ServerConfigFields serverConfig = ConfigTools.load(serverConfigPath, Jsons.ServerConfigFields.class); - - if (serverConfig != null && serverConfig.enableFullServerPack) { - this.addDrawableChild(buttonWidget(this.width / 2, this.height / 2 + 100, 160, 20, VersionedText.literal("automodpack.danger.fullserverpack").formatted(Formatting.RED), button -> { - SelectionManager.setSelectedPack("fullserver"); - Util.getMainWorkerExecutor().execute(modpackUpdaterInstance::startUpdate); - })); - } - } + this.addRenderableWidget(buttonWidget(this.width / 2 + 15, this.height / 2 + 50, 120, 20, VersionedText.translatable("automodpack.danger.confirm").withStyle(ChatFormatting.BOLD), button -> { + Util.backgroundExecutor().execute(modpackUpdaterInstance::startUpdate); + })); } @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger").formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger.description"), this.width / 2, this.height / 2 - 35, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.danger.secDescription"), this.width / 2, this.height / 2 - 25, 16777215); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.danger").withStyle(ChatFormatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.danger.description"), this.width / 2, this.height / 2 - 35, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.danger.secDescription"), this.width / 2, this.height / 2 - 25, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java index 33062112f..399869c81 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/DownloadScreen.java @@ -1,14 +1,14 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.Identifier; -import net.minecraft.util.Util; import pl.skidam.automodpack.init.Common; import pl.skidam.automodpack_loader_core.screen.ScreenManager; import pl.skidam.automodpack_loader_core.utils.DownloadManager; +import net.minecraft.ChatFormatting; +import net.minecraft.Util; +import net.minecraft.client.gui.components.Button; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; import pl.skidam.automodpack.client.audio.AudioManager; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; @@ -17,22 +17,19 @@ public class DownloadScreen extends VersionedScreen { - private static final Identifier PROGRESS_BAR_EMPTY_TEXTURE = Common.id("gui/progress-bar-empty.png"); - private static final Identifier PROGRESS_BAR_FULL_TEXTURE = Common.id("gui/progress-bar-full.png"); + private static final ResourceLocation PROGRESS_BAR_EMPTY_TEXTURE = Common.id("gui/progress-bar-empty.png"); + private static final ResourceLocation PROGRESS_BAR_FULL_TEXTURE = Common.id("gui/progress-bar-full.png"); private static final int PROGRESS_BAR_WIDTH = 250; private static final int PROGRESS_BAR_HEIGHT = 20; private final DownloadManager downloadManager; private final String header; private static long ticks = 0; - private ButtonWidget cancelButton; + private Button cancelButton; - // Temp save for the last download values -// private final Map mapOfFileStats = new HashMap<>(); // URL, Percentage of download private String lastStage = "-1"; private int lastPercentage = -1; private String lastSpeed = "-1"; private String lastETA = "-1"; - private float lastDownloadedScale = 0.0F; public DownloadScreen(DownloadManager downloadManager, String header) { super(VersionedText.literal("DownloadScreen")); @@ -46,27 +43,13 @@ protected void init() { initWidgets(); - this.addDrawableChild(cancelButton); + this.addRenderableWidget(cancelButton); - Util.getMainWorkerExecutor().execute(() -> { + Util.backgroundExecutor().execute(() -> { while (downloadManager != null && downloadManager.isRunning()) { - -// for (Map.Entry map : ModpackUpdater.downloadManager.downloadsInProgress.entrySet()) { -// mapOfFileStats.put(map.getKey(), ModpackUpdater.downloadManager.getPercentageOfFileSizeDownloaded(map.getKey()) + "%"); -// } -// -// for (Map.Entry map : mapOfFileStats.entrySet()) { -// if (!ModpackUpdater.downloadManager.downloadsInProgress.containsKey(map.getKey())) { -// mapOfFileStats.remove(map.getKey()); -// } -// } - - // TODO make it work better pls lastStage = downloadManager.getStage(); - lastPercentage = (int) downloadManager.getTotalPercentageOfFileSizeDownloaded(); - lastDownloadedScale = (float) (downloadManager.getTotalPercentageOfFileSizeDownloaded() * 0.01); - - lastSpeed = SpeedMeter.formatDownloadSpeedToMbps(downloadManager.getSpeedMeter().getCurrentSpeedInBytes()); + lastPercentage = downloadManager.getTotalPercentageOfFileSizeDownloaded(); + lastSpeed = SpeedMeter.formatDownloadSpeedToMbps(downloadManager.getSpeedMeter().getAverageSpeedOfLastFewSeconds(1)); lastETA = SpeedMeter.formatETAToSeconds(downloadManager.getSpeedMeter().getETAInSeconds()); } }); @@ -81,14 +64,14 @@ private void initWidgets() { ); } - private Text getStage() { + private Component getStage() { if (lastStage.equals("-1")) { return VersionedText.translatable("automodpack.download.calculating"); } return VersionedText.literal(lastStage); } - private Text getPercentage() { + private Component getPercentage() { if (lastPercentage == -1) { return VersionedText.translatable("automodpack.download.calculating"); } @@ -96,14 +79,14 @@ private Text getPercentage() { } - private Text getTotalDownloadSpeed() { + private Component getTotalDownloadSpeed() { if (lastSpeed.equals("-1")) { return VersionedText.translatable("automodpack.download.calculating"); } return VersionedText.literal(lastSpeed); } - private Text getTotalETA() { + private Component getTotalETA() { if (lastETA.equals("-1")) { return VersionedText.translatable("automodpack.download.calculating"); } @@ -111,43 +94,34 @@ private Text getTotalETA() { } private float getDownloadScale() { - return lastDownloadedScale; + return Math.max(0, Math.min(100, lastPercentage)) * 0.01F; // Convert the clamped percentage to a scale between 0.0f and 1.0f } private void drawDownloadingFiles(VersionedMatrices matrices) { float scale = 1.0F; int y = this.height / 2 - 90; - matrices.push(); + matrices.pushPose(); matrices.scale(scale, scale, scale); if (downloadManager != null && !downloadManager.downloadsInProgress.isEmpty()) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.download.downloading").formatted(Formatting.BOLD), this.width / 2, y, 16777215); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.download.downloading").withStyle(ChatFormatting.BOLD), this.width / 2, y, TextColors.WHITE); // Use a separate variable for the current y position int currentY = y + 15; synchronized (downloadManager.downloadsInProgress) { for (DownloadManager.DownloadData downloadData : downloadManager.downloadsInProgress.values()) { - String text = downloadData.getFileName(); - -// DownloadManager.DownloadData downloadData = map.getValue(); -// String percentage = mapOfFileStats.get(map.getKey()); -// -// if (percentage != null) { -// text += " " + percentage; -// } - - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.literal(text).formatted(Formatting.GRAY), (int) (this.width / 2 * scale), currentY, 16777215); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.literal(text), (int) ((float) this.width / 2 * scale), currentY, TextColors.GRAY); currentY += 10; } } } else { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.download.noFiles"), (int) (this.width / 2 * scale), y, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.wait").formatted(Formatting.BOLD), (int) (this.width / 2 * scale), y + 25, 16777215); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.download.noFiles"), (int) ((float) this.width / 2 * scale), y, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.wait").withStyle(ChatFormatting.BOLD), (int) ((float) this.width / 2 * scale), y + 25, TextColors.WHITE); } - matrices.pop(); + matrices.popPose(); } private void checkAndStartMusic() { @@ -167,16 +141,16 @@ private void checkAndStartMusic() { @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { drawDownloadingFiles(matrices); - MutableText titleText = VersionedText.literal(header).formatted(Formatting.BOLD); - drawCenteredTextWithShadow(matrices, this.textRenderer, titleText, this.width / 2, this.height / 2 - 110, 16777215); + MutableComponent titleText = VersionedText.literal(header).withStyle(ChatFormatting.BOLD); + drawCenteredTextWithShadow(matrices, this.font, titleText, this.width / 2, this.height / 2 - 110, TextColors.WHITE); if (downloadManager != null && downloadManager.isRunning()) { - MutableText percentage = (MutableText) this.getPercentage(); - MutableText stage = (MutableText) this.getStage(); - MutableText eta = (MutableText) this.getTotalETA(); - MutableText speed = (MutableText) this.getTotalDownloadSpeed(); - drawCenteredTextWithShadow(matrices, this.textRenderer, stage, this.width / 2, this.height / 2 - 10, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, eta, this.width / 2, this.height / 2 + 10, 16777215); + MutableComponent percentage = (MutableComponent) this.getPercentage(); + MutableComponent stage = (MutableComponent) this.getStage(); + MutableComponent eta = (MutableComponent) this.getTotalETA(); + MutableComponent speed = (MutableComponent) this.getTotalDownloadSpeed(); + drawCenteredTextWithShadow(matrices, this.font, stage, this.width / 2, this.height / 2 - 10, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, eta, this.width / 2, this.height / 2 + 10, TextColors.WHITE); // Render progress bar @@ -186,8 +160,8 @@ public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, drawTexture(PROGRESS_BAR_EMPTY_TEXTURE, matrices, progressX, progressY, 0, 0, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT); drawTexture(PROGRESS_BAR_FULL_TEXTURE, matrices, progressX, progressY, 0, 0, (int) (PROGRESS_BAR_WIDTH * getDownloadScale()), PROGRESS_BAR_HEIGHT, PROGRESS_BAR_WIDTH, PROGRESS_BAR_HEIGHT); - drawCenteredTextWithShadow(matrices, this.textRenderer, percentage, this.width / 2, this.height / 2 + 36, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, speed, this.width / 2, this.height / 2 + 60, 16777215); + drawCenteredTextWithShadow(matrices, this.font, percentage, this.width / 2, this.height / 2 + 36, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, speed, this.width / 2, this.height / 2 + 60, TextColors.WHITE); checkAndStartMusic(); cancelButton.active = true; @@ -207,11 +181,7 @@ public void cancelDownload() { downloadManager.cancelAllAndShutdown(); } - // TODO delete files that were downloaded - // we will use the same method as to modpacks manager - new ScreenManager().title(); - } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java index 52c0b947c..4a6f3f5f9 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ErrorScreen.java @@ -1,7 +1,7 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.util.Formatting; +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.Button; import pl.skidam.automodpack.client.audio.AudioManager; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; @@ -10,7 +10,7 @@ public class ErrorScreen extends VersionedScreen { private final String[] errorMessages; - private ButtonWidget backButton; + private Button backButton; public ErrorScreen(String... errorMessages) { super(VersionedText.literal("ErrorScreen")); @@ -27,21 +27,21 @@ protected void init() { initWidgets(); - this.addDrawableChild(backButton); + this.addRenderableWidget(backButton); } private void initWidgets() { backButton = buttonWidget(this.width / 2 - 100, this.height / 2 + 50, 200, 20, VersionedText.translatable("automodpack.back"), button -> { - assert client != null; - client.setScreen(null); + assert minecraft != null; + minecraft.setScreen(null); }); } @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.literal("[AutoModpack] Error! ").append(VersionedText.translatable("automodpack.error").formatted(Formatting.RED)), this.width / 2, this.height / 2 - 50, 16777215); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.literal("[AutoModpack] Error! ").append(VersionedText.translatable("automodpack.error").withStyle(ChatFormatting.RED)), this.width / 2, this.height / 2 - 50, TextColors.WHITE); for (int i = 0; i < this.errorMessages.length; i++) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable(this.errorMessages[i]), this.width / 2, this.height / 2 - 20 + i * 12, 14687790); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable(this.errorMessages[i]), this.width / 2, this.height / 2 - 20 + i * 12, 14687790); } } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java index 8b00fcb57..ede95d699 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/FetchScreen.java @@ -1,8 +1,7 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.util.Formatting; - +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.Button; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; import pl.skidam.automodpack.client.ui.versioned.VersionedText; @@ -11,7 +10,7 @@ public class FetchScreen extends VersionedScreen { - private ButtonWidget cancelButton; + private Button cancelButton; private final FetchManager fetchManager; public FetchScreen(FetchManager fetchManager) { @@ -25,7 +24,7 @@ protected void init() { initWidgets(); - this.addDrawableChild(cancelButton); + this.addRenderableWidget(cancelButton); } private void initWidgets() { @@ -51,9 +50,9 @@ public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, } // Fetching direct url's from Modrinth and CurseForge. - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.fetch").formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.wait"), this.width / 2, this.height / 2 - 48, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.fetch.found", getFetchesDone()), this.width / 2, this.height / 2 - 30, 16777215); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.fetch").withStyle(ChatFormatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.wait"), this.width / 2, this.height / 2 - 48, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.fetch.found", getFetchesDone()), this.width / 2, this.height / 2 - 30, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java index a569654ef..88b052e5a 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/RestartScreen.java @@ -1,11 +1,10 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.util.Formatting; import pl.skidam.automodpack.client.audio.AudioManager; import java.nio.file.Path; - +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.Button; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; import pl.skidam.automodpack.client.ui.versioned.VersionedText; @@ -17,9 +16,9 @@ public class RestartScreen extends VersionedScreen { private final Path modpackDir; private final UpdateType updateType; private final Changelogs changelogs; - private static ButtonWidget cancelButton; - private static ButtonWidget restartButton; - private static ButtonWidget changelogsButton; + private static Button cancelButton; + private static Button restartButton; + private static Button changelogsButton; public RestartScreen(Path modpackDir, UpdateType updateType, Changelogs changelogs) { super(VersionedText.literal("RestartScreen")); @@ -38,9 +37,9 @@ protected void init() { initWidgets(); - this.addDrawableChild(cancelButton); - this.addDrawableChild(restartButton); - this.addDrawableChild(changelogsButton); + this.addRenderableWidget(cancelButton); + this.addRenderableWidget(restartButton); + this.addRenderableWidget(changelogsButton); if (changelogs == null || changelogs.changesAddedList.isEmpty() && changelogs.changesDeletedList.isEmpty()) { changelogsButton.active = false; @@ -48,12 +47,12 @@ protected void init() { } public void initWidgets() { - assert this.client != null; + assert this.minecraft != null; cancelButton = buttonWidget(this.width / 2 - 155, this.height / 2 + 50, 150, 20, VersionedText.translatable("automodpack.restart.cancel"), button -> { - this.client.setScreen(null); + this.minecraft.setScreen(null); }); - restartButton = buttonWidget(this.width / 2 + 5, this.height / 2 + 50, 150, 20, VersionedText.translatable("automodpack.restart.confirm").formatted(Formatting.BOLD), button -> { + restartButton = buttonWidget(this.width / 2 + 5, this.height / 2 + 50, 150, 20, VersionedText.translatable("automodpack.restart.confirm").withStyle(ChatFormatting.BOLD), button -> { System.exit(0); }); @@ -64,9 +63,9 @@ public void initWidgets() { @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart." + updateType.toString()).formatted(Formatting.BOLD), this.width / 2, this.height / 2 - 60, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart.description"), this.width / 2, this.height / 2 - 35, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.restart.secDescription"), this.width / 2, this.height / 2 - 25, 16777215); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.restart." + updateType.toString()).withStyle(ChatFormatting.BOLD), this.width / 2, this.height / 2 - 60, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.restart.description"), this.width / 2, this.height / 2 - 35, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.restart.secDescription"), this.width / 2, this.height / 2 - 25, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/TextColors.java b/src/main/java/pl/skidam/automodpack/client/ui/TextColors.java new file mode 100644 index 000000000..b164b7d29 --- /dev/null +++ b/src/main/java/pl/skidam/automodpack/client/ui/TextColors.java @@ -0,0 +1,63 @@ +package pl.skidam.automodpack.client.ui; + +@SuppressWarnings("unused") +public class TextColors { + + // Contains constants for commonly used colors in 0xAARRGGBB format. + + /** + * Represents the color white, {@code 0xFFFFFFFF}. + */ + public static final int WHITE = -1; // previously 0xFFFFFF or 16777215 + /** + * Represents the color black, {@code 0xFF000000}. + */ + public static final int BLACK = -16777216; + /** + * Represents the color gray, {@code 0xFF808080}. + */ + public static final int GRAY = -8355712; + public static final int DARK_GRAY = -12566464; + /** + * Represents the color light gray, {@code 0xFFA0A0A0}. + */ + public static final int LIGHT_GRAY = -6250336; + /** + * Represents a variant of the color white, used so that the two alternate with + * each other, {@code 0xFFBABABA}. + */ + public static final int ALTERNATE_WHITE = -4539718; + /** + * Represents the color red, {@code 0xFFFF0000}. + */ + public static final int RED = -65536; + /** + * Represents the color light red, {@code 0xFFDF5050}. + */ + public static final int LIGHT_RED = -2142128; + /** + * Represents the color green, {@code 0xFF00FF00}. + */ + public static final int GREEN = -16711936; + /** + * Represents the color blue, {@code 0xFF0000FF}. + */ + public static final int BLUE = -16776961; + /** + * Represents the color yellow, {@code 0xFFFFFF00}. + */ + public static final int YELLOW = -256; + /** + * Represents the color light yellow, {@code 0xFFFFFF55}. + */ + public static final int LIGHT_YELLOW = -171; + /** + * Represents the color purple, {@code 0xFF500050}. + */ + public static final int PURPLE = -11534256; + /** + * Represents the color cyan, {@code 0xFF57FFE1}. + */ + public static final int CYAN = -11010079; + public static final int LIGHT_PINK = -13108; +} diff --git a/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java index 7d67c36ed..512f81138 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/ValidationScreen.java @@ -1,12 +1,11 @@ package pl.skidam.automodpack.client.ui; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.client.toast.SystemToast; -import net.minecraft.client.toast.Toast; -import net.minecraft.util.Formatting; - +import net.minecraft.ChatFormatting; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.toasts.SystemToast; +import net.minecraft.client.gui.components.toasts.Toast; +import net.minecraft.client.gui.screens.Screen; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; import pl.skidam.automodpack.client.ui.versioned.VersionedText; @@ -18,10 +17,10 @@ public class ValidationScreen extends VersionedScreen { private final Runnable validatedCallback; private final Runnable canceledCallback; private boolean validated = false; - private final Toast failedToast = new SystemToast(SystemToast.Type.PACK_LOAD_FAILURE, VersionedText.translatable("automodpack.validation.failed"), VersionedText.translatable("automodpack.retry")); - private TextFieldWidget textField; - private ButtonWidget backButton; - private ButtonWidget validateButton; + private final Toast failedToast = new SystemToast(SystemToast.SystemToastId.PACK_LOAD_FAILURE, VersionedText.translatable("automodpack.validation.failed"), VersionedText.translatable("automodpack.retry")); + private EditBox textField; + private Button backButton; + private Button validateButton; public ValidationScreen(Screen parent, String serverFingerprint, Runnable validatedCallback, Runnable canceledCallback) { @@ -38,15 +37,15 @@ protected void init() { initWidgets(); - this.addDrawableChild(this.textField); - this.addDrawableChild(this.backButton); - this.addDrawableChild(this.validateButton); + this.addRenderableWidget(this.textField); + this.addRenderableWidget(this.backButton); + this.addRenderableWidget(this.validateButton); this.setInitialFocus(this.textField); } public void initWidgets() { - assert this.client != null; - this.textField = new TextFieldWidget(this.textRenderer, this.width / 2 - 170, this.height / 2 - 20, 340, 20, + assert this.minecraft != null; + this.textField = new EditBox(this.font, this.width / 2 - 170, this.height / 2 - 20, 340, 20, VersionedText.literal("") ); this.textField.setMaxLength(64); // default is 30 which is too little @@ -54,7 +53,7 @@ public void initWidgets() { this.backButton = buttonWidget(this.width / 2 - 155, this.height / 2 + 50, 150, 20, VersionedText.translatable("automodpack.back"), button -> { - this.client.setScreen(parent); + this.minecraft.setScreen(parent); if (!this.validated) { this.canceledCallback.run(); } @@ -63,7 +62,7 @@ public void initWidgets() { this.validateButton = buttonWidget(this.width / 2 + 5, this.height / 2 + 50, 150, 20, VersionedText.translatable("automodpack.validation.run"), - button -> validate(textField.getText())); + button -> validate(textField.getValue())); } private void validate(String input) { @@ -71,28 +70,32 @@ private void validate(String input) { if (input.equals(serverFingerprint) || input.equals("I AM INCREDIBLY STUPID")) { validateButton.active = false; this.validated = true; - if (this.client != null) { - this.client.setScreen(parent); + if (this.minecraft != null) { + this.minecraft.setScreen(parent); } validatedCallback.run(); } else { GlobalVariables.LOGGER.error("Server fingerprint validation failed, try again"); - if (this.client != null) { - this.client.getToastManager().add(failedToast); + if (this.minecraft != null) { + /*? if > 1.21.1 {*/ + this.minecraft.getToastManager().addToast(failedToast); + /*?} else {*/ + /*this.minecraft.getToasts().addToast(failedToast); + *//*?}*/ } } } @Override public void versionedRender(VersionedMatrices matrices, int mouseX, int mouseY, float delta) { - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.text").formatted(Formatting.BOLD), - this.width / 2, this.height / 2 - 100, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.description"), - this.width / 2, this.height / 2 - 75, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.secDescription"), - this.width / 2, this.height / 2 - 65, 16777215); - drawCenteredTextWithShadow(matrices, this.textRenderer, VersionedText.translatable("automodpack.validation.thiDescription"), - this.width / 2, this.height / 2 - 55, 16777215); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.validation.text").withStyle(ChatFormatting.BOLD), + this.width / 2, this.height / 2 - 100, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.validation.description"), + this.width / 2, this.height / 2 - 75, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.validation.secDescription"), + this.width / 2, this.height / 2 - 65, TextColors.WHITE); + drawCenteredTextWithShadow(matrices, this.font, VersionedText.translatable("automodpack.validation.thiDescription"), + this.width / 2, this.height / 2 - 55, TextColors.WHITE); } @Override diff --git a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedCommandSource.java b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedCommandSource.java index 2887de7ab..be8a4ff40 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedCommandSource.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedCommandSource.java @@ -1,29 +1,27 @@ package pl.skidam.automodpack.client.ui.versioned; import com.mojang.brigadier.context.CommandContext; -import net.minecraft.entity.Entity; +import net.minecraft.commands.CommandSource; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.command.CommandOutput; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.Text; -import net.minecraft.util.math.Vec2f; -import net.minecraft.util.math.Vec3d; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; -public class VersionedCommandSource extends ServerCommandSource { +public class VersionedCommandSource extends CommandSourceStack { - public VersionedCommandSource(CommandOutput output, Vec3d pos, Vec2f rot, ServerWorld world, int level, String name, Text displayName, MinecraftServer server, @Nullable Entity entity) { + public VersionedCommandSource(CommandSource output, Vec3 pos, Vec2 rot, ServerLevel world, int level, String name, Component displayName, MinecraftServer server, @Nullable Entity entity) { super(output, pos, rot, world, level, name, displayName, server, entity); } -/*? if >=1.20 {*/ - public static void sendFeedback(CommandContext context, Text message, boolean broadcastToOps) { - context.getSource().sendFeedback(() -> message, broadcastToOps); - } -/*?} else {*/ - /*public static void sendFeedback(CommandContext context, Text message, boolean broadcastToOps) { - context.getSource().sendFeedback(message, broadcastToOps); + public static void sendFeedback(CommandContext context, Component message, boolean broadcastToOps) { + /*? if >=1.20 {*/ + context.getSource().sendSuccess(() -> message, broadcastToOps); + /*?} else {*/ + /*context.getSource().sendSuccess(message, broadcastToOps); + *//*?}*/ } -*//*?}*/ } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java index 82dee9503..c7ef8d638 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedMatrices.java @@ -1,30 +1,52 @@ package pl.skidam.automodpack.client.ui.versioned; /*? if >=1.20 {*/ -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.gui.GuiGraphics; /*?} else {*/ -/*import net.minecraft.client.util.math.MatrixStack; +/*import com.mojang.blaze3d.vertex.PoseStack; *//*?}*/ -public class VersionedMatrices extends /*? if >=1.20 >>*/ DrawContext /*? if <1.20 >>*/ /*MatrixStack*/ { +public class VersionedMatrices /*? if <1.20 {*/ /*extends PoseStack *//*?}*/ { /*? if >=1.20 {*/ - public VersionedMatrices(MinecraftClient client, VertexConsumerProvider.Immediate vertexConsumers) { - super(client, vertexConsumers); + private final GuiGraphics context; + + public VersionedMatrices(GuiGraphics context) { + this.context = context; + } + + public GuiGraphics getContext() { + return context; + } + + /*? if >=1.21.6 {*/ + public void pushPose() { + context.pose().pushMatrix(); + } + + public void popPose() { + context.pose().popMatrix(); } - public void push() { - getMatrices().push(); + public void scale(float x, float y, float z) { + context.pose().scale(x, y); + } + /*?} else {*/ + /*public void pushPose() { + context.pose().pushPose(); } - public void pop() { - getMatrices().pop(); + public void popPose() { + context.pose().popPose(); } public void scale(float x, float y, float z) { - getMatrices().scale(x, y, z); + context.pose().scale(x, y, z); } -/*?}*/ + *//*?}*/ +/*?} else {*/ + /*public PoseStack getContext() { + return this; + } +*//*?}*/ } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java index c5b69ebdc..c7d81330d 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedScreen.java @@ -1,63 +1,57 @@ package pl.skidam.automodpack.client.ui.versioned; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; - -/*? if <=1.16.5 {*//* -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.Element; -import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; + +/*? if >=1.21.6 {*/ +import net.minecraft.client.renderer.RenderPipelines; +/*?} else if >=1.21.2 {*/ +/*import net.minecraft.client.renderer.RenderType; +import java.util.function.Function; *//*?}*/ /*? if <1.20 {*/ -/*import net.minecraft.client.gui.DrawableHelper; -import net.minecraft.client.util.math.MatrixStack; -import com.mojang.blaze3d.systems.RenderSystem; -*//*?}*/ - -/*? if >=1.20 {*/ -import net.minecraft.client.gui.DrawContext; -import pl.skidam.automodpack.mixin.core.DrawContextAccessor; +/*import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.GuiComponent; +*//*?} else {*/ +import net.minecraft.client.gui.GuiGraphics; /*?}*/ -/*? if >=1.21.2 {*/ -/*import net.minecraft.client.render.RenderLayer; -import java.util.function.Function; -*//*?}*/ - public class VersionedScreen extends Screen { - protected VersionedScreen(Text title) { + protected VersionedScreen(Component title) { super(title); } /*? if <1.20 {*/ /*@Override - public void render(MatrixStack matrix, int mouseX, int mouseY, float delta) { + public void render(PoseStack matrix, int mouseX, int mouseY, float delta) { VersionedMatrices matrices = new VersionedMatrices(); *//*?} else {*/ @Override - public void render(DrawContext matrix, int mouseX, int mouseY, float delta) { - VersionedMatrices matrices = new VersionedMatrices(this.client, ((DrawContextAccessor) matrix).vertexConsumers()); - /*?}*/ + public void render(GuiGraphics matrix, int mouseX, int mouseY, float delta) { + VersionedMatrices matrices = new VersionedMatrices(matrix); + /*?}*/ // Render background /*? if <1.20.2 {*/ - /*super.renderBackground(matrices); + /*super.renderBackground(matrices.getContext()); *//*?} elif <1.20.6 {*/ - /*super.renderBackground(matrices, mouseX, mouseY, delta); + /*super.renderBackground(matrices.getContext(), mouseX, mouseY, delta); *//*?} else {*/ - super.render(matrices, mouseX, mouseY, delta); + super.render(matrix, mouseX, mouseY, delta); /*?}*/ + // Render the rest of our screen versionedRender(matrices, mouseX, mouseY, delta); /*? if <1.20.6 {*/ - /*super.render(matrices, mouseX, mouseY, delta); + /*super.render(matrices.getContext(), mouseX, mouseY, delta); *//*?}*/ } @@ -76,43 +70,45 @@ public void addDrawableChild(T child) { *//*?}*/ /*? if >=1.20 {*/ - public static void drawCenteredTextWithShadow(VersionedMatrices matrices, TextRenderer textRenderer, MutableText text, int centerX, int y, int color) { - matrices.drawCenteredTextWithShadow(textRenderer, text, centerX, y, color); + public static void drawCenteredTextWithShadow(VersionedMatrices matrices, Font textRenderer, MutableComponent text, int centerX, int y, int color) { + matrices.getContext().drawCenteredString(textRenderer, text, centerX, y, color); } /*?} else {*/ - /*public static void drawCenteredTextWithShadow(VersionedMatrices matrices, TextRenderer textRenderer, MutableText text, int centerX, int y, int color) { - textRenderer.drawWithShadow(matrices, text, (float)(centerX - textRenderer.getWidth(text) / 2), (float)y, color); + /*public static void drawCenteredTextWithShadow(VersionedMatrices matrices, Font textRenderer, MutableComponent text, int centerX, int y, int color) { + textRenderer.drawShadow(matrices.getContext(), text, (float)(centerX - textRenderer.width(text) / 2), (float)y, color); } *//*?}*/ /*? if <1.19.3 {*/ - /*public static ButtonWidget buttonWidget(int x, int y, int width, int height, Text message, ButtonWidget.PressAction onPress) { - return new ButtonWidget(x, y, width, height, message, onPress); + /*public static Button buttonWidget(int x, int y, int width, int height, Component message, Button.OnPress onPress) { + return new Button(x, y, width, height, message, onPress); } *//*?} else {*/ - public static ButtonWidget buttonWidget(int x, int y, int width, int height, Text message, ButtonWidget.PressAction onPress) { - return ButtonWidget.builder(message, onPress).position(x, y).size(width, height).build(); + public static Button buttonWidget(int x, int y, int width, int height, Component message, Button.OnPress onPress) { + return Button.builder(message, onPress).pos(x, y).size(width, height).build(); } /*?}*/ /*? if <=1.20 {*/ - /*public static void drawTexture(Identifier textureID, VersionedMatrices matrices, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) { + /*public static void drawTexture(ResourceLocation textureID, VersionedMatrices matrices, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) { /^? if <=1.16.5 {^/ - /^MinecraftClient.getInstance().getTextureManager().bindTexture(textureID); + /^Minecraft.getInstance().getTextureManager().bindTexture(textureID); ^//^?} else {^/ RenderSystem.setShaderTexture(0, textureID); /^?}^/ - DrawableHelper.drawTexture(matrices, x, y, u, v, width, height, textureWidth, textureHeight); + GuiComponent.blit(matrices.getContext(), x, y, u, v, width, height, textureWidth, textureHeight); } *//*?} else {*/ - public static void drawTexture(Identifier textureID, VersionedMatrices matrices, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) { - /*? if >=1.21.2 {*/ - /*Function renderLayers = RenderLayer::getGuiTextured; - matrices.drawTexture(renderLayers, textureID, x, y, u, v, width, height, textureWidth, textureHeight); + public static void drawTexture(ResourceLocation textureID, VersionedMatrices matrices, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) { + /*? if >=1.21.6 {*/ + matrices.getContext().blit(RenderPipelines.GUI_TEXTURED, textureID, x, y, u, v, width, height, textureWidth, textureHeight); + /*?} elif >=1.21.2 {*/ + /*Function RenderTypes = RenderType::guiTextured; + matrices.getContext().blit(RenderTypes, textureID, x, y, u, v, width, height, textureWidth, textureHeight); *//*?} else {*/ - matrices.drawTexture(textureID, x, y, u, v, width, height, textureWidth, textureHeight); - /*?}*/ + /*matrices.getContext().blit(textureID, x, y, u, v, width, height, textureWidth, textureHeight); + *//*?}*/ } /*?}*/ } diff --git a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedText.java b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedText.java index 9405c473c..102a51b09 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedText.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/versioned/VersionedText.java @@ -1,25 +1,31 @@ package pl.skidam.automodpack.client.ui.versioned; -import net.minecraft.text.*; +import net.minecraft.network.chat.MutableComponent; +/*? if <= 1.19.1 {*/ +/*import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +*//*?} else {*/ +import net.minecraft.network.chat.Component; +/*?}*/ public class VersionedText { /*? if <=1.19.1 {*/ - /*public static MutableText translatable(String key, Object... args) { - return new TranslatableText(key, args); + /*public static MutableComponent translatable(String key, Object... args) { + return new TranslatableComponent(key, args); } - public static MutableText literal(String string) { - return new LiteralText(string); + public static MutableComponent literal(String string) { + return new TextComponent(string); } *//*?} else {*/ - public static MutableText translatable(String key, Object... args) { - return Text.translatable(key, args); + public static MutableComponent translatable(String key, Object... args) { + return Component.translatable(key, args); } - public static MutableText literal(String string) { - return Text.literal(string); + public static MutableComponent literal(String string) { + return Component.literal(string); } /*?}*/ -} +} \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/client/ui/widget/Badge.java b/src/main/java/pl/skidam/automodpack/client/ui/widget/Badge.java index 7dffde3c2..d5fcbbb54 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/widget/Badge.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/widget/Badge.java @@ -1,15 +1,16 @@ package pl.skidam.automodpack.client.ui.widget; -import net.minecraft.util.Identifier; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; import pl.skidam.automodpack.init.Common; import static pl.skidam.automodpack_core.GlobalVariables.MOD_ID; +import net.minecraft.resources.ResourceLocation; + public class Badge { - private static final Identifier MODRINTH_ICON = Common.id("gui/platform/logo-modrinth.png"); - private static final Identifier CURSEFORGE_ICON = Common.id("gui/platform/logo-curseforge.png"); + private static final ResourceLocation MODRINTH_ICON = Common.id("gui/platform/logo-modrinth.png"); + private static final ResourceLocation CURSEFORGE_ICON = Common.id("gui/platform/logo-curseforge.png"); private static final int textureSize = 32; public static void renderModrinthBadge(VersionedMatrices matrices, int x, int y) { diff --git a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java index a1c6172ba..2f9d0ff2d 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntry.java @@ -1,34 +1,34 @@ package pl.skidam.automodpack.client.ui.widget; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.ObjectSelectionList; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import pl.skidam.automodpack.client.ui.TextColors; import pl.skidam.automodpack.client.ui.versioned.VersionedMatrices; import pl.skidam.automodpack.client.ui.versioned.VersionedScreen; -/*? if <1.20 {*/ -/*import net.minecraft.client.util.math.MatrixStack; -*//*?} else {*/ -import net.minecraft.client.gui.DrawContext; -import pl.skidam.automodpack.mixin.core.DrawContextAccessor; -/*?}*/ +/*? if >=1.20 {*/ +import net.minecraft.client.gui.GuiGraphics; +/*?} else {*/ +/*import com.mojang.blaze3d.vertex.PoseStack; +*//*?}*/ -public class ListEntry extends AlwaysSelectedEntryListWidget.Entry { +public class ListEntry extends ObjectSelectionList.Entry { - protected final MinecraftClient client; - private final MutableText text; + protected final Minecraft client; + private final MutableComponent text; private final String mainPageUrl; private final boolean bigFont; - public ListEntry(MutableText text, String mainPageUrl, boolean bigFont, MinecraftClient client) { + public ListEntry(MutableComponent text, String mainPageUrl, boolean bigFont, Minecraft client) { this.text = text; this.mainPageUrl = mainPageUrl; this.client = client; this.bigFont = bigFont; } - public ListEntry(MutableText text, boolean bigFont, MinecraftClient client) { + public ListEntry(MutableComponent text, boolean bigFont, Minecraft client) { this.text = text; this.mainPageUrl = null; this.client = client; @@ -37,20 +37,23 @@ public ListEntry(MutableText text, boolean bigFont, MinecraftClient client) { /*? if >=1.17 {*/ @Override - public Text getNarration() { + public Component getNarration() { return text; } /*?}*/ @Override /*? if <1.20 {*/ - /*public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - VersionedMatrices versionedMatrices = new VersionedMatrices(); + /*public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + VersionedMatrices versionedMatrices = new VersionedMatrices(); *//*?} else {*/ - public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - VersionedMatrices versionedMatrices = new VersionedMatrices(this.client, ((DrawContextAccessor) context).vertexConsumers()); + public void render(GuiGraphics GuiGraphics, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + VersionedMatrices versionedMatrices = new VersionedMatrices(GuiGraphics); /*?}*/ - versionedMatrices.push(); + versionedRender(versionedMatrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, hovered, tickDelta); + } + public void versionedRender(VersionedMatrices versionedMatrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + versionedMatrices.pushPose(); int centeredX = x + entryWidth / 2; int centeredY = y + entryHeight / 2; @@ -63,7 +66,7 @@ public void render(DrawContext context, int index, int y, int x, int entryWidth, centeredY = centeredY - 10 / 2; } - VersionedScreen.drawCenteredTextWithShadow(versionedMatrices, client.textRenderer, text, centeredX, centeredY, 16777215); + VersionedScreen.drawCenteredTextWithShadow(versionedMatrices, client.font, text, centeredX, centeredY, TextColors.WHITE); // if (mainPageUrls != null) { // int badgeX = x - 42; @@ -75,10 +78,10 @@ public void render(DrawContext context, int index, int y, int x, int entryWidth, // } // } - versionedMatrices.pop(); + versionedMatrices.popPose(); } - public MutableText getText() { + public MutableComponent getText() { return this.text; } @@ -95,4 +98,4 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { public boolean mouseReleased(double mouseX, double mouseY, int button) { return false; } -} +} \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java index 016c39976..6dcf6bd11 100644 --- a/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java +++ b/src/main/java/pl/skidam/automodpack/client/ui/widget/ListEntryWidget.java @@ -1,25 +1,25 @@ package pl.skidam.automodpack.client.ui.widget; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget; -import net.minecraft.text.MutableText; -import net.minecraft.util.Formatting; -import net.minecraft.util.math.MathHelper; import pl.skidam.automodpack.client.ui.versioned.VersionedText; import java.util.Map; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.ObjectSelectionList; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.util.Mth; /*? if <1.20 {*/ -/*import net.minecraft.client.util.math.MatrixStack; +/*import com.mojang.blaze3d.vertex.PoseStack; *//*?} elif <1.20.3 {*/ -/*import net.minecraft.client.gui.DrawContext; +/*import net.minecraft.client.gui.GuiGraphics; *//*?}*/ -public class ListEntryWidget extends AlwaysSelectedEntryListWidget { +public class ListEntryWidget extends ObjectSelectionList { private boolean scrolling; - public ListEntryWidget(Map changelogs, MinecraftClient client, int width, int height, int top, int bottom, int itemHeight) { + public ListEntryWidget(Map changelogs, Minecraft client, int width, int height, int top, int bottom, int itemHeight) { /*? if <1.20.3 {*/ /*super(client, width, height, top, bottom, itemHeight); *//*?} else {*/ @@ -30,7 +30,7 @@ public ListEntryWidget(Map changelogs, MinecraftClient client, i this.clearEntries(); if (changelogs == null || changelogs.isEmpty()) { - ListEntry entry = new ListEntry(VersionedText.literal("No changelogs found").formatted(Formatting.BOLD), true, this.client); + ListEntry entry = new ListEntry(VersionedText.literal("No changelogs found").withStyle(ChatFormatting.BOLD), true, this.minecraft); this.addEntry(entry); return; } @@ -39,58 +39,58 @@ public ListEntryWidget(Map changelogs, MinecraftClient client, i String textString = changelog.getKey(); String mainPageUrl = changelog.getValue(); - MutableText text = VersionedText.literal(textString); + MutableComponent text = VersionedText.literal(textString); if (textString.startsWith("+")) { - text = text.formatted(Formatting.GREEN); + text = text.withStyle(ChatFormatting.GREEN); } else if (textString.startsWith("-")) { - text = text.formatted(Formatting.RED); + text = text.withStyle(ChatFormatting.RED); } - ListEntry entry = new ListEntry(text, mainPageUrl, false, this.client); + ListEntry entry = new ListEntry(text, mainPageUrl, false, this.minecraft); this.addEntry(entry); } } /*? if <=1.20.2 {*/ - /*public void render(/^? if <1.20 {^/ /^MatrixStack ^//^?} else {^/ DrawContext /^?}^/ matrices, int mouseX, int mouseY, float delta) { + /*public void render(/^? if <1.20 {^/ /^PoseStack ^//^?} else {^/ GuiGraphics /^?}^/ matrices, int mouseX, int mouseY, float delta) { super.render(matrices, mouseX, mouseY, delta); } *//*?}*/ - /*? if >1.21.3 {*/ - /*public double getScrollAmount() { - return this.getScrollY(); + /*? if >=1.21.4 {*/ + public double getScrollAmount() { + return this.scrollAmount(); } - *//*?}*/ + /*?}*/ public final ListEntry getEntryAtPos(double x, double y) { - int int_5 = MathHelper.floor(y - (double) getTop()) - this.headerHeight + (int) this.getScrollAmount() - 4; + int int_5 = Mth.floor(y - (double) getTop()) - this.headerHeight + (int) this.getScrollAmount() - 4; int index = int_5 / this.itemHeight; - return x < (double) this.getScrollbarX() && x >= (double) getRowLeft() && x <= (double) (getRowLeft() + getRowWidth()) && index >= 0 && int_5 >= 0 && index < this.getEntryCount() ? this.children().get(index) : null; + return x < (double) this.getScrollbarPosition() && x >= (double) getRowLeft() && x <= (double) (getRowLeft() + getRowWidth()) && index >= 0 && int_5 >= 0 && index < this.getItemCount() ? this.children().get(index) : null; } public int getTop() { /*? if <1.20.3 {*/ - /*return this.top; + /*return this.y0; *//*?} else {*/ return this.getY(); /*?}*/ } /*? if <=1.21.3 {*/ - @Override + /*@Override protected void updateScrollingState(double mouseX, double mouseY, int button) { super.updateScrollingState(mouseX, mouseY, button); - this.scrolling = button == 0 && mouseX >= (double) this.getScrollbarX() && mouseX < (double) (this.getScrollbarX() + 6); + this.scrolling = button == 0 && mouseX >= (double) this.getScrollbarPosition() && mouseX < (double) (this.getScrollbarPosition() + 6); } - /*?}*/ + *//*?}*/ @Override public boolean mouseClicked(double mouseX, double mouseY, int button) { /*? if <=1.21.3 {*/ - this.updateScrollingState(mouseX, mouseY, button); - /*?}*/ + /*this.updateScrollingState(mouseX, mouseY, button); + *//*?}*/ if (!this.isMouseOver(mouseX, mouseY)) { return false; } else { @@ -116,7 +116,7 @@ public void setSelected(ListEntry entry) { } } - protected int getScrollbarX() { + protected int getScrollbarPosition() { return this.width - 6; } diff --git a/src/main/java/pl/skidam/automodpack/init/Common.java b/src/main/java/pl/skidam/automodpack/init/Common.java index 0bf2f45af..c875333ae 100644 --- a/src/main/java/pl/skidam/automodpack/init/Common.java +++ b/src/main/java/pl/skidam/automodpack/init/Common.java @@ -1,7 +1,7 @@ package pl.skidam.automodpack.init; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.util.Identifier; import pl.skidam.automodpack.loader.GameCall; import pl.skidam.automodpack.networking.ModPackets; import pl.skidam.automodpack_core.modpack.ModpackExecutor; @@ -81,11 +81,11 @@ public static void beforeShutdownServer() { fullpacks.shutdownExecutor(); } - public static Identifier id(String path) { + public static ResourceLocation id(String path) { /*? if >1.19.1 {*/ - return Identifier.of(MOD_ID, path); + return ResourceLocation.tryBuild(MOD_ID, path); /*?} else {*/ - /*return new Identifier(MOD_ID, path); + /*return new ResourceLocation(MOD_ID, path); *//*?}*/ } } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ClientConnectionAccessor.java b/src/main/java/pl/skidam/automodpack/mixin/core/ClientConnectionAccessor.java index a26c445f9..4b03d2637 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ClientConnectionAccessor.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ClientConnectionAccessor.java @@ -1,11 +1,11 @@ package pl.skidam.automodpack.mixin.core; import io.netty.channel.Channel; -import net.minecraft.network.ClientConnection; +import net.minecraft.network.Connection; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(ClientConnection.class) +@Mixin(Connection.class) public interface ClientConnectionAccessor { @Accessor("channel") Channel getChannel(); diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerAccessor.java b/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerAccessor.java index f217c2b77..4f5486d14 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerAccessor.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerAccessor.java @@ -1,12 +1,12 @@ package pl.skidam.automodpack.mixin.core; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.ClientConnection; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.Connection; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(ClientLoginNetworkHandler.class) +@Mixin(ClientHandshakePacketListenerImpl.class) public interface ClientLoginNetworkHandlerAccessor { @Accessor("connection") - ClientConnection getConnection(); + Connection getConnection(); } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerMixin.java index 264484667..9afc36c65 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ClientLoginNetworkHandlerMixin.java @@ -1,8 +1,8 @@ package pl.skidam.automodpack.mixin.core; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -12,23 +12,23 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import pl.skidam.automodpack.networking.client.ClientLoginNetworkAddon; -@Mixin(value = ClientLoginNetworkHandler.class, priority = 300) +@Mixin(value = ClientHandshakePacketListenerImpl.class, priority = 300) public class ClientLoginNetworkHandlerMixin { - @Shadow @Final private MinecraftClient client; + @Shadow @Final private Minecraft minecraft; @Unique private ClientLoginNetworkAddon autoModpack$addon; @Inject(method = "", at = @At("RETURN")) private void initAddon(CallbackInfo ci) { - this.autoModpack$addon = new ClientLoginNetworkAddon((ClientLoginNetworkHandler) (Object) this, this.client); + this.autoModpack$addon = new ClientLoginNetworkAddon((ClientHandshakePacketListenerImpl) (Object) this, this.minecraft); } @Inject( - method = "onQueryRequest", + method = "handleCustomQuery", at = @At(value = "HEAD"), cancellable = true ) - private void handleQueryRequest(LoginQueryRequestS2CPacket packet, CallbackInfo ci) { + private void handleQueryRequest(ClientboundCustomQueryPacket packet, CallbackInfo ci) { if (this.autoModpack$addon == null) { return; } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java index 58d98bf70..691dc615a 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ConnectScreenMixin.java @@ -1,8 +1,8 @@ package pl.skidam.automodpack.mixin.core; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ServerAddress; -import net.minecraft.client.network.ServerInfo; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.ConnectScreen; +import net.minecraft.client.multiplayer.resolver.ServerAddress; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -12,27 +12,25 @@ import pl.skidam.automodpack.networking.ModPackets; /*? if >= 1.20.5 {*/ -import net.minecraft.client.network.CookieStorage; +import net.minecraft.client.multiplayer.TransferState; /*?}*/ -/*? if >=1.20.3 {*/ -import net.minecraft.client.gui.screen.multiplayer.ConnectScreen; -/*?} else {*/ -/*import net.minecraft.client.gui.screen.ConnectScreen; - *//*?}*/ +/*? if > 1.19.3 {*/ +import net.minecraft.client.multiplayer.ServerData; +/*?}*/ @Mixin(ConnectScreen.class) public abstract class ConnectScreenMixin { /*? if >= 1.20.5 {*/ - @Inject(method = "connect(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ServerAddress;Lnet/minecraft/client/network/ServerInfo;Lnet/minecraft/client/network/CookieStorage;)V", at = @At("HEAD")) - public void onConnect(MinecraftClient client, ServerAddress address, ServerInfo info, CookieStorage cookieStorage, CallbackInfo ci) { - /*?} else if > 1.19.2 {*/ - /*@Inject(method = "connect(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ServerAddress;Lnet/minecraft/client/network/ServerInfo;)V", at = @At("HEAD")) - public void onConnect(MinecraftClient client, ServerAddress address, ServerInfo info, CallbackInfo ci) { + @Inject(method = "connect", at = @At("HEAD")) + public void onConnect(Minecraft client, ServerAddress address, ServerData info, TransferState cookieStorage, CallbackInfo ci) { + /*?} else if > 1.19.3 {*/ + /*@Inject(method = "connect", at = @At("HEAD")) + public void onConnect(Minecraft client, ServerAddress address, ServerData info, CallbackInfo ci) { *//*?} else {*/ - /*@Inject(method = "connect(Lnet/minecraft/client/MinecraftClient;Lnet/minecraft/client/network/ServerAddress;)V", at = @At("HEAD")) - public void onConnect(MinecraftClient client, ServerAddress address, CallbackInfo ci) { + /*@Inject(method = "connect", at = @At("HEAD")) + public void onConnect(Minecraft client, ServerAddress address, CallbackInfo ci) { *//*?}*/ - ModPackets.setOriginalServerAddress(AddressHelpers.format(address.getAddress(), address.getPort())); + ModPackets.setOriginalServerAddress(AddressHelpers.format(address.getHost(), address.getPort())); } } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/DrawContextAccessor.java b/src/main/java/pl/skidam/automodpack/mixin/core/DrawContextAccessor.java deleted file mode 100644 index f738234f0..000000000 --- a/src/main/java/pl/skidam/automodpack/mixin/core/DrawContextAccessor.java +++ /dev/null @@ -1,21 +0,0 @@ -package pl.skidam.automodpack.mixin.core; - -import org.spongepowered.asm.mixin.Mixin; -/*? if >=1.20 {*/ -import net.minecraft.client.gui.DrawContext; -import org.spongepowered.asm.mixin.gen.Accessor; -import net.minecraft.client.render.VertexConsumerProvider; - -// TODO find better way to do this, its mixin only for 1.20 and above -@Mixin(DrawContext.class) -/*?} else {*/ -/*import pl.skidam.automodpack.init.Common; -@Mixin(Common.class) -*//*?}*/ -public interface DrawContextAccessor { - - /*? if >=1.20 {*/ - @Accessor("vertexConsumers") - VertexConsumerProvider.Immediate vertexConsumers(); - /*?}*/ -} \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/FabricLoginMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/FabricLoginMixin.java index e218d7849..5f9a19e1f 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/FabricLoginMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/FabricLoginMixin.java @@ -2,7 +2,7 @@ import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import net.fabricmc.fabric.impl.networking.server.ServerLoginNetworkAddon; -import net.minecraft.util.Identifier; +import net.minecraft.resources.ResourceLocation; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; @@ -19,7 +19,7 @@ public class FabricLoginMixin { at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") ) private boolean dontRemoveAutoModpackChannels(Map instance, Object key, Object value) { - if (value instanceof Identifier id) { + if (value instanceof ResourceLocation id) { // If AutoModpack id, return false return LoginNetworkingIDs.getByKey(id) == null; } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryRequestS2CPacketMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryRequestS2CPacketMixin.java index 132f8e4d2..a429126b2 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryRequestS2CPacketMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryRequestS2CPacketMixin.java @@ -2,10 +2,10 @@ import org.spongepowered.asm.mixin.Mixin; /*? if >=1.20.2 {*/ -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestPayload; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; -import net.minecraft.util.Identifier; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.network.protocol.login.custom.CustomQueryPayload; +import net.minecraft.resources.ResourceLocation; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -16,7 +16,7 @@ import pl.skidam.automodpack_core.GlobalVariables; // TODO find better way to do this, its mixin only for 1.20.2 and above -@Mixin(value = LoginQueryRequestS2CPacket.class, priority = 300) +@Mixin(value = ClientboundCustomQueryPacket.class, priority = 300) /*?} else {*/ /*import pl.skidam.automodpack.init.Common; @Mixin(Common.class) @@ -27,7 +27,7 @@ public class LoginQueryRequestS2CPacketMixin { @Shadow @Final private static int MAX_PAYLOAD_SIZE; @Inject(method = "readPayload", at = @At("HEAD"), cancellable = true) - private static void readPayload(Identifier id, PacketByteBuf buf, CallbackInfoReturnable cir) { + private static void readPayload(ResourceLocation id, FriendlyByteBuf buf, CallbackInfoReturnable cir) { if (id.getNamespace().equals(GlobalVariables.MOD_ID)) { cir.setReturnValue(new LoginRequestPayload(id, PayloadHelper.read(buf, MAX_PAYLOAD_SIZE))); } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryResponseC2SPacketMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryResponseC2SPacketMixin.java index d59fa759d..b9c4deb84 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryResponseC2SPacketMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/LoginQueryResponseC2SPacketMixin.java @@ -2,10 +2,10 @@ import org.spongepowered.asm.mixin.Mixin; /*? if >=1.20.2 {*/ -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket; -import net.minecraft.network.packet.c2s.login.LoginQueryResponsePayload; -import net.minecraft.util.Identifier; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.network.protocol.login.custom.CustomQueryAnswerPayload; +import net.minecraft.resources.ResourceLocation; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -16,7 +16,7 @@ import pl.skidam.automodpack.networking.client.LoginResponsePayload; // TODO find better way to do this, its mixin only for 1.20.2 and above -@Mixin(value = LoginQueryResponseC2SPacket.class, priority = 300) +@Mixin(value = ServerboundCustomQueryAnswerPacket.class, priority = 300) /*?} else {*/ /*import pl.skidam.automodpack.init.Common; @Mixin(Common.class) @@ -28,8 +28,8 @@ public class LoginQueryResponseC2SPacketMixin { private static int MAX_PAYLOAD_SIZE; @Inject(method = "readPayload", at = @At("HEAD"), cancellable = true) - private static void readResponse(int queryId, PacketByteBuf buf, CallbackInfoReturnable cir) { - Identifier automodpackID = LoginNetworkingIDs.getByValue(queryId); + private static void readResponse(int queryId, FriendlyByteBuf buf, CallbackInfoReturnable cir) { + ResourceLocation automodpackID = LoginNetworkingIDs.getByValue(queryId); if (automodpackID == null) { return; } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java index 40663df21..244195c71 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/MinecraftServerMixin.java @@ -10,17 +10,13 @@ @Mixin(MinecraftServer.class) public class MinecraftServerMixin { - /*? if >=1.19.3 {*/ - @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;createMetadata()Lnet/minecraft/server/ServerMetadata;", ordinal = 0), method = "runServer") -/*?} else {*/ - /*@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;setFavicon(Lnet/minecraft/server/ServerMetadata;)V", ordinal = 0), method = "runServer") -*//*?}*/ + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;initServer()Z", shift = At.Shift.AFTER), method = "runServer") private void afterSetupServer(CallbackInfo info) { Common.server = (MinecraftServer) (Object) this; Common.afterSetupServer(); } - @Inject(at = @At("HEAD"), method = "shutdown") + @Inject(at = @At("HEAD"), method = "stopServer") private void beforeShutdownServer(CallbackInfo info) { Common.beforeShutdownServer(); } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java index 38f39ac66..dd65e1043 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/MusicTrackerMixin.java @@ -1,31 +1,32 @@ package pl.skidam.automodpack.mixin.core; -/*? if >1.21.3 {*/ -/*import net.minecraft.client.sound.MusicInstance; -*//*?} else {*/ -import net.minecraft.sound.MusicSound; -/*?}*/ -import net.minecraft.client.sound.MusicTracker; +import net.minecraft.client.sounds.MusicManager; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import pl.skidam.automodpack.client.audio.AudioManager; -@Mixin(MusicTracker.class) +/*? if >=1.21.4 {*/ +import net.minecraft.client.sounds.MusicInfo; +/*?} else {*/ +/*import net.minecraft.sounds.Music; +*//*?}*/ + +@Mixin(MusicManager.class) public class MusicTrackerMixin { @Inject( - method = "play", + method = "startPlaying", at = @At("HEAD"), cancellable = true ) - /*? if >1.21.3 {*/ - /*private void play(MusicInstance music, CallbackInfo ci) { - *//*?} else {*/ - private void play(MusicSound type, CallbackInfo ci) { - /*?}*/ + /*? if >=1.21.4 {*/ + private void play(MusicInfo p_383115_, CallbackInfo ci) { + /*?} else {*/ + /*private void play(Music type, CallbackInfo ci) { + *//*?}*/ if (AudioManager.isMusicPlaying()) { ci.cancel(); } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java index 76e62d66d..ebbb4628a 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/PlayerManagerMixin.java @@ -1,16 +1,13 @@ package pl.skidam.automodpack.mixin.core; import com.mojang.authlib.GameProfile; -import net.minecraft.network.ClientConnection; -import net.minecraft.server.PlayerManager; -/*? if >1.20.3 {*/ -import net.minecraft.server.network.ConnectedClientData; -/*?}*/ -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.ClickEvent; -import net.minecraft.text.Text; -import net.minecraft.text.TextColor; -import net.minecraft.util.Formatting; +import net.minecraft.ChatFormatting; +import net.minecraft.network.Connection; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextColor; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.PlayerList; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -18,21 +15,25 @@ import pl.skidam.automodpack.client.ui.versioned.VersionedText; import pl.skidam.automodpack.init.Common; -/*? if >1.21.4 {*/ -/*import java.net.URI; -*//*?}*/ +/*? if >1.20.3 {*/ +import net.minecraft.server.network.CommonListenerCookie; +/*?}*/ + +/*? if >=1.21.5 {*/ +import java.net.URI; +/*?}*/ import static pl.skidam.automodpack_core.GlobalVariables.serverConfig; -@Mixin(PlayerManager.class) +@Mixin(PlayerList.class) public class PlayerManagerMixin { /*? if >1.20.3 {*/ - @Inject(at = @At("TAIL"), method = "onPlayerConnect") - private void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, ConnectedClientData clientData, CallbackInfo ci) { + @Inject(at = @At("TAIL"), method = "placeNewPlayer") + private void onPlayerConnect(Connection connection, ServerPlayer player, CommonListenerCookie clientData, CallbackInfo ci) { /*?} else {*/ -/*@Inject(at = @At("TAIL"), method = "onPlayerConnect") -private void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { +/*@Inject(at = @At("TAIL"), method = "placeNewPlayer") +private void onPlayerConnect(Connection netManager, ServerPlayer player, CallbackInfo ci) { *//*?}*/ GameProfile profile = player.getGameProfile(); String playerName = profile.getName(); @@ -44,15 +45,15 @@ private void onPlayerConnect(ClientConnection connection, ServerPlayerEntity pla if (serverConfig.nagUnModdedClients && !Common.players.get(playerName)) { // Send chat nag message which is clickable and opens the link - Text nagText = VersionedText.literal(serverConfig.nagMessage).styled(style -> style.withBold(true)); - Text nagClickableText = VersionedText.literal(serverConfig.nagClickableMessage).styled(style -> style.withUnderline(true).withColor(TextColor.fromFormatting(Formatting.BLUE)) - /*? if >1.21.4 {*/ - /*.withClickEvent(new ClickEvent.OpenUrl(URI.create(serverConfig.nagClickableLink)))); - *//*?} else {*/ - .withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, serverConfig.nagClickableLink))); - /*?}*/ - player.sendMessage(nagText, false); - player.sendMessage(nagClickableText, false); + Component nagText = VersionedText.literal(serverConfig.nagMessage).withStyle(style -> style.withBold(true)); + Component nagClickableText = VersionedText.literal(serverConfig.nagClickableMessage).withStyle(style -> style.withUnderlined(true).withColor(TextColor.fromLegacyFormat(ChatFormatting.BLUE)) + /*? if >=1.21.5 {*/ + .withClickEvent(new ClickEvent.OpenUrl(URI.create(serverConfig.nagClickableLink)))); + /*?} else {*/ + /*.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, serverConfig.nagClickableLink))); + *//*?}*/ + player.displayClientMessage(nagText, false); + player.displayClientMessage(nagClickableText, false); } } } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerAccessor.java b/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerAccessor.java index fa8c8bef4..99a6dfd8c 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerAccessor.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerAccessor.java @@ -1,19 +1,23 @@ package pl.skidam.automodpack.mixin.core; import com.mojang.authlib.GameProfile; -import net.minecraft.network.ClientConnection; +import net.minecraft.network.Connection; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; -@Mixin(ServerLoginNetworkHandler.class) +@Mixin(ServerLoginPacketListenerImpl.class) public interface ServerLoginNetworkHandlerAccessor { - @Accessor("profile") + /*? if >= 1.20.2 {*/ + @Accessor("authenticatedProfile") + /*?} else {*/ + /*@Accessor("gameProfile") + *//*?}*/ GameProfile getGameProfile(); @Accessor - ClientConnection getConnection(); + Connection getConnection(); @Accessor MinecraftServer getServer(); diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerMixin.java index b1f3d1390..89b34384c 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ServerLoginNetworkHandlerMixin.java @@ -1,7 +1,7 @@ package pl.skidam.automodpack.mixin.core; -import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket; -import net.minecraft.server.network.ServerLoginNetworkHandler; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -9,10 +9,10 @@ import pl.skidam.automodpack.networking.client.LoginResponsePayload; import pl.skidam.automodpack.networking.server.ServerLoginNetworkAddon; -@Mixin(value = ServerLoginNetworkHandler.class, priority = 300) +@Mixin(value = ServerLoginPacketListenerImpl.class, priority = 300) public abstract class ServerLoginNetworkHandlerMixin { - @Shadow private ServerLoginNetworkHandler.State state; + @Shadow private ServerLoginPacketListenerImpl.State state; @Unique private ServerLoginNetworkAddon automodpack$addon; @Inject( @@ -20,15 +20,15 @@ public abstract class ServerLoginNetworkHandlerMixin { at = @At("RETURN") ) private void initAddon(CallbackInfo ci) { - this.automodpack$addon = new ServerLoginNetworkAddon((ServerLoginNetworkHandler) (Object) this); + this.automodpack$addon = new ServerLoginNetworkAddon((ServerLoginPacketListenerImpl) (Object) this); } @Inject( - method = "onQueryResponse", + method = "handleCustomQueryPacket", at = @At("HEAD"), cancellable = true ) - private void handleCustomPayload(LoginQueryResponseC2SPacket packet, CallbackInfo ci) { + private void handleCustomPayload(ServerboundCustomQueryAnswerPacket packet, CallbackInfo ci) { if (this.automodpack$addon == null) { return; } @@ -38,7 +38,7 @@ private void handleCustomPayload(LoginQueryResponseC2SPacket packet, CallbackInf ci.cancel(); // We have handled it, cancel vanilla behavior } else { /*? if >=1.20.2 {*/ - if (packet.response() instanceof LoginResponsePayload response) { + if (packet.payload() instanceof LoginResponsePayload response) { response.data().skipBytes(response.data().readableBytes()); } /*?}*/ @@ -55,7 +55,7 @@ private void sendOurPackets(CallbackInfo ci) { return; } - if (state != ServerLoginNetworkHandler.State.NEGOTIATING && state != ServerLoginNetworkHandler.State./*? if <1.20.2 {*/ /*READY_TO_ACCEPT *//*?} else {*/VERIFYING/*?}*/) { + if (state != ServerLoginPacketListenerImpl.State.NEGOTIATING && state != ServerLoginPacketListenerImpl.State./*? if <1.20.2 {*/ /*READY_TO_ACCEPT *//*?} else {*/VERIFYING/*?}*/) { return; } diff --git a/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java b/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java index 7b0994afe..6de05e01c 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java +++ b/src/main/java/pl/skidam/automodpack/mixin/core/ServerNetworkIoMixin.java @@ -10,7 +10,7 @@ import static pl.skidam.automodpack_core.GlobalVariables.*; -@Mixin(targets = "net/minecraft/server/ServerNetworkIo$1", priority = 2137) +@Mixin(targets = "net/minecraft/server/network/ServerConnectionListener$1", priority = 2137) public abstract class ServerNetworkIoMixin { @Inject( @@ -18,7 +18,7 @@ public abstract class ServerNetworkIoMixin { at = @At("TAIL") ) private void injectAutoModpackHost(Channel channel, CallbackInfo ci) { - if (!serverConfig.hostModpackOnMinecraftPort) { + if (serverConfig.bindPort != -1) { return; } diff --git a/src/main/java/pl/skidam/automodpack/mixin/dev/TestButton.java b/src/main/java/pl/skidam/automodpack/mixin/dev/TestButton.java index 5a821a87d..40024ef18 100644 --- a/src/main/java/pl/skidam/automodpack/mixin/dev/TestButton.java +++ b/src/main/java/pl/skidam/automodpack/mixin/dev/TestButton.java @@ -1,8 +1,5 @@ package pl.skidam.automodpack.mixin.dev; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.TitleScreen; -import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -14,10 +11,14 @@ import static pl.skidam.automodpack_core.GlobalVariables.LOADER_MANAGER; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.TitleScreen; +import net.minecraft.network.chat.Component; + @Mixin(TitleScreen.class) public class TestButton extends Screen { - protected TestButton(Text title) { + protected TestButton(Component title) { super(title); } @@ -33,7 +34,7 @@ private void init(CallbackInfo ci) { } /*? if >=1.17 {*/ - this.addDrawableChild( + this.addRenderableWidget( /*?} else {*//* this.addButton( *//*?}*/ diff --git a/src/main/java/pl/skidam/automodpack/modpack/Commands.java b/src/main/java/pl/skidam/automodpack/modpack/Commands.java index 2e255c12b..c804b1fdb 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/Commands.java +++ b/src/main/java/pl/skidam/automodpack/modpack/Commands.java @@ -4,66 +4,97 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.context.CommandContext; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.util.Formatting; -import net.minecraft.util.Util; import pl.skidam.automodpack.client.ui.versioned.VersionedCommandSource; import pl.skidam.automodpack.client.ui.versioned.VersionedText; import pl.skidam.automodpack_core.auth.SecretsStore; import pl.skidam.automodpack_core.config.ConfigTools; import pl.skidam.automodpack_core.config.Jsons; - import java.util.Set; - -import static net.minecraft.server.command.CommandManager.literal; +import net.minecraft.ChatFormatting; +import net.minecraft.Util; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.HoverEvent; +import net.minecraft.network.chat.MutableComponent; + +import static net.minecraft.commands.Commands.literal; import static pl.skidam.automodpack_core.GlobalVariables.*; public class Commands { - public static void register(CommandDispatcher dispatcher) { - dispatcher.register( + public static void register(CommandDispatcher dispatcher) { + var automodpackNode = dispatcher.register( literal("automodpack") .executes(Commands::about) .then(literal("generate") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::generateModpack) ) .then(literal("host") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::modpackHostAbout) .then(literal("start") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::startModpackHost) ) .then(literal("stop") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::stopModpackHost) ) .then(literal("restart") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::restartModpackHost) ) .then(literal("connections") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::connections) ) + .then(literal("fingerprint") + .requires((source) -> source.hasPermission(3)) + .executes(Commands::fingerprint) + ) ) .then(literal("config") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .then(literal("reload") - .requires((source) -> source.hasPermissionLevel(3)) + .requires((source) -> source.hasPermission(3)) .executes(Commands::reload) ) ) ); + + dispatcher.register( + literal("amp") + .executes(Commands::about) + .redirect(automodpackNode) + ); + } + + private static int fingerprint(CommandContext context) { + String fingerprint = hostServer.getCertificateFingerprint(); + if (fingerprint != null) { + MutableComponent fingerprintText = VersionedText.literal(fingerprint).withStyle(style -> style + /*? if >=1.21.5 {*/ + .withHoverEvent(new HoverEvent.ShowText(VersionedText.translatable("chat.copy.click"))) + .withClickEvent(new ClickEvent.CopyToClipboard(fingerprint))); + /*?} else {*/ + /*.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, VersionedText.translatable("chat.copy.click"))) + .withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, fingerprint))); + *//*?}*/ + send(context, "Certificate fingerprint", ChatFormatting.WHITE, fingerprintText, ChatFormatting.YELLOW, false); + } else { + send(context, "Certificate fingerprint is not available. Make sure the server is running with TLS enabled.", ChatFormatting.RED, false); + } + + return Command.SINGLE_SUCCESS; } - private static int connections(CommandContext serverCommandSourceCommandContext) { - Util.getMainWorkerExecutor().execute(() -> { + private static int connections(CommandContext context) { + Util.backgroundExecutor().execute(() -> { var connections = hostServer.getConnections(); var uniqueSecrets = Set.copyOf(connections.values()); - send(serverCommandSourceCommandContext, String.format("Active connections: %d Unique connections: %d ", connections.size(), uniqueSecrets.size()), Formatting.YELLOW, false); + send(context, String.format("Active connections: %d Unique connections: %d ", connections.size(), uniqueSecrets.size()), ChatFormatting.YELLOW, false); for (String secret : uniqueSecrets) { var playerSecretPair = SecretsStore.getHostSecret(secret); @@ -74,65 +105,65 @@ private static int connections(CommandContext serverCommand long connNum = connections.values().stream().filter(secret::equals).count(); - send(serverCommandSourceCommandContext, String.format("Player: %s (%s) is downloading modpack using %d connections", profile.getName(), playerId, connNum), Formatting.GREEN, false); + send(context, String.format("Player: %s (%s) is downloading modpack using %d connections", profile.getName(), playerId, connNum), ChatFormatting.GREEN, false); } }); return Command.SINGLE_SUCCESS; } - private static int reload(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { - var tempServerConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFields.class); + private static int reload(CommandContext context) { + Util.backgroundExecutor().execute(() -> { + var tempServerConfig = ConfigTools.load(serverConfigFile, Jsons.ServerConfigFieldsV2.class); if (tempServerConfig != null) { serverConfig = tempServerConfig; - send(context, "AutoModpack server config reloaded!", Formatting.GREEN, true); + send(context, "AutoModpack server config reloaded!", ChatFormatting.GREEN, true); } else { - send(context, "Error while reloading config file!", Formatting.RED, true); + send(context, "Error while reloading config file!", ChatFormatting.RED, true); } }); return Command.SINGLE_SUCCESS; } - private static int startModpackHost(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { + private static int startModpackHost(CommandContext context) { + Util.backgroundExecutor().execute(() -> { if (!hostServer.isRunning()) { - send(context, "Starting modpack hosting...", Formatting.YELLOW, true); + send(context, "Starting modpack hosting...", ChatFormatting.YELLOW, true); hostServer.start(); if (hostServer.isRunning()) { - send(context, "Modpack hosting started!", Formatting.GREEN, true); + send(context, "Modpack hosting started!", ChatFormatting.GREEN, true); } else { - send(context, "Couldn't start server!", Formatting.RED, true); + send(context, "Couldn't start server!", ChatFormatting.RED, true); } } else { - send(context, "Modpack hosting is already running!", Formatting.RED, false); + send(context, "Modpack hosting is already running!", ChatFormatting.RED, false); } }); return Command.SINGLE_SUCCESS; } - private static int stopModpackHost(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { + private static int stopModpackHost(CommandContext context) { + Util.backgroundExecutor().execute(() -> { if (hostServer.isRunning()) { - send(context, "Stopping modpack hosting...", Formatting.RED, true); + send(context, "Stopping modpack hosting...", ChatFormatting.RED, true); if (hostServer.stop()) { - send(context, "Modpack hosting stopped!", Formatting.RED, true); + send(context, "Modpack hosting stopped!", ChatFormatting.RED, true); } else { - send(context, "Couldn't stop server!", Formatting.RED, true); + send(context, "Couldn't stop server!", ChatFormatting.RED, true); } } else { - send(context, "Modpack hosting is not running!", Formatting.RED, false); + send(context, "Modpack hosting is not running!", ChatFormatting.RED, false); } }); return Command.SINGLE_SUCCESS; } - private static int restartModpackHost(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { - send(context, "Restarting modpack hosting...", Formatting.YELLOW, true); + private static int restartModpackHost(CommandContext context) { + Util.backgroundExecutor().execute(() -> { + send(context, "Restarting modpack hosting...", ChatFormatting.YELLOW, true); boolean needStop = hostServer.isRunning(); boolean stopped = false; if (needStop) { @@ -140,13 +171,13 @@ private static int restartModpackHost(CommandContext contex } if (needStop && !stopped) { - send(context, "Couldn't restart server!", Formatting.RED, true); + send(context, "Couldn't restart server!", ChatFormatting.RED, true); } else { hostServer.start(); if (hostServer.isRunning()) { - send(context, "Modpack hosting restarted!", Formatting.GREEN, true); + send(context, "Modpack hosting restarted!", ChatFormatting.GREEN, true); } else { - send(context, "Couldn't restart server!", Formatting.RED, true); + send(context, "Couldn't restart server!", ChatFormatting.RED, true); } } }); @@ -154,55 +185,65 @@ private static int restartModpackHost(CommandContext contex return Command.SINGLE_SUCCESS; } - - private static int modpackHostAbout(CommandContext context) { - Formatting statusColor = hostServer.isRunning() ? Formatting.GREEN : Formatting.RED; + private static int modpackHostAbout(CommandContext context) { + ChatFormatting statusColor = hostServer.isRunning() ? ChatFormatting.GREEN : ChatFormatting.RED; String status = hostServer.isRunning() ? "running" : "not running"; - send(context, "Modpack hosting status", Formatting.GREEN, status, statusColor, false); + send(context, "Modpack hosting status", ChatFormatting.GREEN, status, statusColor, false); return Command.SINGLE_SUCCESS; } - private static int about(CommandContext context) { - send(context, "AutoModpack", Formatting.GREEN, AM_VERSION, Formatting.WHITE, false); - send(context, "/automodpack generate", Formatting.YELLOW, false); - send(context, "/automodpack host start/stop/restart/connections", Formatting.YELLOW, false); - send(context, "/automodpack config reload", Formatting.YELLOW, false); + private static int about(CommandContext context) { + send(context, "AutoModpack", ChatFormatting.GREEN, AM_VERSION, ChatFormatting.WHITE, false); + send(context, "/automodpack generate", ChatFormatting.YELLOW, false); + send(context, "/automodpack host start/stop/restart/connections/fingerprint", ChatFormatting.YELLOW, false); + send(context, "/automodpack config reload", ChatFormatting.YELLOW, false); return Command.SINGLE_SUCCESS; } - private static int generateModpack(CommandContext context) { - Util.getMainWorkerExecutor().execute(() -> { + private static int generateModpack(CommandContext context) { + Util.backgroundExecutor().execute(() -> { if (modpackExecutor.isGenerating()) { - send(context, "Modpack is already generating! Please wait!", Formatting.RED, false); + send(context, "Modpack is already generating! Please wait!", ChatFormatting.RED, false); return; } - send(context, "Generating Modpack...", Formatting.YELLOW, true); + send(context, "Generating Modpack...", ChatFormatting.YELLOW, true); long start = System.currentTimeMillis(); if (modpackExecutor.generateNew()) { - send(context, "Modpack generated! took " + (System.currentTimeMillis() - start) + "ms", Formatting.GREEN, true); + send(context, "Modpack generated! took " + (System.currentTimeMillis() - start) + "ms", ChatFormatting.GREEN, true); } else { - send(context, "Modpack generation failed! Check logs for more info.", Formatting.RED, true); + send(context, "Modpack generation failed! Check logs for more info.", ChatFormatting.RED, true); } }); return Command.SINGLE_SUCCESS; } - private static void send(CommandContext context, String msg, Formatting msgColor, boolean broadcast) { + private static void send(CommandContext context, String msg, ChatFormatting msgColor, boolean broadcast) { + VersionedCommandSource.sendFeedback(context, + VersionedText.literal(msg) + .withStyle(msgColor), + broadcast); + } + + private static void send(CommandContext context, String msg, ChatFormatting msgColor, String appendMsg, ChatFormatting appendMsgColor, boolean broadcast) { VersionedCommandSource.sendFeedback(context, VersionedText.literal(msg) - .formatted(msgColor), + .withStyle(msgColor) + .append(VersionedText.literal(" - ") + .withStyle(ChatFormatting.WHITE)) + .append(VersionedText.literal(appendMsg) + .withStyle(appendMsgColor)), broadcast); } - private static void send(CommandContext context, String msg, Formatting msgColor, String appendMsg, Formatting appendMsgColor, boolean broadcast) { + private static void send(CommandContext context, String msg, ChatFormatting msgColor, MutableComponent appendMsg, ChatFormatting appendMsgColor, boolean broadcast) { VersionedCommandSource.sendFeedback(context, VersionedText.literal(msg) - .formatted(msgColor) - .append(VersionedText.literal(" - ") - .formatted(Formatting.WHITE)) - .append(VersionedText.literal(appendMsg) - .formatted(appendMsgColor)), + .withStyle(msgColor) + .append(VersionedText.literal(" - ") + .withStyle(ChatFormatting.WHITE)) + .append(appendMsg + .withStyle(appendMsgColor)), broadcast); } } diff --git a/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java b/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java index 1fc9bcd9d..4d8ac6c5d 100644 --- a/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java +++ b/src/main/java/pl/skidam/automodpack/modpack/GameHelpers.java @@ -1,10 +1,11 @@ package pl.skidam.automodpack.modpack; import com.mojang.authlib.GameProfile; -import net.minecraft.util.UserCache; - import java.net.SocketAddress; import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +import net.minecraft.server.players.GameProfileCache; import static pl.skidam.automodpack.init.Common.server; @@ -12,18 +13,23 @@ public class GameHelpers { // Simpler version of `PlayerManager.checkCanJoin` public static boolean isPlayerAuthorized(SocketAddress address, GameProfile profile) { - var playerManager = server.getPlayerManager(); - if (playerManager.getUserBanList().contains(profile)) { - return false; - } - if (!playerManager.isWhitelisted(profile)) { - return false; - } - if (playerManager.getIpBanList().isBanned(address)) { - return false; - } - - return true; + AtomicBoolean isAuthorized = new AtomicBoolean(false); + server.submit(() -> { + var playerManager = server.getPlayerList(); + if (playerManager.getBans().isBanned(profile)) { + return; + } + if (!playerManager.isWhiteListed(profile)) { + return; + } + if (playerManager.getIpBans().isBanned(address)) { + return; + } + + isAuthorized.set(true); + }).join(); + + return isAuthorized.get(); } // Method to get GameProfile from UUID with accounting for a fact that this player may not be on the server right now @@ -32,9 +38,9 @@ public static GameProfile getPlayerProfile(String id) { String playerName = "Player"; // mock name, name matters less than UUID anyway GameProfile profile = new GameProfile(uuid, playerName); - UserCache userCache = server.getUserCache(); + GameProfileCache userCache = server.getProfileCache(); if (userCache != null) { - profile = userCache.getByUuid(uuid).orElse(profile); + profile = userCache.get(uuid).orElse(profile); } return profile; diff --git a/src/main/java/pl/skidam/automodpack/networking/LoginNetworkingIDs.java b/src/main/java/pl/skidam/automodpack/networking/LoginNetworkingIDs.java index 7567b18f9..62490f45d 100644 --- a/src/main/java/pl/skidam/automodpack/networking/LoginNetworkingIDs.java +++ b/src/main/java/pl/skidam/automodpack/networking/LoginNetworkingIDs.java @@ -1,10 +1,11 @@ package pl.skidam.automodpack.networking; -import net.minecraft.util.Identifier; import pl.skidam.automodpack.init.Common; import static pl.skidam.automodpack_core.GlobalVariables.MOD_ID; +import net.minecraft.resources.ResourceLocation; + public enum LoginNetworkingIDs { // AutoModpack login query id's HANDSHAKE(-100), @@ -20,11 +21,11 @@ public int getValue() { return value; } - public static Identifier getIdentifier(LoginNetworkingIDs ID) { + public static ResourceLocation getResourceLocation(LoginNetworkingIDs ID) { return Common.id(ID.toString().toLowerCase()); } - public static Integer getByKey(Identifier key) { + public static Integer getByKey(ResourceLocation key) { if (key.getNamespace().equalsIgnoreCase(MOD_ID)) { for (var ID : LoginNetworkingIDs.values()) { if (ID.name().equalsIgnoreCase(key.getPath())) { @@ -35,10 +36,10 @@ public static Integer getByKey(Identifier key) { return null; } - public static Identifier getByValue(int value) { + public static ResourceLocation getByValue(int value) { for (var ID : LoginNetworkingIDs.values()) { if (ID.getValue() == value) { - return getIdentifier(ID); + return getResourceLocation(ID); } } return null; diff --git a/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java b/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java index 5ab854890..15d3cd146 100644 --- a/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java +++ b/src/main/java/pl/skidam/automodpack/networking/LoginQueryParser.java @@ -1,53 +1,50 @@ package pl.skidam.automodpack.networking; -/*? if <=1.19.2 {*/ -/*import net.minecraft.network.Packet; -*//*?} else {*/ -import net.minecraft.network.packet.Packet; -/*?}*/ -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; -import net.minecraft.util.Identifier; /*? if >=1.20.2 {*/ import pl.skidam.automodpack.networking.client.LoginResponsePayload; import pl.skidam.automodpack.networking.server.LoginRequestPayload; -import net.minecraft.network.packet.c2s.login.LoginQueryResponsePayload; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestPayload; +import net.minecraft.network.protocol.login.custom.CustomQueryAnswerPayload; +import net.minecraft.network.protocol.login.custom.CustomQueryPayload; /*?}*/ import static pl.skidam.automodpack_core.GlobalVariables.LOGGER; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.resources.ResourceLocation; + public class LoginQueryParser { public Packet packet; public boolean success = true; public int queryId; - public PacketByteBuf buf; - public Identifier channelName; + public FriendlyByteBuf buf; + public ResourceLocation channelName; public LoginQueryParser(Packet packet) { - if (packet instanceof LoginQueryRequestS2CPacket packetS2C) { + if (packet instanceof ClientboundCustomQueryPacket packetS2C) { this.packet = packetS2C; /*? if <1.20.2 {*/ - /*this.queryId = packetS2C.getQueryId(); - this.buf = packetS2C.getPayload(); - this.channelName = packetS2C.getChannel(); + /*this.queryId = packetS2C.getTransactionId(); + this.buf = packetS2C.getData(); + this.channelName = packetS2C.getIdentifier(); *//*?} else {*/ - this.queryId = packetS2C.queryId(); - LoginQueryRequestPayload payload = packetS2C.payload(); + this.queryId = packetS2C.transactionId(); + CustomQueryPayload payload = packetS2C.payload(); if (payload instanceof LoginRequestPayload loginRequestPayload) { this.buf = loginRequestPayload.data(); this.channelName = loginRequestPayload.id(); } /*?}*/ - } else if (packet instanceof LoginQueryResponseC2SPacket packetC2S) { + } else if (packet instanceof ServerboundCustomQueryAnswerPacket packetC2S) { this.packet = packetC2S; /*? if <1.20.2 {*/ - /*this.queryId = packetC2S.getQueryId(); - this.buf = packetC2S.getResponse(); + /*this.queryId = packetC2S.getTransactionId(); + this.buf = packetC2S.getData(); *//*?} else {*/ - this.queryId = packetC2S.queryId(); - LoginQueryResponsePayload payload = packetC2S.response(); + this.queryId = packetC2S.transactionId(); + CustomQueryAnswerPayload payload = packetC2S.payload(); if (payload instanceof LoginResponsePayload loginRequestPayload) { this.buf = loginRequestPayload.data(); this.channelName = loginRequestPayload.id(); diff --git a/src/main/java/pl/skidam/automodpack/networking/ModPackets.java b/src/main/java/pl/skidam/automodpack/networking/ModPackets.java index db6532de6..f941bd531 100644 --- a/src/main/java/pl/skidam/automodpack/networking/ModPackets.java +++ b/src/main/java/pl/skidam/automodpack/networking/ModPackets.java @@ -1,10 +1,10 @@ package pl.skidam.automodpack.networking; import io.netty.buffer.Unpooled; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.util.Identifier; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import pl.skidam.automodpack.networking.client.ClientLoginNetworking; import pl.skidam.automodpack.networking.content.HandshakePacket; import pl.skidam.automodpack.networking.packet.HandshakeC2SPacket; @@ -19,8 +19,8 @@ import static pl.skidam.automodpack_core.GlobalVariables.*; public class ModPackets { - public static final Identifier HANDSHAKE = LoginNetworkingIDs.getIdentifier(LoginNetworkingIDs.HANDSHAKE); - public static final Identifier DATA = LoginNetworkingIDs.getIdentifier(LoginNetworkingIDs.DATA); + public static final ResourceLocation HANDSHAKE = LoginNetworkingIDs.getResourceLocation(LoginNetworkingIDs.HANDSHAKE); + public static final ResourceLocation DATA = LoginNetworkingIDs.getResourceLocation(LoginNetworkingIDs.DATA); private static InetSocketAddress originalServerAddress; @@ -46,14 +46,14 @@ public static void registerS2CPackets() { } // Fires just after client go into login state and before any FML packet is sent. - public static void onReady(ServerLoginNetworkHandler handler, MinecraftServer server, ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender sender) { + public static void onReady(ServerLoginPacketListenerImpl handler, MinecraftServer server, ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender sender) { synchronizer.waitFor(server.submit(() -> { - PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); HandshakePacket handshakePacket = new HandshakePacket(serverConfig.acceptedLoaders, AM_VERSION, MC_VERSION); String jsonHandshakePacket = handshakePacket.toJson(); - buf.writeString(jsonHandshakePacket, Short.MAX_VALUE); + buf.writeUtf(jsonHandshakePacket, Short.MAX_VALUE); sender.sendPacket(HANDSHAKE, buf); })); } diff --git a/src/main/java/pl/skidam/automodpack/networking/PacketSender.java b/src/main/java/pl/skidam/automodpack/networking/PacketSender.java index 2b105eda5..a1bc268fc 100644 --- a/src/main/java/pl/skidam/automodpack/networking/PacketSender.java +++ b/src/main/java/pl/skidam/automodpack/networking/PacketSender.java @@ -1,10 +1,9 @@ package pl.skidam.automodpack.networking; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; -import net.minecraft.util.Identifier; - import java.util.Objects; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.resources.ResourceLocation; // credits to fabric api public interface PacketSender { @@ -15,14 +14,14 @@ public interface PacketSender { * @param channelName the id of the channel * @param buf the content of the packet */ - LoginQueryRequestS2CPacket createPacket(Identifier channelName, PacketByteBuf buf); + ClientboundCustomQueryPacket createPacket(ResourceLocation channelName, FriendlyByteBuf buf); /** * Sends a packet. * * @param packet the packet */ - void sendPacket(LoginQueryRequestS2CPacket packet); + void sendPacket(ClientboundCustomQueryPacket packet); /** * Sends a packet to a channel. @@ -30,7 +29,7 @@ public interface PacketSender { * @param channel the id of the channel * @param buf the content of the packet */ - default void sendPacket(Identifier channel, PacketByteBuf buf) { + default void sendPacket(ResourceLocation channel, FriendlyByteBuf buf) { Objects.requireNonNull(channel, "Channel cannot be null"); Objects.requireNonNull(buf, "Payload cannot be null"); diff --git a/src/main/java/pl/skidam/automodpack/networking/PayloadHelper.java b/src/main/java/pl/skidam/automodpack/networking/PayloadHelper.java index 9179492cf..b5da6d864 100644 --- a/src/main/java/pl/skidam/automodpack/networking/PayloadHelper.java +++ b/src/main/java/pl/skidam/automodpack/networking/PayloadHelper.java @@ -2,25 +2,25 @@ /*? if >=1.20.2 {*/ import io.netty.buffer.Unpooled; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.FriendlyByteBuf; // credits to fabric api public class PayloadHelper { - public static void write(PacketByteBuf byteBuf, PacketByteBuf data) { + public static void write(FriendlyByteBuf byteBuf, FriendlyByteBuf data) { byteBuf.writeBytes(data.copy()); } - public static PacketByteBuf read(PacketByteBuf byteBuf, int maxSize) { + public static FriendlyByteBuf read(FriendlyByteBuf byteBuf, int maxSize) { assertSize(byteBuf, maxSize); - PacketByteBuf newBuf = new PacketByteBuf(Unpooled.buffer()); + FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer()); newBuf.writeBytes(byteBuf.copy()); byteBuf.skipBytes(byteBuf.readableBytes()); return newBuf; } - private static void assertSize(PacketByteBuf buf, int maxSize) { + private static void assertSize(FriendlyByteBuf buf, int maxSize) { int size = buf.readableBytes(); if (size < 0 || size > maxSize) { diff --git a/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworkAddon.java b/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworkAddon.java index 80cda0100..b84b74d9b 100644 --- a/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworkAddon.java +++ b/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworkAddon.java @@ -1,25 +1,25 @@ package pl.skidam.automodpack.networking.client; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; -import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; import pl.skidam.automodpack.mixin.core.ClientLoginNetworkHandlerAccessor; import pl.skidam.automodpack.networking.LoginQueryParser; import java.util.concurrent.CompletableFuture; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.resources.ResourceLocation; import static pl.skidam.automodpack_core.GlobalVariables.LOGGER; // credits to fabric api public class ClientLoginNetworkAddon { - private final ClientLoginNetworkHandler handler; - private final MinecraftClient client; + private final ClientHandshakePacketListenerImpl handler; + private final Minecraft client; - public ClientLoginNetworkAddon(ClientLoginNetworkHandler clientLoginNetworkHandler, MinecraftClient client) { + public ClientLoginNetworkAddon(ClientHandshakePacketListenerImpl clientLoginNetworkHandler, Minecraft client) { this.handler = clientLoginNetworkHandler; this.client = client; } @@ -30,25 +30,25 @@ public ClientLoginNetworkAddon(ClientLoginNetworkHandler clientLoginNetworkHandl * @param packet the packet to handle * @return true if the packet was handled */ - public boolean handlePacket(LoginQueryRequestS2CPacket packet) { + public boolean handlePacket(ClientboundCustomQueryPacket packet) { LoginQueryParser loginQuery = new LoginQueryParser(packet); if (loginQuery.success) return handlePacket(loginQuery.queryId, loginQuery.channelName, loginQuery.buf); return false; } - private boolean handlePacket(int queryId, Identifier channelName, PacketByteBuf payload) { + private boolean handlePacket(int queryId, ResourceLocation channelName, FriendlyByteBuf payload) { @Nullable ClientLoginNetworking.LoginQueryRequestHandler handler = ClientLoginNetworking.getHandler(channelName); if (handler == null) { return false; } - PacketByteBuf buf = new PacketByteBuf(payload.slice()); + FriendlyByteBuf buf = new FriendlyByteBuf(payload.slice()); try { - CompletableFuture<@Nullable PacketByteBuf> future = handler.receive(this.client, this.handler, buf); + CompletableFuture future = handler.receive(this.client, this.handler, buf); future.thenAccept(resultBuf -> { - LoginQueryResponseC2SPacket packet = new LoginQueryResponseC2SPacket(queryId, /*? if <1.20.2 {*/ /*resultBuf *//*?} else {*/ new LoginResponsePayload(channelName, resultBuf) /*?}*/); + ServerboundCustomQueryAnswerPacket packet = new ServerboundCustomQueryAnswerPacket(queryId, /*? if <1.20.2 {*/ /*resultBuf *//*?} else {*/ new LoginResponsePayload(channelName, resultBuf) /*?}*/); ((ClientLoginNetworkHandlerAccessor) this.handler).getConnection().send(packet); }); } catch (Throwable e) { diff --git a/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworking.java b/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworking.java index 40ebfe8f2..401cd6d68 100644 --- a/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworking.java +++ b/src/main/java/pl/skidam/automodpack/networking/client/ClientLoginNetworking.java @@ -1,20 +1,18 @@ package pl.skidam.automodpack.networking.client; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.Nullable; - import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; // credits to fabric api public class ClientLoginNetworking { - private static final Map handlers = new HashMap<>(); + private static final Map handlers = new HashMap<>(); /** * Registers a handler to a query request channel. @@ -23,14 +21,14 @@ public class ClientLoginNetworking { * @param channelName the id of the channel * @param handler the handler */ - public static void registerGlobalReceiver(Identifier channelName, LoginQueryRequestHandler handler) { + public static void registerGlobalReceiver(ResourceLocation channelName, LoginQueryRequestHandler handler) { Objects.requireNonNull(channelName, "Channel name cannot be null"); Objects.requireNonNull(handler, "Channel handler cannot be null"); handlers.put(channelName, handler); } - public static LoginQueryRequestHandler getHandler(Identifier channelName) { + public static LoginQueryRequestHandler getHandler(ResourceLocation channelName) { return handlers.get(channelName); } @@ -40,7 +38,7 @@ public interface LoginQueryRequestHandler { * Handles an incoming query request from a server. * *

This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}. - * Modification to the game should be {@linkplain net.minecraft.util.thread.ThreadExecutor#submit(Runnable) scheduled} using the provided Minecraft client instance. + * Modification to the game should be {@linkplain net.minecraft.util.thread.BlockableEventLoop#submit(Runnable) scheduled} using the provided Minecraft client instance. * *

The return value of this method is a completable future that may be used to delay the login process to the server until a task {@link CompletableFuture#isDone() is done}. * The future should complete in reasonably time to prevent disconnection by the server. @@ -52,6 +50,6 @@ public interface LoginQueryRequestHandler { * @return a completable future which contains the payload to respond to the server with. * If the future contains {@code null}, then the server will be notified that the client did not understand the query. */ - CompletableFuture<@Nullable PacketByteBuf> receive(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf); + CompletableFuture receive(Minecraft client, ClientHandshakePacketListenerImpl handler, FriendlyByteBuf buf); } } diff --git a/src/main/java/pl/skidam/automodpack/networking/client/LoginResponsePayload.java b/src/main/java/pl/skidam/automodpack/networking/client/LoginResponsePayload.java index 785385564..75e76398c 100644 --- a/src/main/java/pl/skidam/automodpack/networking/client/LoginResponsePayload.java +++ b/src/main/java/pl/skidam/automodpack/networking/client/LoginResponsePayload.java @@ -1,18 +1,18 @@ package pl.skidam.automodpack.networking.client; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.util.Identifier; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; /*? if <1.20.2 {*/ -/*public record LoginResponsePayload(Identifier id, PacketByteBuf data) { } +/*public record LoginResponsePayload(ResourceLocation id, FriendlyByteBuf data) { } *//*?} else {*/ -import net.minecraft.network.packet.c2s.login.LoginQueryResponsePayload; +import net.minecraft.network.protocol.login.custom.CustomQueryAnswerPayload; import pl.skidam.automodpack.networking.PayloadHelper; -public record LoginResponsePayload(Identifier id, PacketByteBuf data) implements LoginQueryResponsePayload { +public record LoginResponsePayload(ResourceLocation id, FriendlyByteBuf data) implements CustomQueryAnswerPayload { @Override - public void write(PacketByteBuf buf) { + public void write(FriendlyByteBuf buf) { PayloadHelper.write(buf, data()); } } -/*}*/ \ No newline at end of file +/*?}*/ \ No newline at end of file diff --git a/src/main/java/pl/skidam/automodpack/networking/content/DataPacket.java b/src/main/java/pl/skidam/automodpack/networking/content/DataPacket.java index aa2775d88..913bad6c7 100644 --- a/src/main/java/pl/skidam/automodpack/networking/content/DataPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/content/DataPacket.java @@ -5,17 +5,19 @@ public class DataPacket { public String address; - public Integer port; + public int port; public String modpackName; public Secrets.Secret secret; public boolean modRequired; + public boolean requiresMagic; - public DataPacket(String address, Integer port, String modpackName, Secrets.Secret secret, boolean modRequired) { + public DataPacket(String address, int port, String modpackName, Secrets.Secret secret, boolean modRequired, boolean requiresMagic) { this.address = address; this.port = port; this.modpackName = modpackName; this.secret = secret; this.modRequired = modRequired; + this.requiresMagic = requiresMagic; } public String toJson() { diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java index a6387064b..25715a3c9 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/DataC2SPacket.java @@ -1,9 +1,6 @@ package pl.skidam.automodpack.networking.packet; import io.netty.buffer.Unpooled; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.PacketByteBuf; import pl.skidam.automodpack.mixin.core.ClientConnectionAccessor; import pl.skidam.automodpack.mixin.core.ClientLoginNetworkHandlerAccessor; import pl.skidam.automodpack.networking.ModPackets; @@ -20,23 +17,28 @@ import java.net.InetSocketAddress; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.FriendlyByteBuf; import static pl.skidam.automodpack_core.GlobalVariables.*; import static pl.skidam.automodpack_core.config.ConfigTools.GSON; public class DataC2SPacket { - public static CompletableFuture receive(MinecraftClient minecraftClient, ClientLoginNetworkHandler handler, PacketByteBuf buf) { + public static CompletableFuture receive(Minecraft Minecraft, ClientHandshakePacketListenerImpl handler, FriendlyByteBuf buf) { try { - String serverResponse = buf.readString(Short.MAX_VALUE); - + String serverResponse = buf.readUtf(Short.MAX_VALUE); DataPacket dataPacket = DataPacket.fromJson(serverResponse); + String packetAddress = dataPacket.address; - Integer packetPort = dataPacket.port; + int packetPort = dataPacket.port; String modpackName = dataPacket.modpackName; Secrets.Secret secret = dataPacket.secret; boolean modRequired = dataPacket.modRequired; + boolean requiresMagic = dataPacket.requiresMagic; if (modRequired) { // TODO set screen to refreshed danger screen which will ask user to install modpack with two options @@ -48,28 +50,37 @@ public static CompletableFuture receive(MinecraftClient minecraft ModPackets.setOriginalServerAddress(null); // Reset for next server reconnection if (serverAddress == null) { LOGGER.error("Server address is null! Something gone very wrong! Please report this issue! https://github.com/Skidamek/AutoModpack/issues"); - return CompletableFuture.completedFuture(new PacketByteBuf(Unpooled.buffer())); + return CompletableFuture.completedFuture(new FriendlyByteBuf(Unpooled.buffer())); } // Get actual address of the server client have connected to and format it - InetSocketAddress modpackAddress = (InetSocketAddress) ((ClientLoginNetworkHandlerAccessor) handler).getConnection().getAddress(); - modpackAddress = AddressHelpers.format(modpackAddress.getHostString(), modpackAddress.getPort()); + InetSocketAddress connectedAddress = (InetSocketAddress) ((ClientLoginNetworkHandlerAccessor) handler).getConnection().getRemoteAddress(); + String effectiveHost; + int effectivePort; + // If the packet specifies a non-blank address, use it or else use address from the server client have connected to. if (packetAddress.isBlank()) { - LOGGER.info("Address from connected server: {}:{}", modpackAddress.getHostString(), modpackAddress.getPort()); - } else if (packetPort != null) { - modpackAddress = InetSocketAddress.createUnresolved(packetAddress, packetPort); - LOGGER.info("Received address packet from server! {}:{}", packetAddress, packetPort); + effectiveHost = connectedAddress.getHostString(); } else { - modpackAddress = AddressHelpers.parse(packetAddress); - LOGGER.info("Received address packet from server! {} With attached port: {}", modpackAddress.getHostString(), modpackAddress.getPort()); + effectiveHost = packetAddress; } + if (packetPort == -1) { + effectivePort = connectedAddress.getPort(); + } else { + effectivePort = packetPort; + } + + // Construct the final modpack address + InetSocketAddress modpackAddress = AddressHelpers.format(effectiveHost, effectivePort); + + LOGGER.info("Modpack address: {}:{} Requires to follow magic protocol: {}", modpackAddress.getHostString(), modpackAddress.getPort(), requiresMagic); + Boolean needsDisconnecting = null; - PacketByteBuf response = new PacketByteBuf(Unpooled.buffer()); + FriendlyByteBuf response = new FriendlyByteBuf(Unpooled.buffer()); Path modpackDir = ModpackUtils.getModpackPath(modpackAddress, modpackName); - Jsons.ModpackAddresses modpackAddresses = new Jsons.ModpackAddresses(modpackAddress, serverAddress); + Jsons.ModpackAddresses modpackAddresses = new Jsons.ModpackAddresses(modpackAddress, serverAddress, requiresMagic); var optionalServerModpackContent = ModpackUtils.requestServerModpackContent(modpackAddresses, secret, true); if (optionalServerModpackContent.isPresent()) { @@ -105,18 +116,18 @@ public static CompletableFuture receive(MinecraftClient minecraft SecretsStore.saveClientSecret(clientConfig.selectedModpack, secret); } - response.writeString(String.valueOf(needsDisconnecting), Short.MAX_VALUE); + response.writeUtf(String.valueOf(needsDisconnecting), Short.MAX_VALUE); return CompletableFuture.completedFuture(response); } catch (Exception e) { LOGGER.error("Error while handling data packet", e); - PacketByteBuf response = new PacketByteBuf(Unpooled.buffer()); - response.writeString("null", Short.MAX_VALUE); - return CompletableFuture.completedFuture(new PacketByteBuf(Unpooled.buffer())); + FriendlyByteBuf response = new FriendlyByteBuf(Unpooled.buffer()); + response.writeUtf("null", Short.MAX_VALUE); + return CompletableFuture.completedFuture(new FriendlyByteBuf(Unpooled.buffer())); } } - private static void disconnectImmediately(ClientLoginNetworkHandler clientLoginNetworkHandler) { + private static void disconnectImmediately(ClientHandshakePacketListenerImpl clientLoginNetworkHandler) { ((ClientConnectionAccessor) ((ClientLoginNetworkHandlerAccessor) clientLoginNetworkHandler).getConnection()).getChannel().disconnect(); } } diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java index e82bd0c44..09cb92d52 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/DataS2CPacket.java @@ -1,15 +1,14 @@ package pl.skidam.automodpack.networking.packet; import com.mojang.authlib.GameProfile; -import net.minecraft.network.ClientConnection; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.s2c.login.LoginDisconnectS2CPacket; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.text.Text; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import pl.skidam.automodpack.networking.PacketSender; import pl.skidam.automodpack.networking.server.ServerLoginNetworking; -import pl.skidam.automodpack_core.GlobalVariables; import pl.skidam.automodpack.client.ui.versioned.VersionedText; import pl.skidam.automodpack.mixin.core.ServerLoginNetworkHandlerAccessor; @@ -17,7 +16,7 @@ public class DataS2CPacket { - public static void receive(MinecraftServer server, ServerLoginNetworkHandler handler, boolean understood, PacketByteBuf buf, ServerLoginNetworking.LoginSynchronizer loginSynchronizer, PacketSender sender) { + public static void receive(MinecraftServer server, ServerLoginPacketListenerImpl handler, boolean understood, FriendlyByteBuf buf, ServerLoginNetworking.LoginSynchronizer loginSynchronizer, PacketSender sender) { try { GameProfile profile = ((ServerLoginNetworkHandlerAccessor) handler).getGameProfile(); @@ -29,44 +28,40 @@ public static void receive(MinecraftServer server, ServerLoginNetworkHandler han return; } - String clientHasUpdate = buf.readString(Short.MAX_VALUE); + String clientHasUpdate = buf.readUtf(Short.MAX_VALUE); if ("true".equals(clientHasUpdate)) { // disconnect - LOGGER.warn("{} has not installed modpack. Certificate fingerprint to verify: {}", profile.getName(), hostServer.getCertificateFingerprint()); - Text reason = VersionedText.literal("[AutoModpack] Install/Update modpack to join"); - ClientConnection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); - connection.send(new LoginDisconnectS2CPacket(reason)); + LOGGER.warn("{} has not installed modpack. Certificate fingerprint: {}", profile.getName(), hostServer.getCertificateFingerprint()); + Component reason = VersionedText.literal("[AutoModpack] Install/Update modpack to join"); + Connection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); + connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); } else if ("false".equals(clientHasUpdate)) { LOGGER.info("{} has installed whole modpack", profile.getName()); } else { - Text reason = VersionedText.literal("[AutoModpack] Host server error. Please contact server administrator to check the server logs!"); - ClientConnection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); - connection.send(new LoginDisconnectS2CPacket(reason)); + Component reason = VersionedText.literal("[AutoModpack] Host server error. Please contact server administrator to check the server logs!"); + Connection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); + connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); LOGGER.error("Host server error. AutoModpack host server is down or server is not configured correctly"); - if (serverConfig.hostModpackOnMinecraftPort) { - LOGGER.warn("You are hosting AutoModpack host server on the minecraft port."); - LOGGER.warn("However client can't access it, try making `hostIp` and `hostLocalIp` blank in the server config."); - LOGGER.warn("If that doesn't work, follow the steps bellow."); - LOGGER.warn(""); + if (serverConfig.bindPort == -1) { + LOGGER.warn("You are hosting AutoModpack host server on the Minecraft port."); } else { - LOGGER.warn("Please check if AutoModpack host server (TCP) port '{}' is forwarded / opened correctly", GlobalVariables.serverConfig.hostPort); - LOGGER.warn(""); + LOGGER.warn("Please check if AutoModpack host server (TCP) port '{}' is forwarded / opened correctly", serverConfig.bindPort); } - LOGGER.warn("Make sure that host IP '{}' and host local IP '{}' are correct in the config file!", GlobalVariables.serverConfig.hostIp, GlobalVariables.serverConfig.hostLocalIp); - LOGGER.warn("host IP should be an ip which are players outside of server network connecting to and host local IP should be an ip which are players inside of server network connecting to"); - LOGGER.warn("It can be Ip or a correctly set domain"); - LOGGER.warn("If you need, change port in config file, forward / open it and restart server"); + LOGGER.warn("Make sure that 'addressToSend' is correctly set in the config file!"); + LOGGER.warn("It can be either an IP address or a domain pointing to your modpack host server."); + LOGGER.warn("If nothing works, try changing the 'bindPort' in the config file, then forward / open it and restart server"); + LOGGER.warn("Note that some hosting providers may proxy this port internally and give you a different address and port to use. In this case, separate the given address with ':', and set the first part as 'addressToSend' and the second part as 'portToSend' in the config file."); - if (serverConfig.reverseProxy) { - LOGGER.error("Turn off reverseProxy in config, if you don't actually use it!"); + if (serverConfig.bindPort != serverConfig.portToSend && serverConfig.bindPort != -1 && serverConfig.portToSend != -1) { + LOGGER.error("bindPort '{}' is different than portToSend '{}'. If you are not using reverse proxy, match them! If you do use reverse proxy, make sure it is setup correctly.", serverConfig.bindPort, serverConfig.portToSend); } - LOGGER.warn("Server certificate fingerprint to verify: {}", hostServer.getCertificateFingerprint()); + LOGGER.warn("Server certificate fingerprint: {}", hostServer.getCertificateFingerprint()); } } catch (Exception e) { LOGGER.error("Error while handling DataS2CPacket", e); diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java index d4ee10d20..fa6735d06 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeC2SPacket.java @@ -1,9 +1,6 @@ package pl.skidam.automodpack.networking.packet; import io.netty.buffer.Unpooled; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientLoginNetworkHandler; -import net.minecraft.network.PacketByteBuf; import pl.skidam.automodpack.mixin.core.ClientConnectionAccessor; import pl.skidam.automodpack.mixin.core.ClientLoginNetworkHandlerAccessor; import pl.skidam.automodpack.networking.content.HandshakePacket; @@ -12,27 +9,31 @@ import java.util.List; import java.util.concurrent.CompletableFuture; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientHandshakePacketListenerImpl; +import net.minecraft.network.FriendlyByteBuf; import static pl.skidam.automodpack_core.GlobalVariables.*; +import static pl.skidam.automodpack_loader_core.SelfUpdater.validUpdate; public class HandshakeC2SPacket { - public static CompletableFuture receive(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf) { + public static CompletableFuture receive(Minecraft client, ClientHandshakePacketListenerImpl handler, FriendlyByteBuf buf) { try { - String serverResponse = buf.readString(Short.MAX_VALUE); + String serverResponse = buf.readUtf(Short.MAX_VALUE); HandshakePacket serverHandshakePacket = HandshakePacket.fromJson(serverResponse); String loader = LOADER_MANAGER.getPlatformType().toString().toLowerCase(); - PacketByteBuf outBuf = new PacketByteBuf(Unpooled.buffer()); + FriendlyByteBuf outBuf = new FriendlyByteBuf(Unpooled.buffer()); HandshakePacket clientHandshakePacket = new HandshakePacket(List.of(loader), AM_VERSION, MC_VERSION); - outBuf.writeString(clientHandshakePacket.toJson(), Short.MAX_VALUE); + outBuf.writeUtf(clientHandshakePacket.toJson(), Short.MAX_VALUE); if (serverHandshakePacket.equals(clientHandshakePacket) || (serverHandshakePacket.loaders.contains(loader) && serverHandshakePacket.amVersion.equals(AM_VERSION))) { - LOGGER.info("Versions match " + serverHandshakePacket.amVersion); + LOGGER.info("Versions match {}", serverHandshakePacket.amVersion); } else { - LOGGER.warn("Versions mismatch " + serverHandshakePacket.amVersion); + LOGGER.warn("Versions mismatch. Server: {}: Client: {}", serverHandshakePacket.amVersion, AM_VERSION); LOGGER.info("Trying to change automodpack version to the version required by server..."); updateMod(handler, serverHandshakePacket.amVersion, serverHandshakePacket.mcVersion); } @@ -40,11 +41,11 @@ public static CompletableFuture receive(MinecraftClient client, C return CompletableFuture.completedFuture(outBuf); } catch (Exception e) { LOGGER.error("Error while handling HandshakeC2SPacket", e); - return CompletableFuture.completedFuture(new PacketByteBuf(Unpooled.buffer())); + return CompletableFuture.completedFuture(new FriendlyByteBuf(Unpooled.buffer())); } } - private static void updateMod(ClientLoginNetworkHandler handler, String serverAMVersion, String serverMCVersion) { + private static void updateMod(ClientHandshakePacketListenerImpl handler, String serverAMVersion, String serverMCVersion) { if (!serverMCVersion.equals(MC_VERSION)) { return; } @@ -58,8 +59,11 @@ private static void updateMod(ClientLoginNetworkHandler handler, String serverAM return; } - ((ClientConnectionAccessor) ((ClientLoginNetworkHandlerAccessor) handler).getConnection()).getChannel().disconnect(); + if (!validUpdate(automodpack)) { + return; + } + ((ClientConnectionAccessor) ((ClientLoginNetworkHandlerAccessor) handler).getConnection()).getChannel().disconnect(); SelfUpdater.installModVersion(automodpack); } } diff --git a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java index 5f68a5467..d88c49e75 100644 --- a/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java +++ b/src/main/java/pl/skidam/automodpack/networking/packet/HandshakeS2CPacket.java @@ -2,12 +2,12 @@ import com.mojang.authlib.GameProfile; import io.netty.buffer.Unpooled; -import net.minecraft.network.ClientConnection; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.s2c.login.LoginDisconnectS2CPacket; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket; import net.minecraft.server.*; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.text.Text; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import pl.skidam.automodpack.client.ui.versioned.VersionedText; import pl.skidam.automodpack.init.Common; import pl.skidam.automodpack.mixin.core.ServerLoginNetworkHandlerAccessor; @@ -18,7 +18,6 @@ import pl.skidam.automodpack.networking.server.ServerLoginNetworking; import pl.skidam.automodpack_core.auth.Secrets; import pl.skidam.automodpack_core.auth.SecretsStore; -import pl.skidam.automodpack_core.utils.AddressHelpers; import java.nio.charset.StandardCharsets; import java.util.UUID; @@ -28,8 +27,8 @@ public class HandshakeS2CPacket { - public static void receive(MinecraftServer server, ServerLoginNetworkHandler handler, boolean understood, PacketByteBuf buf, ServerLoginNetworking.LoginSynchronizer loginSynchronizer, PacketSender sender) { - ClientConnection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); + public static void receive(MinecraftServer server, ServerLoginPacketListenerImpl handler, boolean understood, FriendlyByteBuf buf, ServerLoginNetworking.LoginSynchronizer loginSynchronizer, PacketSender sender) { + Connection connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); GameProfile profile = ((ServerLoginNetworkHandlerAccessor) handler).getGameProfile(); String playerName = profile.getName(); @@ -48,11 +47,11 @@ public static void receive(MinecraftServer server, ServerLoginNetworkHandler han profile = new GameProfile(offlineUUID, playerName); } - if (!connection.isEncrypted()) { - LOGGER.warn("Connection is not encrypted for player: {}", playerName); - } +// if (!connection.isEncrypted()) { +// LOGGER.warn("Connection is not encrypted for player: {}", playerName); +// } - if (!GameHelpers.isPlayerAuthorized(connection.getAddress(), profile)) { + if (!GameHelpers.isPlayerAuthorized(connection.getRemoteAddress(), profile)) { return; } @@ -60,22 +59,22 @@ public static void receive(MinecraftServer server, ServerLoginNetworkHandler han Common.players.put(playerName, false); LOGGER.warn("{} has not installed AutoModpack.", playerName); if (serverConfig.requireAutoModpackOnClient) { - Text reason = VersionedText.literal("AutoModpack mod for " + LOADER_MANAGER.getPlatformType().toString().toLowerCase() + " modloader is required to play on this server!"); - connection.send(new LoginDisconnectS2CPacket(reason)); + Component reason = VersionedText.literal("AutoModpack mod for " + LOADER_MANAGER.getPlatformType().toString().toLowerCase() + " modloader is required to play on this server!"); + connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); } } else { Common.players.put(playerName, true); GameProfile finalProfile = profile; - loginSynchronizer.waitFor(server.submit(() -> handleHandshake(connection, finalProfile, server.getServerPort(), buf, sender))); + loginSynchronizer.waitFor(server.submit(() -> handleHandshake(connection, finalProfile, server.getPort(), buf, sender))); } } - public static void handleHandshake(ClientConnection connection, GameProfile profile, int minecraftServerPort, PacketByteBuf buf, PacketSender packetSender) { + public static void handleHandshake(Connection connection, GameProfile profile, int minecraftServerPort, FriendlyByteBuf buf, PacketSender packetSender) { try { LOGGER.info("{} has installed AutoModpack.", profile.getName()); - String clientResponse = buf.readString(Short.MAX_VALUE); + String clientResponse = buf.readUtf(Short.MAX_VALUE); HandshakePacket clientHandshakePacket = HandshakePacket.fromJson(clientResponse); boolean isAcceptedLoader = false; @@ -87,11 +86,11 @@ public static void handleHandshake(ClientConnection connection, GameProfile prof } if (!isAcceptedLoader || !clientHandshakePacket.amVersion.equals(AM_VERSION)) { - Text reason = VersionedText.literal("AutoModpack version mismatch! Install " + AM_VERSION + " version of AutoModpack mod for " + LOADER_MANAGER.getPlatformType().toString().toLowerCase() + " to play on this server!"); + Component reason = VersionedText.literal("AutoModpack version mismatch! Install " + AM_VERSION + " version of AutoModpack mod for " + LOADER_MANAGER.getPlatformType().toString().toLowerCase() + " to play on this server!"); if (isClientVersionHigher(clientHandshakePacket.amVersion)) { reason = VersionedText.literal("You are using a more recent version of AutoModpack than the server. Please contact the server administrator to update the AutoModpack mod."); } - connection.send(new LoginDisconnectS2CPacket(reason)); + connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); return; } @@ -102,60 +101,28 @@ public static void handleHandshake(ClientConnection connection, GameProfile prof } if (modpackExecutor.isGenerating()) { - Text reason = VersionedText.literal("AutoModapck is generating modpack. Please wait a moment and try again."); - connection.send(new LoginDisconnectS2CPacket(reason)); + Component reason = VersionedText.literal("AutoModpack is generating modpack. Please wait a moment and try again."); + connection.send(new ClientboundLoginDisconnectPacket(reason)); connection.disconnect(reason); return; } - String playerAddress = connection.getAddress().toString(); - String addressToSend; - - // If the player is connecting locally, use the local host IP - if (AddressHelpers.isLocal(playerAddress)) { - addressToSend = serverConfig.hostLocalIp; - } else { - addressToSend = serverConfig.hostIp; - } - // now we know player is authenticated, packets are encrypted and player is whitelisted // regenerate unique secret Secrets.Secret secret = Secrets.generateSecret(); SecretsStore.saveHostSecret(profile.getId().toString(), secret); - // We send empty string if hostIp/hostLocalIp is not specified in server config. Client will use ip by which it connected to the server in first place. - DataPacket dataPacket = new DataPacket(addressToSend, null, serverConfig.modpackName, secret, serverConfig.requireAutoModpackOnClient); - - if (serverConfig.reverseProxy) { - // With reverse proxy we dont append port to the link, it should be already included in the link - // But we need to check if the port is set in the config, since that's where modpack is actually hosted - if (serverConfig.hostPort == -1 && !serverConfig.hostModpackOnMinecraftPort) { - LOGGER.error("Reverse proxy is enabled but host port is not set in config! Please set it manually."); - } - - LOGGER.info("Sending {} modpack url: {}", profile.getName(), addressToSend); - } else { // Append server port - int portToSend; - if (serverConfig.hostModpackOnMinecraftPort) { - portToSend = minecraftServerPort; - } else { - portToSend = serverConfig.hostPort; - - if (serverConfig.hostPort == -1) { - LOGGER.error("Host port is not set in config! Please set it manually."); - } - } + String addressToSend = serverConfig.addressToSend; + int portToSend = serverConfig.portToSend; + boolean requiresMagic = serverConfig.bindPort == -1; - if (!addressToSend.isBlank()) { - LOGGER.info("Sending {} modpack url: {}:{}", profile.getName(), addressToSend, portToSend); - } - dataPacket = new DataPacket(addressToSend, portToSend, serverConfig.modpackName, secret, serverConfig.requireAutoModpackOnClient); - } + LOGGER.info("Sending {} modpack host address: {}:{}", profile.getName(), addressToSend, portToSend); + DataPacket dataPacket = new DataPacket(addressToSend, portToSend, serverConfig.modpackName, secret, serverConfig.requireAutoModpackOnClient, requiresMagic); String packetContentJson = dataPacket.toJson(); - PacketByteBuf outBuf = new PacketByteBuf(Unpooled.buffer()); - outBuf.writeString(packetContentJson, Short.MAX_VALUE); + FriendlyByteBuf outBuf = new FriendlyByteBuf(Unpooled.buffer()); + outBuf.writeUtf(packetContentJson, Short.MAX_VALUE); packetSender.sendPacket(DATA, outBuf); } catch (Exception e) { LOGGER.error("Error while handling handshake for {}", profile.getName(), e); diff --git a/src/main/java/pl/skidam/automodpack/networking/server/LoginRequestPayload.java b/src/main/java/pl/skidam/automodpack/networking/server/LoginRequestPayload.java index 83d0b9d25..7bd3f928b 100644 --- a/src/main/java/pl/skidam/automodpack/networking/server/LoginRequestPayload.java +++ b/src/main/java/pl/skidam/automodpack/networking/server/LoginRequestPayload.java @@ -1,17 +1,16 @@ package pl.skidam.automodpack.networking.server; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.util.Identifier; - +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; /*? if <1.20.2 {*/ -/*public record LoginRequestPayload(Identifier id, PacketByteBuf data) { } +/*public record LoginRequestPayload(ResourceLocation id, FriendlyByteBuf data) { } *//*?} else {*/ -import net.minecraft.network.packet.s2c.login.LoginQueryRequestPayload; +import net.minecraft.network.protocol.login.custom.CustomQueryPayload; import pl.skidam.automodpack.networking.PayloadHelper; -public record LoginRequestPayload(Identifier id, PacketByteBuf data) implements LoginQueryRequestPayload { +public record LoginRequestPayload(ResourceLocation id, FriendlyByteBuf data) implements CustomQueryPayload { @Override - public void write(PacketByteBuf buf) { + public void write(FriendlyByteBuf buf) { PayloadHelper.write(buf, data()); } } diff --git a/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworkAddon.java b/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworkAddon.java index 58ce10d1a..9b9cfb8e1 100644 --- a/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworkAddon.java +++ b/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworkAddon.java @@ -1,13 +1,13 @@ package pl.skidam.automodpack.networking.server; import io.netty.buffer.Unpooled; -import net.minecraft.network.ClientConnection; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.packet.c2s.login.LoginQueryResponseC2SPacket; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.util.Identifier; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import org.jetbrains.annotations.Nullable; import pl.skidam.automodpack.mixin.core.ServerLoginNetworkHandlerAccessor; import pl.skidam.automodpack.networking.LoginNetworkingIDs; @@ -25,14 +25,14 @@ // credits to fabric api public class ServerLoginNetworkAddon implements PacketSender { - private final ServerLoginNetworkHandler handler; - private final ClientConnection connection; + private final ServerLoginPacketListenerImpl handler; + private final Connection connection; private final MinecraftServer server; private final Collection> synchronizers = new ConcurrentLinkedQueue<>(); - public final Map channels = new ConcurrentHashMap<>(); + public final Map channels = new ConcurrentHashMap<>(); private boolean firstTick = true; - public ServerLoginNetworkAddon(ServerLoginNetworkHandler serverLoginNetworkHandler) { + public ServerLoginNetworkAddon(ServerLoginPacketListenerImpl serverLoginNetworkHandler) { this.handler = serverLoginNetworkHandler; this.connection = ((ServerLoginNetworkHandlerAccessor) handler).getConnection(); this.server = ((ServerLoginNetworkHandlerAccessor) handler).getServer(); @@ -70,15 +70,15 @@ public boolean queryTick() { * @param packet the packet to handle * @return true if the packet was handled */ - public boolean handle(LoginQueryResponseC2SPacket packet) { + public boolean handle(ServerboundCustomQueryAnswerPacket packet) { LoginQueryParser loginQuery = new LoginQueryParser(packet); if (loginQuery.success) return handle(loginQuery.queryId, loginQuery.buf); return false; } - private boolean handle(int queryId, @Nullable PacketByteBuf originalBuf) { + private boolean handle(int queryId, @Nullable FriendlyByteBuf originalBuf) { - Identifier channel = this.channels.remove(queryId); + ResourceLocation channel = this.channels.remove(queryId); if (channel == null) { // Not an AutoModpack packet. @@ -92,7 +92,7 @@ private boolean handle(int queryId, @Nullable PacketByteBuf originalBuf) { return false; } - PacketByteBuf buf = understood ? new PacketByteBuf(originalBuf.slice()) : new PacketByteBuf(Unpooled.EMPTY_BUFFER); + FriendlyByteBuf buf = understood ? new FriendlyByteBuf(originalBuf.slice()) : new FriendlyByteBuf(Unpooled.EMPTY_BUFFER); try { handler.receive(this.server, this.handler, understood, buf, this.synchronizers::add, this); @@ -105,18 +105,18 @@ private boolean handle(int queryId, @Nullable PacketByteBuf originalBuf) { } @Override - public LoginQueryRequestS2CPacket createPacket(Identifier channelName, PacketByteBuf buf) { + public ClientboundCustomQueryPacket createPacket(ResourceLocation channelName, FriendlyByteBuf buf) { Integer queryId = LoginNetworkingIDs.getByKey(channelName); if (queryId == null) { return null; } - return new LoginQueryRequestS2CPacket(queryId, /*? if <1.20.2 {*/ /*channelName, buf *//*?} else {*/ new LoginRequestPayload(channelName, buf) /*?}*/); + return new ClientboundCustomQueryPacket(queryId, /*? if <1.20.2 {*/ /*channelName, buf *//*?} else {*/ new LoginRequestPayload(channelName, buf) /*?}*/); } @Override - public void sendPacket(LoginQueryRequestS2CPacket packet) { + public void sendPacket(ClientboundCustomQueryPacket packet) { Objects.requireNonNull(packet, "Connection cannot be null"); LoginQueryParser loginQuery = new LoginQueryParser(packet); diff --git a/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworking.java b/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworking.java index 3a92a1086..2d6f41127 100644 --- a/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworking.java +++ b/src/main/java/pl/skidam/automodpack/networking/server/ServerLoginNetworking.java @@ -1,9 +1,9 @@ package pl.skidam.automodpack.networking.server; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.network.ServerLoginNetworkHandler; -import net.minecraft.util.Identifier; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; import pl.skidam.automodpack.networking.PacketSender; import java.util.HashMap; @@ -14,7 +14,7 @@ // credits to fabric api public class ServerLoginNetworking { - private static final Map handlers = new HashMap<>(); + private static final Map handlers = new HashMap<>(); /** * Registers a handler to a query response channel. @@ -23,14 +23,14 @@ public class ServerLoginNetworking { * @param channelName the id of the channel * @param handler the handler */ - public static void registerGlobalReceiver(Identifier channelName, LoginQueryResponseHandler handler) { + public static void registerGlobalReceiver(ResourceLocation channelName, LoginQueryResponseHandler handler) { Objects.requireNonNull(channelName, "Channel name cannot be null"); Objects.requireNonNull(handler, "Channel handler cannot be null"); handlers.put(channelName, handler); } - public static LoginQueryResponseHandler getHandler(Identifier channelName) { + public static LoginQueryResponseHandler getHandler(ResourceLocation channelName) { return handlers.get(channelName); } @@ -40,7 +40,7 @@ public interface LoginQueryResponseHandler { * Handles an incoming query response from a client. * *

This method is executed on {@linkplain io.netty.channel.EventLoop netty's event loops}. - * Modification to the game should be {@linkplain net.minecraft.util.thread.ThreadExecutor#submit(Runnable) scheduled} using the provided Minecraft client instance. + * Modification to the game should be {@linkplain net.minecraft.util.thread.BlockableEventLoop#submit(Runnable) scheduled} using the provided Minecraft client instance. * *

Whether the client understood the query should be checked before reading from the payload of the packet. * @param server the server @@ -49,7 +49,7 @@ public interface LoginQueryResponseHandler { * @param buf the payload of the packet * @param synchronizer the synchronizer which may be used to delay log-in till a {@link Future} is completed. */ - void receive(MinecraftServer server, ServerLoginNetworkHandler handler, boolean understood, PacketByteBuf buf, LoginSynchronizer synchronizer, PacketSender responseSender); + void receive(MinecraftServer server, ServerLoginPacketListenerImpl handler, boolean understood, FriendlyByteBuf buf, LoginSynchronizer synchronizer, PacketSender responseSender); } @@ -92,7 +92,7 @@ public interface LoginSynchronizer { * })); * }); * } - * Usually it is enough to pass the return value for {@link net.minecraft.util.thread.ThreadExecutor#submit(Runnable)} for {@code future}.

+ * Usually it is enough to pass the return value for {@link net.minecraft.util.thread.BlockableEventLoop#submit(Runnable)} for {@code future}.

* * @param future the future that must be done before the player can log in */ diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 000000000..e67148695 --- /dev/null +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1 @@ +public net.minecraft.server.network.ServerLoginPacketListenerImpl$State \ No newline at end of file diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 45ab91744..c07f28b9b 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -8,15 +8,13 @@ version = "${version}" displayName = "${name}" displayURL = "https://modrinth.com/mod/automodpack" authors = "Skidam" -description = ''' - -''' -logoFile = "icon.png" +description = "${description}" +logoFile = "assets/automodpack/icon.png" [[dependencies."${id}"]] modId = "minecraft" mandatory = true -versionRange = "${minecraft_dependency}" +versionRange = "${minecraft}" ordering = "NONE" side = "BOTH" diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index 45ab91744..c07f28b9b 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -8,15 +8,13 @@ version = "${version}" displayName = "${name}" displayURL = "https://modrinth.com/mod/automodpack" authors = "Skidam" -description = ''' - -''' -logoFile = "icon.png" +description = "${description}" +logoFile = "assets/automodpack/icon.png" [[dependencies."${id}"]] modId = "minecraft" mandatory = true -versionRange = "${minecraft_dependency}" +versionRange = "${minecraft}" ordering = "NONE" side = "BOTH" diff --git a/src/main/resources/assets/automodpack/lang/fr_fr.json b/src/main/resources/assets/automodpack/lang/fr_fr.json index 5ec44f2b5a2bcf7b70853fd5b276dba058dd58c3..cff53b2c475acedcfc81563903dc5357f60df17d 100644 GIT binary patch literal 3433 zcmai1%Wm676y5tPrUn8hfWz*+DGbE{3bctE2icFwOL^Sk3_TCoR?&ayu2t6Eb;%#= zxkE~%q%m#4fGKhAoAOS&C7ymbJ%2e$Y%3XPi1TZo0&yI5e@77wVk=Lg`%B#0=;lLt(o}|nk{mrm zHP>(`!Mp;6+fewq9#ib%& zI@NM%obH;tcXV%+sT`f-Pc2PJYi{bEVlfA?r}D#ov~M0$d-}~{JLJehi%_)UEK_J_ zcp74bJ9R+QRUqG!dID>aV1Tl#2 zdvKUcY$a{ghYpK8rM%jx5F2e+1o`)CnVy3J%F? z+M3N=s}q90j;8qnGkselGpL#C*aIXxLvUiSqwTU-DSF)biL7f?-tpi{p_LB=iJ`6Z zaz7JqcPIV3oLF{W7sf8qInU8AV+qQ~K!WNTHVdS)!!$;*EtzY%O#9$-TsfAyM3YOD z78&Au3fT@T`)IV4RZ&V^MlFLf%OuA1NJWuNygNlf^4njjY1t96;7qs>Z+x!j-J$It zL#io%jGnldJ#Fkq1qT}8VlEWOz^>b4l;v}5i}04AWsdNL-tV1ZgP3v1@$liSEwEQa zi?Makxp3Xa+X%-@UJ3Ybb@xvLjmQ<1oMSLd2KTr`a?U14_$`uthy+1c>~f!mu#uX@ ziv=t$e#`-tP8anSqo14BdarJg6h64$Y)KANXS^67^jWg|fLN+QK_raY6_y^>YWj%l z)7GWIokmYgj;moNGR7;h`K{YHRjag{v1IUR0#vjb4uaaTZc(jfZ5?A)Qz-8eqqslC z7NH|(G;A~uV z8%ppUm$s5x;jV^iiU1eq2x+G#XM~R#E^N*6#q8g8|H4lAlC<5a%J4h9EWo5h_)4byj z^_i?1W#F0Mhcz^@`^mDvYbykW&a=9i^nJYi7laMH?Hld&Di9c$GD5?c8{p7=hkvt0 z)>8W(hOJr>BTsnZTaUqJ4LqED2ikYC8Gz;fBy7)coM4ibr42j6Pj=pF7=*-m9EF(mv9)?VH#@P z8R=J}>zSTg=yedj)!Kvbz3x{#t13NRg*!cer*A$NdXK~VxXZbIf2EaA2g%eLhD=hr0q=UVRmIpIy zK{b^I*ZRD#NgeHK66Qj^)a(CPHqQXZeio6`LWv|ip11WxNSzN=c%ds;)winYwP<%5 zYiAgRzNsy^hKFCW#&F+EB&(KZ*3w}me5R9YdLmqAZIjOZaeNYAJZ(`fglwt*r{Pxr z82SrcZ4@IzxsA8S;rKyxXAvFpZo@M%1FY}{%T46}%V;sKup3u%;Urp?5pffq55_l+ z9-C>$wRU!U{U`+UXqQ=(da2L8MTXjAytQ$cZ(c_XcwsK9yXM!rLc@uW!Ouqb;WHNC z|4plNNydX6*f-5Er`lnq7mQ)Yj+1?6mui~II27uK_-G!t+<;_sG}aMW=6&$;gk@UO zxrzUz@#tASpOedZ+ecr{S`f1KdaFU6wZo#RkE&6%nLiRsFQbp^UL4ioWArY&Rc(Bc zso=b`np)9kLwQW~QRX8m$2Ry_wRNJ2xYxyTyerc)R~J=prLD0EEiG~^n;E0a;)Z?C zwG+s!J2;9~+^u4>$lJA^o=2@V`sFpF_vC)kV1Lgk~D;PZdg%7$%?z?I;Ae|lSL1i&brtN!qsJ}W4X^r_)`{y*>Ck9eq5de|=rC&}s0W7D=vY9xEORG9TjR(YLlsr>;Hwf()Fv5dGa<#dY9rbC<}m(gxMk9SYG z)yOaFh{Li~4tJMPbKUtcyp~zJY7J4!PytN*>vE6_HuSwX+29AeX5dW=({d_~Puu?(oD)_w9_tA{OnK z^xkU)vMSXzc7U<3q8F$v`(^D8HT)ziGgG<-WaWoYru7lr``8QA*gZWjI+dmTKR@9~ z&l_@?tHd|INlwQUYVN!^M#%DR51YkHqbt{Qy^wvG&e5{GmwP8Zayscaj)kebUfyUm my|1r6Rc<2_rCl=0.3.5" } } diff --git a/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta index d0bc05bac..75c979a41 100644 --- a/src/main/resources/pack.mcmeta +++ b/src/main/resources/pack.mcmeta @@ -1,6 +1,7 @@ { "pack": { "description": "${name} Resources", - "pack_format": 1 + "pack_format": 15, + "supported_formats": [1, 1000] } } \ No newline at end of file diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 3cbc12acc..45bea7b03 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -1,269 +1,34 @@ -import java.io.FileInputStream -import java.io.FileOutputStream -import java.util.concurrent.CompletableFuture -import java.util.zip.ZipEntry -import java.util.zip.ZipInputStream -import java.util.zip.ZipOutputStream - plugins { id("dev.kikugie.stonecutter") + kotlin("jvm") version "2.1.21" apply false + id("fabric-loom") version "1.11-SNAPSHOT" apply false + id("net.neoforged.moddev") version "2.0.103" apply false + id("com.gradleup.shadow") version "8.3.6" apply false + id("org.moddedmc.wiki.toolkit") version "0.2.7" } -stonecutter active "1.21.1-neoforge" /* [SC] DO NOT EDIT */ - -stonecutter registerChiseled tasks.register("chiseledBuild", stonecutter.chiseled) { - group = "project" - ofTask("build") - finalizedBy("mergeJars") -} - -stonecutter parameters { - val loader = metadata.project.substringAfterLast("-") - consts(loader, "fabric", "forge", "neoforge") -} - -// Non stonecutter stuff -val mergedDir = File("${rootProject.projectDir}/merged") - -class MinecraftVersionData(private val name: String) { - fun greaterThan(other: String) : Boolean { - return stonecutter.eval(name, ">" + other.lowercase()) - } - - fun lessThan(other: String) : Boolean { - return stonecutter.eval(name, "<" + other.lowercase()) - } - - fun greaterOrEqual(other: String) : Boolean { - return stonecutter.eval(name, ">=" + other.lowercase()) - } - - fun lessOrEqual(other: String) : Boolean { - return stonecutter.eval(name, "<=" + other.lowercase()) - } - - override fun equals(other: Any?) : Boolean { - return name == other - } - - override fun toString(): String { - return name - } - - override fun hashCode(): Int { - return name.hashCode() +wiki { + docs.create("automodpack") { + root = file("docs") } } -val coreModules = getProperty("core_modules")!!.split(',').map { it.trim() } +stonecutter active "1.21.6-neoforge" /* [SC] DO NOT EDIT */ -fun getProperty(key: String): String? { - return project.findProperty(key) as? String -} - -// TODO find better way to do it -// If you get Array Exception, run "clean" task -tasks.register("mergeJars") { - coreModules.forEach { module -> - dependsOn(":loader-$module:build") - } - - doLast { - mergedDir.mkdirs() - val jarsToMerge = File("$rootDir/versions").listFiles() - ?.flatMap { - File("$it/build/libs").listFiles() - ?.filter { file -> file.isFile && !file.name.endsWith("-sources.jar") && file.name.endsWith(".jar") } - ?: emptyList() - } - ?: emptyList() - - val tasks = mutableListOf>() - val time = System.currentTimeMillis() - val size = jarsToMerge.size - var current = 0 - - for (jarToMerge in jarsToMerge) { - val task = CompletableFuture.runAsync { - val minecraftVersionStr = jarToMerge.name.substringAfterLast("-mc").substringBefore("-") - val minecraftVersion = MinecraftVersionData(minecraftVersionStr) - - var loaderModule = "" - if (jarToMerge.name.contains("fabric")) { - loaderModule = "fabric/core" - } else if (jarToMerge.name.contains("neoforge")) { - loaderModule = if (minecraftVersion.greaterOrEqual("1.20.6")) { - "neoforge/fml4" - } else { - "neoforge/fml2" - } - } else if (jarToMerge.name.contains("forge")) { - loaderModule = if (minecraftVersion.lessOrEqual("1.18.2")) { - "forge/fml40" - } else { - "forge/fml47" - } - } - - val loaderFile = File("${rootProject.projectDir}/loader/$loaderModule/build/libs").listFiles() - ?.single { it.isFile && !it.name.endsWith("-sources.jar") && it.name.endsWith(".jar") } ?: return@runAsync - val finalJar = File("$mergedDir/${jarToMerge.name}") - - loaderFile.copyTo(finalJar, overwrite = true) - appendFileToZip(finalJar, jarToMerge, "automodpack-mod.jar") - - println("${++current}/$size - Merged: ${jarToMerge.name} into: ${finalJar.name} from: ${loaderFile.name}") - } +stonecutter.parameters { + constants.match(node.metadata.project.substringAfterLast('-'), "fabric", "neoforge", "forge") - tasks.add(task) + replacements { + string { + direction = eval(current.version, ">=1.20.2") + phase = "FIRST" + replace("ServerboundCustomQueryPacket", "ServerboundCustomQueryAnswerPacket") } - tasks.forEach { it.join() } - - if (size == 0) { - error("No jars to merge!") - } else if (size != current) { - error("Not all jars were merged!") - } else { - println("All jars were merged! Took: ${System.currentTimeMillis() - time}ms") + string { + direction = eval(current.version, ">=1.20.2") + phase = "FIRST" + replace(".SystemToastIds.", ".SystemToastId.") } } -} - -fun appendFileToZip(zipFile: File, fileToAppend: File, entryName: String) { - val entries = ZipInputStream(FileInputStream(zipFile)).use { zipStream -> - generateSequence { zipStream.nextEntry } - .toList() - } - - val graph = mutableMapOf>() - - entries.forEach { entry -> - val children = entry.name.split("/") - var currentParent = "" - children.forEach { child -> - if (child.isNotEmpty()) { - currentParent = "$currentParent$child/" - val parent = graph.getOrPut(currentParent) { mutableListOf() } - parent.add(child) - } - } - } - -// graph.forEach { (parent, children) -> -// println("$parent -> $children") -// } - - val filteredGraph = filterEntries(entries, graph, zipFile) - - // Doing with temp file since for some reason just adding the file breaks the zip/jar file - val tempFile = File("$zipFile.temp") - tempFile.createNewFile() - - ZipOutputStream(FileOutputStream(tempFile)).use { zipStream -> - ZipInputStream(FileInputStream(zipFile)).use { existingZipStream -> - while (true) { - val entry = existingZipStream.nextEntry ?: break - if (filteredGraph.containsKey("${entry.name}/")) { - try { - val zipEntry = ZipEntry(entry.name) - zipStream.putNextEntry(zipEntry) - existingZipStream.copyTo(zipStream) - zipStream.closeEntry() - } catch (e: Exception) { - println("Error while copying entry: ${entry.name}") - } - } - } - } - - // Add the new entry - zipStream.putNextEntry(ZipEntry(entryName)) - FileInputStream(fileToAppend).use { fileInputStream -> - fileInputStream.copyTo(zipStream) - } - zipStream.closeEntry() - } - - // Replace the original zip file with the one containing the new entry - zipFile.delete() - tempFile.renameTo(zipFile) -} - -val dupesDir = File("${rootProject.projectDir}/dupes") - -fun filterEntries(entries: List, graph: Map>, zipFile: File): Map> { - - val emptyDirs = mutableSetOf() - - // find empty directories - graph.forEach { (parent, children) -> - if (children.size == 0) { - emptyDirs.add(parent) - } - } - - // dump duplicate entries - dupesDir.deleteRecursively() - dupesDir.mkdirs() - - dumpDupeEntries(zipFile, entries) - - // filter empty directories - // filter single duplicate files (leave only one) - val dupes = mutableSetOf() - - val filteredGraph = graph.filter { (parent, children) -> - if (emptyDirs.contains(parent)) { - println("Filtering empty dir: $parent -> $children") - return@filter false - } - - val count = graph.count { parent == it.key } - if (count > 1 && !dupes.add(children[0])) { - return@filter false - } - - return@filter true - } - - return filteredGraph -} - -fun dumpDupeEntries(zipFile: File, entries: List) { - // check for duplicates - val entryNames = mutableSetOf() - entries.forEach { duplicate -> - if (!entryNames.add(duplicate.name)) { - println("Duplicate entry: $duplicate") - - // write the entry to the file - ZipInputStream(FileInputStream(zipFile)).use { zipStream -> - var i = 0 - generateSequence { zipStream.nextEntry } - .filter { it.name == duplicate.name } - .forEach { _ -> - i++ - val dupeFile = File("$dupesDir/$i-$duplicate.dupe") - println("Extracting to: $dupeFile") - dupeFile.parentFile.mkdirs() - dupeFile.createNewFile() - FileOutputStream(dupeFile).use { fileOutputStream -> - zipStream.copyTo(fileOutputStream) - } - } - } - } - } -} - - -tasks.register("clean") { - dependsOn("cleanMerged") -} - -tasks.register("cleanMerged") { - doLast { - mergedDir.deleteRecursively() - } -} +} \ No newline at end of file diff --git a/versions/1.18.2-fabric/gradle.properties b/versions/1.18.2-fabric/gradle.properties index ceb436883..fbe2a23dd 100644 --- a/versions/1.18.2-fabric/gradle.properties +++ b/versions/1.18.2-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.18.2 -yarn_mappings=1.18.2+build.4 -loom.platform=fabric -minecraft_dependency=1.18.x -fabric_version=0.76.0+1.18.2 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.18\n1.18.1\n1.18.2 \ No newline at end of file +deps.minecraft=1.18.2 +deps.parchment=1.18.2:2022.11.06 +deps.fabric-api=0.76.0+1.18.2 +meta.minecraft=1.18.x +publish_versions=1.18\n1.18.1\n1.18.2 \ No newline at end of file diff --git a/versions/1.18.2-forge/gradle.properties b/versions/1.18.2-forge/gradle.properties index 3d10c6025..062b54532 100644 --- a/versions/1.18.2-forge/gradle.properties +++ b/versions/1.18.2-forge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.18.2 -yarn_mappings=1.18.2+build.4 -loom.platform=forge -minecraft_dependency=1.18.x -loader_forge=40.2.10 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.18\n1.18.1\n1.18.2 +deps.minecraft=1.18.2 +deps.parchment=1.18.2:2022.11.06 +deps.forge=1.18.2-40.2.10 +meta.minecraft=1.18.x +publish_versions=1.18\n1.18.1\n1.18.2 diff --git a/versions/1.19.2-fabric/gradle.properties b/versions/1.19.2-fabric/gradle.properties index 09e1b3394..d6edf72fa 100644 --- a/versions/1.19.2-fabric/gradle.properties +++ b/versions/1.19.2-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.19.2 -yarn_mappings=1.19.2+build.28 -loom.platform=fabric -minecraft_dependency=>=1.19 <=1.19.2 -fabric_version=0.76.1+1.19.2 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file +deps.minecraft=1.19.2 +deps.fabric-api=0.76.1+1.19.2 +deps.parchment=1.19.2:2022.11.27 +meta.minecraft=>=1.19 <=1.19.2 +publish_versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file diff --git a/versions/1.19.2-forge/gradle.properties b/versions/1.19.2-forge/gradle.properties index c0a58b92a..d4ec7ed84 100644 --- a/versions/1.19.2-forge/gradle.properties +++ b/versions/1.19.2-forge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.19.2 -yarn_mappings=1.19.2+build.28 -loom.platform=forge -minecraft_dependency=>=1.19 <=1.19.2 -loader_forge=43.3.0 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file +deps.minecraft=1.19.2 +deps.forge=1.19.2-43.3.0 +deps.parchment=1.19.2:2022.11.27 +meta.minecraft=>=1.19 <=1.19.2 +publish_versions=1.19\n1.19.1\n1.19.2 \ No newline at end of file diff --git a/versions/1.19.4-fabric/gradle.properties b/versions/1.19.4-fabric/gradle.properties index 2f06d41f4..8f72b9e20 100644 --- a/versions/1.19.4-fabric/gradle.properties +++ b/versions/1.19.4-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.19.4 -yarn_mappings=1.19.4+build.2 -loom.platform=fabric -minecraft_dependency=1.19.4 -fabric_version=0.87.2+1.19.4 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.19.4 \ No newline at end of file +deps.minecraft=1.19.4 +deps.fabric-api=0.87.2+1.19.4 +deps.parchment=1.19.4:2023.06.26 +meta.minecraft=1.19.4 +publish_versions=1.19.4 \ No newline at end of file diff --git a/versions/1.19.4-forge/gradle.properties b/versions/1.19.4-forge/gradle.properties index e579ae507..297fbf226 100644 --- a/versions/1.19.4-forge/gradle.properties +++ b/versions/1.19.4-forge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.19.4 -yarn_mappings=1.19.4+build.2 -loom.platform=forge -minecraft_dependency=1.19.4 -loader_forge=45.3.0 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.19.4 \ No newline at end of file +deps.minecraft=1.19.4 +deps.forge=1.19.4-45.3.0 +deps.parchment=1.19.4:2023.06.26 +meta.minecraft=1.19.4 +publish_versions=1.19.4 \ No newline at end of file diff --git a/versions/1.20.1-fabric/gradle.properties b/versions/1.20.1-fabric/gradle.properties index 967732db3..e12481046 100644 --- a/versions/1.20.1-fabric/gradle.properties +++ b/versions/1.20.1-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=fabric -minecraft_dependency=>=1.20 <=1.20.1 -fabric_version=0.92.5+1.20.1 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20\n1.20.1 \ No newline at end of file +deps.minecraft=1.20.1 +deps.fabric-api=0.92.6+1.20.1 +deps.parchment=1.20.1:2023.09.03 +meta.minecraft=>=1.20 <=1.20.1 +publish_versions=1.20\n1.20.1 \ No newline at end of file diff --git a/versions/1.20.1-forge/gradle.properties b/versions/1.20.1-forge/gradle.properties index ddeefc921..93985f5f1 100644 --- a/versions/1.20.1-forge/gradle.properties +++ b/versions/1.20.1-forge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.1 -yarn_mappings=1.20.1+build.10 -loom.platform=forge -minecraft_dependency=>=1.20 <=1.20.1 -loader_forge=47.3.0 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20\n1.20.1 +deps.minecraft=1.20.1 +deps.forge=1.20.1-47.3.0 +deps.parchment=1.20.1:2023.09.03 +meta.minecraft=>=1.20 <=1.20.1 +publish_versions=1.20\n1.20.1 diff --git a/versions/1.20.4-fabric/gradle.properties b/versions/1.20.4-fabric/gradle.properties index eab4d0458..8723a8ce9 100644 --- a/versions/1.20.4-fabric/gradle.properties +++ b/versions/1.20.4-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.4 -yarn_mappings=1.20.4+build.3 -loom.platform=fabric -minecraft_dependency=1.20.4 -fabric_version=0.97.1+1.20.4 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20.4 \ No newline at end of file +deps.minecraft=1.20.4 +deps.fabric-api=0.97.3+1.20.4 +deps.parchment=1.20.4:2024.04.14 +meta.minecraft=1.20.4 +publish_versions=1.20.4 \ No newline at end of file diff --git a/versions/1.20.4-neoforge/gradle.properties b/versions/1.20.4-neoforge/gradle.properties index bc596fdbb..4899839a0 100644 --- a/versions/1.20.4-neoforge/gradle.properties +++ b/versions/1.20.4-neoforge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.4 -yarn_mappings=1.20.4+build.3 -loom.platform=neoforge -minecraft_dependency=1.20.4 -loader_neoforge=20.4.237 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20.4 \ No newline at end of file +deps.minecraft=1.20.4 +deps.neoforge=20.4.248 +deps.parchment=1.20.4:2024.04.14 +meta.minecraft=1.20.4 +publish_versions=1.20.4 \ No newline at end of file diff --git a/versions/1.20.6-fabric/gradle.properties b/versions/1.20.6-fabric/gradle.properties index afcb225bb..b13737fbf 100644 --- a/versions/1.20.6-fabric/gradle.properties +++ b/versions/1.20.6-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.6 -yarn_mappings=1.20.6+build.3 -loom.platform=fabric -minecraft_dependency=1.20.6 -fabric_version=0.100.0+1.20.6 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20.6 \ No newline at end of file +deps.minecraft=1.20.6 +deps.fabric-api=0.100.8+1.20.6 +deps.parchment=1.20.6:2024.06.16 +meta.minecraft=1.20.6 +publish_versions=1.20.6 \ No newline at end of file diff --git a/versions/1.20.6-neoforge/gradle.properties b/versions/1.20.6-neoforge/gradle.properties index 5a5586036..1d286b9d0 100644 --- a/versions/1.20.6-neoforge/gradle.properties +++ b/versions/1.20.6-neoforge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.20.6 -yarn_mappings=1.20.6+build.3 -loom.platform=neoforge -minecraft_dependency=1.20.6 -loader_neoforge=20.6.117 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.20.6 +deps.minecraft=1.20.6 +deps.neoforge=20.6.135 +deps.parchment=1.20.6:2024.06.16 +meta.minecraft=1.20.6 +publish_versions=1.20.6 diff --git a/versions/1.21.1-fabric/gradle.properties b/versions/1.21.1-fabric/gradle.properties index 5a8155fee..664fdb11f 100644 --- a/versions/1.21.1-fabric/gradle.properties +++ b/versions/1.21.1-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21.1 -yarn_mappings=1.21.1+build.3 -loom.platform=fabric -minecraft_dependency=>=1.21 <=1.21.1 -fabric_version=0.116.0+1.21.1 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21\n1.21.1 \ No newline at end of file +deps.minecraft=1.21.1 +deps.fabric-api=0.116.3+1.21.1 +deps.parchment=1.21.1:2024.11.17 +meta.minecraft=>=1.21 <=1.21.1 +publish_versions=1.21\n1.21.1 \ No newline at end of file diff --git a/versions/1.21.1-neoforge/gradle.properties b/versions/1.21.1-neoforge/gradle.properties index e7ffd7574..8b0a89942 100644 --- a/versions/1.21.1-neoforge/gradle.properties +++ b/versions/1.21.1-neoforge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21 -yarn_mappings=1.21.1+build.3 -loom.platform=neoforge -minecraft_dependency=>=1.21 <=1.21.1 -loader_neoforge=21.1.169 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21\n1.21.1 \ No newline at end of file +deps.minecraft=1.21.1 +deps.neoforge=21.1.169 +deps.parchment=1.21.1:2024.11.17 +meta.minecraft=>=1.21 <=1.21.1 +publish_versions=1.21\n1.21.1 \ No newline at end of file diff --git a/versions/1.21.3-fabric/gradle.properties b/versions/1.21.3-fabric/gradle.properties index a4bcde35e..62015b1bb 100644 --- a/versions/1.21.3-fabric/gradle.properties +++ b/versions/1.21.3-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21.3 -yarn_mappings=1.21.3+build.2 -loom.platform=fabric -minecraft_dependency=>=1.21.2 <=1.21.3 -fabric_version=0.114.0+1.21.3 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21.2\n1.21.3 \ No newline at end of file +deps.minecraft=1.21.3 +deps.fabric-api=0.114.1+1.21.3 +deps.parchment=1.21.3:2024.12.07 +meta.minecraft=>=1.21.2 <=1.21.3 +publish_versions=1.21.2\n1.21.3 \ No newline at end of file diff --git a/versions/1.21.3-neoforge/gradle.properties b/versions/1.21.3-neoforge/gradle.properties index cca94e4f2..1a3adccf5 100644 --- a/versions/1.21.3-neoforge/gradle.properties +++ b/versions/1.21.3-neoforge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21.3 -yarn_mappings=1.21.3+build.2 -loom.platform=neoforge -minecraft_dependency=>=1.21.2 <=1.21.3 -loader_neoforge=21.3.76 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21.2\n1.21.3 \ No newline at end of file +deps.minecraft=1.21.3 +deps.neoforge=21.3.76 +deps.parchment=1.21.3:2024.12.07 +meta.minecraft=>=1.21.2 <=1.21.3 +publish_versions=1.21.2\n1.21.3 \ No newline at end of file diff --git a/versions/1.21.4-fabric/gradle.properties b/versions/1.21.4-fabric/gradle.properties index 36b761cca..32a9524c8 100644 --- a/versions/1.21.4-fabric/gradle.properties +++ b/versions/1.21.4-fabric/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21.4 -yarn_mappings=1.21.4+build.8 -loom.platform=fabric -minecraft_dependency=>=1.21.4 -fabric_version=0.119.2+1.21.4 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21.4 \ No newline at end of file +deps.minecraft=1.21.4 +deps.fabric-api=0.119.3+1.21.4 +deps.parchment=1.21.4:2025.03.23 +meta.minecraft=>=1.21.4 +publish_versions=1.21.4 \ No newline at end of file diff --git a/versions/1.21.4-neoforge/gradle.properties b/versions/1.21.4-neoforge/gradle.properties index 2f7877b87..e31bf34fa 100644 --- a/versions/1.21.4-neoforge/gradle.properties +++ b/versions/1.21.4-neoforge/gradle.properties @@ -1,8 +1,5 @@ -minecraft_version=1.21.4 -yarn_mappings=1.21.4+build.8 -loom.platform=neoforge -minecraft_dependency=>=1.21.4 -loader_neoforge=21.4.135 - -# The target mc versions for the mod during mod publishing, separated with \n -game_versions=1.21.4 \ No newline at end of file +deps.minecraft=1.21.4 +deps.neoforge=21.4.135 +deps.parchment=1.21.4:2025.03.23 +meta.minecraft=>=1.21.4 +publish_versions=1.21.4 \ No newline at end of file diff --git a/versions/1.21.5-fabric/gradle.properties b/versions/1.21.5-fabric/gradle.properties new file mode 100644 index 000000000..509c61a89 --- /dev/null +++ b/versions/1.21.5-fabric/gradle.properties @@ -0,0 +1,5 @@ +deps.minecraft=1.21.5 +deps.fabric-api=0.128.0+1.21.5 +deps.parchment=1.21.5:2025.06.15 +meta.minecraft=1.21.5 +publish_versions=1.21.5 \ No newline at end of file diff --git a/versions/1.21.5-neoforge/gradle.properties b/versions/1.21.5-neoforge/gradle.properties new file mode 100644 index 000000000..7a0f88d7a --- /dev/null +++ b/versions/1.21.5-neoforge/gradle.properties @@ -0,0 +1,5 @@ +deps.minecraft=1.21.5 +deps.neoforge=21.5.75 +deps.parchment=1.21.5:2025.06.15 +meta.minecraft=1.21.5 +publish_versions=1.21.5 \ No newline at end of file diff --git a/versions/1.21.6-fabric/gradle.properties b/versions/1.21.6-fabric/gradle.properties new file mode 100644 index 000000000..0c926b8c5 --- /dev/null +++ b/versions/1.21.6-fabric/gradle.properties @@ -0,0 +1,4 @@ +deps.minecraft=1.21.6 +deps.fabric-api=0.128.0+1.21.6 +meta.minecraft=>=1.21.6 +publish_versions=1.21.6 \ No newline at end of file diff --git a/versions/1.21.6-neoforge/gradle.properties b/versions/1.21.6-neoforge/gradle.properties new file mode 100644 index 000000000..dfe3f829e --- /dev/null +++ b/versions/1.21.6-neoforge/gradle.properties @@ -0,0 +1,4 @@ +deps.minecraft=1.21.6 +deps.neoforge=21.6.11-beta +meta.minecraft=>=1.21.6 +publish_versions=1.21.6 \ No newline at end of file