From 11c2508b47d0bba8b7e36276d7ced8f706d4d5f6 Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 18:12:15 +0800 Subject: [PATCH 01/13] fix(build): package release zip and fix Phosphor mixin refmap in production jar Co-authored-by: Cursor --- .github/workflows/build.yml | 4 +- .github/workflows/release.yml | 4 +- build.gradle | 37 +++++++- .../mixins/plugins/LightingEnginePlugin.java | 2 +- src/main/resources/mixins.phosphor.json | 2 +- zip_include/BILIBILI.URL | 2 + zip_include/DISCONTINUATION_NOTICE.md | 29 ------- zip_include/FORUM.URL | 2 - zip_include/QQ.URL | 2 + zip_include/README.txt | 87 +++++++++---------- zip_include/TWITTER.URL | 2 - zip_include/WEBSITE.URL | 2 - zip_include/YOUTUBE.URL | 2 - 13 files changed, 88 insertions(+), 89 deletions(-) create mode 100644 zip_include/BILIBILI.URL delete mode 100644 zip_include/DISCONTINUATION_NOTICE.md delete mode 100644 zip_include/FORUM.URL create mode 100644 zip_include/QQ.URL delete mode 100644 zip_include/TWITTER.URL delete mode 100644 zip_include/WEBSITE.URL delete mode 100644 zip_include/YOUTUBE.URL diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1118dd8e..1a87e8dc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,4 +38,6 @@ jobs: uses: actions/upload-artifact@v4 with: name: AirClient - path: build/libs/*.jar + path: | + build/libs/*.jar + build/libs/*.zip diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5c48b522..87c5fc35 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,7 +47,9 @@ jobs: - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: - files: build/libs/*.jar + files: | + build/libs/*.jar + build/libs/*.zip generate_release_notes: true prerelease: ${{ steps.prerelease.outputs.prerelease }} env: diff --git a/build.gradle b/build.gradle index 64bf46c0..fcbf43b1 100644 --- a/build.gradle +++ b/build.gradle @@ -167,11 +167,21 @@ mcversion=${project.minecraft.version} } task moveResources { + dependsOn compileJava + doLast { copy { from "${buildDir}/resources/main" into "${buildDir}/classes/java" } + + def refmap = file("${buildDir}/tmp/compileJava/liquidbounce.mixins.refmap.json") + if (refmap.exists()) { + copy { + from refmap + into "${buildDir}/classes/java" + } + } } } @@ -209,7 +219,32 @@ reobfJar.enabled = false shadowJar.finalizedBy(reobfShadowJar) -build.dependsOn(reobfShadowJar) +task packageReleaseZip(type: Zip) { + group = "build" + description = "Packages zip_include files and the built mod jar into a release zip." + + dependsOn reobfShadowJar + + archiveBaseName.set("${project.archives_base_name}-${project.version}") + archiveExtension.set("zip") + destinationDirectory.set(file("${buildDir}/libs")) + + doFirst { + file("${buildDir}/libs").listFiles()?.each { candidate -> + if (candidate.isFile() && candidate.name.endsWith(".zip")) { + candidate.delete() + } + } + } + + from("zip_include") + + from(shadowJar.archiveFile) { + rename { "airclient.jar" } + } +} + +build.dependsOn packageReleaseZip // ForgeGradle 2.x puts the packaged mod jar on runClient's classpath. This // project's normal build jar is reobfuscated for Minecraft launchers, so it diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/plugins/LightingEnginePlugin.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/plugins/LightingEnginePlugin.java index e254d0ce..c03e78f5 100644 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/plugins/LightingEnginePlugin.java +++ b/src/main/java/me/jellysquid/mods/phosphor/mixins/plugins/LightingEnginePlugin.java @@ -71,7 +71,7 @@ public String getRefMapperConfig() { return null; } - return "mixins.phosphor.refmap.json"; + return "liquidbounce.mixins.refmap.json"; } @Override diff --git a/src/main/resources/mixins.phosphor.json b/src/main/resources/mixins.phosphor.json index f07903c2..0bb77073 100644 --- a/src/main/resources/mixins.phosphor.json +++ b/src/main/resources/mixins.phosphor.json @@ -3,7 +3,7 @@ "minVersion": "0.7", "package": "me.jellysquid.mods.phosphor.mixins", "plugin": "me.jellysquid.mods.phosphor.mixins.plugins.LightingEnginePlugin", - "refmap": "mixins.phosphor.refmap.json", + "refmap": "liquidbounce.mixins.refmap.json", "compatibilityLevel": "JAVA_8", "client": [ "client.ClientChunkProviderAccessor", diff --git a/zip_include/BILIBILI.URL b/zip_include/BILIBILI.URL new file mode 100644 index 00000000..d6f2d328 --- /dev/null +++ b/zip_include/BILIBILI.URL @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=https://space.bilibili.com/3706927473232492 diff --git a/zip_include/DISCONTINUATION_NOTICE.md b/zip_include/DISCONTINUATION_NOTICE.md deleted file mode 100644 index 5d7964a5..00000000 --- a/zip_include/DISCONTINUATION_NOTICE.md +++ /dev/null @@ -1,29 +0,0 @@ -# LiquidBounce Legacy - -## Discontinuation Notice - -[Legacy](https://github.com/CCBlueX/LiquidBounce/tree/legacy) development was maintained by the community from 2020 to 2025. However, Legacy is now discontinued in favor of [Nextgen](https://github.com/CCBlueX/LiquidBounce/tree/nextgen). Since 2020, the project was officially discontinued by us, [CCBlueX](https://ccbluex.net), which started the development in 2015. - -**b100 is the final release published by our community maintainers, and no further pull requests will be accepted.** A huge thanks to everyone who contributed to the project over the years, with special recognition to our lead maintainers, @EclipsesDev and @mems01. - -> "I have started LiquidBounce in 2015, and since 2020 I have been working on a new code base, Nextgen, which left the Legacy code base to the community. -> A huge thank you for the community maintainers who have kept this branch of the client alive for so long, while our CCBlueX team was able to focus on our new branch. -> Looking at the current state, I'm not entirely happy with the direction this branch has taken, but I'm grateful for the work that has been done and the time that has been invested in it. -> I'm looking forward to the future of LiquidBounce and hope that we can merge our efforts in the future to create a better client for everyone." -> -> — Izuna, Founder - -> "With this being the final release of Legacy, I’m grateful for how much this client has progressed. -> It has not only improved significantly over the previous version but also helped me grow as a developer, pushing me to learn and do better. -> I’d like to thank the CCBlueX team for their client support throughout this journey and @mems01 for his help during the development." -> -> — EclipsesDev, Lead Community Maintainer - -> "This is the final Legacy version, meaning that the development continuation by the community (me and @EclipsesDev) is coming to a stop, permanently. -> I am proud to have reached this moment and this far with this client's development as it taught me new things everyday. -> I thank the CCBlueX team for allowing me in the first place to continue the development of this client, and @EclipsesDev for helping me do so. -> It was a good run." -> -> — mems01, Lead Community Maintainer - -We are grateful for the support we received from the community and hope that you will continue to support us in the future on our new code base, [Nextgen](https://github.com/CCBlueX/LiquidBounce/tree/nextgen) that will continue to be maintained by us. diff --git a/zip_include/FORUM.URL b/zip_include/FORUM.URL deleted file mode 100644 index 55145694..00000000 --- a/zip_include/FORUM.URL +++ /dev/null @@ -1,2 +0,0 @@ -[InternetShortcut] -URL=https://forums.ccbluex.net/ diff --git a/zip_include/QQ.URL b/zip_include/QQ.URL new file mode 100644 index 00000000..d40c2b39 --- /dev/null +++ b/zip_include/QQ.URL @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=https://qm.qq.com/q/tQBfGJNpte diff --git a/zip_include/README.txt b/zip_include/README.txt index 304a8c43..2a72b8f2 100644 --- a/zip_include/README.txt +++ b/zip_include/README.txt @@ -1,47 +1,40 @@ - ▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄ ▄▄ ▄▄ ▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄ ▄▄ ▄▄ ▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ -█ █ █ █ █ █ █ █ █ ██ ▄ █ █ █ █ █ █ █ █ █ █ -█ █ █ █ ▄ █ █ █ █ █ ▄ █ █▄█ █ ▄ █ █ █ █ █▄█ █ █ ▄▄▄█ -█ █ █ █ █ █ █ █▄█ █ █ █ █ █ █ █ █ █ █▄█ █ █ ▄▄█ █▄▄▄ -█ █▄▄▄█ █ █▄█ █ █ █ █▄█ █ ▄ ██ █▄█ █ █ ▄ █ █ █ ▄▄▄█ -█ █ █ ██ █ █ █ █▄█ █ █ █ █ █ █ █▄▄█ █▄▄▄ -█▄▄▄▄▄▄▄█▄▄▄█▄▄▄▄██▄█▄▄▄▄▄▄▄█▄▄▄█▄▄▄▄▄▄██▄▄▄▄▄▄▄█▄▄▄▄▄▄▄█▄▄▄▄▄▄▄█▄█ █▄▄█▄▄▄▄▄▄▄█▄▄▄▄▄▄▄█ -a open-source minecraft hacked-client for 1.8.9. -https://liquidbounce.net/ - -➔ How to install on Forge -1. Install Forge for 1.8.9 (from https://files.minecraftforge.net/net/minecraftforge/forge/index_1.8.9.html). -2. Create a folder called "mods" inside in the ".minecraft" directory. -3. Copy "liquidbounce.jar" into the mods folder. -4. RECOMMENDED: Install OptiFine mod for improved performance (from https://optifine.net/downloads) -5. RECOMMENDED: Install ViaForge mod to join any 1.7-1.18.2 server (from https://www.curseforge.com/minecraft/mc-mods/viaforge) -7. Launch Forge through the Minecraft launcher. - -➔ Reminder for nightly builds -This is a development build. Use these versions only if you know what you are doing! Bugs can and will occur! - -➔ How to report issues -If you found an issue with this version, please report it on our GitHub Issues: -https://github.com/CCBlueX/LiquidBounce/issues - -Make sure it includes all required information, so we can reproduce it on our own and solve the issue. -In case of an error, please always provide a full log and crash log. (.minecraft/logs/latest.log) - -➔ Support -If you need support, join our Guilded below. Feel free to tell us your question in the support channel, no need to ask for asking. There will always be people helping out. - -Please don't contact us in direct messages, when you're looking for support. - -➔ Do you want to contribute or want to know how our client is written? -Feel free to check the source code on GitHub: -https://github.com/CCBlueX/LiquidBounce - -➔ Links -Our website: https://ccbluex.net/ -Our youtube: https://youtube.com/ccbluex -Our twitter: https://twitter.com/ccbluex -Our guilded: https://www.guilded.gg/CCBlueX - -➔ License -This project is subject to the GNU General Public License v3.0. This does only apply for source code located directly in this clean repository. During the development and compilation process, additional source code may be used to which we have obtained no rights. Such code is not covered by the GPL license. - -Copyright © 2015 - 2025 | CCBlueX | All rights reserved. \ No newline at end of file + ▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄ ▄▄ ▄ ▄▄▄▄▄▄▄ +█ ██ █ ▄ █ █ █ █ █ █ █ █ █ █ █ +█ ▄ ██ █ █ █ █ █ █ █ █ █ ▄▄▄█ █▄█ █▄ ▄█ +█ █▄█ ██ █ █▄▄█▄█ █ █ █ █ █▄▄▄█ █ █ █ +█ ██ █ ▄▄ █ ▄█ █▄▄▄█ █ ▄▄▄█ ▄ █ █ █ +█ ▄ ██ █ █ █ █ █▄█ █ █ █▄▄▄█ █ █ █ █ █ +█▄█ █▄▄█▄▄▄█▄▄▄█ █▄█▄▄▄▄▄▄▄█▄▄▄▄▄▄▄█▄▄▄█▄▄▄▄▄▄▄█▄█ █▄▄█ █▄▄▄█ +一个基于liquidbounce legacy的黑客端。使用skid+aicode。 + +➔ 怎么安装? +1. 安装forge 1.8.9在https://files.minecraftforge.net/net/minecraftforge/forge/index_1.8.9.html +2. 在您的.minecraft目录创建"mods"文件夹 +3. 复制"airclient.jar"到"mods"文件夹 +4. (推荐) 安装Optfine以性能提升(在https://optifine.net/downloads) +5. (推荐) 安装ViaForge可以使你加入1.0.0 - 1.21.7的任何版本(在https://modrinth.com/mod/viaforge) +6. 启动客户端以享受 + +➔ 启动失败? +必须使用 Forge 1.8.9(不要用 1.8.8) +请勿单独安装 Phosphor 模组(客户端已内置,重复安装会导致注入失败) +如仍失败,请删除 mods 文件夹内其他旧版 AirClient jar 后重试 + +➔ 提出建议 +如果你找到了bug,或者需要加入哪些模块之类的,可以在Github提出issues: +https://github.com/lmx0721/AirClient/issues + +如果遇到bug,请提供视频、日志、截图什么的,我们可以快速定位到并修复 +如果要增加功能,请提供思路、源码(可选) + +➔ 想要贡献代码? +你可以fork本仓库,在你的分支增加或修改后,可以点击Contribute按钮创建Pull Requests(可以ai) + +➔ 链接 +B站 ➔ https://space.bilibili.com/3706927473232492 +QQ群 ➔ https://qm.qq.com/q/tQBfGJNpte + + +➔ 协议 +本项目使用GPL3。你可以修改代码,但是必须开源。 +原作者:CCBlueX(https://github.com/CCBlueX)、1zun4(https://github.com/1zun4) \ No newline at end of file diff --git a/zip_include/TWITTER.URL b/zip_include/TWITTER.URL deleted file mode 100644 index 7ec956c8..00000000 --- a/zip_include/TWITTER.URL +++ /dev/null @@ -1,2 +0,0 @@ -[InternetShortcut] -URL=https://twitter.com/CCBlueX diff --git a/zip_include/WEBSITE.URL b/zip_include/WEBSITE.URL deleted file mode 100644 index 474101a8..00000000 --- a/zip_include/WEBSITE.URL +++ /dev/null @@ -1,2 +0,0 @@ -[InternetShortcut] -URL=https://liquidbounce.net/ diff --git a/zip_include/YOUTUBE.URL b/zip_include/YOUTUBE.URL deleted file mode 100644 index d4b99483..00000000 --- a/zip_include/YOUTUBE.URL +++ /dev/null @@ -1,2 +0,0 @@ -[InternetShortcut] -URL=https://www.youtube.com/ccbluex From a151c04e6aa4b20840240c8725062db4021ed498 Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 18:17:41 +0800 Subject: [PATCH 02/13] ddd --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index fcbf43b1..a012f078 100644 --- a/build.gradle +++ b/build.gradle @@ -225,7 +225,7 @@ task packageReleaseZip(type: Zip) { dependsOn reobfShadowJar - archiveBaseName.set("${project.archives_base_name}-${project.version}") + archiveBaseName.set("${project.archives_base_name}") archiveExtension.set("zip") destinationDirectory.set(file("${buildDir}/libs")) From 52df5e29d5a13f014c8c61a8ed543bf9d66e1102 Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 18:19:27 +0800 Subject: [PATCH 03/13] ci: upload only release zip artifact in build and release workflows Co-authored-by: Cursor --- .github/workflows/build.yml | 4 +--- .github/workflows/release.yml | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1a87e8dc..5e9b3fd7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,6 +38,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: AirClient - path: | - build/libs/*.jar - build/libs/*.zip + path: build/libs/*.zip diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 87c5fc35..267bb280 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,9 +47,7 @@ jobs: - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: - files: | - build/libs/*.jar - build/libs/*.zip + files: build/libs/*.zip generate_release_notes: true prerelease: ${{ steps.prerelease.outputs.prerelease }} env: From 7ba3e7322a4c03268d69c10559f22a58f1b9a3d8 Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 18:22:52 +0800 Subject: [PATCH 04/13] ci: upload flat release bundle with airclient.jar and zip_include files Co-authored-by: Cursor --- .github/workflows/build.yml | 8 +++++++- .github/workflows/release.yml | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e9b3fd7..7b249997 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,8 +34,14 @@ jobs: - name: Build with Gradle run: ./gradlew build --no-daemon + - name: Prepare release files + run: | + mkdir -p release + cp build/libs/AirClient-*.jar release/airclient.jar + cp -r zip_include/. release/ + - name: Upload artifact uses: actions/upload-artifact@v4 with: name: AirClient - path: build/libs/*.zip + path: release/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 267bb280..471aa64d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,6 +32,12 @@ jobs: - name: Build with Gradle run: ./gradlew build --no-daemon + - name: Prepare release files + run: | + mkdir -p release + cp build/libs/AirClient-*.jar release/airclient.jar + cp -r zip_include/. release/ + - name: Detect prerelease id: prerelease run: | @@ -47,7 +53,7 @@ jobs: - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: - files: build/libs/*.zip + files: release/* generate_release_notes: true prerelease: ${{ steps.prerelease.outputs.prerelease }} env: From 21c1de70d9591e639a66e1aa6bef51ffa66549d7 Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 18:28:52 +0800 Subject: [PATCH 05/13] dd --- src/main/resources/instructions.html | 31 ---------------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/main/resources/instructions.html diff --git a/src/main/resources/instructions.html b/src/main/resources/instructions.html deleted file mode 100644 index 94261f39..00000000 --- a/src/main/resources/instructions.html +++ /dev/null @@ -1,31 +0,0 @@ - - -
- -
- -

How to install LiquidBounce

- -
    -
  1. Download Forge for Minecraft 1.8.9 from minecraftforge.net.
  2. -
  3. Install Forge.
  4. -
  5. Open the Minecraft folder.
  6. -
  7. Create a folder called mods.
  8. -
  9. Copy this file into it.
  10. -
  11. Run Minecraft with the Forge profile selected.
  12. -
- -
For more detailed instructions, visit liquidbounce.net
- -

Links

- - - - LiquidBounce by CCBlueX © 2015 - 2025 - - \ No newline at end of file From 44ef9b23ae95d4f87ae4ae727f12898492f5ac28 Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 18:32:59 +0800 Subject: [PATCH 06/13] fix(music): defer MusicPlayer GUI open so .music gui works from chat GuiChat closes the screen after sendChatMessage returns, which dismissed the GUI immediately when opened from .music gui or on module enable. Co-authored-by: Cursor --- .../features/module/modules/music/MusicPlayer.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/ccbluex/liquidbounce/features/module/modules/music/MusicPlayer.kt b/src/main/java/net/ccbluex/liquidbounce/features/module/modules/music/MusicPlayer.kt index eba4a719..3342c435 100644 --- a/src/main/java/net/ccbluex/liquidbounce/features/module/modules/music/MusicPlayer.kt +++ b/src/main/java/net/ccbluex/liquidbounce/features/module/modules/music/MusicPlayer.kt @@ -18,6 +18,7 @@ import net.ccbluex.liquidbounce.features.module.modules.music.core.Track import net.ccbluex.liquidbounce.features.module.modules.music.core.TrackSource import net.ccbluex.liquidbounce.ui.client.music.GuiMusicPlayer import net.ccbluex.liquidbounce.utils.client.chat +import net.minecraft.client.gui.GuiScreen import net.ccbluex.liquidbounce.utils.kotlin.SharedScopes import kotlinx.coroutines.launch @@ -163,7 +164,7 @@ object MusicPlayer : Module("MusicPlayer", Category.CLIENT) { } if (openGuiOnEnable && !suppressOpenGuiOnEnable) { - mc.displayGuiScreen(GuiMusicPlayer(mc.currentScreen)) + displayMusicGui(mc.currentScreen) } } @@ -388,7 +389,7 @@ object MusicPlayer : Module("MusicPlayer", Category.CLIENT) { fun getVolume(): Int = volumeValue - fun openGui(prev: net.minecraft.client.gui.GuiScreen? = mc.currentScreen) { + fun openGui(prev: GuiScreen? = mc.currentScreen) { if (!state) { suppressOpenGuiOnEnable = true try { @@ -397,6 +398,13 @@ object MusicPlayer : Module("MusicPlayer", Category.CLIENT) { suppressOpenGuiOnEnable = false } } - mc.displayGuiScreen(GuiMusicPlayer(prev)) + displayMusicGui(prev) + } + + /** Deferred to the next tick so chat closing after send does not dismiss the GUI. */ + private fun displayMusicGui(prev: GuiScreen?) { + mc.addScheduledTask { + mc.displayGuiScreen(GuiMusicPlayer(prev)) + } } } From a4dea29753e8149046b187d94edd7fbaf4d0bc7e Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 18:36:39 +0800 Subject: [PATCH 07/13] chore: bump AirClient version to b1.3 Co-authored-by: Cursor --- README.md | 2 +- gradle.properties | 2 +- src/main/java/net/ccbluex/liquidbounce/LiquidBounce.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c1d67c1c..c6ab5e91 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A free open-source mixin-based injection hacked client for Minecraft using Minecraft Forge. -**Version:** b1.2 | **Minecraft:** 1.8.9 +**Version:** b1.3 | **Minecraft:** 1.8.9 [GitHub](https://github.com/lmx0721/AirClient) | [Bilibili](https://space.bilibili.com/3706927473232492) | QQ Group: 722573066 diff --git a/gradle.properties b/gradle.properties index dd637450..ae85d083 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.daemon=true org.gradle.parallel=true org.gradle.caching=true -mod_version=b1.2 +mod_version=b1.3 maven_group=net.ccbluex archives_base_name=AirClient diff --git a/src/main/java/net/ccbluex/liquidbounce/LiquidBounce.kt b/src/main/java/net/ccbluex/liquidbounce/LiquidBounce.kt index 68189656..4aafb815 100644 --- a/src/main/java/net/ccbluex/liquidbounce/LiquidBounce.kt +++ b/src/main/java/net/ccbluex/liquidbounce/LiquidBounce.kt @@ -94,7 +94,7 @@ object LiquidBounce { LOGGER.warn("Failed to load version properties: ${e.message}") } - clientVersionText = props.getProperty("version", "b1.2") + clientVersionText = props.getProperty("version", "b1.3") clientVersionNumber = 100 clientCommit = props.getProperty("commit", "unknown") clientBranch = props.getProperty("branch", "unknown") From 1e2da0cf268724c0c5e592592859d6cf697c3d75 Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 18:47:54 +0800 Subject: [PATCH 08/13] remove: drop bundled Phosphor lighting mod Remove embedded Phosphor mixins and lighting engine so users can install Phosphor separately if desired without mixin conflicts. Co-authored-by: Cursor --- build.gradle | 4 +- .../mods/phosphor/api/IChunkLighting.java | 8 - .../mods/phosphor/api/IChunkLightingData.java | 13 - .../mods/phosphor/api/ILightingEngine.java | 12 - .../phosphor/api/ILightingEngineProvider.java | 5 - .../client/ClientChunkProviderAccessor.java | 13 - .../mixins/client/MixinMinecraft.java | 35 - .../mixins/common/MixinAnvilChunkLoader.java | 53 -- .../mixins/common/MixinChunk$Legacy.java | 51 -- .../mixins/common/MixinChunk$Vanilla.java | 50 -- .../phosphor/mixins/common/MixinChunk.java | 481 -------------- .../common/MixinChunkProviderServer.java | 47 -- .../common/MixinExtendedBlockStorage.java | 101 --- .../mixins/common/MixinSPacketChunkData.java | 23 - .../phosphor/mixins/common/MixinWorld.java | 53 -- .../common/ServerChunkProviderAccessor.java | 13 - .../mixins/plugins/LightingEnginePlugin.java | 126 ---- .../mods/phosphor/mod/PhosphorConfig.java | 75 --- .../mods/phosphor/mod/PhosphorConstants.java | 9 - .../mods/phosphor/mod/PhosphorMod.java | 10 - .../mod/collections/PooledLongQueue.java | 163 ----- .../phosphor/mod/world/WorldChunkSlice.java | 57 -- .../mod/world/lighting/LightingEngine.java | 620 ------------------ .../world/lighting/LightingEngineHelpers.java | 76 --- .../mod/world/lighting/LightingHooks.java | 388 ----------- .../injection/forge/MixinLoader.java | 1 - src/main/resources/liquidbounce_at.cfg | 5 - src/main/resources/mixins.phosphor.json | 29 - zip_include/README.txt | 1 - 29 files changed, 2 insertions(+), 2520 deletions(-) delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/api/IChunkLighting.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/api/IChunkLightingData.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/api/ILightingEngine.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/api/ILightingEngineProvider.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/client/ClientChunkProviderAccessor.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/client/MixinMinecraft.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinAnvilChunkLoader.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk$Legacy.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk$Vanilla.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunkProviderServer.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinExtendedBlockStorage.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinSPacketChunkData.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinWorld.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/common/ServerChunkProviderAccessor.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mixins/plugins/LightingEnginePlugin.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorConfig.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorConstants.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorMod.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mod/collections/PooledLongQueue.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mod/world/WorldChunkSlice.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingEngine.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingEngineHelpers.java delete mode 100644 src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingHooks.java delete mode 100644 src/main/resources/mixins.phosphor.json diff --git a/build.gradle b/build.gradle index a012f078..279b2cc6 100644 --- a/build.gradle +++ b/build.gradle @@ -193,7 +193,7 @@ jar { "FMLCorePlugin": "net.ccbluex.liquidbounce.injection.forge.MixinLoader", "FMLCorePluginContainsFMLMod": true, "ForceLoadAsMod": true, - "MixinConfigs": "liquidbounce.forge.mixins.json,mixins.phosphor.json", + "MixinConfigs": "liquidbounce.forge.mixins.json", "ModSide": "CLIENT", "TweakClass": "org.spongepowered.asm.launch.MixinTweaker", "TweakOrder": "0", @@ -265,7 +265,7 @@ task runClientDevJar(type: Jar) { "FMLCorePlugin": "net.ccbluex.liquidbounce.injection.forge.MixinLoader", "FMLCorePluginContainsFMLMod": true, "ForceLoadAsMod": true, - "MixinConfigs": "liquidbounce.forge.mixins.json,mixins.phosphor.json", + "MixinConfigs": "liquidbounce.forge.mixins.json", "ModSide": "CLIENT", "TweakClass": "org.spongepowered.asm.launch.MixinTweaker", "TweakOrder": "0", diff --git a/src/main/java/me/jellysquid/mods/phosphor/api/IChunkLighting.java b/src/main/java/me/jellysquid/mods/phosphor/api/IChunkLighting.java deleted file mode 100644 index 6a5c5f9d..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/api/IChunkLighting.java +++ /dev/null @@ -1,8 +0,0 @@ -package me.jellysquid.mods.phosphor.api; - -import net.minecraft.util.BlockPos; -import net.minecraft.world.EnumSkyBlock; - -public interface IChunkLighting { - int getCachedLightFor(EnumSkyBlock enumSkyBlock, BlockPos pos); -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/api/IChunkLightingData.java b/src/main/java/me/jellysquid/mods/phosphor/api/IChunkLightingData.java deleted file mode 100644 index 0deb6c6c..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/api/IChunkLightingData.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.jellysquid.mods.phosphor.api; - -public interface IChunkLightingData { - short[] getNeighborLightChecks(); - - void setNeighborLightChecks(short[] data); - - boolean isLightInitialized(); - - void setLightInitialized(boolean val); - - void setSkylightUpdatedPublic(); -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/api/ILightingEngine.java b/src/main/java/me/jellysquid/mods/phosphor/api/ILightingEngine.java deleted file mode 100644 index 957ba3ba..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/api/ILightingEngine.java +++ /dev/null @@ -1,12 +0,0 @@ -package me.jellysquid.mods.phosphor.api; - -import net.minecraft.util.BlockPos; -import net.minecraft.world.EnumSkyBlock; - -public interface ILightingEngine { - void scheduleLightUpdate(EnumSkyBlock lightType, BlockPos pos); - - void processLightUpdates(); - - void processLightUpdatesForType(EnumSkyBlock lightType); -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/api/ILightingEngineProvider.java b/src/main/java/me/jellysquid/mods/phosphor/api/ILightingEngineProvider.java deleted file mode 100644 index 79117cfd..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/api/ILightingEngineProvider.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.jellysquid.mods.phosphor.api; - -public interface ILightingEngineProvider { - ILightingEngine getLightingEngine(); -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/client/ClientChunkProviderAccessor.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/client/ClientChunkProviderAccessor.java deleted file mode 100644 index 9b0542d8..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/client/ClientChunkProviderAccessor.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.client; - -import net.minecraft.client.multiplayer.ChunkProviderClient; -import net.minecraft.util.LongHashMap; -import net.minecraft.world.chunk.Chunk; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(ChunkProviderClient.class) -public interface ClientChunkProviderAccessor { - @Accessor("chunkMapping") - LongHashMap getChunkStorage(); -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/client/MixinMinecraft.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/client/MixinMinecraft.java deleted file mode 100644 index 29a2d120..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/client/MixinMinecraft.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.client; - -import me.jellysquid.mods.phosphor.api.ILightingEngineProvider; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.WorldClient; -import net.minecraft.profiler.Profiler; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(Minecraft.class) -public abstract class MixinMinecraft { - @Shadow - @Final - public Profiler mcProfiler; - - @Shadow - public WorldClient theWorld; - - /** - * @author Angeline - * Forces the client to process light updates before rendering the world. We inject before the call to the profiler - * which designates the start of world rendering. This is a rather injection site. - */ - @Inject(method = "runTick", at = @At(value = "CONSTANT", args = "stringValue=levelRenderer", shift = At.Shift.BY, by = -3)) - private void onRunTick(CallbackInfo ci) { - this.mcProfiler.endStartSection("lighting"); - - ((ILightingEngineProvider) this.theWorld).getLightingEngine().processLightUpdates(); - } - -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinAnvilChunkLoader.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinAnvilChunkLoader.java deleted file mode 100644 index e723ce73..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinAnvilChunkLoader.java +++ /dev/null @@ -1,53 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.common; - -import me.jellysquid.mods.phosphor.api.IChunkLightingData; -import me.jellysquid.mods.phosphor.api.ILightingEngineProvider; -import me.jellysquid.mods.phosphor.mod.world.lighting.LightingHooks; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.world.World; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.storage.AnvilChunkLoader; -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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(AnvilChunkLoader.class) -public abstract class MixinAnvilChunkLoader { - /** - * Injects into the head of saveChunk() to forcefully process all pending light updates. Fail-safe. - * - * @author Angeline - */ - @Inject(method = "saveChunk", at = @At("HEAD")) - private void onConstructed(World world, Chunk chunkIn, CallbackInfo callbackInfo) { - ((ILightingEngineProvider) world).getLightingEngine().processLightUpdates(); - } - - /** - * Injects the deserialization logic for chunk data on load so we can extract whether or not we've populated light yet. - * - * @author Angeline - */ - @Inject(method = "readChunkFromNBT", at = @At("RETURN")) - private void onReadChunkFromNBT(World world, NBTTagCompound compound, CallbackInfoReturnable cir) { - Chunk chunk = cir.getReturnValue(); - - LightingHooks.readNeighborLightChecksFromNBT(chunk, compound); - - ((IChunkLightingData) chunk).setLightInitialized(compound.getBoolean("LightPopulated")); - - } - - /** - * Injects the serialization logic for chunk data on save so we can store whether or not we've populated light yet. - * @author Angeline - */ - @Inject(method = "writeChunkToNBT", at = @At("RETURN")) - private void onWriteChunkToNBT(Chunk chunk, World world, NBTTagCompound compound, CallbackInfo ci) { - LightingHooks.writeNeighborLightChecksToNBT(chunk, compound); - - compound.setBoolean("LightPopulated", ((IChunkLightingData) chunk).isLightInitialized()); - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk$Legacy.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk$Legacy.java deleted file mode 100644 index 2a8a8f17..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk$Legacy.java +++ /dev/null @@ -1,51 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.common; - -import net.minecraft.util.BlockPos; -import net.minecraft.world.EnumSkyBlock; -import net.minecraft.world.chunk.Chunk; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; -import org.spongepowered.asm.mixin.injection.Redirect; - -@SuppressWarnings("all") -@Mixin(value = Chunk.class) -public class MixinChunk$Legacy { - - /** - * Modifies the flag variable of setBlockState(BlockPos, IBlockState) to always be false after it is set. - * - * @author Angeline - */ - @ModifyVariable( - method = "setBlockState", - at = @At( - value = "STORE", - ordinal = 1 - ), - index = 12, - name = "flag", - allow = 1 - ) - private boolean setBlockStateInjectGenerateSkylightMapVanilla(boolean generateSkylight) { - return false; - } - - /** - * Prevent propagateSkylightOcclusion from being called. - * @author embeddedt - */ - @Redirect(method = "setBlockState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/Chunk;propagateSkylightOcclusion(II)V")) - private void doPropagateSkylight(Chunk chunk, int i1, int i2) { - /* No-op, we don't want skylight propagated */ - } - - /** - * Prevent getLightFor from being called. - * @author embeddedt - */ - @Redirect(method = "setBlockState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/Chunk;getLightFor(Lnet/minecraft/world/EnumSkyBlock;Lnet/minecraft/util/BlockPos;)I")) - private int getFakeLightFor(Chunk chunk, EnumSkyBlock skyBlock, BlockPos blockPos) { - return 0; - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk$Vanilla.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk$Vanilla.java deleted file mode 100644 index 9431d710..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk$Vanilla.java +++ /dev/null @@ -1,50 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.common; - -import net.minecraft.util.BlockPos; -import net.minecraft.world.EnumSkyBlock; -import net.minecraft.world.chunk.Chunk; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; -import org.spongepowered.asm.mixin.injection.Redirect; - -@Mixin(value = Chunk.class) -public class MixinChunk$Vanilla { - - /** - * Modifies the flag variable of setBlockState(BlockPos, IBlockState) to always be false after it is set. - * - * @author Angeline - */ - @ModifyVariable( - method = "setBlockState", - at = @At( - value = "STORE", - ordinal = 1 - ), - index = 13, - name = "flag", - allow = 1 - ) - private boolean setBlockStateInjectGenerateSkylightMapVanilla(boolean generateSkylight) { - return false; - } - - /** - * Prevent propagateSkylightOcclusion from being called. - * @author embeddedt - */ - @Redirect(method = "setBlockState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/Chunk;propagateSkylightOcclusion(II)V")) - private void doPropagateSkylight(Chunk chunk, int i1, int i2) { - /* No-op, we don't want skylight propagated */ - } - - /** - * Prevent getLightFor from being called. - * @author embeddedt - */ - @Redirect(method = "setBlockState", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/Chunk;getLightFor(Lnet/minecraft/world/EnumSkyBlock;Lnet/minecraft/util/BlockPos;)I")) - private int getFakeLightFor(Chunk chunk, EnumSkyBlock skyBlock, BlockPos blockPos) { - return 0; - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk.java deleted file mode 100644 index 6d61701a..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunk.java +++ /dev/null @@ -1,481 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.common; - -import me.jellysquid.mods.phosphor.api.IChunkLighting; -import me.jellysquid.mods.phosphor.api.IChunkLightingData; -import me.jellysquid.mods.phosphor.api.ILightingEngine; -import me.jellysquid.mods.phosphor.api.ILightingEngineProvider; -import me.jellysquid.mods.phosphor.mod.PhosphorMod; -import me.jellysquid.mods.phosphor.mod.world.WorldChunkSlice; -import me.jellysquid.mods.phosphor.mod.world.lighting.LightingHooks; -import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.*; -import net.minecraft.world.ChunkCoordIntPair; -import net.minecraft.world.EnumSkyBlock; -import net.minecraft.world.World; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.storage.ExtendedBlockStorage; -import org.spongepowered.asm.mixin.*; -import org.spongepowered.asm.mixin.injection.*; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@SuppressWarnings("all") -@Mixin(Chunk.class) -public abstract class MixinChunk implements IChunkLighting, IChunkLightingData, ILightingEngineProvider { - - // === SHADOW START === - - @Shadow - @Final - private ExtendedBlockStorage[] storageArrays; - - @Shadow - private boolean isModified; - - @Shadow - @Final - private int[] heightMap; - - @Shadow - private int heightMapMinimum; - - @Shadow - @Final - private World worldObj; - - @Shadow - private boolean isTerrainPopulated; - - @Final - @Shadow - private boolean[] updateSkylightColumns; - - @Final - @Shadow - public int xPosition; - - @Final - @Shadow - public int zPosition; - - @Shadow - private boolean isGapLightingUpdated; - - @Shadow - private int queuedLightChecks; - - @Shadow - private boolean isChunkLoaded; - - @Shadow - public abstract TileEntity getTileEntity(BlockPos pos, Chunk.EnumCreateEntityType type); - - @Shadow - public abstract IBlockState getBlockState(BlockPos pos); - - @Shadow - public abstract int getHeightValue(int i, int j); - - @Shadow - public abstract boolean canSeeSky(BlockPos pos); - - // === SHADOW END === - - // === FIELD START === - - private static final EnumFacing[] HORIZONTAL = EnumFacing.Plane.HORIZONTAL.facings(); - - // === FIELD END === - - // === MIXINS START === - - /** - * Callback injected into the Chunk ctor to cache a reference to the lighting engine from the world. - * - * @author Angeline - */ - @Inject(method = "(Lnet/minecraft/world/World;II)V", at = @At("RETURN")) - private void onConstructed(CallbackInfo ci) { - this.lightingEngine = ((ILightingEngineProvider) this.worldObj).getLightingEngine(); - } - - /** - * Callback injected to the head of getLightSubtracted(BlockPos, int) to force deferred light updates to be processed. - * - * @author Angeline - */ - @Inject(method = "getLightSubtracted", at = @At("HEAD")) - private void onGetLightSubtracted(BlockPos pos, int amount, CallbackInfoReturnable cir) { - this.getLightingEngine().processLightUpdates(); - } - - /** - * Callback injected at the end of onLoad() to have previously scheduled light updates scheduled again. - * - * @author Angeline - */ - @Inject(method = "onChunkLoad", at = @At("RETURN")) - private void onLoad(CallbackInfo ci) { - LightingHooks.scheduleRelightChecksForChunkBoundaries(this.worldObj, (Chunk) (Object) this); - } - - // === REPLACEMENTS START === - - /** - * @reason Replaces the call in setLightFor(Chunk, EnumSkyBlock, BlockPos) with our hook. - * @author Angeline - */ - @Redirect( - method = "setLightFor", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/world/chunk/Chunk;generateSkylightMap()V" - ), - expect = 0 - ) - private void setLightForRedirectGenerateSkylightMap(Chunk chunk, EnumSkyBlock type, BlockPos pos, int value) { - LightingHooks.initSkylightForSection(this.worldObj, (Chunk) (Object) this, this.storageArrays[pos.getY() >> 4]); - } - - /** - * @reason Forge: Can sometimes be called before we are added to the global world list. So use the less accurate one during that. It'll be recalculated later. - * @author MinecraftForge 1.9.4, 1.11, 1.12.2 - */ - @Overwrite - public int getBlockLightOpacity(int x, int y, int z) { - IBlockState state = this.getBlockState(new BlockPos (x, y, z)); - return !this.isChunkLoaded ? state.getBlock().getLightOpacity() : state.getBlock().getLightOpacity(this.worldObj, new BlockPos(this.xPosition << 4 | x & 15, y, this.zPosition << 4 | z & 15)); - } - - /** - * @reason Overwrites relightBlock with a more efficient implementation. - * @author Angeline - */ - @Overwrite - public void relightBlock(int x, int y, int z) { - int i = this.heightMap[z << 4 | x] & 255; - int j = i; - - if (y > i) { - j = y; - } - - while (j > 0 && this.getBlockLightOpacity(x, j - 1, z) == 0) { - --j; - } - - if (j != i) { - this.heightMap[z << 4 | x] = j; - - if (!this.worldObj.provider.getHasNoSky()) { - LightingHooks.relightSkylightColumn(this.worldObj, (Chunk) (Object) this, x, z, i, j); - } - - int l1 = this.heightMap[z << 4 | x]; - - if (l1 < this.heightMapMinimum) { - this.heightMapMinimum = l1; - } - } - } - - /** - * @reason Hook for calculating light updates only as needed. {@link MixinChunk#getCachedLightFor(EnumSkyBlock, BlockPos)} does not - * call this hook. - * @author Angeline - */ - @Overwrite - public int getLightFor(EnumSkyBlock type, BlockPos pos) { - this.getLightingEngine().processLightUpdatesForType(type); - - return this.getCachedLightFor(type, pos); - } - - /** - * @reason Hooks into checkLight() to check chunk lighting and returns immediately after, voiding the rest of the function. - * @author Angeline - */ - @Overwrite - public void func_150809_p() { - this.isTerrainPopulated = true; - - LightingHooks.checkChunkLighting((Chunk) (Object) this, this.worldObj); - } - - /** - * @reason Optimized version of recheckGaps. Avoids chunk fetches as much as possible. - * @author Angeline - */ - @Overwrite - public void recheckGaps(boolean onlyOne) { - this.worldObj.theProfiler.startSection("recheckGaps"); - - WorldChunkSlice slice = new WorldChunkSlice(this.worldObj, this.xPosition, this.zPosition); - - if (this.worldObj.isAreaLoaded(new BlockPos(this.xPosition * 16 + 8, 0, this.zPosition * 16 + 8), 16)) { - for (int x = 0; x < 16; ++x) { - for (int z = 0; z < 16; ++z) { - if (this.recheckGapsForColumn(slice, x, z)) { - if (onlyOne) { - this.worldObj.theProfiler.endSection(); - - return; - } - } - } - } - - this.isGapLightingUpdated = false; - } - - this.worldObj.theProfiler.endSection(); - } - - /** - * @author embeddedt - * @reason optimize random light checks so they complete faster - */ - @Overwrite - public void enqueueRelightChecks() { - if (this.queuedLightChecks >= 4096) - { - return; - } - - boolean isActiveChunk = worldObj.activeChunkSet.contains(new ChunkCoordIntPair(this.xPosition, this.zPosition)); - int lightRecheckSpeed; - - if (worldObj.isRemote && isActiveChunk) { - lightRecheckSpeed = 256; - } else if (worldObj.isRemote) { - lightRecheckSpeed = 64; - } else { - lightRecheckSpeed = 32; - } - - BlockPos blockpos = new BlockPos(this.xPosition << 4, 0, this.zPosition << 4); - for (int i = 0; i < lightRecheckSpeed; ++i) { - - int j = this.queuedLightChecks % 16; - int k = this.queuedLightChecks / 16 % 16; - int l = this.queuedLightChecks / 256; - ++this.queuedLightChecks; - - for (int y = 0; y < 16; ++y) - { - ExtendedBlockStorage storage = this.storageArrays[j]; - BlockPos blockpos1 = blockpos.add(k, (j << 4) + y, l); - boolean flag = y == 0 || y == 15 || k == 0 || k == 15 || l == 0 || l == 15; - - if (storage == null && flag || storage != null && storage.getBlockByExtId(k, y, l).getMaterial() == Material.air) - { - for (EnumFacing enumfacing : EnumFacing.values()) - { - BlockPos blockpos2 = blockpos1.offset(enumfacing); - - if (this.worldObj.getBlockState(blockpos2).getBlock().getLightValue() > 0) - { - this.worldObj.checkLight(blockpos2); - } - } - - this.worldObj.checkLight(blockpos1); - } - } - } - } - - // === REPLACEMENTS END === - - private boolean recheckGapsForColumn(WorldChunkSlice slice, int x, int z) { - int i = x + z * 16; - - if (this.updateSkylightColumns[i]) { - this.updateSkylightColumns[i] = false; - - int height = this.getHeightValue(x, z); - - int x1 = this.xPosition * 16 + x; - int z1 = this.zPosition * 16 + z; - - int max = this.recheckGapsGetLowestHeight(slice, x1, z1); - - this.recheckGapsSkylightNeighborHeight(slice, x1, z1, height, max); - - return true; - } - - return false; - } - - private int recheckGapsGetLowestHeight(WorldChunkSlice slice, int x, int z) { - int max = Integer.MAX_VALUE; - - for (EnumFacing facing : HORIZONTAL) { - int j = x + facing.getFrontOffsetX(); - int k = z + facing.getFrontOffsetZ(); - Chunk chunk = slice.getChunkFromWorldCoords(j, k); - if (chunk != null) { - max = Math.min(max, chunk.getLowestHeight()); - } - - } - - return max; - } - - private void recheckGapsSkylightNeighborHeight(WorldChunkSlice slice, int x, int z, int height, int max) { - this.checkSkylightNeighborHeight(slice, x, z, max); - - for (EnumFacing facing : HORIZONTAL) { - int j = x + facing.getFrontOffsetX(); - int k = z + facing.getFrontOffsetZ(); - - this.checkSkylightNeighborHeight(slice, j, k, height); - } - } - - private void checkSkylightNeighborHeight(WorldChunkSlice slice, int x, int z, int maxValue) { - - Chunk chunk = slice.getChunkFromWorldCoords(x, z); - if (chunk != null) { - int i = chunk.getHeightValue(x & 15, z & 15); - - if (i > maxValue) { - this.updateSkylightNeighborHeight(slice, x, z, maxValue, i + 1); - } else if (i < maxValue) { - this.updateSkylightNeighborHeight(slice, x, z, i, maxValue + 1); - } - - } else { - PhosphorMod.LOGGER.warn("Chunk is null! x: " + x + " z: " + z + " maxValue: " + maxValue); - } - } - - private void updateSkylightNeighborHeight(WorldChunkSlice slice, int x, int z, int startY, int endY) { - if (endY > startY) { - - if (!slice.isLoaded(x, z, 16)) { - return; - } - - for (int i = startY; i < endY; ++i) { - this.worldObj.checkLightFor(EnumSkyBlock.SKY, new BlockPos(x, i, z)); - } - - this.isModified = true; - } - } - - // === INTERFACE IMPL === - - private short[] neighborLightChecks; - - private boolean isLightInitialized; - - private ILightingEngine lightingEngine; - - @Override - public short[] getNeighborLightChecks() { - return this.neighborLightChecks; - } - - @Override - public void setNeighborLightChecks(short[] data) { - this.neighborLightChecks = data; - } - - @Override - public ILightingEngine getLightingEngine() { - if (this.lightingEngine == null) { - this.lightingEngine = ((ILightingEngineProvider)this.worldObj).getLightingEngine(); - if (this.lightingEngine == null) { - throw new IllegalStateException("Cannot get Lighting Engine from instance"); - } - } - return this.lightingEngine; - } - - @Override - public boolean isLightInitialized() { - return this.isLightInitialized; - } - - @Override - public void setLightInitialized(boolean lightInitialized) { - this.isLightInitialized = lightInitialized; - } - - @Shadow - protected abstract void func_177441_y(); - - @Override - public void setSkylightUpdatedPublic() { - this.func_177441_y(); - } - - @Override - public int getCachedLightFor(EnumSkyBlock type, BlockPos pos) { - int i = pos.getX() & 15; - int j = pos.getY(); - int k = pos.getZ() & 15; - - ExtendedBlockStorage storage = this.storageArrays[j >> 4]; - - if (storage == null) { - if (this.canSeeSky(pos)) { - return type.defaultLightValue; - } - else { - return 0; - } - } - else if (type == EnumSkyBlock.SKY) { - if (this.worldObj.provider.getHasNoSky()) { - return 0; - } - else { - return storage.getExtSkylightValue(i, j & 15, k); - } - } - else { - if (type == EnumSkyBlock.BLOCK) { - return storage.getExtBlocklightValue(i, j & 15, k); - } - else { - return type.defaultLightValue; - } - } - } - - // === END OF INTERFACE IMPL === - - private ExtendedBlockStorage initSection(int y, boolean storeSkylight) { - - ExtendedBlockStorage storage = new ExtendedBlockStorage(y, storeSkylight); - - LightingHooks.initSkylightForSection(this.worldObj, (Chunk) (Object) this, storage); - - return storage; - } - - /** - * Redirects the construction of the ExtendedBlockStorage in setBlockState(BlockPos, IBlockState). We need to initialize - * the skylight data for the constructed section as soon as possible. - * - * @author Angeline - */ - @Redirect( - method = "setBlockState", - at = @At( - value = "NEW", - args = "class=net/minecraft/world/chunk/storage/ExtendedBlockStorage" - ), - expect = 0 - ) - private ExtendedBlockStorage setBlockStateCreateSectionVanilla(int y, boolean storeSkylight) { - return this.initSection(y, storeSkylight); - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunkProviderServer.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunkProviderServer.java deleted file mode 100644 index 2d5621b1..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinChunkProviderServer.java +++ /dev/null @@ -1,47 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.common; - -import me.jellysquid.mods.phosphor.api.ILightingEngineProvider; -import net.minecraft.util.IProgressUpdate; -import net.minecraft.world.WorldServer; -import net.minecraft.world.gen.ChunkProviderServer; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.Set; - -@Mixin(ChunkProviderServer.class) -public abstract class MixinChunkProviderServer { - @Shadow - public WorldServer worldObj; - - @Shadow - private Set droppedChunksSet; - - /** - * Injects a callback into the start of saveChunks(boolean) to force all light updates to be processed before saving. - * - * @author Angeline - */ - @Inject(method = "saveChunks", at = @At("HEAD")) - private void onSaveChunks(boolean p_saveChunks_1_, IProgressUpdate p_saveChunks_2_, CallbackInfoReturnable cir) { - ((ILightingEngineProvider) this.worldObj).getLightingEngine().processLightUpdates(); - } - - /** - * Injects a callback into the start of the onTick() method to process all pending light updates. This is not necessarily - * required, but we don't want our work queues getting too large. - * - * @author Angeline - */ - @Inject(method = "unloadQueuedChunks", at = @At("HEAD")) - private void onTick(CallbackInfoReturnable cir) { - if (!this.worldObj.disableLevelSaving) { - if (!this.droppedChunksSet.isEmpty()) { - ((ILightingEngineProvider) this.worldObj).getLightingEngine().processLightUpdates(); - } - } - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinExtendedBlockStorage.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinExtendedBlockStorage.java deleted file mode 100644 index d3aa2e5e..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinExtendedBlockStorage.java +++ /dev/null @@ -1,101 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.common; - -import net.minecraft.world.chunk.NibbleArray; -import net.minecraft.world.chunk.storage.ExtendedBlockStorage; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; - -@Mixin(ExtendedBlockStorage.class) -public class MixinExtendedBlockStorage { - - @Shadow - private int blockRefCount; - - @Shadow - private NibbleArray blocklightArray; - - @Shadow - private NibbleArray skylightArray; - - private int lightRefCount = -1; - - /** - * @author Angeline - * @reason Reset lightRefCount on call - */ - @Overwrite - public void setExtSkylightValue(int x, int y, int z, int value) { - this.skylightArray.set(x, y, z, value); - this.lightRefCount = -1; - } - - /** - * @author Angeline - * @reason Reset lightRefCount on call - */ - @Overwrite - public void setExtBlocklightValue(int x, int y, int z, int value) { - this.blocklightArray.set(x, y, z, value); - this.lightRefCount = -1; - } - - /** - * @author Angeline - * @reason Reset lightRefCount on call - */ - @Overwrite - public void setBlocklightArray(NibbleArray array) { - this.blocklightArray = array; - this.lightRefCount = -1; - } - - /** - * @author Angeline - * @reason Reset lightRefCount on call - */ - @Overwrite - public void setSkylightArray(NibbleArray array) { - this.skylightArray = array; - this.lightRefCount = -1; - } - - - /** - * @author Angeline - * @reason Send light data to clients when lighting is non-trivial - */ - @Overwrite - public boolean isEmpty() { - if (this.blockRefCount != 0) { - return false; - } - - // -1 indicates the lightRefCount needs to be re-calculated - if (this.lightRefCount == -1) { - if (this.checkLightArrayEqual(this.skylightArray, (byte) 0xFF) && this.checkLightArrayEqual(this.blocklightArray, (byte) 0x00)) { - this.lightRefCount = 0; // Lighting is trivial, don't send to clients - } else { - this.lightRefCount = 1; // Lighting is not trivial, send to clients - } - } - - return this.lightRefCount == 0; - } - - private boolean checkLightArrayEqual(NibbleArray storage, byte val) { - if (storage == null) { - return true; - } - - byte[] arr = storage.getData(); - - for (byte b : arr) { - if (b != val) { - return false; - } - } - - return true; - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinSPacketChunkData.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinSPacketChunkData.java deleted file mode 100644 index 3a619542..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinSPacketChunkData.java +++ /dev/null @@ -1,23 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.common; - -import me.jellysquid.mods.phosphor.api.ILightingEngineProvider; -import net.minecraft.network.play.server.S21PacketChunkData; -import net.minecraft.world.chunk.Chunk; -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.CallbackInfoReturnable; - -@Mixin(S21PacketChunkData.class) -public abstract class MixinSPacketChunkData { - /** - * @author Angeline - * Injects a callback into S21PacketChunkData#calculateChunkSize(Chunk, booolean, int) to force light updates to be - * processed before creating the client payload. We use this method rather than the constructor as it is not valid - * to inject elsewhere other than the RETURN of a ctor, which is too late for our needs. - */ - @Inject(method = "getExtractedData", at = @At("HEAD")) - private static void onCalculateChunkSize(Chunk chunkIn, boolean load, boolean hasSkyLight, int changedSectionFilter, CallbackInfoReturnable cir) { - ((ILightingEngineProvider) chunkIn).getLightingEngine().processLightUpdates(); - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinWorld.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinWorld.java deleted file mode 100644 index 98d63de2..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/MixinWorld.java +++ /dev/null @@ -1,53 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.common; - -import me.jellysquid.mods.phosphor.api.ILightingEngineProvider; -import me.jellysquid.mods.phosphor.mod.world.lighting.LightingEngine; -import net.minecraft.util.BlockPos; -import net.minecraft.world.EnumSkyBlock; -import net.minecraft.world.World; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(World.class) -public abstract class MixinWorld implements ILightingEngineProvider { - @Shadow - public abstract boolean isAreaLoaded(BlockPos pos, int i, boolean b); - - private LightingEngine lightingEngine; - - @Override - public LightingEngine getLightingEngine() { - return this.lightingEngine; - } - - /** - * @author Angeline - * Initialize the lighting engine on world construction. - */ - @Inject(method = "", at = @At("RETURN")) - private void onConstructed(CallbackInfo ci) { - this.lightingEngine = new LightingEngine((World) (Object) this); - } - - /** - * Directs the light update to the lighting engine and always returns a success value. - * @author Angeline - */ - @Inject(method = "checkLightFor", at = @At("HEAD"), cancellable = true) - private void checkLightFor(EnumSkyBlock type, BlockPos pos, CallbackInfoReturnable cir) { - - if (!this.isAreaLoaded(pos, 16, false)) - { - cir.setReturnValue(false); - } - - this.lightingEngine.scheduleLightUpdate(type, pos); - - cir.setReturnValue(true); - } - -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/ServerChunkProviderAccessor.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/common/ServerChunkProviderAccessor.java deleted file mode 100644 index 1d531da4..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/common/ServerChunkProviderAccessor.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.common; - -import net.minecraft.util.LongHashMap; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.gen.ChunkProviderServer; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(ChunkProviderServer.class) -public interface ServerChunkProviderAccessor { - @Accessor("id2ChunkMap") - LongHashMap getChunkStorage(); -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mixins/plugins/LightingEnginePlugin.java b/src/main/java/me/jellysquid/mods/phosphor/mixins/plugins/LightingEnginePlugin.java deleted file mode 100644 index c03e78f5..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mixins/plugins/LightingEnginePlugin.java +++ /dev/null @@ -1,126 +0,0 @@ -package me.jellysquid.mods.phosphor.mixins.plugins; - -import me.jellysquid.mods.phosphor.mod.PhosphorConfig; -import net.minecraft.launchwrapper.Launch; -import net.minecraftforge.fml.common.FMLCommonHandler; -import net.minecraftforge.fml.common.Loader; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.spongepowered.asm.lib.tree.ClassNode; -import org.spongepowered.asm.mixin.MixinEnvironment; -import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; -import org.spongepowered.asm.mixin.extensibility.IMixinInfo; - -import java.util.List; -import java.util.Set; - -@SuppressWarnings("all") -public class LightingEnginePlugin implements IMixinConfigPlugin { - private static final Logger logger = LogManager.getLogger("Phosphor Plugin"); - - public static boolean ENABLE_ILLEGAL_THREAD_ACCESS_WARNINGS = false; - - private PhosphorConfig config; - - public boolean spongePresent; - - public boolean legacy = false; - - @Override - public void onLoad(String mixinPackage) { - logger.debug("Loading configuration"); - - this.config = PhosphorConfig.loadConfig(); - - if (!this.config.enablePhosphor) { - logger.warn("Phosphor has been disabled through mod configuration! No patches will be applied..."); - } - - ENABLE_ILLEGAL_THREAD_ACCESS_WARNINGS = this.config.enableIllegalThreadAccessWarnings; - - String mcVersion = Loader.instance().getMCVersionString(); - - logger.info("Detected MC Version : " + mcVersion); - - if (!mcVersion.contains("8.9")) { - logger.warn("You're using legacy 1.8.8 instead of 1.8.9"); - this.legacy = true; - } - - try { - // This class will always be loaded by Forge prior to us (due to the tweak class ordering) and should have - // no effect. On the off chance it isn't, early class loading shouldn't cause any issues as nobody seems to - // transform core-mods themselves, or at least I hope they don't... - Class.forName("org.spongepowered.mod.SpongeCoremod"); - - this.spongePresent = true; - } catch (Exception e) { - this.spongePresent = false; - } - - if (this.spongePresent) { - logger.error("Sponge (Forge) has been detected on the classpath, you will run into issues. "); - logger.error("Due to SpongeForge 1.8.9 's codebase was outdated and uses an extremely outdated Mixin (0.5.11), This mod can't be compatible with it! "); - FMLCommonHandler.instance().exitJava(-1, false); - } - } - - @Override - public String getRefMapperConfig() { - if (Launch.blackboard.get("fml.deobfuscatedEnvironment") == Boolean.TRUE) { - return null; - } - - return "liquidbounce.mixins.refmap.json"; - } - - @Override - public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - if (!this.config.enablePhosphor) { - return false; - } - - // Do not apply client transformations if we are not in a client environment! - if (targetClassName.startsWith("net.minecraft.client") && MixinEnvironment.getCurrentEnvironment().getSide() != MixinEnvironment.Side.CLIENT) { - logger.debug("Disabled patch '{}' because it targets an client-side class unavailable in the current environment", mixinClassName); - - return false; - } - - if (mixinClassName.endsWith("$Vanilla")) { - if (this.legacy) { - logger.debug("Disable Chunk Patch For 1.8.8"); - return false; - } - } - - if (mixinClassName.endsWith("$Legacy")) { - if (!this.legacy) { - logger.debug("Disable Chunk Patch For 1.8.9"); - return false; - } - } - - return true; - } - - @Override - public void acceptTargets(Set myTargets, Set otherTargets) { - - } - - @Override - public List getMixins() { - return null; - } - - @Override - public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } - - @Override - public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorConfig.java b/src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorConfig.java deleted file mode 100644 index ed809b6b..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorConfig.java +++ /dev/null @@ -1,75 +0,0 @@ -package me.jellysquid.mods.phosphor.mod; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.annotations.SerializedName; - -import java.io.*; - -// This class will be initialized very early and should never load any game/mod code. -public class PhosphorConfig { - private static final Gson gson = createGson(); - - private static PhosphorConfig INSTANCE; - - @SerializedName("enable_illegal_thread_access_warnings") - public boolean enableIllegalThreadAccessWarnings = true; - - @SerializedName("enable_phosphor") - public boolean enablePhosphor = true; - - public static PhosphorConfig loadConfig() { - if (INSTANCE != null) { - return INSTANCE; - } - - File file = getConfigFile(); - - PhosphorConfig config; - - if (!file.exists()) { - config = new PhosphorConfig(); - config.saveConfig(); - } else { - try (Reader reader = new FileReader(file)) { - config = gson.fromJson(reader, PhosphorConfig.class); - } catch (IOException e) { - throw new RuntimeException("Failed to deserialize config from disk", e); - } - } - - INSTANCE = config; - - return config; - } - - public void saveConfig() { - File dir = getConfigDirectory(); - - if (!dir.exists()) { - if (!dir.mkdirs()) { - throw new RuntimeException("Could not create configuration directory at '" + dir.getAbsolutePath() + "'"); - } - } else if (!dir.isDirectory()) { - throw new RuntimeException("Configuration directory at '" + dir.getAbsolutePath() + "' is not a directory"); - } - - try (Writer writer = new FileWriter(getConfigFile())) { - gson.toJson(this, writer); - } catch (IOException e) { - throw new RuntimeException("Failed to serialize config to disk", e); - } - } - - private static File getConfigDirectory() { - return new File("config"); - } - - private static File getConfigFile() { - return new File(getConfigDirectory(), "phosphor.json"); - } - - private static Gson createGson() { - return new GsonBuilder().setPrettyPrinting().create(); - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorConstants.java b/src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorConstants.java deleted file mode 100644 index f6b3a719..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorConstants.java +++ /dev/null @@ -1,9 +0,0 @@ -package me.jellysquid.mods.phosphor.mod; - -public class PhosphorConstants { - public static final String MOD_ID = "phosphor-lighting"; - - public static final String MOD_NAME = "Phosphor"; - - public static final String MOD_VERSION = "1.8.9-0.2.9.4"; -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorMod.java b/src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorMod.java deleted file mode 100644 index 03355acd..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mod/PhosphorMod.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.jellysquid.mods.phosphor.mod; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class PhosphorMod { - - public static final Logger LOGGER = LogManager.getLogger("Phosphor"); - -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mod/collections/PooledLongQueue.java b/src/main/java/me/jellysquid/mods/phosphor/mod/collections/PooledLongQueue.java deleted file mode 100644 index a0d57a2c..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mod/collections/PooledLongQueue.java +++ /dev/null @@ -1,163 +0,0 @@ -package me.jellysquid.mods.phosphor.mod.collections; - -import java.util.ArrayDeque; -import java.util.Deque; - -//Implement own queue with pooled segments to reduce allocation costs and reduce idle memory footprint -public class PooledLongQueue { - private static final int CACHED_QUEUE_SEGMENTS_COUNT = 1 << 12; // 4096 - private static final int QUEUE_SEGMENT_SIZE = 1 << 10; // 1024 - - private final Pool pool; - - private Segment cur, last; - - private int size = 0; - - // Stores whether or not the queue is empty. Updates to this field will be seen by all threads immediately. Writes - // to volatile fields are generally quite a bit more expensive, so we avoid repeatedly setting this flag to true. - private volatile boolean empty; - - public PooledLongQueue(Pool pool) { - this.pool = pool; - } - - /** - * Not thread-safe! If you must know whether or not the queue is empty, please use {@link PooledLongQueue#isEmpty()}. - * - * @return The number of encoded values present in this queue - */ - public int size() { - return this.size; - } - - /** - * Thread-safe method to check whether or not this queue has work to do. Significantly cheaper than acquiring a lock. - * @return True if the queue is empty, otherwise false - */ - public boolean isEmpty() { - return this.empty; - } - - /** - * Not thread-safe! Adds an encoded long value into this queue. - * @param val The encoded value to add - */ - public void add(final long val) { - if (this.cur == null) { - this.empty = false; - this.cur = this.last = this.pool.acquire(); - } - - if (this.last.index == QUEUE_SEGMENT_SIZE) { - Segment ret = this.last.next = this.last.pool.acquire(); - ret.longArray[ret.index++] = val; - - this.last = ret; - } else { - this.last.longArray[this.last.index++] = val; - } - - ++this.size; - } - - /** - * Not thread safe! Creates an iterator over the values in this queue. Values will be returned in a FIFO fashion. - * @return The iterator - */ - public LongQueueIterator iterator() { - return new LongQueueIterator(this.cur); - } - - private void clear() { - Segment segment = this.cur; - - while (segment != null) { - Segment next = segment.next; - segment.release(); - segment = next; - } - - this.size = 0; - this.cur = null; - this.last = null; - this.empty = true; - } - - public class LongQueueIterator { - private Segment cur; - private long[] curArray; - - private int index, capacity; - - private LongQueueIterator(Segment cur) { - this.cur = cur; - - if (this.cur != null) { - this.curArray = cur.longArray; - this.capacity = cur.index; - } - } - - public boolean hasNext() { - return this.cur != null; - } - - public long next() { - final long ret = this.curArray[this.index++]; - - if (this.index == this.capacity) { - this.index = 0; - - this.cur = this.cur.next; - - if (this.cur != null) { - this.curArray = this.cur.longArray; - this.capacity = this.cur.index; - } - } - - return ret; - } - - public void finish() { - PooledLongQueue.this.clear(); - } - } - - public static class Pool { - private final Deque segmentPool = new ArrayDeque<>(); - - private Segment acquire() { - if (this.segmentPool.isEmpty()) { - return new Segment(this); - } - - return this.segmentPool.pop(); - } - - private void release(Segment segment) { - if (this.segmentPool.size() < CACHED_QUEUE_SEGMENTS_COUNT) { - this.segmentPool.push(segment); - } - } - } - - private static class Segment { - private final long[] longArray = new long[QUEUE_SEGMENT_SIZE]; - private int index = 0; - private Segment next; - private final Pool pool; - - private Segment(Pool pool) { - this.pool = pool; - } - - private void release() { - this.index = 0; - this.next = null; - - this.pool.release(this); - } - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mod/world/WorldChunkSlice.java b/src/main/java/me/jellysquid/mods/phosphor/mod/world/WorldChunkSlice.java deleted file mode 100644 index eebf3d78..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mod/world/WorldChunkSlice.java +++ /dev/null @@ -1,57 +0,0 @@ -package me.jellysquid.mods.phosphor.mod.world; - -import me.jellysquid.mods.phosphor.mod.world.lighting.LightingEngineHelpers; -import net.minecraft.world.World; -import net.minecraft.world.chunk.Chunk; - -public class WorldChunkSlice { - private static final int DIAMETER = 5; - - private final Chunk[] chunks; - - private final int x, z; - - public WorldChunkSlice(World world, int x, int z) { - this.chunks = new Chunk[DIAMETER * DIAMETER]; - - int radius = DIAMETER / 2; - - for (int xDiff = -radius; xDiff <= radius; xDiff++) { - for (int zDiff = -radius; zDiff <= radius; zDiff++) { - this.chunks[((xDiff + radius) * DIAMETER) + (zDiff + radius)] = LightingEngineHelpers.getLoadedChunk(world.getChunkProvider(), x + xDiff, z + zDiff); - } - } - - this.x = x - radius; - this.z = z - radius; - } - - public Chunk getChunk(int x, int z) { - return this.chunks[(x * DIAMETER) + z]; - } - - public Chunk getChunkFromWorldCoords(int x, int z) { - return this.getChunk((x >> 4) - this.x, (z >> 4) - this.z); - } - - public boolean isLoaded(int x, int z, int radius) { - return this.isLoaded(x - radius, z - radius, x + radius, z + radius); - } - - public boolean isLoaded(int xStart, int zStart, int xEnd, int zEnd) { - xStart = (xStart >> 4) - this.x; - zStart = (zStart >> 4) - this.z; - xEnd = (xEnd >> 4) - this.x; - zEnd = (zEnd >> 4) - this.z; - - for (int i = xStart; i <= xEnd; ++i) { - for (int j = zStart; j <= zEnd; ++j) { - if (this.getChunk(i, j) == null) { - return false; - } - } - } - - return true; - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingEngine.java b/src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingEngine.java deleted file mode 100644 index 76c18c2e..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingEngine.java +++ /dev/null @@ -1,620 +0,0 @@ -package me.jellysquid.mods.phosphor.mod.world.lighting; - -import me.jellysquid.mods.phosphor.api.IChunkLighting; -import me.jellysquid.mods.phosphor.api.ILightingEngine; -import me.jellysquid.mods.phosphor.mixins.plugins.LightingEnginePlugin; -import me.jellysquid.mods.phosphor.mod.PhosphorMod; -import me.jellysquid.mods.phosphor.mod.collections.PooledLongQueue; -import net.minecraft.block.state.IBlockState; -import net.minecraft.client.Minecraft; -import net.minecraft.profiler.Profiler; -import net.minecraft.util.*; -import net.minecraft.world.EnumSkyBlock; -import net.minecraft.world.World; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.storage.ExtendedBlockStorage; -import net.minecraftforge.fml.common.Loader; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -import java.util.concurrent.locks.ReentrantLock; - -import static net.minecraft.util.BlockPos.MutableBlockPos; - -public class LightingEngine implements ILightingEngine { - private static final int MAX_SCHEDULED_COUNT = 1 << 22; - - private static final int MAX_LIGHT = 15; - - private final Thread ownedThread = Thread.currentThread(); - - private final World world; - - private final Profiler profiler; - - //Layout of longs: [padding(4)] [y(8)] [x(26)] [z(26)] - private final PooledLongQueue[] queuedLightUpdates = new PooledLongQueue[EnumSkyBlock.values().length]; - - //Layout of longs: see above - private final PooledLongQueue[] queuedDarkenings = new PooledLongQueue[MAX_LIGHT + 1]; - private final PooledLongQueue[] queuedBrightenings = new PooledLongQueue[MAX_LIGHT + 1]; - - //Layout of longs: [newLight(4)] [pos(60)] - private final PooledLongQueue initialBrightenings; - //Layout of longs: [padding(4)] [pos(60)] - private final PooledLongQueue initialDarkenings; - - private boolean updating = false; - - //Layout parameters - //Length of bit segments - private static final int - lX = 26, - lY = 8, - lZ = 26, - lL = 4; - - //Bit segment shifts/positions - private static final int - sZ = 0, - sX = sZ + lZ, - sY = sX + lX, - sL = sY + lY; - - //Bit segment masks - private static final long - mX = (1L << lX) - 1, - mY = (1L << lY) - 1, - mZ = (1L << lZ) - 1, - mL = (1L << lL) - 1, - mPos = (mY << sY) | (mX << sX) | (mZ << sZ); - - //Bit to check whether y had overflow - private static final long yCheck = 1L << (sY + lY); - - private static final long[] neighborShifts = new long[6]; - - static { - for (int i = 0; i < 6; ++i) { - final Vec3i offset = EnumFacing.VALUES[i].getDirectionVec(); - neighborShifts[i] = ((long) offset.getY() << sY) | ((long) offset.getX() << sX) | ((long) offset.getZ() << sZ); - } - } - - //Mask to extract chunk identifier - private static final long mChunk = ((mX >> 4) << (4 + sX)) | ((mZ >> 4) << (4 + sZ)); - - //Iteration state data - //Cache position to avoid allocation of new object each time - private final MutableBlockPos curPos = new MutableBlockPos(); - private Chunk curChunk; - private long curChunkIdentifier; - private long curData; - public static boolean isDynamicLightsLoaded; - - //Cached data about neighboring blocks (of tempPos) - private boolean isNeighborDataValid = false; - - private final NeighborInfo[] neighborInfos = new NeighborInfo[6]; - private PooledLongQueue.LongQueueIterator queueIt; - - private final ReentrantLock lock = new ReentrantLock(); - - public LightingEngine(final World world) { - this.world = world; - this.profiler = world.theProfiler; - isDynamicLightsLoaded = Loader.isModLoaded("DynamicLights"); - - PooledLongQueue.Pool pool = new PooledLongQueue.Pool(); - - this.initialBrightenings = new PooledLongQueue(pool); - this.initialDarkenings = new PooledLongQueue(pool); - - for (int i = 0; i < EnumSkyBlock.values().length; ++i) { - this.queuedLightUpdates[i] = new PooledLongQueue(pool); - } - - for (int i = 0; i < this.queuedDarkenings.length; ++i) { - this.queuedDarkenings[i] = new PooledLongQueue(pool); - } - - for (int i = 0; i < this.queuedBrightenings.length; ++i) { - this.queuedBrightenings[i] = new PooledLongQueue(pool); - } - - for (int i = 0; i < this.neighborInfos.length; ++i) { - this.neighborInfos[i] = new NeighborInfo(); - } - } - - /** - * Schedules a light update for the specified light type and position to be processed later by {@link ILightingEngine#processLightUpdatesForType(EnumSkyBlock)} - */ - @Override - public void scheduleLightUpdate(final EnumSkyBlock lightType, final BlockPos pos) { - this.acquireLock(); - - try { - this.scheduleLightUpdate(lightType, encodeWorldCoord(pos)); - } finally { - this.releaseLock(); - } - } - - /** - * Schedules a light update for the specified light type and position to be processed later by {@link ILightingEngine#processLightUpdates()} - */ - private void scheduleLightUpdate(final EnumSkyBlock lightType, final long pos) { - final PooledLongQueue queue = this.queuedLightUpdates[lightType.ordinal()]; - - queue.add(pos); - - //make sure there are not too many queued light updates - if (queue.size() >= MAX_SCHEDULED_COUNT) { - this.processLightUpdatesForType(lightType); - } - } - - /** - * Calls {@link ILightingEngine#processLightUpdatesForType(EnumSkyBlock)} for both light types - */ - @Override - public void processLightUpdates() { - this.processLightUpdatesForType(EnumSkyBlock.SKY); - this.processLightUpdatesForType(EnumSkyBlock.BLOCK); - } - - /** - * Processes light updates of the given light type - */ - @Override - public void processLightUpdatesForType(final EnumSkyBlock lightType) { - // We only want to perform updates if we're being called from a tick event on the client - // There are many locations in the client code which will end up making calls to this method, usually from - // other threads. - if (this.world.isRemote && !this.isCallingFromMainThread()) { - return; - } - - final PooledLongQueue queue = this.queuedLightUpdates[lightType.ordinal()]; - - // Quickly check if the queue is empty before we acquire a more expensive lock. - if (queue.isEmpty()) { - return; - } - - this.acquireLock(); - - try { - this.processLightUpdatesForTypeInner(lightType, queue); - } finally { - this.releaseLock(); - } - } - - @SideOnly(Side.CLIENT) - private boolean isCallingFromMainThread() { - return Minecraft.getMinecraft().isCallingFromMinecraftThread(); - } - - private void acquireLock() { - if (!this.lock.tryLock()) { - // If we cannot lock, something has gone wrong... Only one thread should ever acquire the lock. - // Validate that we're on the right thread immediately so we can gather information. - // It is NEVER valid to call World methods from a thread other than the owning thread of the world instance. - // Users can safely disable this warning, however it will not resolve the issue. - if (LightingEnginePlugin.ENABLE_ILLEGAL_THREAD_ACCESS_WARNINGS) { - Thread current = Thread.currentThread(); - - if (current != this.ownedThread) { - IllegalAccessException e = new IllegalAccessException(String.format("World is owned by '%s' (ID: %s)," + - " but was accessed from thread '%s' (ID: %s)", - this.ownedThread.getName(), this.ownedThread.getId(), current.getName(), current.getId())); - - PhosphorMod.LOGGER.warn( - "Something (likely another mod) has attempted to modify the world's state from the wrong thread!\n" + - "This is *bad practice* and can cause severe issues in your game. Phosphor has done as best as it can to mitigate this violation," + - " but it may negatively impact performance or introduce stalls.\nIn a future release, this violation may result in a hard crash instead" + - " of the current soft warning. You should report this issue to our issue tracker with the following stacktrace information.\n(If you are" + - " aware you have misbehaving mods and cannot resolve this issue, you can safely disable this warning by setting" + - " `enable_illegal_thread_access_warnings` to `false` in Phosphor's configuration file for the time being.)", e); - - } - - } - - // Wait for the lock to be released. This will likely introduce unwanted stalls, but will mitigate the issue. - this.lock.lock(); - } - } - - private void releaseLock() { - this.lock.unlock(); - } - - private void processLightUpdatesForTypeInner(final EnumSkyBlock lightType, final PooledLongQueue queue) { - //avoid nested calls - if (this.updating) { - throw new IllegalStateException("Already processing updates!"); - } - - this.updating = true; - - this.curChunkIdentifier = -1; //reset chunk cache - - this.profiler.startSection("lighting"); - - this.profiler.startSection("checking"); - - this.queueIt = queue.iterator(); - - //process the queued updates and enqueue them for further processing - while (this.nextItem()) { - if (this.curChunk == null) { - continue; - } - - final int oldLight = this.getCursorCachedLight(lightType); - final int newLight = this.calculateNewLightFromCursor(lightType); - - if (oldLight < newLight) { - //don't enqueue directly for brightening in order to avoid duplicate scheduling - this.initialBrightenings.add(((long) newLight << sL) | this.curData); - } else if (oldLight > newLight) { - //don't enqueue directly for darkening in order to avoid duplicate scheduling - this.initialDarkenings.add(this.curData); - } - } - - this.queueIt = this.initialBrightenings.iterator(); - - while (this.nextItem()) { - final int newLight = (int) (this.curData >> sL & mL); - - if (newLight > this.getCursorCachedLight(lightType)) { - //Sets the light to newLight to only schedule once. Clear leading bits of curData for later - this.enqueueBrightening(this.curPos, this.curData & mPos, newLight, this.curChunk, lightType); - } - } - - this.queueIt = this.initialDarkenings.iterator(); - - while (this.nextItem()) { - final int oldLight = this.getCursorCachedLight(lightType); - - if (oldLight != 0) { - //Sets the light to 0 to only schedule once - this.enqueueDarkening(this.curPos, this.curData, oldLight, this.curChunk, lightType); - } - } - - this.profiler.endSection(); - - //Iterate through enqueued updates (brightening and darkening in parallel) from brightest to darkest so that we only need to iterate once - for (int curLight = MAX_LIGHT; curLight >= 0; --curLight) { - this.profiler.startSection("darkening"); - - this.queueIt = this.queuedDarkenings[curLight].iterator(); - - while (this.nextItem()) { - if (this.getCursorCachedLight(lightType) >= curLight) //don't darken if we got brighter due to some other change - { - continue; - } - - final IBlockState state = LightingEngineHelpers.posToState(this.curPos, this.curChunk); - final int luminosity = this.getCursorLuminosity(state, lightType); - final int opacity; //if luminosity is high enough, opacity is irrelevant - - if (luminosity >= MAX_LIGHT - 1) { - opacity = 1; - } else { - opacity = this.getPosOpacity(this.curPos, state); - } - - //only darken neighbors if we indeed became darker - if (this.calculateNewLightFromCursor(luminosity, opacity, lightType) < curLight) { - //need to calculate new light value from neighbors IGNORING neighbors which are scheduled for darkening - int newLight = luminosity; - - this.fetchNeighborDataFromCursor(lightType); - - for (NeighborInfo info : this.neighborInfos) { - final Chunk nChunk = info.chunk; - - if (nChunk == null) { - continue; - } - - final int nLight = info.light; - - if (nLight == 0) { - continue; - } - - final MutableBlockPos nPos = info.pos; - - if (curLight - this.getPosOpacity(nPos, LightingEngineHelpers.posToState(nPos, info.section)) >= nLight) //schedule neighbor for darkening if we possibly light it - { - this.enqueueDarkening(nPos, info.key, nLight, nChunk, lightType); - } - else //only use for new light calculation if not - { - //if we can't darken the neighbor, no one else can (because of processing order) -> safe to let us be illuminated by it - newLight = Math.max(newLight, nLight - opacity); - } - } - - //schedule brightening since light level was set to 0 - this.enqueueBrighteningFromCursor(newLight, lightType); - } - else //we didn't become darker, so we need to re-set our initial light value (was set to 0) and notify neighbors - { - this.enqueueBrighteningFromCursor(curLight, lightType); //do not spread to neighbors immediately to avoid scheduling multiple times - } - } - - this.profiler.endStartSection("brightening"); - - this.queueIt = this.queuedBrightenings[curLight].iterator(); - - while (this.nextItem()) { - final int oldLight = this.getCursorCachedLight(lightType); - - if (oldLight == curLight) //only process this if nothing else has happened at this position since scheduling - { - this.world.notifyLightSet(this.curPos); - - if (curLight > 1) { - this.spreadLightFromCursor(curLight, lightType); - } - } - } - - this.profiler.endSection(); - } - - this.profiler.endSection(); - - this.updating = false; - } - - /** - * Gets data for neighbors of curPos and saves the results into neighbor state data members. If a neighbor can't be accessed/doesn't exist, the corresponding entry in neighborChunks is null - others are not reset - */ - private void fetchNeighborDataFromCursor(final EnumSkyBlock lightType) { - //only update if curPos was changed - if (this.isNeighborDataValid) { - return; - } - - this.isNeighborDataValid = true; - - for (int i = 0; i < this.neighborInfos.length; ++i) { - NeighborInfo info = this.neighborInfos[i]; - - final long nLongPos = info.key = this.curData + neighborShifts[i]; - - if ((nLongPos & yCheck) != 0) { - info.chunk = null; - info.section = null; - continue; - } - - final MutableBlockPos nPos = decodeWorldCoord(info.pos, nLongPos); - - final Chunk nChunk; - - if ((nLongPos & mChunk) == this.curChunkIdentifier) { - nChunk = info.chunk = this.curChunk; - } else { - nChunk = info.chunk = this.getChunk(nPos); - } - - if (nChunk != null) { - ExtendedBlockStorage nSection = nChunk.getBlockStorageArray()[nPos.getY() >> 4]; - - info.light = getCachedLightFor(nChunk, nSection, nPos, lightType); - info.section = nSection; - } - } - } - - - private static int getCachedLightFor(Chunk chunk, ExtendedBlockStorage storage, BlockPos pos, EnumSkyBlock type) { - int i = pos.getX() & 15; - int j = pos.getY(); - int k = pos.getZ() & 15; - - if (storage == null) { - if (type == EnumSkyBlock.SKY && chunk.canSeeSky(pos)) { - return type.defaultLightValue; - } - else { - return 0; - } - } - else if (type == EnumSkyBlock.SKY) { - if (chunk.getWorld().provider.getHasNoSky()) { - return 0; - } - else { - return storage.getExtSkylightValue(i, j & 15, k); - } - } - else { - if (type == EnumSkyBlock.BLOCK) { - return storage.getExtBlocklightValue(i, j & 15, k); - } - else { - return type.defaultLightValue; - } - } - } - - - private int calculateNewLightFromCursor(final EnumSkyBlock lightType) { - final IBlockState state = LightingEngineHelpers.posToState(this.curPos, this.curChunk); - - final int luminosity = this.getCursorLuminosity(state, lightType); - final int opacity; - - if (luminosity >= MAX_LIGHT - 1) { - opacity = 1; - } else { - opacity = this.getPosOpacity(this.curPos, state); - } - - return this.calculateNewLightFromCursor(luminosity, opacity, lightType); - } - - private int calculateNewLightFromCursor(final int luminosity, final int opacity, final EnumSkyBlock lightType) { - if (luminosity >= MAX_LIGHT - opacity) { - return luminosity; - } - - int newLight = luminosity; - - this.fetchNeighborDataFromCursor(lightType); - - for (NeighborInfo info : this.neighborInfos) { - if (info.chunk == null) { - continue; - } - - final int nLight = info.light; - - newLight = Math.max(nLight - opacity, newLight); - } - - return newLight; - } - - private void spreadLightFromCursor(final int curLight, final EnumSkyBlock lightType) { - this.fetchNeighborDataFromCursor(lightType); - - for (NeighborInfo info : this.neighborInfos) { - final Chunk nChunk = info.chunk; - - if (nChunk == null) { - continue; - } - - final int newLight = curLight - this.getPosOpacity(info.pos, LightingEngineHelpers.posToState(info.pos, info.section)); - - if (newLight > info.light) { - this.enqueueBrightening(info.pos, info.key, newLight, nChunk, lightType); - } - } - } - - private void enqueueBrighteningFromCursor(final int newLight, final EnumSkyBlock lightType) { - this.enqueueBrightening(this.curPos, this.curData, newLight, this.curChunk, lightType); - } - - /** - * Enqueues the pos for brightening and sets its light value to newLight - */ - private void enqueueBrightening(final BlockPos pos, final long longPos, final int newLight, final Chunk chunk, final EnumSkyBlock lightType) { - this.queuedBrightenings[newLight].add(longPos); - - chunk.setLightFor(lightType, pos, newLight); - } - - /** - * Enqueues the pos for darkening and sets its light value to 0 - */ - private void enqueueDarkening(final BlockPos pos, final long longPos, final int oldLight, final Chunk chunk, final EnumSkyBlock lightType) { - this.queuedDarkenings[oldLight].add(longPos); - - chunk.setLightFor(lightType, pos, 0); - } - - private static MutableBlockPos decodeWorldCoord(final MutableBlockPos pos, final long longPos) { - final int posX = (int) (longPos >> sX & mX) - (1 << lX - 1); - final int posY = (int) (longPos >> sY & mY); - final int posZ = (int) (longPos >> sZ & mZ) - (1 << lZ - 1); - - return pos.set(posX, posY, posZ); - } - - private static long encodeWorldCoord(final BlockPos pos) { - return encodeWorldCoord(pos.getX(), pos.getY(), pos.getZ()); - } - - private static long encodeWorldCoord(final long x, final long y, final long z) { - return (y << sY) | (x + (1 << lX - 1) << sX) | (z + (1 << lZ - 1) << sZ); - } - - @SuppressWarnings("unused") - private static int ITEMS_PROCESSED = 0, CHUNKS_FETCHED = 0; - - /** - * Polls a new item from curQueue and fills in state data members - * - * @return If there was an item to poll - */ - private boolean nextItem() { - if (!this.queueIt.hasNext()) { - this.queueIt.finish(); - this.queueIt = null; - - return false; - } - - this.curData = this.queueIt.next(); - this.isNeighborDataValid = false; - - decodeWorldCoord(this.curPos, this.curData); - - final long chunkIdentifier = this.curData & mChunk; - - if (this.curChunkIdentifier != chunkIdentifier) { - this.curChunk = this.getChunk(this.curPos); - this.curChunkIdentifier = chunkIdentifier; - CHUNKS_FETCHED++; - } - - ITEMS_PROCESSED++; - - return true; - } - - private int getCursorCachedLight(final EnumSkyBlock lightType) { - return ((IChunkLighting) this.curChunk).getCachedLightFor(lightType, this.curPos); - } - - /** - * Calculates the luminosity for curPos, taking into account lightType - */ - private int getCursorLuminosity(final IBlockState state, final EnumSkyBlock lightType) { - if (lightType == EnumSkyBlock.SKY) { - if (this.curChunk.canSeeSky(this.curPos)) { - return EnumSkyBlock.SKY.defaultLightValue; - } else { - return 0; - } - } - return MathHelper.clamp_int(LightingEngineHelpers.getLightValueForState(state, this.world, this.curPos), 0, MAX_LIGHT); - } - - private int getPosOpacity(final BlockPos pos, final IBlockState state) { - return MathHelper.clamp_int(state.getBlock().getLightOpacity(this.world, pos), 1, MAX_LIGHT); - } - - private Chunk getChunk(final BlockPos pos) { - return LightingEngineHelpers.getLoadedChunk(this.world.getChunkProvider(), pos.getX() >> 4, pos.getZ() >> 4); - } - - private static class NeighborInfo { - Chunk chunk; - ExtendedBlockStorage section; - - int light; - - long key; - - final MutableBlockPos pos = new MutableBlockPos(); - } -} - diff --git a/src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingEngineHelpers.java b/src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingEngineHelpers.java deleted file mode 100644 index 921d230e..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingEngineHelpers.java +++ /dev/null @@ -1,76 +0,0 @@ -package me.jellysquid.mods.phosphor.mod.world.lighting; - -import me.jellysquid.mods.phosphor.mixins.client.ClientChunkProviderAccessor; -import me.jellysquid.mods.phosphor.mixins.common.ServerChunkProviderAccessor; -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.client.multiplayer.ChunkProviderClient; -import net.minecraft.init.Blocks; -import net.minecraft.util.BlockPos; -import net.minecraft.util.LongHashMap; -import net.minecraft.world.ChunkCoordIntPair; -import net.minecraft.world.IBlockAccess; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.IChunkProvider; -import net.minecraft.world.chunk.storage.ExtendedBlockStorage; -import net.minecraft.world.gen.ChunkProviderServer; - -public class LightingEngineHelpers { - - private static final IBlockState DEFAULT_BLOCK_STATE = Blocks.air.getDefaultState(); - - // Avoids some additional logic in Chunk#getBlockState... 0 is always air - static IBlockState posToState(final BlockPos pos, final Chunk chunk) { - return posToState(pos, chunk.getBlockStorageArray()[pos.getY() >> 4]); - } - - static IBlockState posToState(final BlockPos pos, final ExtendedBlockStorage section) { - final int x = pos.getX(); - final int y = pos.getY(); - final int z = pos.getZ(); - - if (section != null) - { - IBlockState state = section.getBlockByExtId((x & 15), (y & 15), (z & 15)).getDefaultState(); - if (state != null) { - return state; - } - } - - return DEFAULT_BLOCK_STATE; - } - - static int getLightValueForState(final IBlockState state, final IBlockAccess world, final BlockPos pos) { - Block block = state.getBlock(); - if (LightingEngine.isDynamicLightsLoaded) { - try { - Class dynamicLights = Class.forName("atomicstryker.dynamiclights.client.DynamicLights"); - return (Integer) dynamicLights.getMethod("getLightValue", Block.class, IBlockAccess.class, BlockPos.class).invoke(null, block, world, pos); - } catch (ReflectiveOperationException | ClassCastException ignored) { - LightingEngine.isDynamicLightsLoaded = false; - } - } - - Block block1 = world.getBlockState(pos).getBlock(); - if (block1 != block) { - return block1.getLightValue(world, pos); - } else { - /* Use the vanilla implementation */ - return block.getLightValue(world, pos); - } - } - - public static Chunk getLoadedChunk(IChunkProvider chunkProvider, int x, int z) { - if (chunkProvider instanceof ChunkProviderServer) { - LongHashMap chunkStorage = ((ServerChunkProviderAccessor) chunkProvider).getChunkStorage(); - return chunkStorage.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(x, z)); - } - if (chunkProvider instanceof ChunkProviderClient) { - LongHashMap chunkStorage = ((ClientChunkProviderAccessor) chunkProvider).getChunkStorage(); - return chunkStorage.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(x, z)); - } - - // Fallback for other providers, hopefully this doesn't break... - return chunkProvider.provideChunk(x, z); - } -} diff --git a/src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingHooks.java b/src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingHooks.java deleted file mode 100644 index 4754d1b4..00000000 --- a/src/main/java/me/jellysquid/mods/phosphor/mod/world/lighting/LightingHooks.java +++ /dev/null @@ -1,388 +0,0 @@ -package me.jellysquid.mods.phosphor.mod.world.lighting; - -import me.jellysquid.mods.phosphor.api.IChunkLighting; -import me.jellysquid.mods.phosphor.api.IChunkLightingData; -import me.jellysquid.mods.phosphor.api.ILightingEngine; -import me.jellysquid.mods.phosphor.api.ILightingEngineProvider; -import me.jellysquid.mods.phosphor.mod.PhosphorMod; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.nbt.NBTTagShort; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.BlockPos; -import net.minecraft.world.EnumSkyBlock; -import net.minecraft.world.World; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.storage.ExtendedBlockStorage; - -@SuppressWarnings("unused") -public class LightingHooks { - private static final EnumSkyBlock[] ENUM_SKY_BLOCK_VALUES = EnumSkyBlock.values(); - - private static final EnumFacing.AxisDirection[] ENUM_AXIS_DIRECTION_VALUES = EnumFacing.AxisDirection.values(); - - private static final int FLAG_COUNT = 32; //2 light types * 4 directions * 2 halves * (inwards + outwards) - - public static void relightSkylightColumn(final World world, final Chunk chunk, final int x, final int z, final int height1, final int height2) { - final int yMin = Math.min(height1, height2); - final int yMax = Math.max(height1, height2) - 1; - - final ExtendedBlockStorage[] sections = chunk.getBlockStorageArray(); - - final int xBase = (chunk.xPosition << 4) + x; - final int zBase = (chunk.zPosition << 4) + z; - - scheduleRelightChecksForColumn(world, EnumSkyBlock.SKY, xBase, zBase, yMin, yMax); - - if (sections[yMin >> 4] == null && yMin > 0) { - world.checkLightFor(EnumSkyBlock.SKY, new BlockPos(xBase, yMin - 1, zBase)); - } - - short emptySections = 0; - - for (int sec = yMax >> 4; sec >= yMin >> 4; --sec) { - if (sections[sec] == null) { - emptySections |= 1 << sec; - } - } - - if (emptySections != 0) { - for (final EnumFacing dir : EnumFacing.HORIZONTALS) { - final int xOffset = dir.getFrontOffsetX(); - final int zOffset = dir.getFrontOffsetZ(); - - final boolean neighborColumnExists = (((x + xOffset) | (z + zOffset)) & 16) == 0 - //Checks whether the position is at the specified border (the 16 bit is set for both 15+1 and 0-1) - || LightingEngineHelpers.getLoadedChunk(world.getChunkProvider(),chunk.xPosition + xOffset, chunk.zPosition + zOffset) != null; - - if (neighborColumnExists) { - for (int sec = yMax >> 4; sec >= yMin >> 4; --sec) { - if ((emptySections & (1 << sec)) != 0) { - scheduleRelightChecksForColumn(world, EnumSkyBlock.SKY, xBase + xOffset, zBase + zOffset, sec << 4, (sec << 4) + 15); - } - } - } else { - flagChunkBoundaryForUpdate(chunk, emptySections, EnumSkyBlock.SKY, dir, getAxisDirection(dir, x, z), EnumBoundaryFacing.OUT); - } - } - } - } - - public static void scheduleRelightChecksForArea(final World world, final EnumSkyBlock lightType, final int xMin, final int yMin, final int zMin, final int xMax, final int yMax, final int zMax) { - for (int x = xMin; x <= xMax; ++x) { - for (int z = zMin; z <= zMax; ++z) { - scheduleRelightChecksForColumn(world, lightType, x, z, yMin, yMax); - } - } - } - - private static void scheduleRelightChecksForColumn(final World world, final EnumSkyBlock lightType, final int x, final int z, final int yMin, final int yMax) { - BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); - - for (int y = yMin; y <= yMax; ++y) { - world.checkLightFor(lightType, pos.set(x, y, z)); - } - } - - public enum EnumBoundaryFacing { - IN, OUT; - - public EnumBoundaryFacing getOpposite() { - return this == IN ? OUT : IN; - } - } - - public static void flagSecBoundaryForUpdate(final Chunk chunk, final BlockPos pos, final EnumSkyBlock lightType, final EnumFacing dir, final EnumBoundaryFacing boundaryFacing) { - flagChunkBoundaryForUpdate(chunk, (short) (1 << (pos.getY() >> 4)), lightType, dir, getAxisDirection(dir, pos.getX(), pos.getZ()), boundaryFacing); - } - - public static void flagChunkBoundaryForUpdate(final Chunk chunk, final short sectionMask, final EnumSkyBlock lightType, final EnumFacing dir, final EnumFacing.AxisDirection axisDirection, final EnumBoundaryFacing boundaryFacing) { - initNeighborLightChecks(chunk); - ((IChunkLightingData) chunk).getNeighborLightChecks()[getFlagIndex(lightType, dir, axisDirection, boundaryFacing)] |= sectionMask; - chunk.setChunkModified(); - } - - public static int getFlagIndex(final EnumSkyBlock lightType, final int xOffset, final int zOffset, final EnumFacing.AxisDirection axisDirection, final EnumBoundaryFacing boundaryFacing) { - return (lightType == EnumSkyBlock.BLOCK ? 0 : 16) | ((xOffset + 1) << 2) | ((zOffset + 1) << 1) | (axisDirection.getOffset() + 1) | boundaryFacing.ordinal(); - } - - public static int getFlagIndex(final EnumSkyBlock lightType, final EnumFacing dir, final EnumFacing.AxisDirection axisDirection, final EnumBoundaryFacing boundaryFacing) { - return getFlagIndex(lightType, dir.getFrontOffsetX(), dir.getFrontOffsetZ(), axisDirection, boundaryFacing); - } - - private static EnumFacing.AxisDirection getAxisDirection(final EnumFacing dir, final int x, final int z) { - return ((dir.getAxis() == EnumFacing.Axis.X ? z : x) & 15) < 8 ? EnumFacing.AxisDirection.NEGATIVE : EnumFacing.AxisDirection.POSITIVE; - } - - public static void scheduleRelightChecksForChunkBoundaries(final World world, final Chunk chunk) { - for (final EnumFacing dir : EnumFacing.HORIZONTALS) { - final int xOffset = dir.getFrontOffsetX(); - final int zOffset = dir.getFrontOffsetZ(); - - final Chunk nChunk = LightingEngineHelpers.getLoadedChunk(world.getChunkProvider(),chunk.xPosition + xOffset, chunk.zPosition + zOffset); - - if (nChunk == null) { - continue; - } - - for (final EnumSkyBlock lightType : ENUM_SKY_BLOCK_VALUES) { - for (final EnumFacing.AxisDirection axisDir : ENUM_AXIS_DIRECTION_VALUES) { - //Merge flags upon loading of a chunk. This ensures that all flags are always already on the IN boundary below - mergeFlags(lightType, chunk, nChunk, dir, axisDir); - mergeFlags(lightType, nChunk, chunk, dir.getOpposite(), axisDir); - - //Check everything that might have been canceled due to this chunk not being loaded. - //Also, pass in chunks if already known - //The boundary to the neighbor chunk (both ways) - scheduleRelightChecksForBoundary(world, chunk, nChunk, null, lightType, xOffset, zOffset, axisDir); - scheduleRelightChecksForBoundary(world, nChunk, chunk, null, lightType, -xOffset, -zOffset, axisDir); - //The boundary to the diagonal neighbor (since the checks in that chunk were aborted if this chunk wasn't loaded, see scheduleRelightChecksForBoundary) - scheduleRelightChecksForBoundary(world, nChunk, null, chunk, lightType, (zOffset != 0 ? axisDir.getOffset() : 0), - (xOffset != 0 ? axisDir.getOffset() : 0), - dir.getAxisDirection() == EnumFacing.AxisDirection.POSITIVE ? - EnumFacing.AxisDirection.NEGATIVE : - EnumFacing.AxisDirection.POSITIVE); - } - } - } - } - - private static void mergeFlags(final EnumSkyBlock lightType, final Chunk inChunk, final Chunk outChunk, final EnumFacing dir, final EnumFacing.AxisDirection axisDir) { - IChunkLightingData outChunkLightingData = (IChunkLightingData) outChunk; - - if (outChunkLightingData.getNeighborLightChecks() == null) { - return; - } - - IChunkLightingData inChunkLightingData = (IChunkLightingData) inChunk; - - initNeighborLightChecks(inChunk); - - final int inIndex = getFlagIndex(lightType, dir, axisDir, EnumBoundaryFacing.IN); - final int outIndex = getFlagIndex(lightType, dir.getOpposite(), axisDir, EnumBoundaryFacing.OUT); - - inChunkLightingData.getNeighborLightChecks()[inIndex] |= outChunkLightingData.getNeighborLightChecks()[outIndex]; - //no need to call Chunk.setModified() since checks are not deleted from outChunk - } - - private static void scheduleRelightChecksForBoundary(final World world, final Chunk chunk, Chunk nChunk, Chunk sChunk, final EnumSkyBlock lightType, final int xOffset, final int zOffset, final EnumFacing.AxisDirection axisDir) { - IChunkLightingData chunkLightingData = (IChunkLightingData) chunk; - - if (chunkLightingData.getNeighborLightChecks() == null) { - return; - } - - final int flagIndex = getFlagIndex(lightType, xOffset, zOffset, axisDir, EnumBoundaryFacing.IN); //OUT checks from neighbor are already merged - - final int flags = chunkLightingData.getNeighborLightChecks()[flagIndex]; - - if (flags == 0) { - return; - } - - if (nChunk == null) { - nChunk = LightingEngineHelpers.getLoadedChunk(world.getChunkProvider(), chunk.xPosition + xOffset, chunk.zPosition + zOffset); - - if (nChunk == null) { - return; - } - } - - if (sChunk == null) { - sChunk = LightingEngineHelpers.getLoadedChunk(world.getChunkProvider(), chunk.xPosition + (zOffset != 0 ? axisDir.getOffset() : 0), chunk.zPosition + (xOffset != 0 ? axisDir.getOffset() : 0)); - - if (sChunk == null) { - return; //Cancel, since the checks in the corner columns require the corner column of sChunk - } - } - - final int reverseIndex = getFlagIndex(lightType, -xOffset, -zOffset, axisDir, EnumBoundaryFacing.OUT); - - chunkLightingData.getNeighborLightChecks()[flagIndex] = 0; - - IChunkLightingData nChunkLightingData = (IChunkLightingData) nChunk; - - if (nChunkLightingData.getNeighborLightChecks() != null) { - nChunkLightingData.getNeighborLightChecks()[reverseIndex] = 0; //Clear only now that it's clear that the checks are processed - } - - chunk.setChunkModified(); - nChunk.setChunkModified(); - - //Get the area to check - //Start in the corner... - int xMin = chunk.xPosition << 4; - int zMin = chunk.zPosition << 4; - - //move to other side of chunk if the direction is positive - if ((xOffset | zOffset) > 0) { - xMin += 15 * xOffset; - zMin += 15 * zOffset; - } - - //shift to other half if necessary (shift perpendicular to dir) - if (axisDir == EnumFacing.AxisDirection.POSITIVE) { - xMin += 8 * (zOffset & 1); //x & 1 is same as abs(x) for x=-1,0,1 - zMin += 8 * (xOffset & 1); - } - - //get maximal values (shift perpendicular to dir) - final int xMax = xMin + 7 * (zOffset & 1); - final int zMax = zMin + 7 * (xOffset & 1); - - for (int y = 0; y < 16; ++y) { - if ((flags & (1 << y)) != 0) { - scheduleRelightChecksForArea(world, lightType, xMin, y << 4, zMin, xMax, (y << 4) + 15, zMax); - } - } - } - - public static void initNeighborLightChecks(final Chunk chunk) { - IChunkLightingData lightingData = (IChunkLightingData) chunk; - - if (lightingData.getNeighborLightChecks() == null) { - lightingData.setNeighborLightChecks(new short[FLAG_COUNT]); - } - } - - public static final String neighborLightChecksKey = "NeighborLightChecks"; - - public static void writeNeighborLightChecksToNBT(final Chunk chunk, final NBTTagCompound nbt) { - short[] neighborLightChecks = ((IChunkLightingData) chunk).getNeighborLightChecks(); - - if (neighborLightChecks == null) { - return; - } - - boolean empty = true; - - final NBTTagList list = new NBTTagList(); - - for (final short flags : neighborLightChecks) { - list.appendTag(new NBTTagShort(flags)); - - if (flags != 0) { - empty = false; - } - } - - if (!empty) { - nbt.setTag(neighborLightChecksKey, list); - } - } - - public static void readNeighborLightChecksFromNBT(final Chunk chunk, final NBTTagCompound nbt) { - if (nbt.hasKey(neighborLightChecksKey, 9)) { - final NBTTagList list = nbt.getTagList(neighborLightChecksKey, 2); - - if (list.tagCount() == FLAG_COUNT) { - initNeighborLightChecks(chunk); - - short[] neighborLightChecks = ((IChunkLightingData) chunk).getNeighborLightChecks(); - - for (int i = 0; i < FLAG_COUNT; ++i) { - neighborLightChecks[i] = ((NBTTagShort) list.get(i)).getShort(); - } - } else { - PhosphorMod.LOGGER.warn("Chunk field {} had invalid length, ignoring it (chunk coordinates: {} {})", neighborLightChecksKey, chunk.xPosition, chunk.zPosition); - } - } - } - - public static void initChunkLighting(final Chunk chunk, final World world) { - final int xBase = chunk.xPosition << 4; - final int zBase = chunk.zPosition << 4; - - final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(xBase, 0, zBase); - - if (world.isAreaLoaded(pos.add(-16, 0, -16), pos.add(31, 255, 31), false)) { - final ExtendedBlockStorage[] extendedBlockStorage = chunk.getBlockStorageArray(); - - for (int j = 0; j < extendedBlockStorage.length; ++j) { - final ExtendedBlockStorage storage = extendedBlockStorage[j]; - - if (storage == null) { - continue; - } - - int yBase = j * 16; - - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - IBlockState state = storage.getBlockByExtId(x & 15, y & 15, z & 15).getDefaultState(); - if (state != null) { - int light = LightingEngineHelpers.getLightValueForState(state, world, pos); - - if (light > 0) { - pos.set(xBase + x, yBase + y, zBase + z); - - world.checkLightFor(EnumSkyBlock.BLOCK, pos); - } - } - } - } - } - } - - if (!world.provider.getHasNoSky()) { - ((IChunkLightingData) chunk).setSkylightUpdatedPublic(); - } - - ((IChunkLightingData) chunk).setLightInitialized(true); - } - } - - public static void checkChunkLighting(final Chunk chunk, final World world) { - if (!((IChunkLightingData) chunk).isLightInitialized()) { - initChunkLighting(chunk, world); - } - - for (int x = -1; x <= 1; ++x) { - for (int z = -1; z <= 1; ++z) { - if (x != 0 || z != 0) { - Chunk nChunk = LightingEngineHelpers.getLoadedChunk(world.getChunkProvider(), chunk.xPosition + x, chunk.zPosition + z); - - if (nChunk == null || !((IChunkLightingData) nChunk).isLightInitialized()) { - return; - } - } - } - } - - chunk.setLightPopulated(true); - } - - public static void initSkylightForSection(final World world, final Chunk chunk, final ExtendedBlockStorage section) { - if (!world.provider.getHasNoSky()) { - for (int x = 0; x < 16; ++x) { - for (int z = 0; z < 16; ++z) { - if (chunk.getHeightValue(x, z) <= section.getYLocation()) { - for (int y = 0; y < 16; ++y) { - section.setExtSkylightValue(x, y, z, EnumSkyBlock.SKY.defaultLightValue); - } - } - } - } - } - } - - private static short[] getNeighborLightChecks(Chunk chunk) { - return ((IChunkLightingData) chunk).getNeighborLightChecks(); - } - - private static void setNeighborLightChecks(Chunk chunk, short[] table) { - ((IChunkLightingData) chunk).setNeighborLightChecks(table); - } - - public static int getCachedLightFor(Chunk chunk, EnumSkyBlock type, BlockPos pos) { - return ((IChunkLighting) chunk).getCachedLightFor(type, pos); - } - - public static ILightingEngine getLightingEngine(World world) { - return ((ILightingEngineProvider) world).getLightingEngine(); - } - -} diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/forge/MixinLoader.java b/src/main/java/net/ccbluex/liquidbounce/injection/forge/MixinLoader.java index c3986db7..38a9e2bc 100644 --- a/src/main/java/net/ccbluex/liquidbounce/injection/forge/MixinLoader.java +++ b/src/main/java/net/ccbluex/liquidbounce/injection/forge/MixinLoader.java @@ -23,7 +23,6 @@ public MixinLoader() { MixinBootstrap.init(); Mixins.addConfiguration("liquidbounce.forge.mixins.json"); - Mixins.addConfiguration("mixins.phosphor.json"); MixinEnvironment.getDefaultEnvironment().setSide(MixinEnvironment.Side.CLIENT); } diff --git a/src/main/resources/liquidbounce_at.cfg b/src/main/resources/liquidbounce_at.cfg index d2ed3284..dd1f765d 100644 --- a/src/main/resources/liquidbounce_at.cfg +++ b/src/main/resources/liquidbounce_at.cfg @@ -1,8 +1,3 @@ -# Phosphor lighting optimization -public net.minecraft.world.World field_72993_I # activeChunkSet -public net.minecraft.world.WorldProvider field_76576_e # hasNoSky -public net.minecraft.world.chunk.storage.ExtendedBlockStorage field_177488_d # data - # Minecraft public net.minecraft.client.Minecraft field_71428_T # timer public-f net.minecraft.client.Minecraft field_71449_j # session diff --git a/src/main/resources/mixins.phosphor.json b/src/main/resources/mixins.phosphor.json deleted file mode 100644 index 0bb77073..00000000 --- a/src/main/resources/mixins.phosphor.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "required": true, - "minVersion": "0.7", - "package": "me.jellysquid.mods.phosphor.mixins", - "plugin": "me.jellysquid.mods.phosphor.mixins.plugins.LightingEnginePlugin", - "refmap": "liquidbounce.mixins.refmap.json", - "compatibilityLevel": "JAVA_8", - "client": [ - "client.ClientChunkProviderAccessor", - "client.MixinMinecraft" - ], - "mixins": [ - "common.MixinAnvilChunkLoader", - "common.MixinChunk", - "common.MixinChunk$Legacy", - "common.MixinChunk$Vanilla", - "common.MixinChunkProviderServer", - "common.MixinExtendedBlockStorage", - "common.MixinSPacketChunkData", - "common.MixinWorld", - "common.ServerChunkProviderAccessor" - ], - "injectors": { - "defaultRequire": 1 - }, - "overwrites": { - "conformVisibility": true - } -} diff --git a/zip_include/README.txt b/zip_include/README.txt index 2a72b8f2..880d6c9a 100644 --- a/zip_include/README.txt +++ b/zip_include/README.txt @@ -17,7 +17,6 @@ ➔ 启动失败? 必须使用 Forge 1.8.9(不要用 1.8.8) -请勿单独安装 Phosphor 模组(客户端已内置,重复安装会导致注入失败) 如仍失败,请删除 mods 文件夹内其他旧版 AirClient jar 后重试 ➔ 提出建议 From 61645601f4492e786f045a1a4d0a83f79924b6c5 Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 18:52:03 +0800 Subject: [PATCH 09/13] remove(speed): delete VulcanFDP speed mode Co-authored-by: Cursor --- .../features/module/modules/movement/Speed.kt | 2 - .../movement/speedmodes/vulcan/VulcanFDP.kt | 166 ------------------ 2 files changed, 168 deletions(-) delete mode 100644 src/main/java/net/ccbluex/liquidbounce/features/module/modules/movement/speedmodes/vulcan/VulcanFDP.kt diff --git a/src/main/java/net/ccbluex/liquidbounce/features/module/modules/movement/Speed.kt b/src/main/java/net/ccbluex/liquidbounce/features/module/modules/movement/Speed.kt index ca325a5c..b91ec56f 100644 --- a/src/main/java/net/ccbluex/liquidbounce/features/module/modules/movement/Speed.kt +++ b/src/main/java/net/ccbluex/liquidbounce/features/module/modules/movement/Speed.kt @@ -31,7 +31,6 @@ import net.ccbluex.liquidbounce.features.module.modules.movement.speedmodes.veru import net.ccbluex.liquidbounce.features.module.modules.movement.speedmodes.verus.LatestVerusHop import net.ccbluex.liquidbounce.features.module.modules.movement.speedmodes.verus.VerusLowHop import net.ccbluex.liquidbounce.features.module.modules.movement.speedmodes.verus.VerusLowHopNew -import net.ccbluex.liquidbounce.features.module.modules.movement.speedmodes.vulcan.VulcanFDP import net.ccbluex.liquidbounce.features.module.modules.movement.speedmodes.vulcan.VulcanGround288 import net.ccbluex.liquidbounce.features.module.modules.movement.speedmodes.vulcan.VulcanHop import net.ccbluex.liquidbounce.features.module.modules.movement.speedmodes.vulcan.VulcanLowHop @@ -77,7 +76,6 @@ object Speed : Module("Speed", Category.MOVEMENT, Keyboard.KEY_X) { VulcanHop, VulcanLowHop, VulcanGround288, - VulcanFDP, // Matrix OldMatrixHop, diff --git a/src/main/java/net/ccbluex/liquidbounce/features/module/modules/movement/speedmodes/vulcan/VulcanFDP.kt b/src/main/java/net/ccbluex/liquidbounce/features/module/modules/movement/speedmodes/vulcan/VulcanFDP.kt deleted file mode 100644 index 2e9e8b2a..00000000 --- a/src/main/java/net/ccbluex/liquidbounce/features/module/modules/movement/speedmodes/vulcan/VulcanFDP.kt +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Air Client - * A free open source mixin-based injection hacked client for Minecraft using Minecraft Forge. - */ -package net.ccbluex.liquidbounce.features.module.modules.movement.speedmodes.vulcan - -import net.ccbluex.liquidbounce.config.BoolValue -import net.ccbluex.liquidbounce.config.IntValue -import net.ccbluex.liquidbounce.config.ListValue -import net.ccbluex.liquidbounce.event.MoveEvent -import net.ccbluex.liquidbounce.features.module.modules.movement.speedmodes.SpeedMode -import net.ccbluex.liquidbounce.utils.extensions.isMoving -import net.ccbluex.liquidbounce.utils.movement.MovementUtils -import net.minecraft.client.settings.GameSettings -import kotlin.math.abs - -object VulcanFDP : SpeedMode("VulcanFDP") { - private val vulcanMode = ListValue("VulcanFDP-Mode", arrayOf("LowHop", "Hop", "OldGround", "YPort", "LowHop2"), "LowHop") - private val boostDelay = IntValue("VulcanFDP-BoostDelay", 8, 2..15) - private val boostSpeed = BoolValue("VulcanFDP-GroundBoost", true) - - private var wasTimer = false - private var ticks = 0 - private var jumped = false - private var jumpCount = 0 - private var launchY = 0.0 - - override fun onEnable() { - ticks = 0 - wasTimer = true - mc.timer.timerSpeed = 1.0f - jumpCount = 0 - launchY = mc.thePlayer?.posY ?: 0.0 - } - - override fun onDisable() { - mc.timer.timerSpeed = 1.0f - ticks = 0 - jumpCount = 0 - mc.thePlayer?.jumpMovementFactor = 0.02f - } - - override fun onMove(event: MoveEvent) { - if (vulcanMode.get() == "OldGround") { - if (jumpCount >= boostDelay.get() && boostSpeed.get()) { - event.x *= 1.7181145141919810 - event.z *= 1.7181145141919810 - jumpCount = 0 - } else if (!boostSpeed.get()) { - jumpCount = 4 - } - } - } - - override fun onUpdate() { - val player = mc.thePlayer ?: return - val settings = mc.gameSettings - - when (vulcanMode.get()) { - "LowHop" -> { - ticks++ - player.jumpMovementFactor = 0.0245f - if (player.onGround && player.isMoving) { - player.jump() - ticks = 0 - MovementUtils.strafe() - if (MovementUtils.speed < 0.5f) MovementUtils.strafe(0.484f) - launchY = player.posY - } else if (player.posY > launchY && ticks <= 1) { - player.setPosition(player.posX, launchY, player.posZ) - } else if (ticks == 5) { - player.motionY = -0.17 - } - if (MovementUtils.speed < 0.215f) MovementUtils.strafe(0.215f) - } - - "Hop" -> { - if (wasTimer) { - mc.timer.timerSpeed = 1.0f - wasTimer = false - } - player.jumpMovementFactor = if (abs(player.movementInput.moveStrafe) < 0.1f) 0.026499f else 0.0244f - settings.keyBindJump.pressed = GameSettings.isKeyDown(settings.keyBindJump) - if (MovementUtils.speed < 0.215f && !player.onGround) MovementUtils.strafe(0.215f) - if (player.onGround && player.isMoving) { - settings.keyBindJump.pressed = false - player.jump() - if (!player.isAirBorne) return - mc.timer.timerSpeed = 1.25f - wasTimer = true - MovementUtils.strafe() - if (MovementUtils.speed < 0.5f) MovementUtils.strafe(0.4849f) - } else if (!player.isMoving) { - mc.timer.timerSpeed = 1.0f - player.motionX = 0.0 - player.motionZ = 0.0 - } - } - - "OldGround" -> { - if (jumped) { - player.motionY = -0.1 - player.onGround = false - jumped = false - } - player.jumpMovementFactor = 0.025f - if (player.onGround && player.isMoving) { - if (player.isCollidedHorizontally || settings.keyBindJump.pressed) { - if (!settings.keyBindJump.pressed) player.jump() - return - } - player.jump() - player.motionY = 0.0 - MovementUtils.strafe(0.48f + jumpCount * 0.001f) - jumpCount++ - jumped = true - } else if (player.isMoving) { - MovementUtils.strafe(0.27f + jumpCount * 0.0018f) - } - } - - "YPort" -> { - ticks++ - if (wasTimer) { - mc.timer.timerSpeed = 1.0f - wasTimer = false - } - player.jumpMovementFactor = 0.0245f - if (!player.onGround && ticks > 3 && player.motionY > 0) player.motionY = -0.27 - settings.keyBindJump.pressed = GameSettings.isKeyDown(settings.keyBindJump) - if (MovementUtils.speed < 0.215f && !player.onGround) MovementUtils.strafe(0.215f) - if (player.onGround && player.isMoving) { - ticks = 0 - settings.keyBindJump.pressed = false - player.jump() - if (!player.isAirBorne) return - mc.timer.timerSpeed = 1.2f - wasTimer = true - if (MovementUtils.speed < 0.48f) MovementUtils.strafe(0.48f) else MovementUtils.strafe(MovementUtils.speed * 0.985f) - } else if (!player.isMoving) { - mc.timer.timerSpeed = 1.0f - player.motionX = 0.0 - player.motionZ = 0.0 - } - } - - "LowHop2" -> { - if (player.isMoving) { - if (!player.onGround && player.fallDistance > 1.1) { - mc.timer.timerSpeed = 1f - player.motionY = -0.25 - return - } - if (player.onGround) { - player.jump() - MovementUtils.strafe(0.4815f) - mc.timer.timerSpeed = 1.263f - } else if (player.ticksExisted % 4 == 0) { - player.motionY = if (player.ticksExisted % 3 == 0) -0.01 / player.motionY else -player.motionY / player.posY - mc.timer.timerSpeed = 0.8985f - } - } - } - } - } -} From 78259c4393cd79c1d569729e9976e23aa18d53d6 Mon Sep 17 00:00:00 2001 From: SuperS0nic <3855995453@qq.com> Date: Thu, 18 Jun 2026 07:58:26 -0300 Subject: [PATCH 10/13] Delete .github/workflows/release.yml --- .github/workflows/release.yml | 60 ----------------------------------- 1 file changed, 60 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 471aa64d..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Release - -on: - push: - tags: - - "v*" - - "b[0-9]*" - -jobs: - release: - name: Create Release - runs-on: ubuntu-latest - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up JDK 8 - uses: actions/setup-java@v4 - with: - java-version: "8" - distribution: "temurin" - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Make Gradle wrapper executable - run: chmod +x ./gradlew - - - name: Build with Gradle - run: ./gradlew build --no-daemon - - - name: Prepare release files - run: | - mkdir -p release - cp build/libs/AirClient-*.jar release/airclient.jar - cp -r zip_include/. release/ - - - name: Detect prerelease - id: prerelease - run: | - TAG="${{ github.ref_name }}" - if echo "$TAG" | grep -qiE '^b[0-9]|alpha|beta|rc|pre|dev'; then - echo "prerelease=true" >> $GITHUB_OUTPUT - echo "Tag '$TAG' is a prerelease" - else - echo "prerelease=false" >> $GITHUB_OUTPUT - echo "Tag '$TAG' is a stable release" - fi - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - files: release/* - generate_release_notes: true - prerelease: ${{ steps.prerelease.outputs.prerelease }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 34e071a07e80d39cbe8f3f87d8b2aaeda8d99982 Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 19:09:01 +0800 Subject: [PATCH 11/13] fix(music): keep MusicPlayer GUI open when .music gui is sent from chat Prevent GuiChat from closing the screen after client commands so the MusicPlayer GUI is not dismissed immediately after opening. Co-authored-by: Cursor --- .../features/command/commands/MusicCommand.kt | 5 +++- .../module/modules/music/MusicPlayer.kt | 7 ++--- .../forge/mixins/gui/MixinGuiChat.java | 28 +++++++++++++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/ccbluex/liquidbounce/features/command/commands/MusicCommand.kt b/src/main/java/net/ccbluex/liquidbounce/features/command/commands/MusicCommand.kt index b19eb9dd..258b8df6 100644 --- a/src/main/java/net/ccbluex/liquidbounce/features/command/commands/MusicCommand.kt +++ b/src/main/java/net/ccbluex/liquidbounce/features/command/commands/MusicCommand.kt @@ -35,7 +35,10 @@ object MusicCommand : Command("music", "mu") { "stop" -> { MusicPlayer.stopMusic(); chat("§a已停止播放") } "local" -> handleLocal(args) "volume", "vol" -> handleVolume(args) - "gui" -> MusicPlayer.openGui() + "gui" -> { + MusicPlayer.openGui() + chat("§a正在打开音乐播放器...") + } else -> printUsage() } } diff --git a/src/main/java/net/ccbluex/liquidbounce/features/module/modules/music/MusicPlayer.kt b/src/main/java/net/ccbluex/liquidbounce/features/module/modules/music/MusicPlayer.kt index 3342c435..3209de59 100644 --- a/src/main/java/net/ccbluex/liquidbounce/features/module/modules/music/MusicPlayer.kt +++ b/src/main/java/net/ccbluex/liquidbounce/features/module/modules/music/MusicPlayer.kt @@ -389,7 +389,7 @@ object MusicPlayer : Module("MusicPlayer", Category.CLIENT) { fun getVolume(): Int = volumeValue - fun openGui(prev: GuiScreen? = mc.currentScreen) { + fun openGui(prev: GuiScreen? = null) { if (!state) { suppressOpenGuiOnEnable = true try { @@ -401,10 +401,7 @@ object MusicPlayer : Module("MusicPlayer", Category.CLIENT) { displayMusicGui(prev) } - /** Deferred to the next tick so chat closing after send does not dismiss the GUI. */ private fun displayMusicGui(prev: GuiScreen?) { - mc.addScheduledTask { - mc.displayGuiScreen(GuiMusicPlayer(prev)) - } + mc.displayGuiScreen(GuiMusicPlayer(prev)) } } diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/gui/MixinGuiChat.java b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/gui/MixinGuiChat.java index 32ebd0ed..76136863 100644 --- a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/gui/MixinGuiChat.java +++ b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/gui/MixinGuiChat.java @@ -59,6 +59,34 @@ private void updateLength(CallbackInfo callbackInfo) { inputField.setMaxStringLength(100); } + /** + * Client commands are handled in {@link MixinGuiScreen#messageSend} and cancel + * {@code sendChatMessage}, but vanilla {@code GuiChat.keyTyped} still closes the + * chat afterwards. Skip that close so command-triggered screens (e.g. MusicPlayer) + * are not immediately dismissed. + */ + @Inject( + method = "keyTyped", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/Minecraft;displayGuiScreen(Lnet/minecraft/client/gui/GuiScreen;)V", + ordinal = 1 + ), + cancellable = true + ) + private void preventCloseOnClientCommand(char typedChar, int keyCode, CallbackInfo callbackInfo) { + if (keyCode != 28) { + return; + } + + final String text = inputField.getText().trim(); + if (text.isEmpty() || !text.startsWith(String.valueOf(CommandManager.INSTANCE.getPrefix()))) { + return; + } + + callbackInfo.cancel(); + } + @Inject(method = "updateScreen", at = @At("HEAD")) private void updateScreen(CallbackInfo callbackInfo) { final int delta = RenderUtils.INSTANCE.getDeltaTime(); From c3975da5b4d7acda433a258bbe59341df0896fad Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 20:09:41 +0800 Subject: [PATCH 12/13] fix: add Vec3i getX/getY/getZ accessors for Kotlin 2 on 1.8.9 Kotlin 2 compiles blockPos.x to getX(), which does not exist on MC 1.8.9 Vec3i/BlockPos and caused NoSuchMethodError during block collision ticks. Co-authored-by: Cursor --- .../forge/mixins/block/MixinVec3i.java | 43 +++++++++++++++++++ .../resources/liquidbounce.forge.mixins.json | 1 + 2 files changed, 44 insertions(+) create mode 100644 src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/block/MixinVec3i.java diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/block/MixinVec3i.java b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/block/MixinVec3i.java new file mode 100644 index 00000000..460078bf --- /dev/null +++ b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/block/MixinVec3i.java @@ -0,0 +1,43 @@ +/* + * Air Client + * A free open source mixin-based injection hacked client for Minecraft using Minecraft Forge. + */ +package net.ccbluex.liquidbounce.injection.forge.mixins.block; + +import net.minecraft.util.Vec3i; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +/** + * Kotlin 2 compiles {@code vec3i.x} to {@code getX()}, but MC 1.8.9 only exposes coordinate + * fields. Adding these accessors fixes {@code NoSuchMethodError} for {@link Vec3i} and + * subclasses such as {@link net.minecraft.util.BlockPos}. + */ +@Mixin(Vec3i.class) +public class MixinVec3i { + + @Shadow + protected int x; + + @Shadow + protected int y; + + @Shadow + protected int z; + + @Unique + public int getX() { + return x; + } + + @Unique + public int getY() { + return y; + } + + @Unique + public int getZ() { + return z; + } +} diff --git a/src/main/resources/liquidbounce.forge.mixins.json b/src/main/resources/liquidbounce.forge.mixins.json index 960ec814..210c8a1b 100644 --- a/src/main/resources/liquidbounce.forge.mixins.json +++ b/src/main/resources/liquidbounce.forge.mixins.json @@ -6,6 +6,7 @@ "minVersion": "0.7", "mixins": [ "block.MixinBlock", + "block.MixinVec3i", "block.MixinBlockAnvil", "block.MixinBlockLadder", "block.MixinBlockLiquid", From 3a3d653a1bc9ea9131760ed30772ea4cb139f372 Mon Sep 17 00:00:00 2001 From: Harmd244 <3855995453@qq.com> Date: Thu, 18 Jun 2026 20:34:03 +0800 Subject: [PATCH 13/13] fix: add EntityPlayerSP.getGameProfile accessor for Kotlin 2 on 1.8.9 Kotlin 2 emits direct getGameProfile() calls on EntityPlayerSP, which crashes at runtime; expose gameProfile via access transformer and a mixin accessor, and compare players by UUID in SilentHotbar. Co-authored-by: Cursor --- .../forge/mixins/entity/MixinEntityPlayer.java | 3 +++ .../forge/mixins/entity/MixinEntityPlayerSP.java | 12 ++++++++++++ .../forge/mixins/entity/MixinInventoryPlayer.java | 2 +- src/main/resources/liquidbounce_at.cfg | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinEntityPlayer.java b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinEntityPlayer.java index 735f8b8c..e7104f9c 100644 --- a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinEntityPlayer.java +++ b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinEntityPlayer.java @@ -36,6 +36,9 @@ public abstract class MixinEntityPlayer extends MixinEntityLivingBase { @Shadow public abstract GameProfile getGameProfile(); + @Shadow + protected GameProfile gameProfile; + @Shadow protected abstract boolean canTriggerWalking(); diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinEntityPlayerSP.java b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinEntityPlayerSP.java index 749bd6f9..a9fc57bc 100644 --- a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinEntityPlayerSP.java +++ b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinEntityPlayerSP.java @@ -25,6 +25,7 @@ import net.ccbluex.liquidbounce.utils.rotation.RotationUtils; import net.ccbluex.liquidbounce.utils.extensions.MathExtensionsKt; import net.ccbluex.liquidbounce.utils.extensions.PlayerExtensionKt; +import com.mojang.authlib.GameProfile; import net.minecraft.block.Block; import net.minecraft.block.BlockFence; import net.minecraft.block.BlockFenceGate; @@ -50,6 +51,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyVariable; @@ -101,6 +103,16 @@ public abstract class MixinEntityPlayerSP extends MixinAbstractClientPlayer { @Shadow private float lastReportedPitch; + /** + * Kotlin 2 compiles {@code thePlayer.gameProfile} to {@code getGameProfile()} on this class, but + * MC 1.8.9 only declares the accessor on {@link net.minecraft.entity.player.EntityPlayer}. + * {@code gameProfile} is shadowed on {@link MixinEntityPlayer} where the field is declared. + */ + @Unique + public GameProfile getGameProfile() { + return gameProfile; + } + @Shadow public abstract void playSound(String name, float volume, float pitch); diff --git a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinInventoryPlayer.java b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinInventoryPlayer.java index 9b7aaab0..33f4ebec 100644 --- a/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinInventoryPlayer.java +++ b/src/main/java/net/ccbluex/liquidbounce/injection/forge/mixins/entity/MixinInventoryPlayer.java @@ -21,6 +21,6 @@ private int hookSilentHotbar(InventoryPlayer instance) { if (instance == null || instance.player == null || mc.thePlayer == null) return instance != null ? instance.currentItem : 0; - return instance.player.getGameProfile().equals(mc.thePlayer.getGameProfile()) ? SilentHotbar.INSTANCE.getCurrentSlot() : instance.currentItem; + return instance.player.getUniqueID().equals(mc.thePlayer.getUniqueID()) ? SilentHotbar.INSTANCE.getCurrentSlot() : instance.currentItem; } } diff --git a/src/main/resources/liquidbounce_at.cfg b/src/main/resources/liquidbounce_at.cfg index dd1f765d..093e1bb5 100644 --- a/src/main/resources/liquidbounce_at.cfg +++ b/src/main/resources/liquidbounce_at.cfg @@ -29,6 +29,7 @@ public net.minecraft.network.play.client.C17PacketCustomPayload field_149561_c # public net.minecraft.entity.Entity field_70134_J # isInWeb # EntityPlayer +public net.minecraft.entity.player.EntityPlayer field_146106_i # gameProfile public net.minecraft.entity.player.EntityPlayer field_71072_f # itemInUseCount public net.minecraft.entity.player.EntityPlayer field_71083_bS # sleeping public net.minecraft.entity.player.EntityPlayer field_71076_b # sleepTimer