Compare commits

...

150 Commits

Author SHA1 Message Date
James Seibel 42ab9eebe5 Fully move getShade into ClientLevelWrapper 2026-05-31 07:43:28 -05:00
Acuadragon100 49e3e11250 Move getShade into ClientLevelWrapper. 2026-05-31 11:16:05 +02:00
Acuadragon100 10925d7c57 Include both the dimension type name and the dimension id number in the dimension name on 1.12.2. 2026-05-16 14:23:16 +02:00
Acuadragon100 7d918fe8dc Also use the original camera position when Immersive Portals is loaded. 2026-05-16 13:25:19 +02:00
Acuadragon100 938631a379 Merge remote-tracking branch 'root/main' into fix-portals
# Conflicts:
#	coreSubProjects
2026-05-15 16:42:34 +02:00
Acuadragon100 48966bf32a Use the dimension id number as the level key on 1.12.2. 2026-05-15 16:01:58 +02:00
Acuadragon100 2dc38622e2 Fix compile errors with 1.12.2. 2026-05-15 15:16:18 +02:00
James Seibel 36f1c49f49 Add MC Version locking to the config 2026-05-15 07:44:22 -05:00
James Seibel 271f4e8b21 Fix compiling MC 1.16.5+ 2026-05-14 20:52:36 -05:00
James Seibel b164e4646d add 1.12.2 to the build script 2026-05-14 20:13:22 -05:00
James Seibel 14fd5729e2 use ModInfo packet path 2026-05-14 20:11:41 -05:00
James Seibel 7c3e279237 preprocessor cleanup 2026-05-14 20:11:32 -05:00
Acuadragon100 536389af0b Merge remote-tracking branch 'refs/remotes/root/main' into fix-portals
# Conflicts:
#	common/src/main/java/com/seibel/distanthorizons/common/commonMixins/MixinVanillaFogCommon.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/level/KeyedClientLevelManager.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftRenderWrapper.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ServerLevelWrapper.java
#	coreSubProjects
#	forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinFogRenderer.java
2026-05-14 22:46:23 +02:00
James Seibel 2884094dee refactoring and cleanup 2026-05-14 07:49:32 -05:00
James Seibel 817c268491 Tint color cleanup 2026-05-14 07:11:44 -05:00
Vojtěch Šokala 071a306f14 Preprocessor cleanup 2026-05-14 13:03:32 +02:00
Acuadragon100 7ad0599beb Only tick levels the player is in on the client. 2026-05-14 12:09:02 +02:00
Vojtěch Šokala c97fea6d0a Fix compile of modern 2026-05-14 07:53:00 +02:00
Vojtěch Šokala d94faf828d Preprocessor cleanup + colored beacons for 1.12.2 2026-05-13 23:59:09 +02:00
Vojtěch Šokala 0d6d4b133e Fix compile of modern 2026-05-13 23:58:25 +02:00
Acuadragon100 8234ac60b8 Always return the player's level and position even when a portal is being rendered. 2026-05-13 21:06:43 +02:00
James Seibel 5ab6bfb663 preprocessor cleanup 2026-05-13 07:49:18 -05:00
James Seibel e087dbc878 temp unit test disable 2026-05-12 21:56:30 -05:00
James Seibel 391e96ad3d abstract mod init cleanup 2026-05-12 21:54:03 -05:00
James Seibel fbf812091d MC 1.12.2 refactoring and cleanup 2026-05-12 21:38:07 -05:00
Acuadragon100 31ab738392 Fix Immersive Portals not properly detected on Forge 1.16.5 2026-05-12 21:23:50 +02:00
James Seibel 7448483f84 revert option screen name for consistency 2026-05-12 08:02:26 -05:00
James Seibel e3d6e1bcc6 use vanilla fog common 2026-05-12 07:32:57 -05:00
James Seibel fbbcd1b95b fix forgix running on only a single modloader 2026-05-12 07:28:59 -05:00
James Seibel d8e7325044 minor gradle cleanup 2026-05-12 07:28:45 -05:00
James Seibel f17aeca79c add cleanroom run config 2026-05-12 07:28:37 -05:00
James Seibel ac3a223114 add missing licensing headers 2026-05-12 07:28:25 -05:00
James Seibel e4312c2f8b reformat 1.12.2 version prop 2026-05-12 07:28:11 -05:00
James Seibel 17bde631ac Put back jvmdowngrader 2026-05-12 07:02:04 -05:00
Vojtěch Šokala 578efbf15f Fix compile of 1.21.6+ 2026-05-08 02:19:45 +02:00
Vojtěch Šokala 8b6bb228e3 Put 26.1.2 back in mcVer 2026-05-08 01:17:19 +02:00
Vojtěch Šokala 611c4606d7 Fix 1.12.2 bugs, fill mcmod.info 2026-05-08 01:15:13 +02:00
Vojtěch Šokala 05bdaf0390 Prevent air transforming into water if waterSubSurfaceBlockReplacementCsv contains invalid resource location 2026-05-07 15:39:14 +02:00
Vojtěch Šokala eb8fcaee36 Fix compilation of modern 2026-05-07 15:34:26 +02:00
Vojtěch Šokala 32a71933d6 Bump forge version on 1.20.1, 1.20.4 for Java 26 support 2026-05-07 15:34:01 +02:00
Vojtěch Šokala a4baf9ca0a make cleanroom work in dev 2026-05-07 12:23:01 +02:00
Vojtěch Šokala 72bfd0a2bb fix color resolve for liquid blocks 2026-05-07 03:39:49 +02:00
Vojtěch Šokala 7ae4a9f460 1.12.2 compiles again 2026-05-06 20:51:08 +02:00
Vojtěch Šokala 9bd6fb0105 merge upstream 2026-05-05 10:00:18 +02:00
James Seibel 1f1024251b up version number 3.0.3 -> 3.0.4 2026-05-04 07:41:48 -05:00
James Seibel fc516a20d5 remove dev from version number 2026-05-03 20:47:00 -05:00
James Seibel 791c2c3426 up api version 6.1.0 -> 6.1.1 2026-05-03 20:47:00 -05:00
James Seibel b00079897a fix blaze generic obj render close on wrong thread 2026-05-03 20:47:00 -05:00
James Seibel 37b73e1d5c Improve stack/GC tracking for GL buffers 2026-05-03 16:45:40 -05:00
James Seibel 4bd1136713 Fix generic buffer cleanup 2026-05-03 16:36:53 -05:00
Acuadragon100 42344e9c31 Fix some compile errors and backport stuff to Forge. 2026-05-03 23:03:25 +02:00
Acuadragon100 904cc4760f Prevent multiple DhClientLevels of the same level from existing at once. 2026-05-03 21:09:43 +02:00
Acuadragon100 297a8dc980 Fix current level unloading on the client. 2026-05-03 21:08:43 +02:00
James Seibel ef98dbd5fd color override API tests 2026-05-02 21:26:33 -05:00
James Seibel 5df0a60b06 add extra optional GLBuffer phantom logging 2026-05-02 21:18:24 -05:00
James Seibel 9feb20eff8 remove unnecessary warning in GLBuffer 2026-05-02 21:18:01 -05:00
James Seibel c9267d61a8 allow toggling tracy via gradle.properties 2026-05-02 15:21:44 -05:00
James Seibel a29e225a80 Fix LOD shading applying incorrectly with Iris 2026-05-02 15:14:33 -05:00
James Seibel ae0f3c2b3b Add oculus api implementation 2026-05-02 12:43:43 -05:00
James Seibel 852ea75449 Update coreSubProjects 2026-05-02 11:45:54 -05:00
James Seibel ab6a5dad2b Revert "Attempt to merge CI changes"
This reverts commit 1a1eaca280.
2026-05-02 11:45:29 -05:00
James Seibel 1a1eaca280 Attempt to merge CI changes 2026-05-02 11:44:09 -05:00
James Seibel 0272f8c57f remove unused manual build scripts 2026-05-02 11:43:13 -05:00
James Seibel cf09358710 fix fog common mixin compiling 2026-05-02 11:04:10 -05:00
James Seibel b5d833fa3d simplify mixin fog common code 2026-05-02 10:57:37 -05:00
James Seibel 329dbe9585 Fix block wrapper null pointer 2026-05-02 10:43:41 -05:00
James Seibel c7ae7f155e move fog common mixin code to common 2026-05-02 10:42:05 -05:00
James Seibel 30eb02f3b7 lightmap move common level code to common 2026-05-02 10:02:08 -05:00
James Seibel 86cda56381 optimize and fix block wrapper duplicate init 2026-05-02 10:01:08 -05:00
James Seibel 35c1bae156 move fog common mixin code to common 2026-05-02 10:00:24 -05:00
James Seibel e1ce3ae200 neo/fabric move lightmap level to core 2026-05-02 09:57:27 -05:00
James Seibel ed16c83271 minor format updating 2026-05-02 09:54:25 -05:00
James Seibel 5f7dbb8662 Fix neoforge debugging 2026-05-02 08:00:14 -05:00
James Seibel ee21548151 comment on why sponge maven can't have fabric 2026-05-01 07:42:53 -05:00
Ran-Mewo ca00125960 exclude FabricMC groups from Sponge repository in dh-loader.gradle 2026-04-30 23:28:29 +10:00
James Seibel 4067264e72 Merge branch 'distant-horizons-fix' 2026-04-29 07:45:29 -05:00
James Seibel 9c2d243ad4 Add position finder debug config 2026-04-29 07:35:24 -05:00
James Seibel de9d8b0d2e Optimize BlockStateWrapper getter 2026-04-28 21:15:07 -05:00
James Seibel 67f4615b34 add backup to texture color getting 2026-04-28 07:46:53 -05:00
James Seibel cd7a130ee4 use camera pos for detail calculations 2026-04-28 07:09:26 -05:00
James Seibel 4d17f7aecf fix null pointer on dedicated server shutdown 2026-04-27 07:48:11 -05:00
James Seibel a59e7500ab Fix wyncraft getting stuck at low LOD quality 2026-04-27 07:27:07 -05:00
Acuadragon100 9d9fdd8ddd Added some missing license headers. 2026-04-26 15:10:01 +02:00
Acuadragon100 2711d8dccc More cleanup 2026-04-26 14:53:27 +02:00
Acuadragon100 c8535f13a6 Check if same level before trying to decode the data. 2026-04-26 14:53:23 +02:00
Acuadragon100 b5ba7862a9 Actually, we can re-enable this if we just make sure it applies to the correct level. 2026-04-26 14:53:21 +02:00
Acuadragon100 33e61b7ef0 Fix not unloading client levels on servers. 2026-04-26 14:53:11 +02:00
Acuadragon100 1617ebda9e Disable fading detection on 1.21.6+ for now.
Might be worth revisiting once a proper fork of Immersive Portals appear for newer versions.
2026-04-26 13:51:52 +02:00
Acuadragon100 29042e2ced Cleanup 2026-04-26 13:51:37 +02:00
Acuadragon100 390dda94c6 Fix server loading. 2026-04-26 13:49:36 +02:00
Acuadragon100 6600703d52 Redo loading 2026-04-26 13:45:06 +02:00
Acuadragon100 3a065d1784 Bug fixes and refactoring. 2026-04-26 13:44:25 +02:00
Acuadragon100 0e340e4558 Tweaked portal loading and fix portals not being detected to disable fading until entered at least once. 2026-04-26 13:43:26 +02:00
Acuadragon100 ca550a0a57 Disable fade rendering when immersive portals and sodium are active at once. 2026-04-26 13:41:03 +02:00
Acuadragon100 30143cbbcb 26.1 support 2026-04-26 13:40:31 +02:00
Acuadragon100 81abcbdb86 Move timer out of mixin to fix crash. 2026-04-26 13:40:13 +02:00
Michael Harvey 3080102f06 Address maintainer feedback: single level loading system, TimerUtil cleanup, consistent formatting 2026-04-26 00:49:31 +02:00
Michael Harvey 6316587ebe remove duplicate copy 2026-04-26 00:49:31 +02:00
Michael Harvey 2e1a2367bd add server support for immersive portals 2026-04-26 00:49:31 +02:00
Michael Harvey e65b1e2dfc add immersive portals support 2026-04-26 00:49:31 +02:00
James Seibel 40040294e7 up fabric api 1.21.1 version 2026-04-25 15:09:48 -05:00
James Seibel 783e61ec3d Update the readme 2026-04-24 07:47:41 -05:00
James Seibel e09db5d7df up version number 3.0.2 -> 3.0.3 2026-04-24 06:51:44 -05:00
James Seibel 91f9ef3f4b remove dev from the version number 2026-04-24 06:51:09 -05:00
James Seibel d52a3abb14 Add a rough build all parallel batch script 2026-04-23 20:38:51 -05:00
James Seibel 16370b0b6e ignore parallel build folders 2026-04-23 20:38:23 -05:00
James Seibel bfa60b48cf Fix iris transparent blending 2026-04-23 17:54:41 -05:00
James Seibel 50518bfe21 Fix "fog" rendering when underwater with Iris 2026-04-23 17:39:46 -05:00
James Seibel 80e4467829 Fix near clip plane to close with shaders 2026-04-23 17:09:27 -05:00
James Seibel 396315bd05 Fix GC rarely deleting in use GL buffers 2026-04-23 16:57:17 -05:00
James Seibel 7a0fec2c2f Maybe fix a buffer deletion issue? 2026-04-23 07:44:33 -05:00
James Seibel 4afaaa7b12 up api version 6.0.0 -> 6.1.0 2026-04-23 07:42:31 -05:00
James Seibel b057041467 Add more locking and volatile buffer ID checks 2026-04-23 07:16:31 -05:00
James Seibel 33e6ce6376 potential nvidia null VBO pointer crash fix 2026-04-22 22:33:17 -05:00
James Seibel 118ef39c30 Fix flashing when moving over root node boundaries 2026-04-22 18:36:13 -05:00
James Seibel 1013e1c824 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2026-04-22 17:15:09 -05:00
James Seibel b0e924c7fe Fix nvidia driver crash 2026-04-22 17:14:50 -05:00
James Seibel 1777acd1d4 remove unused var in ThreadWorldGenParams 2026-04-22 16:54:41 -05:00
s809 8276a862f8 Clean up received payload buffer check a bit 2026-04-23 00:26:37 +05:00
James Seibel 4329acf91d Try fixing rare null pointer in render 2026-04-22 07:51:11 -05:00
James Seibel 72f83b40f7 Fix quad tree unit tests 2026-04-22 07:41:56 -05:00
James Seibel a33eb30a53 fix rare race condition preventing world gen 2026-04-21 22:20:14 -05:00
James Seibel d3e96f50a8 Maybe fix native GL crash due to buffer free 2 2026-04-21 21:40:33 -05:00
James Seibel 3aefeb98b4 Maybe fix native GL crash due to buffer free 2026-04-21 21:37:14 -05:00
James Seibel 3553ff8e60 handle a weird double start issue 2026-04-21 21:25:44 -05:00
James Seibel 945a2c0c5a Fix garbage collector warning not using config 2026-04-21 19:59:23 -05:00
James Seibel 8c7974e216 Improve node out-of-bound logic
This fixes some overlapping rendering issues, fixes LOD generating outside of render distance, and fixes low-detail LODs flashing when moving into previously-explored LODs
2026-04-21 19:50:30 -05:00
James Seibel 37756cd759 Try fixing LOD flashing/stuck low details 2026-04-21 07:48:46 -05:00
James Seibel c60cc4f013 Merge branch 'main' into 'main'
Remove broken TerraFirmaCraft compatibility code

See merge request distant-horizons-team/distant-horizons!86
2026-04-21 11:59:12 +00:00
James Seibel 87cce2e33c Fix LODs loading outside render distance
Fixes !1233
2026-04-19 21:48:33 -05:00
James Seibel 40ada9c186 fix debug wireframe on blaze3D 2026-04-19 16:34:44 -05:00
James Seibel 55fb458266 Maybe reduce memory when using internal world gen 2026-04-19 16:29:27 -05:00
James Seibel 79d2466fa2 Revert "Auto-change rendering backend when Iris is present"
This reverts commit d750e489df.
2026-04-19 15:09:16 -05:00
s809 d750e489df Auto-change rendering backend when Iris is present 2026-04-19 12:57:23 +05:00
James Seibel a206e49b2b up version number 3.0.1 -> 3.0.2 2026-04-18 21:44:33 -05:00
Vojtěch Šokala 9fea29cbb6 Put canvas back 2026-03-17 15:46:00 +01:00
Vojtěch Šokala 7e1e5a56e2 Set back 1.21.11 as default mcVer 2026-03-15 23:41:13 +01:00
Vojtěch Šokala 059293ebe0 Add missing shaders into cleanroom project 2026-03-15 23:31:35 +01:00
Vojtěch Šokala 4f9d4e2a14 Fix compile error 2026-03-15 23:31:13 +01:00
Vojtěch Šokala 6bd3c825c3 Merge branch 'main' of https://gitlab.com/distant-horizons-team/distant-horizons
# Conflicts:
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/InternalServerGenerator.java
2026-03-15 23:27:20 +01:00
Vojtěch Šokala c75595a5e2 [1.12.2] implement pregen command 2026-03-15 23:17:12 +01:00
Vojtěch Šokala 266d463873 fix compile errors 2026-03-15 23:14:54 +01:00
Vojtěch Šokala 73c718c676 [1.12.2] improve config gui to match other versions 2026-03-15 22:13:07 +01:00
Vojtěch Šokala 3df5c04759 Fix issue when MC framebuffer is using renderbuffer for depth 2026-03-15 22:10:03 +01:00
Vojtěch Šokala d72c34a926 Merge branch 'main' of https://gitlab.com/distant-horizons-team/distant-horizons
# Conflicts:
#	common/src/main/java/com/seibel/distanthorizons/common/AbstractModInitializer.java
#	common/src/main/java/com/seibel/distanthorizons/common/commands/PregenCommand.java
#	common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhTerrainShaderProgram.java
2026-03-15 15:49:41 +01:00
Vojtěch Šokala 8991338be1 [1.12.2] add support for 3.0.0b 2026-03-15 15:42:08 +01:00
Vojtěch Šokala 6b32ab02d3 Merge branch 'main' of https://gitlab.com/distant-horizons-team/distant-horizons
# Conflicts:
#	common/src/main/java/com/seibel/distanthorizons/common/AbstractModInitializer.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftGLWrapper.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/misc/LightMapWrapper.java
2026-03-11 09:42:18 +01:00
Vojtěch Šokala c5adc3f72a 1.12.2 working in dev 2026-03-11 09:20:32 +01:00
Vojtěch Šokala 1d0d67d215 1.12.2 buildscript 2026-02-20 15:00:13 +01:00
Daniel e5536de44f Remove broken TerraFirmaCraft compatibility code 2025-12-23 19:27:48 -08:00
164 changed files with 7177 additions and 1532 deletions
-19
View File
@@ -1,19 +0,0 @@
**/.git
**/.gitlab
**/.cache
buildAllJars
**/_Misc Files
*.bat
*.md
*.sh
*.txt
coreSubProjects/*.md
coreSubProjects/*.txt
**/.gitignore
**/.gitattributes
**/.gitlab-cy.yml
**/.gitmodules
+2
View File
@@ -25,6 +25,8 @@ hs_err_pid*
Merged/
# Folder created by the buildAll scripts
buildAllJars/
_buildAllJars/
_buildWorkers/
relocate_natives/.venv/
relocate_natives/__pycache__/
+2 -1
View File
@@ -41,7 +41,8 @@ build:
"1.19.4", "1.19.2",
"1.18.2",
"1.17.1",
"1.16.5"
"1.16.5",
"1.12.2"
]
script:
# this both runs the unit tests and assembles the code
@@ -0,0 +1,27 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [cleanroom:runClient]" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="cleanroom:runClient" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<ExternalSystemDebugDisabled>false</ExternalSystemDebugDisabled>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<GradleProfilingDisabled>false</GradleProfilingDisabled>
<GradleCoverageDisabled>false</GradleCoverageDisabled>
<method v="2" />
</configuration>
</component>
-12
View File
@@ -1,12 +0,0 @@
FROM eclipse-temurin:25-jdk
WORKDIR /home/build/
COPY ./gradlew .
RUN chmod +x ./gradlew
CMD echo "\r========== [CLEAN: $MC_VER] ==========" && \
./gradlew clean -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
echo "\r========== [BUILD: $MC_VER] ==========" && \
./gradlew build -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
echo "\r========== [MERGE: $MC_VER] ==========" && \
./gradlew mergeJars -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
echo "\r========== [DONE: $MC_VER] =========="
+29 -127
View File
@@ -14,89 +14,11 @@ Below is a video demonstrating the system:
<br>
## Minecraft and Library Versions
### This branch supports the following versions of Minecraft:
#### 1.20.4, 1.20.3 (Default)
Fabric: 0.15.1\
Fabric API: 0.91.2+1.20.4\
Forge: 49.0.30\
NeoForge: 118-beta\
Parchment: 1.20.2:2023.12.10\
Modmenu: 9.0.0-pre.1
#### 1.20.2
Fabric: 0.14.24\
Fabric API: 0.90.4+1.20.2\
Forge: 48.0.13\
Parchment: 1.20.1:2023.09.03\
Modmenu: 8.0.0
#### 1.20.1, 1.20
Fabric: 0.14.24\
Fabric API: 0.90.4+1.20.1\
Forge: 47.2.1\
Parchment: 1.20.1:2023.09.03\
Modmenu: 7.2.2
#### 1.19.4
Fabric: 0.14.24\
Fabric API: 0.87.1+1.19.4\
Forge: 45.2.4\
Parchment: 1.19.4:2023.06.26\
Modmenu: 6.3.1
#### 1.19.2
Fabric: 0.14.24\
Fabric API: 0.76.1+1.19.2\
Forge: 43.3.2\
Parchment: 1.19.2:2022.11.27\
Modmenu: 4.2.0-beta.2
#### 1.18.2
Fabric: 0.14.24\
Fabric API: 0.76.0+1.18.2\
Forge: 40.2.10\
Parchment: 1.18.2:2022.11.06\
Modmenu: 3.2.5
#### 1.17.1, 1.17
Fabric: 0.14.24\
Fabric API: 0.46.1+1.17\
Forge: 37.1.1\
Parchment: 1.17.1:2021.12.12\
Modmenu: 2.0.14
#### 1.16.5, 1.16.4
Fabric: 0.14.24\
Fabric API: 0.42.0+1.16\
Forge: 36.2.39\
Parchment: 1.16.5:2022.03.06\
Modmenu: 1.16.22
### Versions no longer supported
- 1.18.1, 1.18
- 1.19.1, 1.19
- 1.19.3
<br>
### Plugin and Library versions
Gradle: 8.5\
Fabric loom: 1.4-SNAPSHOT\
Architectury loom (Forge gradle replacement): 1.4-SNAPSHOT\
Sponge vanilla gradle: 0.2.1-SNAPSHOT\
Java Preprocessor plugin: Manifold Preprocessor
<br>
## Source Code Installation
### Prerequisites
* A Java Development Kit (JDK) for Java 17 (recommended) or newer. <br>
* A Java Development Kit (JDK) for Java 25 (recommended) or newer. <br>
Visit https://www.oracle.com/java/technologies/downloads/ for installers.
* Git or someway to clone git projects. <br>
Visit https://git-scm.com/ for installers.
@@ -104,15 +26,10 @@ Java Preprocessor plugin: Manifold Preprocessor
**If using IntelliJ:**
1. Install the Manifold plugin
- https://plugins.jetbrains.com/plugin/10057-manifold-ij
2. Open IDEA and import the build.gradle
3. Refresh the Gradle project in IDEA if required
**If using Eclipse: (Note that Eclipse doesn't support Manifold's preprocessor!)**
1. Run the command: `./gradlew geneclipseruns`
2. Run the command: `./gradlew eclipse`
3. Make sure eclipse has the JDK 17 installed. (This is needed so that eclipse can run minecraft)
4. Import the project into eclipse
<br>
## Switching Versions
@@ -127,72 +44,42 @@ In IntelliJ, you will also need to do a gradle sync if it didn't happen automati
## Compiling
Prerequisites:
- JDK 17 or newer
From the File Explorer:
1. Download and extract the project zip
2. Download the core from https://gitlab.com/distant-horizons-team/distant-horizons-core and extract into a folder called `coreSubProjects`
3. Open a terminal emulator in the project folder (On Windows you can type `cmd` in the title bar)
4. Run the commands: `./gradlew assemble` (You may need to use a `.\` on Windows)
5. Merge the jars with `./gradlew mergeJars`
6. The compiled jar file will be in the folder `Merged`
- JDK 25 or newer
From the command line:
1. `git clone --recurse-submodules https://gitlab.com/distant-horizons-team/distant-horizons.git`
2. `cd distant-horizons`
3. `./gradlew assemble`
4. `./gradlew mergeJars`
5. The compiled jar file will be in the folder `Merged`
5. The compiled jar file will be in the folder `\build\libs`
Run tests with: `./gradlew test`
From the File Explorer:
1. Download and extract the project zip
2. Download the core from https://gitlab.com/distant-horizons-team/distant-horizons-core and extract into a folder called `coreSubProjects`
3. Open command prompt/terminal in the project folder
4. Run the commands: `./gradlew assemble`
6. The compiled jar file will be in the folder `\build\libs`
>Note: You can add the argument `-PmcVer=?` to tell gradle to build a selected MC version instead of having to modify the `gradle.properties` file.\
> For example: `./gradlew assemble -PmcVer=1.18.2`
<br>
## Compiling with Docker
`./compile <version>`
You can also locally compile the DH jars without a Java environment by using Docker. Where `<version>` is the version of Minecraft to compile for (ie `1.20.1`), or the keyword `all`. See [Versions](#minecraft-and-library-versions) for a list of all supported values.
<br>
## Other commands
`./gradlew --refresh-dependencies` to refresh local dependencies.
`./gradlew clean` to delete any compiled code.
<br>
## Note to self
The Minecraft source code is NOT added to your workspace in an editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes only.
Source code uses Mojang mappings & [Parchment](https://parchmentmc.org/) mappings.
To generate the source code run `./gradlew genSources` <br>
If your IDE fails to auto-detect the source jars when browsing Minecraft classes; manually select the JAR file ending with -sources.jar when prompted by your IDE. <br>
(In IntelliJ it's at the top where it says "choose sources" when browsing a Minecraft class)
<br>
## Other Useful commands
Run the standalone jar: `./gradlew run` <br>
Build the standalone jar: `./gradlew core:build` <br>
Only build Fabric: `./gradlew fabric:assemble` or `./gradlew fabric:build` <br>
Only build Forge: `./gradlew forge:assemble` or `./gradlew forge:build` <br>
Run the Fabric client (for debugging): `./gradlew fabric:runClient` <br>
Run the Forge client (for debugging): `./gradlew forge:runClient` <br>
Delete all compiled code: `./gradlew clean` <br>
Refresh local dependencies: `./gradlew --refresh-dependencies`
To build all versions: `./buildAll` (all builds will end up in the `Merged` folder)
To build all versions: `./buildAll`
<br>
## Open Source Acknowledgements
## Open Source Libraries
Forgix (To merge multiple mod versions into one jar) [_Formerly_ [_DHJarMerger_](https://github.com/Ran-helo/DHJarMerger)]\
https://github.com/PacifistMC/Forgix
@@ -208,3 +95,18 @@ https://github.com/blackears/svgSalamander
sqlite-jdbc\
https://github.com/xerial/sqlite-jdbc
## Acknowledgements
Distant Horizons has been graciously provided an open source license for <a href="https://www.yourkit.com/java/profiler/">YourKit Java Profiler</a>.
> <img src="https://www.yourkit.com/images/yklogo.png">
>
> YourKit supports open source projects with innovative and intelligent tools
for monitoring and profiling Java and .NET applications.
YourKit is the creator of <a href="https://www.yourkit.com/java/profiler/">YourKit Java Profiler</a>,
<a href="https://www.yourkit.com/dotnet-profiler/">YourKit .NET Profiler</a>,
and <a href="https://www.yourkit.com/youmonitor/">YourKit YouMonitor</a>.
+7 -1
View File
@@ -4,10 +4,10 @@ plugins {
}
forgix {
autoRun = true
// add the mod loaders to the end of the jar
// put together in the format: "a", "a-b", "a-b-c"
int loaderCount = 0;
String modLoaders = "";
((String) gradle.builds_for)
.split(",")
@@ -20,7 +20,13 @@ forgix {
}
modLoaders += loaderName;
loaderCount++;
}
// run if there are multiple launchers that need merging
autoRun = (loaderCount > 1);
// merged jars are named in the format:
// "DistantHorizons-3.0.1-b-dev-26.1-fabric-neoforge.jar"
archiveClassifier = modLoaders
+35
View File
@@ -0,0 +1,35 @@
@echo off & setlocal enabledelayedexpansion
echo ==================== Getting versions to build... ====================
mkdir _buildAllJars 2>nul
del _buildAllJars\* /Q 2>nul
set "ROOT=%~dp0"
set "WORK_DIR=%ROOT%_buildWorkers"
mkdir "%WORK_DIR%" 2>nul
REM get the number of versions to compile
set count=0
for %%f in (versionProperties\*) do set /a count+=1
echo ==================== Found %count% versions to build in parallel ====================
REM Launch a parallel job for each version
for %%f in (%ROOT%versionProperties\*) do (
set version=%%~nf
echo starting [!version!]...
start "Build !version!" cmd /c ""%ROOT%build_worker.bat" "!version!" "%ROOT%" "%WORK_DIR%" ""..\..\_buildAllJars"""
REM Minor timeout between launches so we can stop the build early if we only want
REM to test part of the script and to reduce startup load
timeout /t 3 /nobreak
REM 2>nul to supress a harmless warning that the for loop
REM "cannot find the drive specified"
) 2>nul
echo ==================== All builds started... Completed Jars will be in _buildAllJars ====================
endlocal
+110 -7
View File
@@ -6,6 +6,7 @@ import org.apache.tools.zip.ZipOutputStream
import javax.annotation.Nonnull
import java.util.function.Function
import java.util.function.Predicate
import groovy.json.JsonSlurper
// Convention plugin for all MC-facing subprojects (common + loaders).
// Common uses this directly; loaders use it via unimined-fabric/forge/neoforge.
@@ -23,6 +24,9 @@ plugins {
def isNotCommonProject = project.name != "common"
if (isNotCommonProject) {
evaluationDependsOn(":common")
}
// ==================== Version Properties ====================
@@ -54,7 +58,12 @@ repositories {
url "https://www.cursemaven.com"
content { includeGroup "curse.maven" }
}
maven { url "https://repo.spongepowered.org/maven/" }
maven {
url "https://repo.spongepowered.org/maven/"
// exclusion is needed since sponge has a deprecated version of fabric
// that gradle would prefer to get over the up-to-date version modrinth provides
content { excludeGroupByRegex "net\\.fabricmc(\\..*)?" }
}
maven { url "https://maven.terraformersmc.com/" }
maven { url "https://maven.neoforged.net/releases/" }
flatDir {
@@ -116,6 +125,15 @@ if (isNotCommonProject) {
'Multi-Release': true,
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain',
)
if (project.name == "cleanroom") {
attributes(
'ModType': 'CRL',
'MixinConfigs': "${mod_id}.default.mixin.json,${mod_id}.mod.mixin.json",
'FMLCorePlugin': 'com.seibel.distanthorizons.cleanroom.DistantHorizonsLoadingPlugin',
'FMLCorePluginContainsFMLMod': true,
'FMLAT': "src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons_at.cfg"
)
}
}
}
}
@@ -128,9 +146,15 @@ unimined.minecraft(sourceSets.main, true) {
if (gradle.ext.minecraft_version.startsWith("1.")) { // 26.1+ doesn't use obfuscation
mappings {
if(gradle.ext.minecraft_version.startsWith("1.12.2")){
mcp("stable", "39-1.12")
}
else{
mojmap()
devNamespace "mojmap"
}
}
}
}
@@ -160,18 +184,22 @@ if (isNotCommonProject) {
source(project(":common").sourceSets.main.allSource)
}
} else {
// Common: fabric for compilation + access widener, no jar remapping or runs
unimined.minecraft {
if (gradle.ext.minecraft_version.equals("1.12.2")) {
cleanroom {
loader gradle.ext.cleanroom_loader_version
}
} else {
fabric {
loader gradle.ext.fabric_loader_version
accessWidener project.file("src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons.accesswidener")
}
}
defaultRemapJar = false
runs.off = true
}
}
// ==================== Configurations ====================
evaluationDependsOn(":core")
@@ -341,13 +369,26 @@ if (isNotCommonProject) {
def loaderPaths = configurations.minecraftLibraries.resolve().collect { it.path }.toSet()
tasks.withType(JavaExec).configureEach { runTask ->
dependsOn(shadowJar)
if (project.name != "cleanroom") {
classpath = files(shadowJar.archiveFile) + classpath.filter { file ->
file != shadowJar.archiveFile.get().asFile &&
!file.path.contains(project.buildDir.path) &&
!file.path.contains("core${File.separator}build") &&
!file.path.contains("api${File.separator}build") &&
!file.path.contains("common${File.separator}build") &&
!(shadowedPaths.contains(file.path) && !loaderPaths.contains(file.path))
}
} else {
// For cleanroom, don't put shadow jar on app classpath.
// crl.dev.extrapath loads it via LaunchClassLoader instead.
classpath = classpath.filter { file ->
!file.path.contains(project.buildDir.path) &&
!file.path.contains("core${File.separator}build") &&
!file.path.contains("api${File.separator}build") &&
!file.path.contains("common${File.separator}build") &&
!(shadowedPaths.contains(file.path) && !loaderPaths.contains(file.path))
}
}
// Shared run directory so all loaders use the same worlds
def isClient = runTask.name.toLowerCase().contains("client")
@@ -362,6 +403,31 @@ if (isNotCommonProject) {
}
runTask.jvmArgs = filteredArgs
if(project.name == "forge"
|| project.name == "neoforge"
|| project.name == "cleanroom")
{
// fix (Neo)forge, Cleanroom debug running
doFirst {
def modsDir = rootProject.file("run/${isClient ? 'client' : 'server'}/mods")
modsDir.mkdirs()
// Remove any stale DH jars before copying the fresh one
modsDir.listFiles()?.each({ file ->
if (file.name.startsWith(rootProject.mod_name))
{
file.delete()
}
});
// Copy shadow jar into mods folder so (Neo)Forge discovers it properly
copy {
from shadowJar.archiveFile
into modsDir
}
}
}
// JVM args
runTask.jvmArgs(
"-Dio.netty.leakDetection.level=advanced",
@@ -371,7 +437,8 @@ if (isNotCommonProject) {
//"-XX:+ZGenerational",
rootProject.minecraftMemoryJavaArg,
)
if (isClient) {
if (isClient)
{
runTask.jvmArgs(
"-Dminecraft.api.auth.host=https://nope.invalid",
"-Dminecraft.api.account.host=https://nope.invalid",
@@ -382,11 +449,23 @@ if (isNotCommonProject) {
// use a consistent username for easier debugging in a given world (vs randomly teleporting to a new user each time the game boots)
"--username", "Dev",
// "--renderDebugLabels" is a Mojang command to show render names in RenderDoc
"--renderDebugLabels",
"--renderDebugLabels"
)
// enabling tracy causes constant memory growth so it isn't always desired
if (rootProject.minecraftEnableTracy == "true")
{
// "--tracy" is a Mojang command to allow individual frames to be debugged using Tracy https://github.com/wolfpld/tracy/releases/tag/v0.13.1
"--tracy")
runTask.args("--tracy")
}
}
}
tasks.downgradeJar.inputFile.set(tasks.named("remapJar").flatMap { it.archiveFile })
tasks.jar.finalizedBy(tasks.named("remapJar"))
tasks.named("remapJar").configure { finalizedBy(tasks.shadeDowngradedApi) }
}
}
@@ -398,6 +477,7 @@ if (isNotCommonProject) {
def resourceTargets = [
"build_info.json",
"fabric.mod.json",
"mcmod.info",
"quilt.mod.json",
"META-INF/mods.toml",
"META-INF/neoforge.mods.toml",
@@ -408,6 +488,7 @@ if (isNotCommonProject) {
// Quilt contributors
def quilt_contributors = []
def mod_author_list = rootProject.mod_authors.replaceAll("\"", "").replace("[", "").replace("]", "").split(",")
def cleanroom_author_list = rootProject.mod_authors.replace('[', '').replace(']', '').split(',').collect({ it.trim().replace('"', '') }).join('", "')
for (dev in mod_author_list) {
quilt_contributors.push("\"${dev.strip()}\": \"Developer\"")
}
@@ -425,9 +506,11 @@ if (isNotCommonProject) {
def replaceProperties = [
version : rootProject.mod_version,
mod_id : rootProject.mod_id,
mod_name : rootProject.mod_readable_name,
group : rootProject.maven_group,
authors : rootProject.mod_authors,
cleanroom_authors : cleanroom_author_list,
description : rootProject.mod_description,
homepage : rootProject.mod_homepage,
source : rootProject.mod_source,
@@ -445,6 +528,7 @@ if (isNotCommonProject) {
fabric_incompatibility_list : rootProject.fabric_incompatibility_list,
fabric_recommend_list : rootProject.fabric_recommend_list,
neoforge_version_range : rootProject.neoforge_version_range,
logo_path : "assets/distanthorizons/icon.png"
]
inputs.properties replaceProperties
@@ -455,7 +539,7 @@ if (isNotCommonProject) {
// Remove unused access wideners
exclude { file ->
if (file.name.contains(".distanthorizons.accesswidener") && file.name != "${rootProject.accessWidenerVersion}.distanthorizons.accesswidener") {
if ((file.name.contains(".distanthorizons.accesswidener") && file.name != "${rootProject.accessWidenerVersion}.distanthorizons.accesswidener")) {
return true
}
return false
@@ -476,6 +560,25 @@ if (isNotCommonProject) {
into project.file("build/resources/main")
}
tasks.register("convertJsonToLang") {
dependsOn(copyCoreResources)
File input = project.file("build/resources/main/assets/distanthorizons/lang/en_us.json")
File output = project.file("build/resources/main/assets/distanthorizons/lang/en_us.lang")
inputs.file(input)
outputs.file(output)
doLast {
output.withWriter("UTF-8") { writer ->
writer.writeLine("#PARSE_ESCAPES")
new JsonSlurper()
.parse(input)
.each { key, value ->
writer.writeLine("${key}=${value.toString().replace("%", "%%").replace("\n", "\\n")}")
}
}
}
}
// ==================== JVMDowngrader ====================
@@ -0,0 +1,15 @@
plugins {
id 'dh-loader'
}
unimined.minecraft {
cleanroom {
loader gradle.ext.cleanroom_loader_version
useToolchains = false
accessTransformer project(":common").file("src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons_at.cfg")
runs.all {
systemProperty("crl.dev.mixin", "${mod_id}.default.mixin.json,${mod_id}.mod.mixin.json")
systemProperty("fml.coreMods.load", "com.seibel.distanthorizons.cleanroom.DistantHorizonsLoadingPlugin")
}
}
}
+41
View File
@@ -0,0 +1,41 @@
@echo off & setlocal enabledelayedexpansion
set "VERSION=%~1"
set "ROOT=%~2"
set "WORK_DIR=%~3"
set "WORKER=%WORK_DIR%\%VERSION%"
set "JAR_OUTPUT_DIR=%~4"
REM remove the ending "\" from the root folder, otherwise the final quote
REM in the robocopy command will be escaped and it won't run
if "%ROOT:~-1%"=="\" set "ROOT=%ROOT:~0,-1%"
set "WORKER=%~3\%~1"
set "BUILT_JAR_DIR=%WORKER%\build\forgix"
echo ==================== [%VERSION%] Copying workspace ====================
mkdir "%WORKER%"
robocopy "%ROOT%" "%WORKER%" /E /XD "%WORKER%" "_buildWorkers" "buildAllJars" ".gradle" "build" ".git" ".idea" ".gitlab" "run" "testScripts" /NFL /NDL
echo ==================== [%VERSION%] Cleaning ====================
cd /d "%WORKER%"
call .\gradlew.bat clean
REM optional arg that can be added if we want to log the result to a file
REM >"%WORK_DIR%\build_%VERSION%.log" 2>&1
echo ==================== [%VERSION%] Assembling ====================
call .\gradlew.bat assemble -PmcVer="%VERSION%"
REM optional arg that can be added if we want to log the result to a file
REM >>"%WORK_DIR%\build_%VERSION%.log" 2>&1
echo ==================== [%VERSION%] Exporting ====================
mkdir "%JAR_OUTPUT_DIR%"
robocopy "%BUILT_JAR_DIR%" "%JAR_OUTPUT_DIR%" /NFL /NDL
echo ==================== [%VERSION%] Done ====================
endlocal
REM can be uncommented for debugging
REM pause
+48
View File
@@ -0,0 +1,48 @@
plugins {
id 'unimined-cleanroom'
}
// ==================== Mod Dependency Helper ====================
def addMod(path, enabled) {
if (enabled == "2")
dependencies { modImplementation(path) }
else if (enabled == "1")
dependencies { compileOnly(path) }
}
// ==================== Dependencies ====================
dependencies {
}
// ==================== Tasks ====================
task deleteResources(type: Delete) {
delete file("build/resources/main")
}
processResources {
rename '(.+_at.cfg)', 'META-INF/$1'
dependsOn(copyCoreResources)
dependsOn(convertJsonToLang)
// dependsOn(copyCommonLoaderResources)
}
tasks.named('runClient') {
dependsOn(copyCoreResources)
// dependsOn(copyCommonLoaderResources)
finalizedBy(deleteResources)
}
sourcesJar {
def commonSources = project(":common").sourcesJar
dependsOn commonSources
from commonSources.archiveFile.map { zipTree(it) }
}
@@ -0,0 +1,214 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.util.ProxyUtil;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.InputEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL32;
import java.util.concurrent.AbstractExecutorService;
public class CleanroomClientProxy implements AbstractModInitializer.IEventProxy
{
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final CleanroomPluginPacketSender PACKET_SENDER = (CleanroomPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static World GetEventLevel(WorldEvent e) { return e.getWorld(); }
@Override
public void registerEvents()
{
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.register(FMLCommonHandler.instance());
CleanroomPluginPacketSender.setPacketHandler(ClientApi.INSTANCE::pluginMessageReceived);
}
//==============//
// chunk events //
//==============//
//region
@SubscribeEvent
public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
{
if (MC.clientConnectedToDedicatedServer())
{
World level = event.getWorld();
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(level);
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(wrappedLevel, event.getPos().getX(), event.getPos().getZ()))
{
return;
}
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
Chunk chunk = level.getChunk(event.getPos());
SharedApi.INSTANCE.applyChunkUpdate(new ChunkWrapper(chunk, wrappedLevel), wrappedLevel);
});
}
}
}
@SubscribeEvent
public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
{
if (MC.clientConnectedToDedicatedServer())
{
World level = event.getWorld();
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(level);
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(wrappedLevel, event.getPos().getX(), event.getPos().getZ()))
{
return;
}
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
Chunk chunk = level.getChunk(event.getPos());
SharedApi.INSTANCE.applyChunkUpdate(new ChunkWrapper(chunk, wrappedLevel), wrappedLevel);
});
}
}
}
@SubscribeEvent
public void clientChunkLoadEvent(ChunkEvent.Load event)
{
if (MC.clientConnectedToDedicatedServer())
{
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunkWrapper = new ChunkWrapper(event.getChunk(), wrappedLevel);
SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, wrappedLevel);
}
}
//endregion
//==============//
// key bindings //
//==============//
//region
@SubscribeEvent
public void registerKeyBindings(InputEvent.KeyInputEvent event)
{
/* if (Minecraft.getMinecraft().player == null)
{
return;
}
if (event.getAction() != GLFW.GLFW_PRESS)
{
return;
}
ClientApi.INSTANCE.keyPressedEvent(event.getKey());*/
}
//endregion
//===========//
// rendering //
//===========//
//region
@SubscribeEvent
public void afterLevelRenderEvent(TickEvent.RenderTickEvent event)
{
if (event.type.equals(TickEvent.RenderTickEvent.Type.RENDER))
{
try
{
// should generally only need to be set once per game session
// allows DH to render directly to Optifine's level frame buffer,
// allowing better shader support
MinecraftRenderWrapper.INSTANCE.finalLevelFrameBufferId = GL32.glGetInteger(GL32.GL_FRAMEBUFFER_BINDING);
}
catch (Exception | Error e)
{
LOGGER.error("Unexpected error in afterLevelRenderEvent: "+e.getMessage(), e);
}
}
}
@SubscribeEvent
public void onRenderOverlay(RenderGameOverlayEvent.Text event)
{
Minecraft mc = Minecraft.getMinecraft();
if (event.isCanceled()
|| !mc.gameSettings.showDebugInfo)
{
return;
}
F3Screen.addStringToDisplay(event.getRight());
}
//endregion
}
@@ -0,0 +1,147 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom;
import com.seibel.distanthorizons.cleanroom.modAccessor.ModChecker;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.commands.CommandInitializer;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.InternalServerGenerator;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeChunkManager;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.*;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
import java.util.List;
import java.util.function.Consumer;
/**
* Initialize and setup the Mod. <br>
* If you are looking for the real start of the mod
* check out the ClientProxy.
*/
@Mod(modid = ModInfo.ID, name = ModInfo.NAME, version = ModInfo.VERSION)
public class CleanroomMain extends AbstractModInitializer
{
@Mod.Instance
public static CleanroomMain instance;
@Mod.EventHandler
public void preinit(FMLPreInitializationEvent event)
{
Configurator.setLevel("org.sqlite", Level.INFO);
ForgeChunkManager.setForcedChunkLoadingCallback(CleanroomMain.instance, (tickets, world) -> { });
}
@Mod.EventHandler
public void init(FMLInitializationEvent event)
{
if (FMLCommonHandler.instance().getEffectiveSide().isClient())
{
this.onInitializeClient();
}
else
{
this.onInitializeServer();
}
}
@Override
protected void createInitialSharedBindings()
{
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new CleanroomPluginPacketSender());
}
@Override
protected void createInitialClientBindings() { /* no additional setup needed currently */ }
@Override
protected IEventProxy createClientProxy() { return new CleanroomClientProxy(); }
@Override
protected IEventProxy createServerProxy(boolean isDedicated) { return new CleanroomServerProxy(isDedicated); }
@Override
protected void initializeModCompat()
{
}
/* @Override
protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler)
{ MinecraftForge.EVENT_BUS.addListener((RegisterCommandsEvent e) -> { eventHandler.accept(e.getDispatcher()); }); }*/
@Override
protected void subscribeClientStartedEvent(Runnable eventHandler)
{
// Just run the event handler, since there are no proper ClientLifecycleEvent for the client
// to signify readiness other than FmlClientSetupEvent
eventHandler.run();
}
@Mod.EventHandler
public void onServerStarting(FMLServerStartingEvent event)
{
event.registerServerCommand(CommandInitializer.initCommands());
}
@Mod.EventHandler
public void onServerAboutToStart(FMLServerAboutToStartEvent event)
{
if (eventHandlerStartServer != null)
{
eventHandlerStartServer.accept(event.getServer());
}
}
Consumer<MinecraftServer> eventHandlerStartServer;
@Override
protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler)
{
eventHandlerStartServer = eventHandler;
}
@Override
protected void runDelayedSetup() { SingletonInjector.INSTANCE.runDelayedSetup(); }
// ServerWorldLoadEvent
@Mod.EventHandler
public void dedicatedWorldLoadEvent(FMLServerAboutToStartEvent event)
{
ServerApi.INSTANCE.serverLoadEvent(event.getServer().isDedicatedServer());
}
// ServerWorldUnloadEvent
@Mod.EventHandler
public void serverWorldUnloadEvent(FMLServerStoppingEvent event)
{
ServerApi.INSTANCE.serverUnloadEvent();
}
}
@@ -0,0 +1,136 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import net.minecraftforge.fml.relauncher.Side;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class CleanroomPluginPacketSender extends AbstractPluginPacketSender
{
public static final SimpleNetworkWrapper PLUGIN_CHANNEL =
NetworkRegistry.INSTANCE.newSimpleChannel(
AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE
);
public static void setPacketHandler(Consumer<AbstractNetworkMessage> consumer)
{ setPacketHandler((player, message) -> consumer.accept(message)); }
static BiConsumer<IServerPlayerWrapper, AbstractNetworkMessage> consumerPacket;
public static void setPacketHandler(BiConsumer<IServerPlayerWrapper, AbstractNetworkMessage> consumer)
{
PLUGIN_CHANNEL.registerMessage(MessageWrapper.Handler.class, MessageWrapper.class, 0, Side.CLIENT);
PLUGIN_CHANNEL.registerMessage(MessageWrapper.Handler.class, MessageWrapper.class, 0, Side.SERVER);
consumerPacket = consumer;
}
@Override
public void sendToServer(AbstractNetworkMessage message)
{ PLUGIN_CHANNEL.sendToServer(new MessageWrapper(message)); }
@Override
public void sendToClient(EntityPlayerMP serverPlayer, AbstractNetworkMessage message)
{ PLUGIN_CHANNEL.sendTo(new MessageWrapper(message), serverPlayer); }
//================//
// helper classes //
//================//
//region
// Forge doesn't support using abstract classes
@SuppressWarnings({"ClassCanBeRecord", "RedundantSuppression"})
public static class MessageWrapper implements IMessage
{
public AbstractNetworkMessage message;
//=============//
// constructor //
//=============//
//region
public MessageWrapper(AbstractNetworkMessage message) { this.message = message; }
public MessageWrapper()
{
// For reflection
}
//endregion
@Override
public void fromBytes(ByteBuf buf)
{
int messageId = buf.readByte();
message = MessageRegistry.INSTANCE.createMessage(messageId);
message.decode(buf);
}
@Override
public void toBytes(ByteBuf buf)
{
buf.writeByte(MessageRegistry.INSTANCE.getMessageId(message));
message.encode(buf);
}
public static class Handler implements IMessageHandler<MessageWrapper, IMessage>
{
@Override
public IMessage onMessage(MessageWrapper wrapper, MessageContext context)
{
if (wrapper.message != null)
{
if (context.side == Side.SERVER)
{
consumerPacket.accept(ServerPlayerWrapper.getWrapper(context.getServerHandler().player), wrapper.message);
}
else
{
consumerPacket.accept(null, wrapper.message);
}
}
return null; // No response needed
}
}
}
//endregion
}
@@ -0,0 +1,181 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.commands.CommandInitializer;
import com.seibel.distanthorizons.common.commonMixins.MixinChunkMapCommon;
import com.seibel.distanthorizons.common.util.ProxyUtil;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.InternalServerGenerator;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.ForgeChunkManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.ChunkDataEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLServerAboutToStartEvent;
import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
import net.minecraftforge.fml.common.event.FMLServerStoppingEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent;
import java.lang.reflect.Field;
import static com.seibel.distanthorizons.cleanroom.CleanroomMain.instance;
public class CleanroomServerProxy implements AbstractModInitializer.IEventProxy
{
private static final CleanroomPluginPacketSender PACKET_SENDER = (CleanroomPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static World GetEventLevel(WorldEvent e) { return e.getWorld(); }
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private final ServerApi serverApi = ServerApi.INSTANCE;
private final boolean isDedicated;
@Override
public void registerEvents()
{
MinecraftForge.EVENT_BUS.register(this);
FMLCommonHandler.instance().bus().register(this);
if (this.isDedicated)
{
PACKET_SENDER.setPacketHandler(ServerApi.INSTANCE::pluginMessageReceived);
}
}
//=============//
// constructor //
//=============//
//region
public CleanroomServerProxy(boolean isDedicated) { this.isDedicated = isDedicated; }
//endregion
//========//
// events //
//========//
//region
// ServerLevelLoadEvent
@SubscribeEvent
public void serverLevelLoadEvent(WorldEvent.Load event)
{
if (GetEventLevel(event) instanceof WorldServer)
{
this.serverApi.serverLevelLoadEvent(getServerLevelWrapper((WorldServer) GetEventLevel(event)));
}
}
// ServerLevelUnloadEvent
@SubscribeEvent
public void serverLevelUnloadEvent(WorldEvent.Unload event)
{
if (GetEventLevel(event) instanceof WorldServer)
{
this.serverApi.serverLevelUnloadEvent(getServerLevelWrapper((WorldServer) GetEventLevel(event)));
}
}
@SubscribeEvent
public void serverChunkLoadEvent(ChunkEvent.Load event)
{
ILevelWrapper levelWrapper = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), levelWrapper);
this.serverApi.serverChunkLoadEvent(chunk, levelWrapper);
}
@SubscribeEvent
public void serverChunkSaveEvent(ChunkDataEvent.Save event)
{
if (event.getWorld() instanceof WorldServer worldServer)
{
MixinChunkMapCommon.onChunkSave(worldServer, event.getChunk());
}
}
@SubscribeEvent
public void playerLoggedInEvent(PlayerEvent.PlayerLoggedInEvent event)
{ this.serverApi.serverPlayerJoinEvent(getServerPlayerWrapper(event)); }
@SubscribeEvent
public void playerLoggedOutEvent(PlayerEvent.PlayerLoggedOutEvent event)
{ this.serverApi.serverPlayerDisconnectEvent(getServerPlayerWrapper(event)); }
@SubscribeEvent
public void playerChangedDimensionEvent(PlayerEvent.PlayerChangedDimensionEvent event)
{
this.serverApi.serverPlayerLevelChangeEvent(
getServerPlayerWrapper(event),
getServerLevelWrapper(event.fromDim, event),
getServerLevelWrapper(event.toDim, event)
);
}
//endregion
//================//
// helper methods //
//================//
//region
private static ServerLevelWrapper getServerLevelWrapper(WorldServer level) { return ServerLevelWrapper.getWrapper(level); }
private static ServerLevelWrapper getServerLevelWrapper(int dimensionId, PlayerEvent event)
{
MinecraftServer server = event.player.getServer();
if (server == null)
{
LOGGER.error("getServerLevelWrapper: server is null for player {}", event.player.getName());
return null;
}
return getServerLevelWrapper(server.getWorld(dimensionId));
}
private static ServerPlayerWrapper getServerPlayerWrapper(PlayerEvent event)
{ return ServerPlayerWrapper.getWrapper((EntityPlayerMP) event.player); }
//endregion
}
@@ -0,0 +1,56 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom;
import net.minecraftforge.fml.common.Loader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import java.util.List;
import java.util.Set;
public class DistantHorizonsConfigPlugin implements IMixinConfigPlugin
{
private static final Logger LOGGER = LogManager.getLogger();
@Override
public void onLoad(String mixinPackage)
{ }
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName)
{
/* return switch (mixinClassName.split("\\.")[5]) {
case "mist" -> Loader.isModLoaded("mist");
default -> true;
};*/
return true;
}
@Override public String getRefMapperConfig() { return null; }
@Override public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { }
@Override public List<String> getMixins() { return null; }
@Override public void preApply(String targetClassName, ClassNode classNode, String mixinClassName, IMixinInfo mixinInfo) { }
@Override public void postApply(String targetClassName, ClassNode classNode, String mixinClassName, IMixinInfo mixinInfo) { }
}
@@ -0,0 +1,52 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom;
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class DistantHorizonsLoadingPlugin implements IFMLLoadingPlugin
{
@Override
public @Nullable String[] getASMTransformerClass()
{
return new String[0];
}
@Override
public @Nullable String getModContainerClass()
{
return null;
}
@Override
public @Nullable String getSetupClass()
{
return null;
}
@Override
public void injectData(Map<String, Object> data) { }
@Override
public @Nullable String getAccessTransformerClass()
{
return null;
}
}
@@ -0,0 +1,82 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom.mixins.client;
import com.seibel.distanthorizons.common.commonMixins.MixinVanillaFogCommon;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.init.MobEffects;
import org.apache.logging.log4j.Logger;
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(EntityRenderer.class)
public class MixinEntityRenderer
{
@Shadow
@Final
private DynamicTexture lightmapTexture;
@Shadow @Final private Minecraft mc;
@Shadow @Final private static Logger LOGGER;
private static final float A_REALLY_REALLY_BIG_VALUE = 420694206942069.F;
private static final float A_EVEN_LARGER_VALUE = 42069420694206942069.F;
@Inject(at = @At("TAIL"), method = "updateLightmap")
public void onUpdateLightmap(float patrialTicks, CallbackInfo ci)
{
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (mc == null || mc.getWrappedClientLevel() == null)
{
return;
}
MinecraftRenderWrapper renderWrapper = (MinecraftRenderWrapper)SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
renderWrapper.setLightmapId(lightmapTexture.getGlTextureId());
}
@Inject(at = @At("RETURN"), method = "setupFog")
private void disableSetupFog(int startCoords, float partialTicks, CallbackInfo ci)
{
boolean cancelFog = MixinVanillaFogCommon.cancelFog(startCoords, mc);
if (cancelFog)
{
GlStateManager.setFogStart(A_REALLY_REALLY_BIG_VALUE);
GlStateManager.setFogEnd(A_EVEN_LARGER_VALUE);
ClientApi.RENDER_STATE.vanillaFogEnabled = false;
}
else
{
ClientApi.RENDER_STATE.vanillaFogEnabled = true;
}
}
}
@@ -0,0 +1,46 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom.mixins.client;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.network.play.server.SPacketJoinGame;
import net.minecraft.util.text.ITextComponent;
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;
@Mixin(NetHandlerPlayClient.class)
public class MixinNetHandlerPlayClient
{
@Inject(method = "handleJoinGame", at = @At("RETURN"))
private void onHandleJoinGameEnd(SPacketJoinGame packet, CallbackInfo ci)
{
ClientApi.INSTANCE.onClientOnlyConnected();
}
@Inject(method = "onDisconnect", at = @At("RETURN"))
private void onDisconnect(ITextComponent reason, CallbackInfo ci)
{
ClientApi.INSTANCE.onClientOnlyDisconnected();
}
}
@@ -0,0 +1,83 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom.mixins.client;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiOptions;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.util.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
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.callback.CallbackInfo;
/**
* Adds a button to the menu to goto the config
*
* @author coolGi
* @version 12-02-2021
*/
@Mixin(GuiOptions.class)
public class MixinOptionsScreen extends GuiScreen
{
// Get the texture for the button
@Unique private static final ResourceLocation ICON_TEXTURE = new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
@Unique private static final int button_id = 99;
@Inject(at = @At("HEAD"), method = "initGui")
private void lodconfig$init(CallbackInfo ci)
{
if (Config.Client.showDhOptionsButtonInMinecraftUi.get())
this.buttonList.add(
(new TexturedButtonWidget(
button_id,
// Where the button is on the screen
this.width / 2 - 180, this.height / 6 - 12,
// Width and height of the button
20, 20,
// Offset
0, 0,
// Some textuary stuff
20, ICON_TEXTURE, 20, 40,
// Create the button and tell it where to go
// For now it goes to the client option by default
// Add a title to the button
ModInfo.ID + ".title")));
}
@Inject(at = @At("HEAD"), method = "actionPerformed", cancellable = true)
private void lodconfig$actionPerformed(GuiButton button, CallbackInfo ci)
{
if (button.id == button_id)
{
Minecraft.getMinecraft().displayGuiScreen(GetConfigScreen.getScreen(this));
ci.cancel();
}
}
}
@@ -0,0 +1,81 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom.mixins.client;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.entity.Entity;
import net.minecraft.util.BlockRenderLayer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
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;
@Mixin(RenderGlobal.class)
public class MixinRenderGlobal
{
@Shadow private WorldClient world;
@Inject(method = "renderBlockLayer(Lnet/minecraft/util/BlockRenderLayer;DILnet/minecraft/entity/Entity;)I", at = @At("HEAD"))
private void renderChunkLayer(BlockRenderLayer blockLayerIn, double partialTicks, int pass, Entity entityIn, CallbackInfoReturnable<Integer> cir)
{
if (blockLayerIn == BlockRenderLayer.SOLID)
{
float[] mcProjMatrixRaw = new float[16];
GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
ClientApi.RENDER_STATE.mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
ClientApi.RENDER_STATE.mcProjectionMatrix.transpose();
float[] mcModelViewRaw = new float[16];
GL11.glGetFloatv(GL11.GL_MODELVIEW_MATRIX, mcModelViewRaw);
ClientApi.RENDER_STATE.mcModelViewMatrix = new Mat4f(mcModelViewRaw);
ClientApi.RENDER_STATE.mcModelViewMatrix.transpose();
ClientApi.RENDER_STATE.partialTickTime = (float) partialTicks;
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, this.world);
int blendSrc = GL11.glGetInteger(GL11.GL_BLEND_SRC);
int blendDst = GL11.glGetInteger(GL11.GL_BLEND_DST);
int boundTexture = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
ClientApi.INSTANCE.renderLods();
GL30.glBindVertexArray(0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
GL20.glUseProgram(0);
//Restore vanilla states
GlStateManager.depthFunc(GL11.GL_LEQUAL);
GlStateManager.bindTexture(boundTexture);
GlStateManager.tryBlendFuncSeparate(blendSrc, blendDst, GL11.GL_ONE, GL11.GL_ZERO);
}
}
}
@@ -0,0 +1,66 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom.mixins.server;
import com.seibel.distanthorizons.common.wrappers.misc.IMixinServerPlayer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.util.ITeleporter;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(EntityPlayerMP.class)
public abstract class MixinEntityPlayerMP implements IMixinServerPlayer
{
@Shadow
@Final
public MinecraftServer server;
@Unique
@Nullable
private volatile WorldServer distantHorizons$dimensionChangeDestination;
@Override
@Nullable
public WorldServer distantHorizons$getDimensionChangeDestination()
{ return this.distantHorizons$dimensionChangeDestination; }
@Inject(at = @At("HEAD"), method = "changeDimension(ILnet/minecraftforge/common/util/ITeleporter;)Lnet/minecraft/entity/Entity;")
public void setDimensionChangeDestination(int destinationDimensionID, ITeleporter teleporter, CallbackInfoReturnable<Entity> cir)
{ this.distantHorizons$dimensionChangeDestination = this.server.getWorld(destinationDimensionID); }
@Inject(at = @At("RETURN"), method = "clearInvulnerableDimensionChange")
public void clearDimensionChangeDestination(CallbackInfo ci)
{ this.distantHorizons$dimensionChangeDestination = null; }
}
@@ -0,0 +1,52 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.cleanroom.modAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.ModContainer;
import java.io.File;
import java.util.Optional;
import java.util.stream.Stream;
public class ModChecker implements IModChecker
{
public static final ModChecker INSTANCE = new ModChecker();
@Override
public boolean isModLoaded(String modid)
{
return Loader.isModLoaded(modid);
}
@Override
public File modLocation(String modid)
{
Stream<ModContainer> foundStream = Loader.instance().getModList().stream().filter(x -> x.getModId().equals(modid));
Optional<ModContainer> container = foundStream.findFirst();
if (!container.isPresent())
{
throw new RuntimeException("Mod not found: " + modid);
}
return container.get().getSource();
}
}
@@ -0,0 +1,17 @@
{
"required": true,
"package": "com.seibel.distanthorizons.cleanroom.mixins",
"compatibilityLevel": "JAVA_8",
"target": "@env(DEFAULT)",
"mixins": [],
"minVersion": "0.8.7",
"server": [
"server.MixinEntityPlayerMP"
],
"client": [
"client.MixinEntityRenderer",
"client.MixinNetHandlerPlayClient",
"client.MixinOptionsScreen",
"client.MixinRenderGlobal"
]
}
@@ -0,0 +1,11 @@
{
"required": false,
"package": "com.seibel.distanthorizons.cleanroom.mixins.mod",
"compatibilityLevel": "JAVA_8",
"target": "@env(MOD)",
"mixins": [],
"minVersion": "0.8.7",
"plugin": "com.seibel.distanthorizons.cleanroom.DistantHorizonsConfigPlugin",
"client": [
]
}
+12
View File
@@ -0,0 +1,12 @@
[{
"modid": "${mod_id}",
"name": "${mod_name}",
"version": "${version}",
"mcversion": "1.12.2",
"description": "${description}",
"authorList": ["${cleanroom_authors}"],
"credits": "",
"url": "",
"updateJSON": "",
"logoFile": "${logo_path}"
}]
+6
View File
@@ -0,0 +1,6 @@
{
"pack": {
"description": "${mod_name} Resources",
"pack_format": 3
}
}
@@ -1,15 +1,16 @@
package com.seibel.distanthorizons.common;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
import com.seibel.distanthorizons.api.enums.config.EDhApiRenderApi;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
import com.seibel.distanthorizons.common.commands.CommandInitializer;
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import com.seibel.distanthorizons.common.wrappers.gui.DhDebugScreenEntry;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
import com.seibel.distanthorizons.core.Initializer;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
@@ -30,7 +31,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccesso
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import com.seibel.distanthorizons.core.logging.DhLogger;
@@ -39,6 +39,11 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Supplier;
#if MC_VER > MC_1_12_2
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.commands.CommandSourceStack;
#endif
/**
* Base for all mod loader initializers
* and handles most setup.
@@ -62,7 +67,9 @@ public abstract class AbstractModInitializer
protected abstract IEventProxy createServerProxy(boolean isDedicated);
protected abstract void initializeModCompat();
#if MC_VER > MC_1_12_2
protected abstract void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler);
#endif
protected abstract void subscribeClientStartedEvent(Runnable eventHandler);
protected abstract void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler);
@@ -95,7 +102,9 @@ public abstract class AbstractModInitializer
// Client uses config for auto-updater, so it's initialized here instead of post-init stage
this.initConfig();
logModIncompatibilityWarnings(); // needs to be called after config loading
logIncompatibilityWarnings(); // needs to be called after config loading
setUnsupportedConfigsBasedOnMcVersion();
Initializer.postConfigInit();
LOGGER.info(ModInfo.READABLE_NAME + " client Initialized.");
@@ -129,21 +138,33 @@ public abstract class AbstractModInitializer
this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized, adding event subscribers...");
#if MC_VER > MC_1_12_2
this.commandInitializer = new CommandInitializer();
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer.initCommands(dispatcher); });
#endif
this.subscribeServerStartingEvent(server ->
{
MinecraftServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server;
this.initConfig();
Initializer.postConfigInit();
this.postInit();
this.postServerInit();
#if MC_VER > MC_1_12_2
this.commandInitializer.onServerReady();
#endif
this.checkForUpdates();
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized at " + server.getServerDirectory());
String serverFolderPath;
#if MC_VER <= MC_1_12_2
serverFolderPath = server.getDataDirectory() + "";
#else
serverFolderPath = server.getServerDirectory() + "";
#endif
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized at " + serverFolderPath);
});
}
@@ -159,7 +180,7 @@ public abstract class AbstractModInitializer
private void startup()
{
DependencySetup.createSharedBindings();
SharedApi.init();
Initializer.preConfigInit();
this.createInitialSharedBindings();
}
@@ -261,9 +282,9 @@ public abstract class AbstractModInitializer
//==================================//
// mod partial compatibility checks //
//==================================//
//======================//
// compatibility checks //
//======================//
//region
/**
@@ -272,7 +293,7 @@ public abstract class AbstractModInitializer
* This method will log (and display to chat if enabled)
* these warnings and potential fixes.
*/
private static void logModIncompatibilityWarnings()
private static void logIncompatibilityWarnings()
{
boolean showChatWarnings = Config.Common.Logging.Warning.showModCompatibilityWarningsOnStartup.get();
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
@@ -391,6 +412,24 @@ public abstract class AbstractModInitializer
}
/**
* Some Minecraft versions don't support all
* DH options.
* In that case we need to override what options are available.
*/
private static void setUnsupportedConfigsBasedOnMcVersion()
{
#if MC_VER <= MC_1_12_2
Config.Client.Advanced.Graphics.Experimental.renderingApi.setMcVersionOverrideValue(EDhApiRenderApi.OPEN_GL);
Config.Client.Advanced.Graphics.Quality.vanillaFadeMode.setMcVersionOverrideValue(EDhApiMcRenderingFadeMode.NONE);
Config.Common.WorldGenerator.distantGeneratorMode.setMcVersionOverrideValue(EDhApiDistantGeneratorMode.INTERNAL_SERVER);
#elif MC_VER <= MC_1_21_10
Config.Client.Advanced.Graphics.Experimental.renderingApi.setMcVersionOverrideValue(EDhApiRenderApi.OPEN_GL);
#else
#endif
}
//endregion
@@ -12,10 +12,17 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSende
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import io.netty.buffer.ByteBufUtil;
#if MC_VER <= MC_1_12_2
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.PacketBuffer;
#else
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
#endif
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
import net.minecraft.util.ResourceLocation;
#elif MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
@@ -30,7 +37,9 @@ public abstract class AbstractPluginPacketSender implements IPluginPacketSender
.fileLevelConfig(Config.Common.Logging.logNetworkEventToFile)
.build();
#if MC_VER <= MC_1_20_6
#if MC_VER <= MC_1_12_2
public static final String WRAPPER_PACKET_RESOURCE = ModInfo.RESOURCE_NAMESPACE + ModInfo.WRAPPER_PACKET_PATH;
#elif MC_VER <= MC_1_20_6
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
#elif MC_VER <= MC_1_21_10
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
@@ -52,14 +61,28 @@ public abstract class AbstractPluginPacketSender implements IPluginPacketSender
@Override
public final void sendToClient(IServerPlayerWrapper serverPlayer, AbstractNetworkMessage message)
{
#if MC_VER <= MC_1_12_2
this.sendToClient((EntityPlayerMP) serverPlayer.getWrappedMcObject(), message);
#else
this.sendToClient((ServerPlayer) serverPlayer.getWrappedMcObject(), message);
#endif
}
#if MC_VER <= MC_1_12_2
public abstract void sendToClient(EntityPlayerMP serverPlayer, AbstractNetworkMessage message);
#else
public abstract void sendToClient(ServerPlayer serverPlayer, AbstractNetworkMessage message);
#endif
@Override
public abstract void sendToServer(AbstractNetworkMessage message);
#if MC_VER <= MC_1_12_2
public AbstractNetworkMessage decodeMessage(PacketBuffer in)
#else
public AbstractNetworkMessage decodeMessage(FriendlyByteBuf in)
#endif
{
AbstractNetworkMessage message = null;
@@ -100,7 +123,11 @@ public abstract class AbstractPluginPacketSender implements IPluginPacketSender
}
}
#if MC_VER <= MC_1_12_2
public void encodeMessage(PacketBuffer out, AbstractNetworkMessage message)
#else
public void encodeMessage(FriendlyByteBuf out, AbstractNetworkMessage message)
#endif
{
// This is intentionally unhandled, because errors related to this are unlikely to appear in wild
Objects.requireNonNull(message);
@@ -1,5 +1,9 @@
package com.seibel.distanthorizons.common.commands;
#if MC_VER <= MC_1_12_2
public abstract class AbstractCommand {}
#else
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
@@ -100,3 +104,4 @@ public abstract class AbstractCommand
}
}
#endif
@@ -1,12 +1,26 @@
package com.seibel.distanthorizons.common.commands;
#if MC_VER <= MC_1_12_2
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import net.minecraft.command.CommandBase;
import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.text.TextComponentString;
#else
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import static net.minecraft.commands.Commands.literal;
#endif
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE;
import static net.minecraft.commands.Commands.literal;
#if MC_VER <= MC_1_21_10
#else
@@ -25,6 +39,70 @@ public class CommandInitializer
#endif
#if MC_VER <= MC_1_12_2
public static ICommand initCommands()
{
return new CommandBase()
{
@Override
public String getName() { return "dh"; }
@Override
public String getUsage(ICommandSender sender) { return "/dh <debug|config|pregen>"; }
@Override
public void execute(MinecraftServer server, ICommandSender sender, String[] args)
{
if (args.length == 0)
{
if (DEBUG_CODEC_CRASH_MESSAGE)
{
sender.sendMessage(new TextComponentString("Usage: /dh <debug|config|crash|pregen>"));
}
else
{
sender.sendMessage(new TextComponentString("Usage: /dh <debug|config|pregen"));
}
return;
}
switch (args[0])
{
case "debug":
DebugCommand debugCommand = new DebugCommand();
debugCommand.execute(sender);
break;
case "config":
ConfigCommand configCommand = new ConfigCommand();
configCommand.execute(sender, args);
break;
case "crash":
if (DEBUG_CODEC_CRASH_MESSAGE)
{
CrashCommand crashCommand = new CrashCommand();
crashCommand.execute(sender, args);
}
break;
case "pregen":
if (!server.isDedicatedServer())
{
sender.sendMessage(new TextComponentString("Pregen command is only available on dedicated servers"));
break;
}
PregenCommand pregenCommand = new PregenCommand();
pregenCommand.execute(server, sender, args);
break;
default:
sender.sendMessage(new TextComponentString("Unknown subcommand: " + args[0]));
}
}
};
}
#else
/**
* A received command dispatcher, which is held until the server is ready to initialize the commands.
*/
@@ -80,5 +158,5 @@ public class CommandInitializer
commandDispatcher.register(builder);
}
#endif
}
@@ -1,13 +1,25 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.arguments.*;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
#if MC_VER <= MC_1_12_2
import net.minecraft.command.ICommandSender;
import net.minecraft.util.text.TextComponentString;
#else
import com.mojang.brigadier.arguments.*;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.commands.CommandSourceStack;
import static com.mojang.brigadier.arguments.DoubleArgumentType.doubleArg;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
#endif
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -16,16 +28,75 @@ import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.ToIntBiFunction;
import static com.mojang.brigadier.arguments.DoubleArgumentType.doubleArg;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
/**
* Command for managing config.
*/
public class ConfigCommand extends AbstractCommand
{
#if MC_VER <= MC_1_12_2
@SuppressWarnings({"unchecked", "rawtypes"})
private static void setConfigValue(ConfigEntry<?> configEntry, String value)
{
Class<?> type = configEntry.getType();
if (type == Boolean.class) ((ConfigEntry) configEntry).set(Boolean.parseBoolean(value));
else if (type == Integer.class) ((ConfigEntry) configEntry).set(Integer.parseInt(value));
else if (type == Double.class) ((ConfigEntry) configEntry).set(Double.parseDouble(value));
else if (type == Float.class) ((ConfigEntry) configEntry).set(Float.parseFloat(value));
else if (type == Long.class) ((ConfigEntry) configEntry).set(Long.parseLong(value));
else if (type == String.class) ((ConfigEntry) configEntry).set(value);
else if (type.isEnum()) ((ConfigEntry) configEntry).set(Enum.valueOf((Class<Enum>) type, value));
else throw new RuntimeException("Unsupported config type: " + type.getSimpleName());
}
public void execute(ICommandSender sender, String[] args)
{
if (args.length < 2)
{
sender.sendMessage(new TextComponentString("Usage: /dh config <name> [value]"));
return;
}
String configName = args[1];
AbstractConfigBase<?> found = null;
for (AbstractConfigBase<?> entry : ConfigHandler.INSTANCE.configBaseList)
{
if (entry instanceof ConfigEntry && configName.equals(((ConfigEntry<?>) entry).getChatCommandName()))
{
found = entry;
break;
}
}
if (found == null)
{
sender.sendMessage(new TextComponentString("Unknown config: " + configName));
return;
}
ConfigEntry<?> configEntry = (ConfigEntry<?>) found;
if (args.length == 2)
{
sender.sendMessage(new TextComponentString("Current value of " + configName + " is " + configEntry.get()));
}
else
{
String value = args[2];
try
{
setConfigValue(configEntry, value);
sender.sendMessage(new TextComponentString("Changed " + configName + " to " + value));
}
catch (Exception e)
{
sender.sendMessage(new TextComponentString("Invalid value: " + value));
}
}
}
#else
private static final List<CommandArgumentData<?>> commandArguments = Arrays.asList(
new CommandArgumentData<>(Integer.class, configEntry -> integer(configEntry.getMin(), configEntry.getMax()), IntegerArgumentType::getInteger),
new CommandArgumentData<>(Double.class, configEntry -> doubleArg(configEntry.getMin(), configEntry.getMax()), DoubleArgumentType::getDouble),
@@ -150,5 +221,6 @@ public class ConfigCommand extends AbstractCommand
}
}
#endif
}
@@ -1,15 +1,60 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage;
#if MC_VER <= MC_1_12_2
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.text.TextComponentString;
#else
import net.minecraft.commands.CommandSourceStack;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import static net.minecraft.commands.Commands.literal;
#endif
public class CrashCommand extends AbstractCommand
{
#if MC_VER <= MC_1_12_2
public void execute(ICommandSender sender, String[] args)
{
if (!(sender instanceof EntityPlayerMP))
{
sender.sendMessage(new TextComponentString("This command can only be run by a player"));
return;
}
if (args.length < 2)
{
sender.sendMessage(new TextComponentString("Usage: /dh crash <encode|decode>"));
return;
}
if (SharedApi.tryGetDhServerWorld() == null) return;
ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld()
.getServerPlayerStateManager()
.getConnectedPlayer(ServerPlayerWrapper.getWrapper((EntityPlayerMP) sender));
if (serverPlayerState == null) return;
switch (args[1])
{
case "encode":
serverPlayerState.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.ENCODE));
break;
case "decode":
serverPlayerState.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.DECODE));
break;
default:
sender.sendMessage(new TextComponentString("Usage: /dh crash <encode|decode>"));
}
}
#else
@Override
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
{
@@ -40,5 +85,6 @@ public class CrashCommand extends AbstractCommand
return 1;
}));
}
#endif
}
@@ -1,25 +1,44 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
#if MC_VER <= MC_1_12_2
import net.minecraft.command.ICommandSender;
import net.minecraft.util.text.TextComponentString;
#else
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import static net.minecraft.commands.Commands.literal;
#endif
import java.util.ArrayList;
import java.util.List;
import static net.minecraft.commands.Commands.literal;
public class DebugCommand extends AbstractCommand
{
private static String getDebugString()
{
List<String> lines = new ArrayList<>();
F3Screen.addStringToDisplay(lines);
return String.join("\n", lines);
}
#if MC_VER > MC_1_12_2
@Override
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
{
return literal("debug")
.executes(c -> {
List<String> lines = new ArrayList<>();
F3Screen.addStringToDisplay(lines);
return this.sendSuccessResponse(c, String.join("\n", lines), false);
return this.sendSuccessResponse(c, getDebugString(), false);
});
}
#else
public void execute(ICommandSender sender)
{
sender.sendMessage(new TextComponentString(getDebugString()));
}
#endif
}
@@ -1,27 +1,36 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.generation.PregenManager;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
import com.seibel.distanthorizons.core.world.DhServerWorld;
#if MC_VER <= MC_1_12_2
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.WorldServer;
#else
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.DimensionArgument;
import net.minecraft.commands.arguments.coordinates.ColumnPosArgument;
import net.minecraft.server.level.ColumnPos;
import net.minecraft.server.level.ServerLevel;
import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
#endif
import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
public class PregenCommand extends AbstractCommand
{
@@ -31,6 +40,101 @@ public class PregenCommand extends AbstractCommand
return world.getPregenManager();
}
#if MC_VER <= MC_1_12_2
public void execute(MinecraftServer server, ICommandSender sender, String[] args)
{
if (args.length < 2)
{
sender.sendMessage(new TextComponentString("Usage: /dh pregen <status|start|stop>"));
return;
}
switch (args[1])
{
case "status":
{
String statusString = this.getPregenManager().getStatusString();
sender.sendMessage(new TextComponentString(
statusString != null ? statusString : "Pregen is not running"));
break;
}
case "start":
{
if (args.length < 5)
{
sender.sendMessage(new TextComponentString("Usage: /dh pregen start <dimension> <x> <z> <chunkRadius>"));
return;
}
try
{
String dimensionName = args[2];
int x = Integer.parseInt(args[3]);
int z = Integer.parseInt(args[4]);
int chunkRadius = args.length >= 6 ? Integer.parseInt(args[5]) : 32;
// find the world by dimension name
WorldServer world = null;
for (WorldServer w : server.worlds)
{
if (w.provider.getDimensionType().getName().equals(dimensionName))
{
world = w;
break;
}
}
if (world == null)
{
sender.sendMessage(new TextComponentString("Unknown dimension: " + dimensionName));
return;
}
sender.sendMessage(new TextComponentString("Starting pregen. Progress will be in the server console."));
final ICommandSender finalSender = sender;
CompletableFuture<Void> future = this.getPregenManager().startPregen(
ServerLevelWrapper.getWrapper(world),
new DhBlockPos2D(x, z),
chunkRadius
);
future.whenComplete((result, throwable) -> {
if (throwable instanceof CancellationException)
{
finalSender.sendMessage(new TextComponentString("Pregen is cancelled"));
return;
}
else if (throwable != null)
{
finalSender.sendMessage(new TextComponentString("Pregen failed: " + throwable.getMessage()));
return;
}
finalSender.sendMessage(new TextComponentString("Pregen is complete"));
});
}
catch (NumberFormatException e)
{
sender.sendMessage(new TextComponentString("Invalid number format"));
}
break;
}
case "stop":
{
CompletableFuture<Void> runningPregen = this.getPregenManager().getRunningPregen();
if (runningPregen == null)
{
sender.sendMessage(new TextComponentString("Pregen is not running"));
return;
}
runningPregen.cancel(true);
break;
}
default:
sender.sendMessage(new TextComponentString("Unknown subcommand: " + args[1]));
}
}
#else
@Override
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
{
@@ -110,5 +214,5 @@ public class PregenCommand extends AbstractCommand
runningPregen.cancel(true);
return 1;
}
#endif
}
@@ -1,5 +1,6 @@
package com.seibel.distanthorizons.common.commonMixins;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
import com.seibel.distanthorizons.common.wrappers.gui.updater.UpdateModScreen;
import com.seibel.distanthorizons.core.config.Config;
@@ -11,16 +12,18 @@ import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen;
#if MC_VER <= MC_1_12_2
#else
import net.minecraft.client.gui.screens.TitleScreen;
#endif
import java.util.ArrayList;
public class DhUpdateScreenBase
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final Minecraft MC = Minecraft.getInstance();
private static final Minecraft MC = Minecraft #if MC_VER <= MC_1_12_2 .getMinecraft() #else .getInstance() #endif;
public static void tryShowUpdateScreenAndRunAutoUpdateStartup(Runnable runnable)
@@ -86,3 +89,4 @@ public class DhUpdateScreenBase
}
}
#endif
@@ -6,15 +6,23 @@ import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
#if MC_VER <= MC_1_12_2
import net.minecraft.world.WorldServer;
import net.minecraft.world.chunk.Chunk;
#else
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
#endif
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class MixinChunkMapCommon
{
#if MC_VER <= MC_1_12_2
public static void onChunkSave(WorldServer level, Chunk chunk)
#else
public static void onChunkSave(ServerLevel level, ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
#endif
{
IServerLevelWrapper levelWrapper = ServerLevelWrapper.getWrapper(level);
@@ -37,7 +45,7 @@ public class MixinChunkMapCommon
// is this chunk being saved to disk?
boolean savingChunkToDisk = ci.getReturnValue();
boolean savingChunkToDisk = #if MC_VER <= MC_1_12_2 true #else ci.getReturnValue() #endif;
// true means a chunk was saved to disk
if (!savingChunkToDisk)
{
@@ -50,7 +58,12 @@ public class MixinChunkMapCommon
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
// this logic should prevent that from happening
#if MC_VER <= MC_1_17_1
#if MC_VER <= MC_1_12_2
if (!chunk.isTerrainPopulated() || !chunk.isLightPopulated())
{
return;
}
#elif MC_VER <= MC_1_17_1
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
{
return;
@@ -67,7 +80,12 @@ public class MixinChunkMapCommon
// biome validation //
// some chunks may be missing their biomes, which cause issues when attempting to save them
#if MC_VER <= MC_1_17_1
#if MC_VER <= MC_1_12_2
if (chunk. getBiomeArray() == null)
{
return;
}
#elif MC_VER <= MC_1_17_1
if (chunk.getBiomes() == null)
{
return;
@@ -0,0 +1,52 @@
package com.seibel.distanthorizons.common.commonMixins;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.common.wrappers.modAccessor.ImmersivePortalsAccessorCommon;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.phys.Vec3;
public class MixinImmersivePortalsRenderStatesCommon
{
public static void saveVolatileOriginals()
{
Minecraft mc = Minecraft.getInstance();
ImmersivePortalsAccessorCommon.originalLevel = mc.level;
if (mc.player == null) {
ImmersivePortalsAccessorCommon.originalBlockPos = null;
ImmersivePortalsAccessorCommon.originalChunkPos = null;
ImmersivePortalsAccessorCommon.originalCameraPos = null;
return;
}
BlockPos pos = mc.player.blockPosition();
ImmersivePortalsAccessorCommon.originalBlockPos = new DhBlockPos(pos.getX(), pos.getY(), pos.getZ());
#if MC_VER < MC_1_17_1
ChunkPos cPos = new ChunkPos(mc.player.blockPosition());
#else
ChunkPos cPos = mc.player.chunkPosition();
#endif
#if MC_VER <= MC_1_21_11
ImmersivePortalsAccessorCommon.originalChunkPos = new DhChunkPos(cPos.x, cPos.z);
#else
ImmersivePortalsAccessorCommon.originalChunkPos = new DhChunkPos(cPos.x(), cPos.z());
#endif
#if MC_VER <= MC_1_21_10
Vec3 cameraPos = mc.gameRenderer.getMainCamera().getPosition();
#else
Vec3 cameraPos = mc.gameRenderer.getMainCamera().position();
#endif
ImmersivePortalsAccessorCommon.originalCameraPos = new Vec3d(cameraPos.x(), cameraPos.y(), cameraPos.z());
}
}
#endif
@@ -0,0 +1,141 @@
package com.seibel.distanthorizons.common.commonMixins;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IImmersivePortalsAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
#if MC_VER <= MC_1_12_2
#else
import net.minecraft.client.Camera;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
#endif
#if MC_VER <= MC_1_12_2
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.init.MobEffects;
#elif MC_VER < MC_1_17_1
import net.minecraft.world.level.material.FluidState;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_3
import net.minecraft.world.level.material.FogType;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_6
import net.minecraft.world.level.material.FogType;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.blaze3d.shaders.FogShape;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import net.minecraft.client.renderer.FogParameters;
import org.joml.Vector4f;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#else
import net.minecraft.world.level.material.FogType;
import net.minecraft.client.renderer.fog.FogRenderer;
import net.minecraft.client.renderer.fog.FogData;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
#endif
public class MixinVanillaFogCommon
{
#if MC_VER <= MC_1_12_2
public static boolean cancelFog(int startCoords, Minecraft mc)
#elif MC_VER < MC_1_21_6
public static boolean cancelFog(Camera camera, FogRenderer.FogMode fogMode)
#else
public static boolean cancelFog()
#endif
{
#if MC_VER <= MC_1_12_2
EntityPlayerSP entity = mc.player;
#elif MC_VER < MC_1_21_6
Entity entity = camera.getEntity();
#elif MC_VER <= MC_1_21_10
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.getEntity();
#else
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.entity();
#endif
#if MC_VER <= MC_1_12_2
boolean cameraNotInFluid = cameraNotInFluid(mc);
#else
boolean cameraNotInFluid = cameraNotInFluid(camera);
#endif
#if MC_VER <= MC_1_12_2
boolean isSpecialFog = entity.isPotionActive(MobEffects.BLINDNESS);
#else
boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
#endif
boolean cancelFog = !isSpecialFog;
cancelFog = cancelFog && cameraNotInFluid;
#if MC_VER <= MC_1_12_2
cancelFog = cancelFog && startCoords == 0; // 0 = terrain fog
#elif MC_VER < MC_1_21_6
cancelFog = cancelFog && (fogMode == FogRenderer.FogMode.FOG_TERRAIN);
#endif
cancelFog = cancelFog && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial();
cancelFog = cancelFog && !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get();
IImmersivePortalsAccessor immersivePortals = ModAccessorInjector.INSTANCE.get(IImmersivePortalsAccessor.class);
if (immersivePortals != null
&& immersivePortals.isRenderingPortal())
{
cancelFog = false;
}
return cancelFog;
}
#if MC_VER <= MC_1_12_2
private static boolean cameraNotInFluid(Minecraft mc)
#else
private static boolean cameraNotInFluid(Camera camera)
#endif
{
#if MC_VER <= MC_1_12_2
boolean cameraNotInFluid = mc.getRenderViewEntity() != null && !mc.world.getBlockState(mc.getRenderViewEntity().getPosition()).getMaterial().isLiquid();
#elif MC_VER < MC_1_17_1
FluidState fluidState = camera.getFluidInCamera();
boolean cameraNotInFluid = fluidState.isEmpty();
#else
FogType fogTypes = camera.getFluidInCamera();
boolean cameraNotInFluid = fogTypes == FogType.NONE;
#endif
return cameraNotInFluid;
}
}
@@ -299,27 +299,27 @@ public class BlazeDebugWireframeRenderer extends AbstractDebugWireframeRenderer
// render //
//try (RenderPass renderPass = commandEncoder.createRenderPass(
// this::getRenderPassName,
// BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView,
// /*optionalClearColorAsInt*/ OptionalInt.empty(),
// BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView,
// /*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
//{
// // Bind instance data //
// renderPass.setUniform("uniformBlock", this.uniformBuffer);
//
// renderPass.setPipeline(this.pipeline);
// renderPass.setIndexBuffer(this.boxIndexBuffer, VertexFormat.IndexType.INT);
//
// renderPass.setVertexBuffer(0, this.boxVertexBuffer);
//
// renderPass.drawIndexed(
// /*indexStart*/ 0,
// /*firstIndex*/0,
// /*indexCount*/BOX_OUTLINE_INDICES.length,
// /*instanceCount*/1);
//}
try (RenderPass renderPass = commandEncoder.createRenderPass(
this::getRenderPassName,
BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView,
/*optionalClearColorAsInt*/ OptionalInt.empty(),
BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView,
/*optionalDepthValueAsDouble*/ OptionalDouble.empty()))
{
// Bind instance data //
renderPass.setUniform("uniformBlock", this.uniformBuffer);
renderPass.setPipeline(this.pipeline);
renderPass.setIndexBuffer(this.boxIndexBuffer, VertexFormat.IndexType.INT);
renderPass.setVertexBuffer(0, this.boxVertexBuffer);
renderPass.drawIndexed(
/*indexStart*/ 0,
/*firstIndex*/0,
/*indexCount*/BOX_OUTLINE_INDICES.length,
/*instanceCount*/1);
}
}
private String getRenderPassName() { return "distantHorizons:McDebugRenderer"; }
@@ -57,6 +57,7 @@ import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup;
@@ -615,5 +616,27 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer
//================//
// base overrides //
//================//
//region
@Override
public void close()
{
// close is called outside the render thread and buffer closing must be done on the render thread
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("Generic Obj Cleanup", () ->
{
if (this.vertUniformBuffer != null)
{
this.vertUniformBuffer.close();
}
});
}
//endregion
}
#endif
@@ -19,6 +19,7 @@ import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeBufferRenderEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderPassEvent;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil;
import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil;
import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper;
@@ -267,6 +268,8 @@ public class BlazeDhTerrainRenderer implements IDhTerrainRenderer
{
profiler.popPush("rendering");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam);
// create a render pass
try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass(
this::getRenderPassName,
@@ -10,7 +10,6 @@ import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy;
import com.seibel.distanthorizons.common.render.openGl.glObject.GlDhFramebuffer;
import com.seibel.distanthorizons.common.render.openGl.glObject.texture.*;
import com.seibel.distanthorizons.common.render.openGl.postProcessing.apply.GlDhApplyShader;
import com.seibel.distanthorizons.common.render.openGl.terrain.GlDhTerrainShaderProgram;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.config.Config;
@@ -22,7 +21,6 @@ import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
@@ -748,4 +748,27 @@ public class GlGenericObjectRenderer implements IDhGenericRenderer
//================//
// base overrides //
//================//
//region
@Override
public void close()
{
if (this.boxVertexBuffer != null)
{
this.boxVertexBuffer.close();
}
if (this.boxIndexBuffer != null)
{
this.boxIndexBuffer.close();
}
}
//endregion
}
@@ -97,7 +97,9 @@ public class GLState implements AutoCloseable
{
this.frameBufferTexture0 = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
this.frameBufferTexture1 = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
this.frameBufferDepthTexture = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
int depthType = GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE);
this.frameBufferDepthTexture = (depthType == GL32.GL_TEXTURE) ? GL32.glGetFramebufferAttachmentParameteri(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, GL32.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) : 0;
}
else
{
@@ -178,12 +180,24 @@ public class GLState implements AutoCloseable
// attempting to set textures on the default frame buffer (ID 0) will throw errors
if (frameBufferSet)
{
if (GL32.glIsTexture(this.frameBufferTexture0))
{
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.frameBufferTexture0, 0);
}
if (this.frameBufferTexture1 != 0 && GL32.glIsTexture(this.frameBufferTexture1))
{
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT1, GL32.GL_TEXTURE_2D, this.frameBufferTexture1, 0);
}
if (GL32.glIsTexture(this.frameBufferDepthTexture))
{
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_DEPTH_ATTACHMENT, GL32.GL_TEXTURE_2D, this.frameBufferDepthTexture, 0);
}
}
GL32.glBindVertexArray(GL32.glIsVertexArray(this.vao) ? this.vao : 0);
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, GL32.glIsBuffer(this.vbo) ? this.vbo : 0);
GL32.glBindBuffer(GL32.GL_ELEMENT_ARRAY_BUFFER, GL32.glIsBuffer(this.ebo) ? this.ebo: 0);
@@ -21,14 +21,19 @@ package com.seibel.distanthorizons.common.render.openGl.glObject.buffer;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy;
import com.seibel.distanthorizons.common.render.openGl.glObject.GLState;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.jar.EPlatform;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomLoggingHelper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL44;
@@ -36,9 +41,12 @@ import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.StampedLock;
public class GLBuffer implements AutoCloseable
{
@@ -50,26 +58,57 @@ public class GLBuffer implements AutoCloseable
private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE;
// TODO move to a shared location that can be handled by both GL and Blaze3D buffers
/** if enabled the number of GC'ed buffers will be logged */
private static final boolean LOG_PHANTOM_RECOVERY = ModInfo.IS_DEV_BUILD;
/** If enabled buffers allocation/upload stacks will be logged when GC'ed. */
private static final boolean LOG_PHANTOM_ALLOCATION_STACKS = false; // disabled by default due to the increased GC load
public static final double BUFFER_EXPANSION_MULTIPLIER = 1.3;
public static final double BUFFER_SHRINK_TRIGGER = BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER;
/**
* On macOS the legacy OpenGL -> Metal bridge crashes with SIGBUS in
* {@code _platform_memmove} when {@code glBufferData} or {@code glBufferSubData}
* are called with a large ByteBuffer in one shot. To work around it, we split
* the upload into smaller sub-data calls that each fit comfortably inside the
* driver's internal staging path. <br>
* 256 KiB tuned empirically against macOS 26.5 — a 1 MiB chunk still
* tripped the same {@code _platform_memmove} crash inside
* {@code glBufferSubData_Exec}, but 256 KiB consistently survives.
*/
public static final int MAC_UPLOAD_CHUNK_BYTES = 256 * 1024;
/** Threshold above which the chunked path kicks in on macOS. */
public static final int MAC_UPLOAD_CHUNK_THRESHOLD = MAC_UPLOAD_CHUNK_BYTES;
/** the number of active buffers, can be used for debugging */
public static AtomicInteger bufferCount = new AtomicInteger(0);
private static final int PHANTOM_REF_CHECK_TIME_IN_MS = 5 * 1000;
private static final ConcurrentHashMap<PhantomReference<? extends GLBuffer>, Integer> PHANTOM_TO_BUFFER_ID = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, PhantomReference<? extends GLBuffer>> BUFFER_ID_TO_PHANTOM = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Integer, String> BUFFER_ID_TO_ALLOCATION_STRING = new ConcurrentHashMap<>();
private static final ReferenceQueue<GLBuffer> PHANTOM_REFERENCE_QUEUE = new ReferenceQueue<>();
private static final ThreadPoolExecutor CLEANUP_THREAD = ThreadUtil.makeSingleDaemonThreadPool("GLBuffer Cleanup");
protected int id;
protected volatile int id = 0;
public final int getId() { return this.id; }
protected int size = 0;
public int getSize() { return this.size; }
protected boolean bufferStorage;
public final boolean isBufferStorage() { return this.bufferStorage; }
protected boolean isMapped = false;
/**
* Locking on the render thread isn't great, but is needed due to an inconsistent
* race condition where VBOs can be marked as deleted outside the render thread. <br><br>
*
* But, due to being a read-write lock the chance of freezing
* the render thread is very low
* and since this is a stamped lock, the optimistic read time is basically zero.
* (The optimistic lock time doesn't even appear in the profiler).
*/
public final StampedLock renderStampLock = new StampedLock();
//==============//
@@ -112,13 +151,28 @@ public class GLBuffer implements AutoCloseable
LodUtil.assertNotReach("Thread ["+Thread.currentThread()+"] tried to create a GLBuffer outside the MC render thread.");
}
// destroy the old buffer if one is present
if (this.id != 0)
// lock to prevent the render thread from accessing the buffer's ID
// while we are removing it
long writeStamp = renderStampLock.writeLock();
try
{
destroyBufferIdNow(this.id);
final int oldId = this.id;
this.id = GLMC.glGenBuffers();
//LOGGER.info("created [" + newId + "].");
// destroy the old buffer
// after the new one has been created
// to hopefully prevent a rare race conditions where the old ID
// is still used somewhere
if (oldId != 0)
{
// this ID doesn't need to be tracked anymore
tryRemoveBufferIdFromPhantom(oldId);
destroyBufferIdNow(oldId, "destroyOldAndCreate");
}
this.id = GLMC.glGenBuffers();
this.bufferStorage = asBufferStorage;
bufferCount.getAndIncrement();
@@ -126,9 +180,20 @@ public class GLBuffer implements AutoCloseable
PHANTOM_TO_BUFFER_ID.put(phantom, this.id);
BUFFER_ID_TO_PHANTOM.put(this.id, phantom);
this.updateAllocationStackTrace();
}
finally
{
renderStampLock.unlock(writeStamp);
}
}
protected void destroyAsync()
{
// lock to prevent the render thread from accessing the buffer's ID
// while we are removing it
long writeStamp = renderStampLock.writeLock();
try
{
if (this.id == 0)
{
@@ -137,47 +202,68 @@ public class GLBuffer implements AutoCloseable
}
final int idToDelete = this.id; // saving the ID to a separate variable is necessary so it can be captured by the lambda
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(idToDelete); });
// remove the phantom tracking now so the phantom doesn't have the chance to
// get garbage collected before the render thread task runs
// (this can happen if MC is running at extremely low framerates like 1 fps via mods)
tryRemoveBufferIdFromPhantom(idToDelete);
// mark the old data is invalid before deleting to prevent a rare race condition
// where the queued on render thread task runs before the ID is cleared
this.id = 0;
this.size = 0;
//LOGGER.info("async destroy [" + idToDelete + "].");
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(idToDelete, "destroyAsync"); });
}
private static void destroyBufferIdNow(int id)
finally
{
renderStampLock.unlock(writeStamp);
}
}
private static void destroyBufferIdNow(int id, String cause)
{
// only delete valid buffers
if (id == 0)
{
LOGGER.warn("Attempted to destroy a buffer with ID 0, VRAM memory leaks may occur.");
LOGGER.warn("Attempted to destroy a buffer with ID 0, VRAM memory leaks may occur, cause: ["+cause+"].");
return;
}
// remove and clear the phantom reference if present
if (BUFFER_ID_TO_PHANTOM.containsKey(id))
{
Reference<? extends GLBuffer> phantom = BUFFER_ID_TO_PHANTOM.get(id);
bufferCount.decrementAndGet();
GLMC.glDeleteBuffers(id);
if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get())
{
LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "], cause: ["+cause+"].");
}
}
/** should be called before {@link GLBuffer#destroyBufferIdNow} */
private static void tryRemoveBufferIdFromPhantom(int id)
{
// will contain nothing if stack tracking isn't enabled
BUFFER_ID_TO_ALLOCATION_STRING.remove(id);
PhantomReference<? extends GLBuffer> phantom = BUFFER_ID_TO_PHANTOM.remove(id);
if (phantom != null)
{
// if we are manually closing this buffer, we don't want the phantom reference to accidentally close it again
// this can cause a race condition were we accidentally delete an in-use buffer and cause NVIDIA
// to throw an EXCEPTION_ACCESS_VIOLATION when we attempt to render it
phantom.clear();
PHANTOM_TO_BUFFER_ID.remove(phantom);
BUFFER_ID_TO_PHANTOM.remove(id);
}
bufferCount.decrementAndGet();
// destroy the buffer if it exists,
// the buffer may not exist if the destroy method is called twice
if (GL32.glIsBuffer(id))
Integer phantomId = PHANTOM_TO_BUFFER_ID.remove(phantom);
if (phantomId == null)
{
GLMC.glDeleteBuffers(id);
if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get())
{
LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]");
LOGGER.warn("No Phantom->ID binding stored for ID ["+id+"]");
}
}
else
{
LOGGER.warn("Unable to remove phantom GLBuffer with ID ["+id+"], buffer may have already been deleted.");
}
}
//endregion
@@ -263,8 +349,25 @@ public class GLBuffer implements AutoCloseable
LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use bufferData upload method!");
int bbSize = bb.limit() - bb.position();
GL32.glBufferData(this.getBufferBindingTarget(), bb, bufferDataHint);
int target = this.getBufferBindingTarget();
if (shouldUploadToGpuInChunks(bbSize))
{
// Two-step path used on macOS to dodge the Apple OpenGL -> Metal
// memmove SIGBUS triggered by uploading a large ByteBuffer in one
// glBufferData call:
// 1) allocate-only with the size overload (no memcpy)
// 2) fill the buffer through chunked glBufferSubData calls
GL32.glBufferData(target, (long) bbSize, bufferDataHint);
subDataUploadInChunks(target, 0, bb, MAC_UPLOAD_CHUNK_BYTES);
}
else
{
GL32.glBufferData(target, bb, bufferDataHint);
}
this.size = bbSize;
this.updateAllocationStackTrace();
}
/** Requires the buffer to be bound */
protected void uploadSubData(ByteBuffer bb, int maxExpansionSize, int bufferDataHint)
@@ -272,14 +375,30 @@ public class GLBuffer implements AutoCloseable
LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use subData upload method!");
int bbSize = bb.limit() - bb.position();
int target = this.getBufferBindingTarget();
if (this.size < bbSize || this.size > bbSize * BUFFER_SHRINK_TRIGGER)
{
int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER);
if (newSize > maxExpansionSize) newSize = maxExpansionSize;
GL32.glBufferData(this.getBufferBindingTarget(), newSize, bufferDataHint);
if (newSize > maxExpansionSize)
{
newSize = maxExpansionSize;
}
// allocate-only — no memcpy, safe on macOS regardless of size
GL32.glBufferData(target, (long) newSize, bufferDataHint);
this.size = newSize;
}
GL32.glBufferSubData(this.getBufferBindingTarget(), 0, bb);
if (shouldUploadToGpuInChunks(bbSize))
{
subDataUploadInChunks(target, 0, bb, MAC_UPLOAD_CHUNK_BYTES);
}
else
{
GL32.glBufferSubData(target, 0, bb);
}
this.updateAllocationStackTrace();
}
//endregion
@@ -321,7 +440,6 @@ public class GLBuffer implements AutoCloseable
{
// recreate if the buffer storage type changed
this.bind();
destroyBufferIdNow(this.id);
this.destroyOldAndCreate(uploadMethod.useBufferStorage);
this.bind();
}
@@ -338,6 +456,91 @@ public class GLBuffer implements AutoCloseable
}
}
/**
* macOS-only mitigation for the SIGBUS in
* {@code libsystem_platform.dylib _platform_memmove} that happens when the
* Apple OpenGL -> Metal translation layer copies a single large ByteBuffer
* out of LWJGL into driver memory. Splitting the copy into
* {@link #MAC_UPLOAD_CHUNK_BYTES} slices keeps every memmove inside a size
* the bridge handles reliably.
*/
private static boolean shouldUploadToGpuInChunks(int byteCount)
{
return EPlatform.get() == EPlatform.MACOS
&& byteCount > MAC_UPLOAD_CHUNK_THRESHOLD;
}
/**
* Uploads {@code bb} into the currently bound buffer at {@code baseOffset}
* using a sequence of {@link GL32#glBufferSubData(int, long, ByteBuffer)}
* calls of at most {@code chunkBytes} each. The buffer's position/limit are
* restored before returning.
*/
private static void subDataUploadInChunks(int target, int baseOffset, ByteBuffer bb, int chunkBytes)
{
final int origPos = bb.position();
final int origLimit = bb.limit();
try
{
final int total = origLimit - origPos;
int uploaded = 0;
while (uploaded < total)
{
int chunk = Math.min(chunkBytes, total - uploaded);
bb.position(origPos + uploaded);
bb.limit(origPos + uploaded + chunk);
GL32.glBufferSubData(target, (long) (baseOffset + uploaded), bb);
uploaded += chunk;
// Force the driver to drain its command queue between chunks
// so the OpenGL -> Metal bridge processes (and frees) each
// staging copy before the next sub-data call piles another
// memmove on top of it.
if (uploaded < total)
{
GL32.glFlush();
}
}
}
finally
{
bb.limit(origLimit);
bb.position(origPos);
}
}
/**
* used to help track down leaks where the buffer isn't properly closed
* Note: this probably needs extending to accept a stack trace from outside where it's being called
* since it's often called on the render thread in an un-helpful location.
*/
public void updateAllocationStackTrace()
{
if (LOG_PHANTOM_ALLOCATION_STACKS)
{
String stack;
RenderThreadTaskHandler.QueuedRunnable parentQueuedRunnable;
// if this is running on the render thread, try getting the render task's stack trace instead
// since it's a lot more helpful than wherever the render thread tasks themselves are being run from
if (RenderThreadTaskHandler.INSTANCE.isCurrentThread()
&& (parentQueuedRunnable = RenderThreadTaskHandler.INSTANCE.getCurrentlyRunningTask()) != null
&& parentQueuedRunnable.stackTrace != null)
{
// trim off the getStacktrace() and queueRunningOnRenderThread() methods
StackTraceElement[] trimmedElements = Arrays.copyOfRange(parentQueuedRunnable.stackTrace, 2, parentQueuedRunnable.stackTrace.length);
stack = StringUtil.join("\n", trimmedElements).intern();
}
else
{
// not running on the render thread, use the normal stack trace
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
stack = StringUtil.join("\n", stackTraceElements).intern();
}
BUFFER_ID_TO_ALLOCATION_STRING.put(this.id, stack);
}
}
//endregion
@@ -349,8 +552,13 @@ public class GLBuffer implements AutoCloseable
private static void runPhantomReferenceCleanupLoop()
{
// these arrays are stored here so they don't have to be re-allocated each loop
ArrayList<Pair<String, AtomicInteger>> allocationStackTraceCountPairList = new ArrayList<>();
while (true)
{
allocationStackTraceCountPairList.clear();
try
{
try
@@ -359,20 +567,53 @@ public class GLBuffer implements AutoCloseable
}
catch (InterruptedException ignore) { }
int collectedCount = 0;
Reference<? extends GLBuffer> phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
while (phantomRef != null)
{
// destroy the buffer if it hasn't been cleared yet
if (PHANTOM_TO_BUFFER_ID.containsKey(phantomRef))
Integer idRef = PHANTOM_TO_BUFFER_ID.remove((PhantomReference<? extends GLBuffer>)phantomRef); // cast to make IntelliJ happy
if (idRef != null)
{
int id = PHANTOM_TO_BUFFER_ID.get(phantomRef);
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer phantom destroy", () -> { destroyBufferIdNow(id); });
//LOGGER.warn("Buffer Phantom collected, ID: ["+id+"]");
BUFFER_ID_TO_PHANTOM.remove(idRef);
final int id = idRef;
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer phantom destroy", () -> { destroyBufferIdNow(id, "runPhantomReferenceCleanupLoop"); });
//LOGGER.info("Buffer Phantom collected, ID: ["+id+"]");
if (LOG_PHANTOM_ALLOCATION_STACKS) // stack trace shouldn't be null, but just in case
{
String stack = BUFFER_ID_TO_ALLOCATION_STRING.get(idRef);
PhantomLoggingHelper.putAndIncrementTrackingString(stack, allocationStackTraceCountPairList);
}
}
else
{
LOGGER.warn("Failed to find Buffer ID for phantom reference: ["+phantomRef+"]");
}
collectedCount++;
phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
}
if (LOG_PHANTOM_RECOVERY)
{
// we only want to log when something has been returned
if (collectedCount != 0)
{
LOGGER.warn("GLBuffer phantom recovered: ["+ F3Screen.NUMBER_FORMAT.format(collectedCount)+"].");
// log stack traces if present
if (LOG_PHANTOM_ALLOCATION_STACKS)
{
PhantomLoggingHelper.LogAllocationStackTracePairCounts(LOGGER, allocationStackTraceCountPairList);
}
}
}
}
catch (Exception e)
{
@@ -37,10 +37,11 @@ public class GLIndexBuffer extends GLBuffer
protected int glType = GL32.GL_UNSIGNED_INT;
public int getGlType() { return this.glType; }
public GLIndexBuffer(boolean isBufferStorage)
{
super(isBufferStorage);
}
public GLIndexBuffer(boolean isBufferStorage) { super(isBufferStorage); }
@Override
public void destroyAsync()
@@ -289,6 +289,9 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS
GLMC.disableBlend();
}
// needs to be triggered after DH attempts to set the GL state so that Iris
// can override it as needed
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam);
@@ -311,6 +314,12 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS
for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++)
{
LodBufferContainer bufferContainer = bufferContainers.get(lodIndex);
if (!bufferContainer.buffersUploaded)
{
// make sure we don't accidentally try
// rendering a buffer that is (or is going to be) freed
continue;
}
// set uniforms and fire events
{
@@ -335,13 +344,27 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS
continue;
}
// for lock information please view the lock's javadocs
long vboReadStamp = vbo.renderStampLock.readLock();
long iboReadStamp = vbo.getQuadIBO().renderStampLock.readLock();
try
{
// don't render empty sections
if (vbo.getVertexCount() == 0)
{
continue;
}
// don't render deleted VBOs (this will crash the driver/game)
if (vbo.getId() == 0
|| vbo.getQuadIBO().getId() == 0)
{
continue;
}
// 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5
int indexCount = (int)(vbo.getVertexCount() * 1.5);
int indexCount = (int) (vbo.getVertexCount() * 1.5);
vbo.bind();
vbo.getQuadIBO().bind();
@@ -355,6 +378,12 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS
vbo.unbind();
vbo.getQuadIBO().unbind();
}
finally
{
vbo.renderStampLock.unlock(vboReadStamp);
vbo.getQuadIBO().renderStampLock.unlock(iboReadStamp);
}
}
}
}
@@ -22,23 +22,34 @@ package com.seibel.distanthorizons.common.util;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
#else
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.LevelAccessor;
#endif
public class ProxyUtil
{
public static ILevelWrapper getLevelWrapper(LevelAccessor level)
public static ILevelWrapper getLevelWrapper(
#if MC_VER <= MC_1_12_2 World #else LevelAccessor #endif level
)
{
ILevelWrapper levelWrapper;
if (level instanceof ServerLevel)
if (level instanceof #if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif)
{
levelWrapper = ServerLevelWrapper.getWrapper((ServerLevel) level);
levelWrapper = ServerLevelWrapper.getWrapper(
#if MC_VER <= MC_1_12_2 (WorldServer) #else (ServerLevel) #endif level
);
}
else
{
levelWrapper = ClientLevelWrapper.getWrapper((ClientLevel) level);
levelWrapper = ClientLevelWrapper.getWrapper(
#if MC_VER <= MC_1_12_2 (WorldClient) #else (ClientLevel) #endif level
);
}
return levelWrapper;
@@ -80,9 +80,20 @@ public class DependencySetup
SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE);
}
private static boolean renderingApiBindingsSet = false;
/** will be called from a DH thread, not the render thread */
public static void setRenderingApiBindings()
public synchronized static void setRenderingApiBindings()
{
// shouldn't happen, but there was a single report that this method was triggered twice
if (renderingApiBindingsSet)
{
LOGGER.warn("Rendering bindings already set, skipping. How did this happen?");
return;
}
renderingApiBindingsSet = true;
EDhApiRenderApi renderingApiEnum = Config.Client.Advanced.Graphics.Experimental.renderingApi.get();
if (renderingApiEnum == EDhApiRenderApi.AUTO)
{
@@ -26,9 +26,15 @@ import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.math.Mat4f;
#if MC_VER <= MC_1_12_2
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
#else
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.ChunkPos;
#endif
/**
* This class converts to and from Minecraft objects (Ex: Matrix4f)
@@ -39,15 +45,17 @@ import net.minecraft.world.level.ChunkPos;
*/
public class McObjectConverter
{
private static int bufferIndex(int x, int y)
{
return y * 4 + x;
}
//========//
// matrix //
//========//
//region
/** 4x4 float matrix converter */
public static Mat4f Convert(
#if MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
#if MC_VER <= MC_1_12_2 org.joml.Matrix4f
#elif MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
#elif MC_VER < MC_1_21_6 org.joml.Matrix4f
#else org.joml.Matrix4fc
#endif
@@ -56,21 +64,24 @@ public class McObjectConverter
FloatBuffer buffer = FloatBuffer.allocate(16);
storeMatrix(mcMatrix, buffer);
Mat4f matrix = new Mat4f(buffer);
#if MC_VER < MC_1_19_4
#if MC_VER > MC_1_12_2 && MC_VER < MC_1_19_4
matrix.transpose(); // In 1.19.3 and later, we no longer need to transpose it
#endif
return matrix;
}
/** Taken from Minecraft's com.mojang.math.Matrix4f class from 1.18.2 */
private static void storeMatrix(
#if MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
#if MC_VER <= MC_1_12_2 org.joml.Matrix4f
#elif MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
#elif MC_VER < MC_1_21_6 org.joml.Matrix4f
#else org.joml.Matrix4fc
#endif
matrix,
FloatBuffer buffer)
{
#if MC_VER < MC_1_19_4
#if MC_VER <= MC_1_12_2
matrix.get(buffer);
#elif MC_VER < MC_1_19_4
matrix.store(buffer);
#else
// Mojang starts to use joml's Matrix4f libary in 1.19.3 so we copy their store method and use it here if its newer than 1.19.3
@@ -92,37 +103,85 @@ public class McObjectConverter
buffer.put(bufferIndex(3, 3), matrix.m33());
#endif
}
private static int bufferIndex(int x, int y) { return y * 4 + x; }
//endregion
static final Direction[] directions;
static final EDhDirection[] lodDirections;
//===========//
// direction //
//===========//
//region
#if MC_VER <= MC_1_12_2
private static final EnumFacing[] mcDirections;
#else
private static final Direction[] mcDirections;
#endif
private static final EDhDirection[] dhDirections;
static
{
EDhDirection[] lodDirs = EDhDirection.values();
directions = new Direction[lodDirs.length];
lodDirections = new EDhDirection[lodDirs.length];
#if MC_VER <= MC_1_12_2
mcDirections = new EnumFacing[lodDirs.length];
#else
mcDirections = new Direction[lodDirs.length];
#endif
dhDirections = new EDhDirection[lodDirs.length];
for (EDhDirection lodDir : lodDirs)
{
#if MC_VER <= MC_1_12_2
EnumFacing dir;
#else
Direction dir;
#endif
switch (lodDir.name().toUpperCase())
{
case "DOWN":
#if MC_VER <= MC_1_12_2
dir = EnumFacing.DOWN;
#else
dir = Direction.DOWN;
#endif
break;
case "UP":
#if MC_VER <= MC_1_12_2
dir = EnumFacing.UP;
#else
dir = Direction.UP;
#endif
break;
case "NORTH":
#if MC_VER <= MC_1_12_2
dir = EnumFacing.NORTH;
#else
dir = Direction.NORTH;
#endif
break;
case "SOUTH":
#if MC_VER <= MC_1_12_2
dir = EnumFacing.SOUTH;
#else
dir = Direction.SOUTH;
#endif
break;
case "WEST":
#if MC_VER <= MC_1_12_2
dir = EnumFacing.WEST;
#else
dir = Direction.WEST;
#endif
break;
case "EAST":
#if MC_VER <= MC_1_12_2
dir = EnumFacing.EAST;
#else
dir = Direction.EAST;
#endif
break;
default:
dir = null;
@@ -131,13 +190,37 @@ public class McObjectConverter
if (dir == null)
{
throw new IllegalArgumentException("Invalid direction on init mapping: " + lodDir);
throw new IllegalArgumentException("Invalid direction on init mapping: [" + lodDir + "].");
}
directions[lodDir.ordinal()] = dir;
lodDirections[dir.ordinal()] = lodDir;
mcDirections[lodDir.ordinal()] = dir;
dhDirections[dir.ordinal()] = lodDir;
}
}
#if MC_VER <= MC_1_12_2
public static EnumFacing Convert(EDhDirection lodDirection)
#else
public static Direction Convert(EDhDirection lodDirection)
#endif
{ return mcDirections[lodDirection.ordinal()]; }
#if MC_VER <= MC_1_12_2
public static EDhDirection Convert(EnumFacing direction)
#else
public static EDhDirection Convert(Direction direction)
#endif
{ return dhDirections[direction.ordinal()]; }
//endregion
//==================//
// position objects //
//==================//
//region
public static BlockPos Convert(DhBlockPos wrappedPos) { return new BlockPos(wrappedPos.getX(), wrappedPos.getY(), wrappedPos.getZ()); }
public static ChunkPos Convert(DhChunkPos wrappedPos) { return new ChunkPos(wrappedPos.getX(), wrappedPos.getZ()); }
@@ -150,7 +233,8 @@ public class McObjectConverter
#endif
}
public static Direction Convert(EDhDirection lodDirection) { return directions[lodDirection.ordinal()]; }
public static EDhDirection Convert(Direction direction) { return lodDirections[direction.ordinal()]; }
//endregion
}
@@ -51,7 +51,10 @@ public class VersionConstants implements IVersionConstants
// it can't load client classes when running as a dedicated server,
// which was how we were dynamically accessing the MC version string
#if MC_VER == MC_1_16_5
#if MC_VER == MC_1_12_2
return "1.12.2";
#elif MC_VER == MC_1_16_5
return "1.16.5";
#elif MC_VER == MC_1_17_1
@@ -46,17 +46,30 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.client.multiplayer.ClientLevel;
#if MC_VER > MC_1_17_1
import net.minecraft.core.Holder;
#endif
#if MC_VER <= MC_1_12_2
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.Chunk;
#else
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
#endif
import java.io.IOException;
import java.util.HashSet;
/**
* This handles creating abstract wrapper objects.
@@ -172,7 +185,8 @@ public class WrapperFactory implements IWrapperFactory
*/
public IChunkWrapper createChunkWrapper(Object[] objectArray) throws ClassCastException
{
if (objectArray.length == 1 && objectArray[0] instanceof IChunkWrapper)
if (objectArray.length == 1
&& objectArray[0] instanceof IChunkWrapper)
{
try
{
@@ -186,42 +200,103 @@ public class WrapperFactory implements IWrapperFactory
}
}
//#if MC_VER <= MC_1_XX_X
else if (objectArray.length == 2)
else if (objectArray.length != 2)
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
// correct number of parameters from the API
// chunk
if (!(objectArray[0] instanceof ChunkAccess))
//=======//
// chunk //
//=======//
//region
boolean chunkClassCorrect;
#if MC_VER <= MC_1_12_2
chunkClassCorrect = (objectArray[0] instanceof Chunk);
#else
chunkClassCorrect = (objectArray[0] instanceof ChunkAccess);
#endif
if (!chunkClassCorrect)
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
#if MC_VER <= MC_1_12_2
Chunk chunk = (Chunk) objectArray[0];
#else
ChunkAccess chunk = (ChunkAccess) objectArray[0];
#endif
// level / light source
if (!(objectArray[1] instanceof Level))
//endregion
//=======//
// level //
//=======//
//region
boolean levelClassCorrect;
#if MC_VER <= MC_1_12_2
levelClassCorrect = (objectArray[1] instanceof World);
#else
levelClassCorrect = (objectArray[1] instanceof Level);
#endif
if (!levelClassCorrect)
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
// the level is needed for the DH level wrapper...
#if MC_VER <= MC_1_12_2
World level = (World) objectArray[1];
#else
Level level = (Level) objectArray[1];
#endif
//endregion
// level wrapper
ILevelWrapper levelWrapper = level.isClientSide()
? ClientLevelWrapper.getWrapper((ClientLevel)level)
: ServerLevelWrapper.getWrapper((ServerLevel)level);
//===============//
// level wrapper //
//===============//
//region
boolean isClientSide;
#if MC_VER <= MC_1_12_2
isClientSide = !level.isRemote;
#else
isClientSide = level.isClientSide();
#endif
ILevelWrapper levelWrapper;
if (isClientSide)
{
#if MC_VER <= MC_1_12_2
levelWrapper = ClientLevelWrapper.getWrapper((WorldClient)level);
#else
levelWrapper = ClientLevelWrapper.getWrapper((ClientLevel)level);
#endif
}
else
{
#if MC_VER <= MC_1_12_2
levelWrapper = ServerLevelWrapper.getWrapper((WorldServer)level);
#else
levelWrapper = ServerLevelWrapper.getWrapper((ServerLevel)level);
#endif
}
//endregion
return new ChunkWrapper(chunk, levelWrapper);
}
// incorrect number of parameters from the API
else
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
//#endif
}
/**
* Note: when this is updated for different MC versions,
* make sure you also update the documentation in {@link IDhApiWorldGenerator#generateChunks}.
@@ -230,13 +305,19 @@ public class WrapperFactory implements IWrapperFactory
{
String[] expectedClassNames;
//#if MC_VER <= MC_1_XX_X
#if MC_VER <= MC_1_12_2
expectedClassNames = new String[]
{
Chunk.class.getName(),
"[WorldClient] or [WorldServer]" // Classes are not referenced by names to avoid exception when one of them is missing
};
#else
expectedClassNames = new String[]
{
ChunkAccess.class.getName(),
"[ServerLevel] or [ClientLevel]" // Classes are not referenced by names to avoid exception when one of them is missing
};
//#endif
#endif
return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray);
}
@@ -322,19 +403,29 @@ public class WrapperFactory implements IWrapperFactory
//#if MC_VER <= MC_1_XX_X
if (objectArray.length != 1)
{
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
}
if (!(objectArray[0] instanceof BlockState))
boolean blockClassCorrect;
#if MC_VER <= MC_1_12_2
blockClassCorrect = (objectArray[0] instanceof IBlockState);
#else
blockClassCorrect = (objectArray[0] instanceof BlockState);
#endif
if (!blockClassCorrect)
{
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
}
#if MC_VER <= MC_1_12_2
IBlockState blockState = (IBlockState) objectArray[0];
return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper);
#else
BlockState blockState = (BlockState) objectArray[0];
return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper);
//#endif
#endif
}
/**
* Note: when this is updated for different MC versions,
@@ -344,7 +435,9 @@ public class WrapperFactory implements IWrapperFactory
{
String[] expectedClassNames;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
#if MC_VER <= MC_1_12_2
expectedClassNames = new String[] { IBlockState.class.getName() };
#elif MC_VER <= MC_1_17_1
expectedClassNames = new String[] { Biome.class.getName() };
#else
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
@@ -357,7 +450,6 @@ public class WrapperFactory implements IWrapperFactory
//================//
// helper methods //
//================//
@@ -1,5 +1,5 @@
package com.seibel.distanthorizons.common.wrappers.block;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.BlockBiomeWrapperPair;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
@@ -11,10 +11,14 @@ import com.seibel.distanthorizons.core.util.FullDataPointUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
#if MC_VER <= MC_1_12_2
import net.minecraft.world.biome.Biome;
#else
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
#endif
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
@@ -45,8 +49,6 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
#endif
private static final ConcurrentHashMap<BlockBiomeWrapperPair, Integer> COLOR_BY_BLOCK_BIOME_PAIR = new ConcurrentHashMap<>();
/** returned if the color cache is incomplete */
public static final int INVALID_COLOR = -1;
protected BiomeWrapper biomeWrapper;
@@ -98,7 +100,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
* Can be called by DH directly, skipping some of MC's logic
* to speed up tint getting slightly.
*
* @return {@link AbstractDhTintGetter#INVALID_COLOR} if any of the biomes needed for this position
* @return {@link ClientBlockStateColorCache#INVALID_COLOR} if any of the biomes needed for this position
* were not cached. In that case calling {@link AbstractDhTintGetter#getBlockTint(BlockPos, ColorResolver)}
* will need to be called by MC's ColorResolver so we can
* populate the color cache.
@@ -159,9 +161,9 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
int id = FullDataPointUtil.getId(dataPoint);
BiomeWrapper biomeWrapper = (BiomeWrapper) this.fullDataSource.mapping.getBiomeWrapper(id);
int color = this.tryGetClientBiomeColor(colorResolver, biomeWrapper);
if (color == INVALID_COLOR)
if (color == ClientBlockStateColorCache.INVALID_COLOR)
{
return INVALID_COLOR;
return ClientBlockStateColorCache.INVALID_COLOR;
}
@@ -210,7 +212,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
// no color resolver is present,
// the cache needs to be populated before
// we can use the fast path
return INVALID_COLOR;
return ClientBlockStateColorCache.INVALID_COLOR;
}
@@ -363,3 +365,4 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
}
#endif
@@ -28,12 +28,15 @@ import java.util.concurrent.ConcurrentMap;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.world.level.Level;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
#if MC_VER > MC_1_12_2
import net.minecraft.world.level.Level;
#endif
#if MC_VER <= MC_1_12_2
#elif MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
import net.minecraft.core.Registry;
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
import net.minecraft.core.Holder;
@@ -45,14 +48,21 @@ import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
#endif
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
import net.minecraft.util.ResourceLocation;
#elif MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
import net.minecraft.core.component.DataComponentMap;
#endif
#if MC_VER <= MC_1_12_2
import net.minecraft.world.biome.Biome;
#else
import net.minecraft.world.level.biome.Biome;
#endif
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.biome.Biomes;
@@ -108,8 +118,13 @@ public class BiomeWrapper implements IBiomeWrapper
//==============//
// constructors //
//==============//
//region
public static BiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
#if MC_VER < MC_1_18_2
public static BiomeWrapper getBiomeWrapper(Biome biome, ILevelWrapper levelWrapper)
#else
public static BiomeWrapper getBiomeWrapper(Holder<Biome> biome, ILevelWrapper levelWrapper)
#endif
{
if (biome == null)
{
@@ -129,7 +144,12 @@ public class BiomeWrapper implements IBiomeWrapper
return newWrapper;
}
}
private BiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
#if MC_VER < MC_1_18_2
private BiomeWrapper(Biome biome, ILevelWrapper levelWrapper)
#else
private BiomeWrapper(Holder<Biome> biome, ILevelWrapper levelWrapper)
#endif
{
this.biome = biome;
this.serialString = this.serialize(levelWrapper);
@@ -138,11 +158,14 @@ public class BiomeWrapper implements IBiomeWrapper
//LOGGER.trace("Created BiomeWrapper ["+this.serialString+"] for ["+biome+"]");
}
//endregion
//=========//
// methods //
//=========//
//region
@Override
public String getName()
@@ -188,11 +211,14 @@ public class BiomeWrapper implements IBiomeWrapper
@Override
public String toString() { return this.getSerialString(); }
//endregion
//=======================//
// serialization methods //
//=======================//
//region
public String serialize(ILevelWrapper levelWrapper)
{
@@ -219,8 +245,10 @@ public class BiomeWrapper implements IBiomeWrapper
// generate the serial string //
#if MC_VER > MC_1_12_2
Level level = (Level)levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
#endif
#if MC_VER <= MC_1_21_10
ResourceLocation resourceLocation;
@@ -228,7 +256,9 @@ public class BiomeWrapper implements IBiomeWrapper
Identifier resourceLocation;
#endif
#if MC_VER <= MC_1_17_1
#if MC_VER <= MC_1_12_2
resourceLocation = biome.getRegistryName();
#elif MC_VER <= MC_1_17_1
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
#elif MC_VER <= MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
@@ -293,11 +323,16 @@ public class BiomeWrapper implements IBiomeWrapper
{
try
{
#if MC_VER > MC_1_12_2
Level level = (Level) levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
#endif
#if MC_VER <= MC_1_12_2
BiomeDeserializeResult deserializeResult = deserializeBiome(resourceLocationString);
#else
BiomeDeserializeResult deserializeResult = deserializeBiome(resourceLocationString, registryAccess);
#endif
if (!deserializeResult.success)
@@ -325,7 +360,11 @@ public class BiomeWrapper implements IBiomeWrapper
}
}
#if MC_VER <= MC_1_12_2
public static BiomeDeserializeResult deserializeBiome(String resourceLocationString) throws IOException
#else
public static BiomeDeserializeResult deserializeBiome(String resourceLocationString, net.minecraft.core.RegistryAccess registryAccess) throws IOException
#endif
{
// parse the resource location
int separatorIndex = resourceLocationString.indexOf(":");
@@ -356,7 +395,10 @@ public class BiomeWrapper implements IBiomeWrapper
boolean success;
#if MC_VER <= MC_1_17_1
#if MC_VER <= MC_1_12_2
Biome biome = Biome.REGISTRY.getObject(resourceLocation);
success = (biome != null);
#elif MC_VER <= MC_1_17_1
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (biome != null);
#elif MC_VER <= MC_1_19_2
@@ -400,10 +442,14 @@ public class BiomeWrapper implements IBiomeWrapper
return new BiomeDeserializeResult(success, biome);
}
//endregion
//================//
// helper classes //
//================//
//region
public static class BiomeDeserializeResult
{
@@ -415,12 +461,19 @@ public class BiomeWrapper implements IBiomeWrapper
public final Holder<Biome> biome;
#endif
public BiomeDeserializeResult(boolean success, #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome)
#if MC_VER < MC_1_18_2
public BiomeDeserializeResult(boolean success, Biome biome)
#else
public BiomeDeserializeResult(boolean success, Holder<Biome> biome)
#endif
{
this.success = success;
this.biome = biome;
}
}
//endregion
}
@@ -32,12 +32,20 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
#if MC_VER <= MC_1_12_2
import net.minecraft.block.*;
import net.minecraft.init.Blocks;
import net.minecraft.block.state.IBlockState;
import net.minecraft.block.properties.IProperty;
import net.minecraftforge.fluids.IFluidBlock;
#else
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.BeaconBeamBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState;
#endif
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.awt.*;
@@ -61,7 +69,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.world.level.EmptyBlockGetter;
#else
#elif MC_VER > MC_1_12_2
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.Level;
import net.minecraft.core.BlockPos;
@@ -70,12 +78,15 @@ import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.core.Holder;
#endif
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
import net.minecraft.util.ResourceLocation;
#elif MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
#endif
public class BlockStateWrapper implements IBlockStateWrapper
{
/** example "minecraft:water" */
@@ -87,7 +98,11 @@ public class BlockStateWrapper implements IBlockStateWrapper
// must be defined before AIR, otherwise a null pointer will be thrown
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER <= MC_1_12_2
public static final ConcurrentHashMap<IBlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>();
#else
public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>();
#endif
public static final ConcurrentHashMap<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
public static final String AIR_STRING = "AIR";
@@ -114,7 +129,11 @@ public class BlockStateWrapper implements IBlockStateWrapper
// properties //
@Nullable
#if MC_VER <= MC_1_12_2
public final IBlockState blockState;
#else
public final BlockState blockState;
#endif
/** technically final, but since it requires a method call to generate it can't be marked as such */
private String serialString;
private final int hashCode;
@@ -139,27 +158,71 @@ public class BlockStateWrapper implements IBlockStateWrapper
// constructors //
//==============//
//region
/**
* Can be faster than {@link BlockStateWrapper#fromBlockState(BlockState, ILevelWrapper)}
* Can be faster than BlockStateWrapper#fromBlockState(BlockState, ILevelWrapper)
* in cases where the same block state is expected to be referenced multiple times.
*/
#if MC_VER <= MC_1_12_2
public static BlockStateWrapper fromBlockState(IBlockState blockState, ILevelWrapper levelWrapper, IBlockStateWrapper guess)
#else
public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper, IBlockStateWrapper guess)
#endif
{
BlockState guessBlockState = (guess == null || guess.isAir()) ? null : (BlockState) guess.getWrappedMcObject();
BlockState inputBlockState = (blockState == null || blockState.isAir()) ? null : blockState;
if (guess instanceof BlockStateWrapper
&& guessBlockState == inputBlockState)
{
return (BlockStateWrapper) guess;
}
else
if (guess == null)
{
return fromBlockState(blockState, levelWrapper);
}
// guess block state
BlockStateWrapper wrapperGuess = (BlockStateWrapper) guess;
#if MC_VER <= MC_1_12_2
IBlockState guessBlockState;
#else
BlockState guessBlockState;
#endif
if(isAir(wrapperGuess.blockState))
{
guessBlockState = null;
}
else
{
#if MC_VER <= MC_1_12_2
guessBlockState = (IBlockState) guess.getWrappedMcObject();
#else
guessBlockState = (BlockState) guess.getWrappedMcObject();
#endif
}
// input block state
#if MC_VER <= MC_1_12_2
IBlockState inputBlockState;
#else
BlockState inputBlockState;
#endif
if (isAir(blockState))
{
inputBlockState = null;
}
else
{
inputBlockState = blockState;
}
if (guessBlockState == inputBlockState)
{
return (BlockStateWrapper) guess;
}
return fromBlockState(blockState, levelWrapper);
}
#if MC_VER <= MC_1_12_2
public static BlockStateWrapper fromBlockState(@Nullable IBlockState blockState, ILevelWrapper levelWrapper)
#else
public static BlockStateWrapper fromBlockState(@Nullable BlockState blockState, ILevelWrapper levelWrapper)
#endif
{
// air is a special case
if (isAir(blockState))
@@ -167,6 +230,27 @@ public class BlockStateWrapper implements IBlockStateWrapper
return AIR;
}
// pooling wrappers significantly improves chunk->LOD processing speed
// and also reduces GC pressure
BlockStateWrapper existingWrapper = WRAPPER_BY_BLOCK_STATE.get(blockState);
if (existingWrapper != null)
{
return existingWrapper;
}
// synchronized so the API event only fires once per block
synchronized (WRAPPER_BY_BLOCK_STATE)
{
// if another thread already finished this block, use that wrapper
existingWrapper = WRAPPER_BY_BLOCK_STATE.get(blockState);
if (existingWrapper != null)
{
return existingWrapper;
}
// create a wrapper specifically for the API event to use
BlockStateWrapper apiWrapper = new BlockStateWrapper(blockState, levelWrapper, null);
DhApiBlockStateWrapperCreatedEvent.EventParam eventParam = new DhApiBlockStateWrapperCreatedEvent.EventParam(apiWrapper);
@@ -174,17 +258,25 @@ public class BlockStateWrapper implements IBlockStateWrapper
if (!eventParam.getOverridesSet())
{
// no changes needed, use the existing object
// no API changes needed, use the existing object
WRAPPER_BY_BLOCK_STATE.putIfAbsent(blockState, apiWrapper);
return apiWrapper;
}
else
{
// create a new wrapper using whatever overrides the API user set
BlockStateWrapper returnWrapper = new BlockStateWrapper(blockState, levelWrapper, eventParam);
WRAPPER_BY_BLOCK_STATE.putIfAbsent(blockState, returnWrapper);
return returnWrapper;
}
private BlockStateWrapper(
@Nullable BlockState blockState, ILevelWrapper levelWrapper,
@Nullable DhApiBlockStateWrapperCreatedEvent.EventParam overrideEventParam)
}
}
#if MC_VER <= MC_1_12_2
private BlockStateWrapper(@Nullable IBlockState blockState, ILevelWrapper levelWrapper, @Nullable DhApiBlockStateWrapperCreatedEvent.EventParam overrideEventParam)
#else
private BlockStateWrapper(@Nullable BlockState blockState, ILevelWrapper levelWrapper, @Nullable DhApiBlockStateWrapperCreatedEvent.EventParam overrideEventParam)
#endif
{
this.blockState = blockState;
this.serialString = serialize(blockState, levelWrapper);
@@ -202,7 +294,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
else
{
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
this.isLiquid = this.blockState.getMaterial().isLiquid() || this.blockState.getBlock() instanceof IFluidBlock;
#elif MC_VER < MC_1_20_1
this.isLiquid = this.blockState.getMaterial().isLiquid() || !this.blockState.getFluidState().isEmpty();
#else
this.isLiquid = !this.blockState.getFluidState().isEmpty();
@@ -306,9 +400,21 @@ public class BlockStateWrapper implements IBlockStateWrapper
&& !this.isBeaconBlock)
{
Block block = this.blockState.getBlock();
int colorInt;
#if MC_VER <= MC_1_12_2
if (block instanceof BlockStainedGlass)
{
colorInt = blockState.getValue(BlockStainedGlass.COLOR).getColorValue();
beaconTintColor = ColorUtil.toColorObjRGB(colorInt);
}
else if (block instanceof BlockStainedGlassPane)
{
colorInt = blockState.getValue(BlockStainedGlassPane.COLOR).getColorValue();
beaconTintColor = ColorUtil.toColorObjRGB(colorInt);
}
#else
if (block instanceof BeaconBeamBlock)
{
int colorInt;
#if MC_VER <= MC_1_19_4
colorInt = ((BeaconBeamBlock) block).getColor().getMaterialColor().col;
#else
@@ -317,6 +423,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
beaconTintColor = ColorUtil.toColorObjRGB(colorInt);
}
#endif
}
this.beaconTintColor = beaconTintColor;
@@ -367,7 +474,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
{
int mcColor = 0;
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
mcColor = this.blockState.getMaterial().getMaterialMapColor().colorValue;
#elif MC_VER < MC_1_20_1
mcColor = this.blockState.getMaterial().getColor().col;
#else
mcColor = this.blockState.getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).col;
@@ -405,19 +514,31 @@ public class BlockStateWrapper implements IBlockStateWrapper
// static constructor helpers //
//region
private static EDhApiBlockMaterial calculateEDhApiBlockMaterialId(
@Nullable BlockState blockState,
String lowercaseSerialString,
boolean isLiquid
)
#if MC_VER <= MC_1_12_2
private static EDhApiBlockMaterial calculateEDhApiBlockMaterialId(@Nullable IBlockState blockState, String lowercaseSerialString, boolean isLiquid)
#else
private static EDhApiBlockMaterial calculateEDhApiBlockMaterialId(@Nullable BlockState blockState, String lowercaseSerialString, boolean isLiquid)
#endif
{
if (blockState == null)
if (isAir(blockState))
{
return EDhApiBlockMaterial.AIR;
}
if (blockState.is(BlockTags.LEAVES)
//========//
// leaves //
//========//
//region
boolean isLeafBlock;
#if MC_VER <= MC_1_12_2
isLeafBlock = blockState.getBlock() instanceof BlockLeaves;
#else
isLeafBlock = blockState.is(BlockTags.LEAVES);
#endif
if (isLeafBlock
|| lowercaseSerialString.contains("bamboo")
|| lowercaseSerialString.contains("cactus")
|| lowercaseSerialString.contains("chorus_flower")
@@ -426,44 +547,139 @@ public class BlockStateWrapper implements IBlockStateWrapper
{
return EDhApiBlockMaterial.LEAVES;
}
else if (blockState.is(Blocks.LAVA))
//endregion
//======//
// lava //
//======//
//region
boolean isLavaBlock;
#if MC_VER <= MC_1_12_2
isLavaBlock = blockState.getBlock() == Blocks.LAVA || blockState.getBlock() == Blocks.FLOWING_LAVA;
#else
isLavaBlock = blockState.is(Blocks.LAVA);
#endif
if (isLavaBlock)
{
return EDhApiBlockMaterial.LAVA;
}
else if (isLiquid
|| blockState.is(Blocks.WATER))
//endregion
//=======//
// water //
//=======//
//region
boolean isWaterBlock;
#if MC_VER <= MC_1_12_2
isWaterBlock = blockState.getBlock() == Blocks.WATER || blockState.getBlock() == Blocks.FLOWING_WATER;
#else
isWaterBlock = blockState.is(Blocks.WATER);
#endif
if (isLiquid
|| isWaterBlock)
{
return EDhApiBlockMaterial.WATER;
}
else if (blockState.getSoundType() == SoundType.WOOD
|| lowercaseSerialString.contains("root")
#if MC_VER >= MC_1_19_4
|| blockState.getSoundType() == SoundType.CHERRY_WOOD
//endregion
//======//
// wood //
//======//
//region
boolean isWoodSoundingBlock;
#if MC_VER <= MC_1_12_2
isWoodSoundingBlock = blockState.getBlock().getSoundType() == SoundType.WOOD;
#else
isWoodSoundingBlock = blockState.getSoundType() == SoundType.WOOD;
#endif
boolean isCherryWood;
#if MC_VER <= MC_1_19_2
isCherryWood = false;
#else
isCherryWood = blockState.getSoundType() == SoundType.CHERRY_WOOD;
#endif
if (isWoodSoundingBlock
|| lowercaseSerialString.contains("root")
|| isCherryWood
)
{
return EDhApiBlockMaterial.WOOD;
}
else if (blockState.getSoundType() == SoundType.METAL
#if MC_VER >= MC_1_19_2
|| blockState.getSoundType() == SoundType.COPPER
//endregion
//=======//
// metal //
//=======//
//region
boolean isMetalSoundingBlock;
#if MC_VER <= MC_1_12_2
isMetalSoundingBlock = blockState.getBlock().getSoundType() == SoundType.METAL;
#else
isMetalSoundingBlock = blockState.getSoundType() == SoundType.METAL;
#endif
#if MC_VER >= MC_1_20_4
boolean isCopperSounding;
#if MC_VER <= MC_1_18_2
isCopperSounding = false;
#elif MC_VER <= MC_1_20_2
isCopperSounding = blockState.getSoundType() == SoundType.COPPER;
#else
isCopperSounding
= blockState.getSoundType() == SoundType.COPPER
|| blockState.getSoundType() == SoundType.COPPER_BULB
|| blockState.getSoundType() == SoundType.COPPER_GRATE
|| blockState.getSoundType() == SoundType.COPPER_GRATE;
#endif
)
if (isMetalSoundingBlock
|| isCopperSounding)
{
return EDhApiBlockMaterial.METAL;
}
else if (
lowercaseSerialString.contains("grass_block")
//endregion
//=======//
// grass //
//=======//
//region
if (lowercaseSerialString.contains("grass_block")
|| lowercaseSerialString.contains("grass_slab")
)
{
return EDhApiBlockMaterial.GRASS;
}
else if (
//endregion
//======//
// dirt //
//======//
//region
if (
lowercaseSerialString.contains("dirt")
|| lowercaseSerialString.contains("gravel")
|| lowercaseSerialString.contains("mud")
@@ -473,8 +689,18 @@ public class BlockStateWrapper implements IBlockStateWrapper
{
return EDhApiBlockMaterial.DIRT;
}
//endregion
//===========//
// deepslate //
//===========//
//region
#if MC_VER >= MC_1_17_1
else if (blockState.getSoundType() == SoundType.DEEPSLATE
if (blockState.getSoundType() == SoundType.DEEPSLATE
|| blockState.getSoundType() == SoundType.DEEPSLATE_BRICKS
|| blockState.getSoundType() == SoundType.DEEPSLATE_TILES
|| blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE
@@ -483,41 +709,75 @@ public class BlockStateWrapper implements IBlockStateWrapper
return EDhApiBlockMaterial.DEEPSLATE;
}
#endif
else if (lowercaseSerialString.contains("snow"))
{
return EDhApiBlockMaterial.SNOW;
}
else if (lowercaseSerialString.contains("sand"))
{
return EDhApiBlockMaterial.SAND;
}
else if (lowercaseSerialString.contains("terracotta"))
{
return EDhApiBlockMaterial.TERRACOTTA;
}
else if (blockState.is(BlockTags.BASE_STONE_NETHER))
//endregion
//============//
// netherrack //
//============//
//region
boolean isNetherRack;
#if MC_VER <= MC_1_12_2
isNetherRack = blockState.getBlock() == Blocks.NETHERRACK;
#else
isNetherRack = blockState.is(BlockTags.BASE_STONE_NETHER);
#endif
if (isNetherRack)
{
return EDhApiBlockMaterial.NETHER_STONE;
}
else if (lowercaseSerialString.contains("stone")
//endregion
//=============//
// misc/simple //
//=============//
//region
if (lowercaseSerialString.contains("snow"))
{
return EDhApiBlockMaterial.SNOW;
}
if (lowercaseSerialString.contains("sand"))
{
return EDhApiBlockMaterial.SAND;
}
if (lowercaseSerialString.contains("terracotta"))
{
return EDhApiBlockMaterial.TERRACOTTA;
}
if (lowercaseSerialString.contains("stone")
|| lowercaseSerialString.contains("ore"))
{
return EDhApiBlockMaterial.STONE;
}
else if (blockState.getLightEmission() > 0)
if (getLightEmission(blockState) > 0)
{
return EDhApiBlockMaterial.ILLUMINATED;
}
else
{
//endregion
return EDhApiBlockMaterial.UNKNOWN;
}
}
private static int calculateOpacity(
@Nullable BlockState blockState,
boolean isAir, boolean isLiquid
)
#if MC_VER <= MC_1_12_2
private static int calculateOpacity(@Nullable IBlockState blockState, boolean isAir, boolean isLiquid)
#else
private static int calculateOpacity(@Nullable BlockState blockState, boolean isAir, boolean isLiquid)
#endif
{
// get block properties (defaults to the values used by air)
boolean canOcclude = getCanOcclude(blockState);
@@ -556,24 +816,40 @@ public class BlockStateWrapper implements IBlockStateWrapper
return opacity;
}
#if MC_VER <= MC_1_12_2
private static boolean getCanOcclude(@Nullable IBlockState blockState)
#else
private static boolean getCanOcclude(@Nullable BlockState blockState)
#endif
{
// defaults to the value used by air
boolean canOcclude = false;
if (blockState != null)
{
#if MC_VER <= MC_1_12_2
canOcclude = blockState.getMaterial().isSolid();
#else
canOcclude = blockState.canOcclude();
#endif
}
return canOcclude;
}
#if MC_VER <= MC_1_12_2
private static boolean getPropagatesSkyLightDown(@Nullable IBlockState blockState)
#else
private static boolean getPropagatesSkyLightDown(@Nullable BlockState blockState)
#endif
{
// defaults to the value used by air
boolean propagatesSkyLightDown = true;
if (blockState != null)
{
#if MC_VER < MC_1_21_3
#if MC_VER <= MC_1_12_2
propagatesSkyLightDown = blockState.getBlock().getLightOpacity(blockState) == 0;
#elif MC_VER < MC_1_21_3
propagatesSkyLightDown = blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
#else
propagatesSkyLightDown = blockState.propagatesSkylightDown();
@@ -633,8 +909,10 @@ public class BlockStateWrapper implements IBlockStateWrapper
return waterSurfaceReplacementBlocks;
}
ObjectOpenHashSet<String> baseIgnoredBlock = new ObjectOpenHashSet<>();
waterSurfaceReplacementBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.waterSurfaceBlockReplacementCsv, baseIgnoredBlock, levelWrapper);
ObjectOpenHashSet<String> baseIgnoredBlockResourceSet = new ObjectOpenHashSet<>();
waterSurfaceReplacementBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.waterSurfaceBlockReplacementCsv, baseIgnoredBlockResourceSet, levelWrapper);
waterSubsurfaceReplacementBlocks.remove(AIR);
return waterSurfaceReplacementBlocks;
}
public static ObjectOpenHashSet<IBlockStateWrapper> getWaterSubsurfaceReplacementBlocks(ILevelWrapper levelWrapper)
@@ -645,8 +923,12 @@ public class BlockStateWrapper implements IBlockStateWrapper
return waterSubsurfaceReplacementBlocks;
}
ObjectOpenHashSet<String> baseIgnoredBlock = new ObjectOpenHashSet<>();
waterSubsurfaceReplacementBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.waterSubSurfaceBlockReplacementCsv, baseIgnoredBlock, levelWrapper);
ObjectOpenHashSet<String> baseIgnoredBlockResourceSet = new ObjectOpenHashSet<>();
waterSubsurfaceReplacementBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.waterSubSurfaceBlockReplacementCsv, baseIgnoredBlockResourceSet, levelWrapper);
// air will be present if any invalid resource locations are present
// but we don't want to replace air with water, that'll cause monoliths
waterSubsurfaceReplacementBlocks.remove(AIR);
return waterSubsurfaceReplacementBlocks;
}
public static IBlockStateWrapper getWaterBlockStateWrapper(ILevelWrapper levelWrapper)
@@ -714,8 +996,17 @@ public class BlockStateWrapper implements IBlockStateWrapper
if (defaultBlockStateToIgnore != AIR)
{
// add all possible blockstates (to account for light blocks with different light values and such)
#if MC_VER <= MC_1_12_2
List<IBlockState> blockStatesToIgnore = defaultBlockStateToIgnore.blockState.getBlock().getBlockState().getValidStates();
#else
List<BlockState> blockStatesToIgnore = defaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
#endif
#if MC_VER <= MC_1_12_2
for (IBlockState blockState : blockStatesToIgnore)
#else
for (BlockState blockState : blockStatesToIgnore)
#endif
{
BlockStateWrapper newBlockToIgnore = fromBlockState(blockState, levelWrapper);
blockStateWrappers.add(newBlockToIgnore);
@@ -762,7 +1053,25 @@ public class BlockStateWrapper implements IBlockStateWrapper
public int getOpacity() { return this.opacity; }
@Override
public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; }
public int getLightEmission() { return getLightEmission(this.blockState); }
#if MC_VER <= MC_1_12_2
public static int getLightEmission(IBlockState blockState)
#else
public static int getLightEmission(BlockState blockState)
#endif
{
if (blockState == null)
{
return 0;
}
#if MC_VER <= MC_1_12_2
return blockState.getLightValue();
#else
return blockState.getLightEmission();
#endif
}
@Override
public String getSerialString() { return this.serialString; }
@@ -772,7 +1081,23 @@ public class BlockStateWrapper implements IBlockStateWrapper
@Override
public boolean isAir() { return isAir(this.blockState); }
public static boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); }
#if MC_VER <= MC_1_12_2
public static boolean isAir(IBlockState blockState)
#else
public static boolean isAir(BlockState blockState)
#endif
{
if (blockState == null)
{
return true;
}
#if MC_VER <= MC_1_12_2
return blockState.getBlock() == Blocks.AIR;
#else
return blockState.isAir();
#endif
}
@Override
public boolean isSolid() { return this.isSolid; }
@@ -806,7 +1131,11 @@ public class BlockStateWrapper implements IBlockStateWrapper
//=======================//
//region
#if MC_VER <= MC_1_12_2
private static String serialize(IBlockState blockState, ILevelWrapper levelWrapper)
#else
private static String serialize(BlockState blockState, ILevelWrapper levelWrapper)
#endif
{
if (blockState == null)
{
@@ -828,7 +1157,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
Identifier resourceLocation;
#endif
#if MC_VER <= MC_1_17_1
#if MC_VER <= MC_1_12_2
resourceLocation = blockState.getBlock().getRegistryName();
#elif MC_VER <= MC_1_17_1
resourceLocation = Registry.BLOCK.getKey(blockState.getBlock());
#elif MC_VER <= MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(blockState.getBlock());
@@ -927,7 +1258,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
#endif
Block block;
#if MC_VER <= MC_1_17_1
#if MC_VER <= MC_1_12_2
block = Block.REGISTRY.getObject(resourceLocation);
#elif MC_VER <= MC_1_17_1
block = Registry.BLOCK.get(resourceLocation);
#elif MC_VER <= MC_1_19_2
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
@@ -956,11 +1289,24 @@ public class BlockStateWrapper implements IBlockStateWrapper
// attempt to find the blockstate from all possibilities
#if MC_VER <= MC_1_12_2
IBlockState foundState = null;
#else
BlockState foundState = null;
#endif
if (blockStatePropertiesString != null)
{
#if MC_VER <= MC_1_12_2
List<IBlockState> possibleStateList = block.getBlockState().getValidStates();
#else
List<BlockState> possibleStateList = block.getStateDefinition().getPossibleStates();
#endif
#if MC_VER <= MC_1_12_2
for (IBlockState possibleState : possibleStateList)
#else
for (BlockState possibleState : possibleStateList)
#endif
{
String possibleStatePropertiesString = serializeBlockStateProperties(possibleState);
if (possibleStatePropertiesString.equals(blockStatePropertiesString))
@@ -984,7 +1330,12 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
}
#if MC_VER <= MC_1_12_2
foundState = block.getDefaultState();
#else
foundState = block.defaultBlockState();
#endif
}
foundWrapper = fromBlockState(foundState, levelWrapper);
@@ -1000,30 +1351,53 @@ public class BlockStateWrapper implements IBlockStateWrapper
// put if absent in case two threads deserialize at the same time
// unfortunately we can't put everything in a computeIfAbsent() since we also throw exceptions
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
if (foundWrapper != AIR)
{
WRAPPER_BY_BLOCK_STATE.putIfAbsent(foundWrapper.blockState, foundWrapper);
}
}
}
/** used to compare and save BlockStates based on their properties */
#if MC_VER <= MC_1_12_2
private static String serializeBlockStateProperties(IBlockState blockState)
#else
private static String serializeBlockStateProperties(BlockState blockState)
#endif
{
// get the property list for this block (doesn't contain this block state's values, just the names and possible values)
java.util.Collection<net.minecraft.world.level.block.state.properties.Property<?>> blockPropertyCollection = blockState.getProperties();
#if MC_VER <= MC_1_12_2
java.util.Collection<IProperty<?>> blockPropertyCollection = blockState.getPropertyKeys();
List<IProperty<?>> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection);
#else
java.util.Collection<net.minecraft.world.level.block.state.properties.Property<?>> blockPropertyCollection = blockState.getProperties();;
List<net.minecraft.world.level.block.state.properties.Property<?>> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection);
#endif
// alphabetically sort the list so they are always in the same order
List<net.minecraft.world.level.block.state.properties.Property<?>> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection);
sortedBlockPropteryList.sort((a, b) -> a.getName().compareTo(b.getName()));
StringBuilder stringBuilder = new StringBuilder();
#if MC_VER <= MC_1_12_2
for (IProperty<?> property : sortedBlockPropteryList)
#else
for (net.minecraft.world.level.block.state.properties.Property<?> property : sortedBlockPropteryList)
#endif
{
String propertyName = property.getName();
String value = "NULL";
#if MC_VER <= MC_1_12_2
value = blockState.getValue(property).toString();
#else
if (blockState.hasProperty(property))
{
value = blockState.getValue(property).toString();
}
#endif
stringBuilder.append("{");
stringBuilder.append(propertyName).append(RESOURCE_LOCATION_SEPARATOR).append(value);
@@ -32,22 +32,31 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
#if MC_VER <= MC_1_12_2
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.EnumBlockRenderType;
import net.minecraft.util.EnumFacing;
import net.minecraft.block.BlockRotatedPillar;
import net.minecraft.block.*;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.renderer.color.BlockColors;
import net.minecraft.util.math.BlockPos;
#else
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockState;
import com.seibel.distanthorizons.core.logging.DhLogger;
import net.minecraft.world.level.block.state.properties.SlabType;
#endif
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
#if MC_VER >= MC_1_19_2
import net.minecraft.util.RandomSource;
#else
import java.util.Random;
#endif
#if MC_VER < MC_1_21_5
@@ -64,7 +73,7 @@ import net.minecraft.client.color.block.BlockTintSource;
/**
* This stores and calculates the colors
* the given {@link BlockState} should have based
* the given BlockState should have based
* on the given {@link IClientLevelWrapper}.
*
* @see ColorUtil
@@ -73,10 +82,23 @@ public class ClientBlockStateColorCache
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER <= MC_1_12_2
private static final Minecraft MC = Minecraft.getMinecraft();
#else
private static final Minecraft MC = Minecraft.getInstance();
#endif
#if MC_VER <= MC_1_12_2
#else
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
#endif
#if MC_VER <= MC_1_12_2
private static final HashSet<IBlockState> BROKEN_BLOCK_STATES = new HashSet<>();
#else
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
#endif
/**
* Methods using MC's "RandomSource" object aren't thread safe <br>
@@ -88,8 +110,22 @@ public class ClientBlockStateColorCache
*/
private static final ReentrantLock RESOLVE_LOCK = new ReentrantLock();
public static final int INVALID_COLOR = -1;
/** This is the order each direction on a block is processed when attempting to get the texture/color */
#if MC_VER <= MC_1_12_2
private static final @Nullable EnumFacing[] COLOR_RESOLUTION_DIRECTION_ORDER =
{
EnumFacing.UP,
null, // null represents "unculled" faces, IE the top of farmland
EnumFacing.NORTH,
EnumFacing.EAST,
EnumFacing.WEST,
EnumFacing.SOUTH,
EnumFacing.DOWN
};
#else
private static final @Nullable Direction[] COLOR_RESOLUTION_DIRECTION_ORDER =
{
Direction.UP,
@@ -100,6 +136,7 @@ public class ClientBlockStateColorCache
Direction.SOUTH,
Direction.DOWN
};
#endif
private static final int FLOWER_COLOR_SCALE = 5;
@@ -113,7 +150,11 @@ public class ClientBlockStateColorCache
#endif
private final IClientLevelWrapper clientLevelWrapper;
#if MC_VER <= MC_1_12_2
private final IBlockState blockState;
#else
private final BlockState blockState;
#endif
private final BlockStateWrapper blockStateWrapper;
private boolean isColorResolved = false;
@@ -191,8 +232,10 @@ public class ClientBlockStateColorCache
};
// these are threadlocals since AbstractDhTintGetter use local variables to handle color queries
#if MC_VER > MC_1_12_2
private static final ThreadLocal<TintWithoutLevelOverrider> TintWithoutLevelOverrideGetter = ThreadLocal.withInitial(TintWithoutLevelOverrider::new);
private static final ThreadLocal<TintGetterOverride> TintOverrideGetter = ThreadLocal.withInitial(TintGetterOverride::new);
#endif
private static final ThreadLocal<DhApiBlockColorOverrideEvent.EventParam> ColorOverrideEventParamGetter = ThreadLocal.withInitial(DhApiBlockColorOverrideEvent.EventParam::new);
//endregion
@@ -204,7 +247,11 @@ public class ClientBlockStateColorCache
//=============//
//region
#if MC_VER <= MC_1_12_2
public ClientBlockStateColorCache(IBlockState blockState, IClientLevelWrapper clientLevelWrapper)
#else
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper clientLevelWrapper)
#endif
{
this.blockState = blockState;
this.blockStateWrapper = BlockStateWrapper.fromBlockState(blockState, clientLevelWrapper);
@@ -220,6 +267,7 @@ public class ClientBlockStateColorCache
//===================//
// color calculation //
//===================//
//region
private void resolveColors()
{
@@ -233,17 +281,44 @@ public class ClientBlockStateColorCache
// getQuads() isn't thread safe so we need to put this logic in a lock
RESOLVE_LOCK.lock();
if (this.blockState.getFluidState().isEmpty())
#if MC_VER <= MC_1_12_2
if (this.blockState.getRenderType() == EnumBlockRenderType.ENTITYBLOCK_ANIMATED)
{
this.needPostTinting = false;
this.tintIndex = 0;
this.baseColor = ColorUtil.argbToInt(255,
this.blockStateWrapper.getMapColor().getRed(),
this.blockStateWrapper.getMapColor().getGreen(),
this.blockStateWrapper.getMapColor().getBlue());
this.isColorResolved = true;
return;
}
#endif
if (!this.blockStateWrapper.isLiquid())
{
// look for the first non-empty direction
List<BakedQuad> quads = null;
for (Direction direction : COLOR_RESOLUTION_DIRECTION_ORDER)
#if MC_VER <= MC_1_12_2
EnumFacing direction;
#else
Direction direction;
#endif
for (int i = 0; i < COLOR_RESOLUTION_DIRECTION_ORDER.length; i++)
{
direction = COLOR_RESOLUTION_DIRECTION_ORDER[i];
quads = this.getQuadsForDirection(direction);
if (quads != null && !quads.isEmpty()
&& !(
#if MC_VER <= MC_1_12_2
this.blockState.getBlock() instanceof BlockRotatedPillar
&& direction == EnumFacing.UP
#else
this.blockState.getBlock() instanceof RotatedPillarBlock
&& direction == Direction.UP
#endif
)
)
{
@@ -259,10 +334,14 @@ public class ClientBlockStateColorCache
if (quads != null
&& !quads.isEmpty()
&& quads.get(0) != null)
{
try
{
BakedQuad firstQuad = quads.get(0);
#if MC_VER <= MC_1_21_11
#if MC_VER <= MC_1_12_2
this.needPostTinting = firstQuad.hasTintIndex();
#elif MC_VER <= MC_1_21_11
this.needPostTinting = firstQuad.isTinted();
#else
this.needPostTinting = firstQuad.materialInfo().isTinted();
@@ -276,7 +355,7 @@ public class ClientBlockStateColorCache
this.tintIndex = firstQuad.materialInfo().tintIndex();
#endif
#if MC_VER < MC_1_17_1
#if MC_VER < MC_1_17_1 && MC_VER > MC_1_12_2
this.baseColor = calculateColorFromTexture(
firstQuad.sprite,
EColorMode.getColorMode(this.blockState.getBlock()));
@@ -294,6 +373,20 @@ public class ClientBlockStateColorCache
EColorMode.getColorMode(this.blockState.getBlock()));
#endif
}
catch (Exception e)
{
// Shouldn't normally happen, but there was at least
// one report of MC's texture being un-loaded
// which prevented us from getting the texture.
// So we should have some basic backup logic.
LOGGER.warn("Failed to get texture color for block ["+this.blockStateWrapper.getSerialString()+"] due to: ["+e.getMessage()+"], falling back to particle color.");
this.needPostTinting = false;
this.tintIndex = 0;
this.baseColor = this.getParticleIconColor();
}
}
else
{
// Backup method.
@@ -322,20 +415,43 @@ public class ClientBlockStateColorCache
@Nullable
private List<BakedQuad> getUnculledQuads() { return this.getQuadsForDirection(null); }
@Nullable
#if MC_VER <= MC_1_12_2
private List<BakedQuad> getQuadsForDirection(@Nullable EnumFacing direction)
#else
private List<BakedQuad> getQuadsForDirection(@Nullable Direction direction)
#endif
{
#if MC_VER <= MC_1_12_2
IBlockState effectiveBlockState = this.blockState;
#else
BlockState effectiveBlockState = this.blockState;
#endif
// if this block is a slab, use it's double variant so we can get the top face,
// otherwise the color will use the side, which isn't as accurate
#if MC_VER <= MC_1_12_2
if (this.blockState.getBlock() instanceof BlockSlab && !((BlockSlab) this.blockState.getBlock()).isDouble())
{
effectiveBlockState = this.blockState.withProperty(BlockSlab.HALF, BlockSlab.EnumBlockHalf.TOP);
}
#else
if (this.blockState.getBlock() instanceof SlabBlock)
{
effectiveBlockState = this.blockState.setValue( SlabBlock.TYPE, SlabType.DOUBLE );
}
#endif
List<BakedQuad> quads;
#if MC_VER < MC_1_21_5
#if MC_VER <= MC_1_12_2
try {
quads = MC.getBlockRendererDispatcher().getModelForState(effectiveBlockState).getQuads(effectiveBlockState, direction, RANDOM.nextLong());
}
catch (Exception e)
{
quads = Collections.emptyList();
}
#elif MC_VER < MC_1_21_5
quads = MC.getModelManager().getBlockModelShaper().
getBlockModel(effectiveBlockState).getQuads(effectiveBlockState, direction, RANDOM);
#elif MC_VER <= MC_1_21_11
@@ -392,29 +508,29 @@ public class ClientBlockStateColorCache
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
#if MC_VER <= MC_1_12_2
int b = (tempColor & 0x000000FF);
int g = (tempColor & 0x0000FF00) >>> 8;
int r = (tempColor & 0x00FF0000) >>> 16;
int a = (tempColor & 0xFF000000) >>> 24;
#else
int r = (tempColor & 0x000000FF);
int g = (tempColor & 0x0000FF00) >>> 8;
int b = (tempColor & 0x00FF0000) >>> 16;
int a = (tempColor & 0xFF000000) >>> 24;
#endif
int scale = 1;
if (colorMode == EColorMode.Leaves)
{
//switch (//FIXME add config option)
// case BLACK:
// a = 255; //simulate black background of fast leaves
// break;
// case IGNORE:
if (a == 0) {
if (a == 0)
{
continue; //same long grass
}
else
{
a = 255; //just in case there are semi transparent pixels
}
// break;
// case TRANSPARENT:
// break; //do nothing, let it count towards transparency
}
else if (a == 0 && colorMode != EColorMode.Glass)
{
@@ -460,7 +576,9 @@ public class ClientBlockStateColorCache
}
private static int getTextureWidth(TextureAtlasSprite texture)
{
#if MC_VER < MC_1_19_4
#if MC_VER <= MC_1_12_2
return texture.getIconWidth();
#elif MC_VER < MC_1_19_4
return texture.getWidth();
#else
return texture.contents().width();
@@ -468,7 +586,9 @@ public class ClientBlockStateColorCache
}
private static int getTextureHeight(TextureAtlasSprite texture)
{
#if MC_VER < MC_1_19_4
#if MC_VER <= MC_1_12_2
return texture.getIconHeight();
#elif MC_VER < MC_1_19_4
return texture.getHeight();
#else
return texture.contents().height();
@@ -502,7 +622,9 @@ public class ClientBlockStateColorCache
private int getParticleIconColor()
{
return calculateColorFromTexture(
#if MC_VER <= MC_1_21_11
#if MC_VER <= MC_1_12_2
Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getTexture(this.blockState),
#elif MC_VER <= MC_1_21_11
Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
#else
Minecraft.getInstance().getModelManager().getBlockStateModelSet().get(this.blockState).particleMaterial().sprite(),
@@ -510,16 +632,20 @@ public class ClientBlockStateColorCache
EColorMode.getColorMode(this.blockState.getBlock()));
}
//endregion
//===============//
// public getter //
//===============//
//region
public int getColor(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, DhBlockPos blockPos)
{
// only get the tint if the block needs to be tinted
int tintColor = AbstractDhTintGetter.INVALID_COLOR;
int tintColor = ClientBlockStateColorCache.INVALID_COLOR;
if (this.needPostTinting)
{
// don't try tinting blocks that don't support our method of tint getting
@@ -532,6 +658,40 @@ public class ClientBlockStateColorCache
// attempt to get the tint
try
{
#if MC_VER <= MC_1_12_2
// 1.12.2 doesn't have BlockAndTintGetter -> get tintColor from biome
WorldClient world = (WorldClient) this.clientLevelWrapper.getWrappedMcObject();
BlockPos mcPos = new BlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
Block block = this.blockState.getBlock();
if (block instanceof BlockGrass
|| block instanceof BlockBush)
{
tintColor = biomeWrapper.biome.getGrassColorAtPos(mcPos);
}
else if (block instanceof BlockLeaves)
{
tintColor = biomeWrapper.biome.getFoliageColorAtPos(mcPos);
}
else if (block instanceof BlockLiquid) // We don't want lava to fall into the else block
{
if(block == Blocks.WATER
|| block == Blocks.FLOWING_WATER)
{
tintColor = biomeWrapper.biome.getWaterColor();
}
}
else
{
BlockColors blockColors = Minecraft.getMinecraft().getBlockColors();
tintColor = blockColors.colorMultiplier(blockState, world, mcPos, this.tintIndex);
if (tintColor == ClientBlockStateColorCache.INVALID_COLOR)
{
tintColor = blockColors.getColor(blockState, world, mcPos);
}
}
#else
// try to use the fast tint getter logic first
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{
@@ -542,7 +702,7 @@ public class ClientBlockStateColorCache
// try using DH's cached tint values first if possible
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
if (tintColor == ClientBlockStateColorCache.INVALID_COLOR)
{
// one or more tint values weren't calculated,
// we need MC's color resolver
@@ -564,18 +724,12 @@ public class ClientBlockStateColorCache
{
BlockPos mcPos = McObjectConverter.Convert(blockPos);
tintColor = tintSource.colorInWorld(this.blockState, tintOverride, mcPos);
if (tintColor == -1)
if (tintColor == ClientBlockStateColorCache.INVALID_COLOR)
{
tintColor = tintSource.colorAsTerrainParticle(this.blockState, tintOverride, mcPos);
}
}
if (tintColor == -1)
{
// no color found, use the base color
tintColor = AbstractDhTintGetter.INVALID_COLOR;
}
// save this color to speed up future queries
TintWithoutLevelOverrider.setStaticColor(this.blockStateWrapper, biomeWrapper, tintColor);
// try to get the blended color with this new information
@@ -599,9 +753,11 @@ public class ClientBlockStateColorCache
#endif
}
}
#endif
// level-specific logic is only needed for MC 1.21.11 and older
#if MC_VER <= MC_1_21_11
#if MC_VER <= MC_1_21_11 && MC_VER > MC_1_12_2
// use the level logic only if requested
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{
@@ -612,7 +768,7 @@ public class ClientBlockStateColorCache
tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
if (tintColor == ClientBlockStateColorCache.INVALID_COLOR)
{
tintColor = Minecraft.getInstance()
.getBlockColors()
@@ -623,6 +779,7 @@ public class ClientBlockStateColorCache
}
}
#endif
}
catch (Exception e)
{
@@ -637,7 +794,7 @@ public class ClientBlockStateColorCache
int returnColor;
if (tintColor != AbstractDhTintGetter.INVALID_COLOR)
if (tintColor != ClientBlockStateColorCache.INVALID_COLOR)
{
returnColor = ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
}
@@ -667,6 +824,8 @@ public class ClientBlockStateColorCache
return returnColor;
}
//endregion
//================//
@@ -684,14 +843,54 @@ public class ClientBlockStateColorCache
static EColorMode getColorMode(Block block)
{
if (block instanceof LeavesBlock)
//========//
// leaves //
//========//
//region
boolean isLeavesBlock;
#if MC_VER <= MC_1_12_2
isLeavesBlock = block instanceof BlockLeaves;
#else
isLeavesBlock = block instanceof LeavesBlock;
#endif
if (isLeavesBlock)
{
return Leaves;
}
if (block instanceof FlowerBlock)
//endregion
//========//
// flower //
//========//
//region
boolean isFlowerBlock;
#if MC_VER <= MC_1_12_2
isFlowerBlock = block instanceof BlockFlower;
#else
isFlowerBlock = block instanceof FlowerBlock;
#endif
if (isFlowerBlock)
{
return Flower;
}
//endregion
//=============//
// misc/simple //
//=============//
//region
if (block.toString().contains("glass"))
{
return Glass;
@@ -700,6 +899,11 @@ public class ClientBlockStateColorCache
{
return Chisel;
}
//endregion
return Default;
}
}
@@ -38,7 +38,10 @@ public class TextureAtlasSpriteWrapper
{
public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y)
{
#if MC_VER < MC_1_17_1
#if MC_VER <= MC_1_12_2
int[][] frameData = sprite.getFrameTextureData(frameIndex);
return frameData[0][y * sprite.getIconWidth() + x];
#elif MC_VER < MC_1_17_1
return sprite.mainImage[0].getPixelRGBA(
x + sprite.framesX[frameIndex] * sprite.getWidth(),
y + sprite.framesY[frameIndex] * sprite.getHeight());
@@ -18,7 +18,7 @@
*/
package com.seibel.distanthorizons.common.wrappers.block;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.core.BlockPos;
@@ -189,3 +189,4 @@ public class TintGetterOverride extends AbstractDhTintGetter
}
#endif
@@ -18,7 +18,7 @@
*/
package com.seibel.distanthorizons.common.wrappers.block;
#if MC_VER > MC_1_12_2
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
@@ -109,3 +109,4 @@ public class TintWithoutLevelOverrider extends AbstractDhTintGetter
}
#endif
@@ -31,12 +31,19 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
#if MC_VER <= MC_1_12_2
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
#else
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap;
#endif
import com.seibel.distanthorizons.core.logging.DhLogger;
@@ -67,9 +74,10 @@ import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.LevelChunkSection;
#endif
#if MC_VER <= MC_1_20_4
#if MC_VER <= MC_1_12_2
#elif MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
#elif MC_VER > MC_1_12_2
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
@@ -86,8 +94,12 @@ public class ChunkWrapper implements IChunkWrapper
private static boolean heightmapThreadWarningLogged = false;
#if MC_VER <= MC_1_12_2
private final Chunk chunk;
#else
private final ChunkAccess chunk;
#endif
private final DhChunkPos chunkPos;
private final ILevelWrapper wrappedLevel;
@@ -112,13 +124,17 @@ public class ChunkWrapper implements IChunkWrapper
//=============//
// constructor //
//=============//
//region
/**
* Note: this constructor should be very
* fast since it will be called frequently on the MC
* server thread and a slow method will cause server lag.
*/
#if MC_VER <= MC_1_12_2
public ChunkWrapper(Chunk chunk, ILevelWrapper wrappedLevel)
#else
public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel)
#endif
{
this.chunk = chunk;
this.wrappedLevel = wrappedLevel;
@@ -133,15 +149,22 @@ public class ChunkWrapper implements IChunkWrapper
@Override
public ChunkWrapper copy() { return new ChunkWrapper(this.chunk, this.wrappedLevel); }
//endregion
//=========//
// getters //
//=========//
//region
@Override
public int getHeight() { return getHeight(this.chunk); }
#if MC_VER <= MC_1_12_2
public static int getHeight(Chunk chunk)
#else
public static int getHeight(ChunkAccess chunk)
#endif
{
#if MC_VER < MC_1_17_1
return 255;
@@ -152,7 +175,11 @@ public class ChunkWrapper implements IChunkWrapper
@Override
public int getInclusiveMinBuildHeight() { return getInclusiveMinBuildHeight(this.chunk); }
#if MC_VER <= MC_1_12_2
public static int getInclusiveMinBuildHeight(Chunk chunk)
#else
public static int getInclusiveMinBuildHeight(ChunkAccess chunk)
#endif
{
#if MC_VER < MC_1_17_1
return 0;
@@ -165,9 +192,15 @@ public class ChunkWrapper implements IChunkWrapper
@Override
public int getExclusiveMaxBuildHeight() { return getExclusiveMaxBuildHeight(this.chunk); }
#if MC_VER <= MC_1_12_2
public static int getExclusiveMaxBuildHeight(Chunk chunk)
#else
public static int getExclusiveMaxBuildHeight(ChunkAccess chunk)
#endif
{
#if MC_VER < MC_1_21_3
#if MC_VER <= MC_1_12_2
return 256;
#elif MC_VER < MC_1_21_3
return chunk.getMaxBuildHeight();
#else
// +1 since Minecraft made the max value inclusive
@@ -188,7 +221,11 @@ public class ChunkWrapper implements IChunkWrapper
this.minNonEmptyHeight = this.getInclusiveMinBuildHeight();
// determine the lowest empty section (bottom up)
#if MC_VER <= MC_1_12_2
ExtendedBlockStorage[] sections = this.chunk.getBlockStorageArray();
#else
LevelChunkSection[] sections = this.chunk.getSections();
#endif
for (int index = 0; index < sections.length; index++)
{
if (sections[index] == null)
@@ -220,7 +257,11 @@ public class ChunkWrapper implements IChunkWrapper
this.maxNonEmptyHeight = this.getExclusiveMaxBuildHeight();
// determine the highest empty section (top down)
#if MC_VER <= MC_1_12_2
ExtendedBlockStorage[] sections = this.chunk.getBlockStorageArray();
#else
LevelChunkSection[] sections = this.chunk.getSections();
#endif
for (int index = sections.length-1; index >= 0; index--)
{
// update at each position to fix using the max height if the chunk is empty
@@ -240,11 +281,13 @@ public class ChunkWrapper implements IChunkWrapper
return this.maxNonEmptyHeight;
}
#if MC_VER <= MC_1_12_2
private static boolean isChunkSectionEmpty(ExtendedBlockStorage section)
#else
private static boolean isChunkSectionEmpty(LevelChunkSection section)
#endif
{
#if MC_VER == MC_1_16_5
return section.isEmpty();
#elif MC_VER == MC_1_17_1
#if MC_VER <= MC_1_17_1
return section.isEmpty();
#else
return section.hasOnlyAir();
@@ -322,7 +365,11 @@ public class ChunkWrapper implements IChunkWrapper
// will be null if we want to use MC heightmaps
if (this.solidHeightMap == null)
{
#if MC_VER <= MC_1_12_2
return this.chunk.getHeightValue(xRel, zRel);
#else
return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel);
#endif
}
else
{
@@ -337,7 +384,11 @@ public class ChunkWrapper implements IChunkWrapper
if (this.lightBlockingHeightMap == null)
{
#if MC_VER <= MC_1_12_2
return this.chunk.getHeightValue(xRel, zRel);
#else
return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel);
#endif
}
else
{
@@ -349,7 +400,14 @@ public class ChunkWrapper implements IChunkWrapper
@Override
public IBiomeWrapper getBiome(int relX, int relY, int relZ)
{
#if MC_VER < MC_1_17_1
#if MC_VER <= MC_1_12_2
BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get();
blockPos.setPos(relX, relY, relZ);
World world = (World) this.wrappedLevel.getWrappedMcObject();
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiome(blockPos, world.getBiomeProvider()), wrappedLevel);
#elif MC_VER < MC_1_17_1
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome(
relX >> 2, relY >> 2, relZ >> 2),
this.wrappedLevel);
@@ -357,10 +415,6 @@ public class ChunkWrapper implements IChunkWrapper
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome(
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
this.wrappedLevel);
#elif MC_VER < MC_1_18_2
return BiomeWrapper.getBiomeWrapper(this.chunk.getNoiseBiome(
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
this.wrappedLevel);
#else
//Now returns a Holder<Biome> instead of Biome
return BiomeWrapper.getBiomeWrapper(this.chunk.getNoiseBiome(
@@ -376,9 +430,13 @@ public class ChunkWrapper implements IChunkWrapper
BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get();
#if MC_VER <= MC_1_12_2
blockPos.setPos(relX, relY, relZ);
#else
blockPos.setX(relX);
blockPos.setY(relY);
blockPos.setZ(relZ);
#endif
try
{
@@ -401,9 +459,13 @@ public class ChunkWrapper implements IChunkWrapper
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
BlockPos.MutableBlockPos pos = (BlockPos.MutableBlockPos)mcBlockPos.getWrappedMcObject();
#if MC_VER <= MC_1_12_2
pos.setPos(relX, relY, relZ);
#else
pos.setX(relX);
pos.setY(relY);
pos.setZ(relZ);
#endif
try
{
@@ -513,8 +575,14 @@ public class ChunkWrapper implements IChunkWrapper
@Override
public DhChunkPos getChunkPos() { return this.chunkPos; }
public ChunkAccess getChunk() { return this.chunk; }
#if MC_VER <= MC_1_12_2
public Chunk getChunk()
#else
public ChunkAccess getChunk()
#endif
{ return this.chunk; }
#if MC_VER > MC_1_12_2
public void trySetStatus(ChunkStatus status) { trySetStatus(this.getChunk(), status); }
/** does nothing if the chunk object doesn't support setting it's status */
public static void trySetStatus(ChunkAccess chunk, ChunkStatus status)
@@ -538,21 +606,53 @@ public class ChunkWrapper implements IChunkWrapper
return chunk.getPersistedStatus();
#endif
}
#endif
@Override
public int getMaxBlockX() { return this.chunk.getPos().getMaxBlockX(); }
public int getMaxBlockX()
{
#if MC_VER <= MC_1_12_2
return this.chunk.getPos().getXEnd();
#else
return this.chunk.getPos().getMaxBlockX();
#endif
}
@Override
public int getMaxBlockZ() { return this.chunk.getPos().getMaxBlockZ(); }
public int getMaxBlockZ()
{
#if MC_VER <= MC_1_12_2
return this.chunk.getPos().getZEnd();
#else
return this.chunk.getPos().getMaxBlockZ();
#endif
}
@Override
public int getMinBlockX() { return this.chunk.getPos().getMinBlockX(); }
public int getMinBlockX()
{
#if MC_VER <= MC_1_12_2
return this.chunk.getPos().getXStart();
#else
return this.chunk.getPos().getMinBlockX();
#endif
}
@Override
public int getMinBlockZ() { return this.chunk.getPos().getMinBlockZ(); }
public int getMinBlockZ()
{
#if MC_VER <= MC_1_12_2
return this.chunk.getPos().getZStart();
#else
return this.chunk.getPos().getMinBlockZ();
#endif
}
//endregion
//==========//
// lighting //
//==========//
//region
@Override
public void setIsDhSkyLightCorrect(boolean isDhLightCorrect) { this.isDhSkyLightCorrect = isDhLightCorrect; }
@@ -629,8 +729,23 @@ public class ChunkWrapper implements IChunkWrapper
{
this.blockLightPosList = new ArrayList<>();
#if MC_VER < MC_1_20_1
//1.12.2 doesn't store lights we must bruteforce it
#if MC_VER <= MC_1_12_2
for (int x = 0; x < 16; x++)
{
for (int z = 0; z < 16; z++)
{
for (int y = 0; y < 256; y++)
{
IBlockState blockState = this.chunk.getBlockState(x, y, z);
if (blockState.getLightValue() > 0)
{
this.blockLightPosList.add(new DhBlockPos(this.chunk.getPos().getXStart() + x, y, this.chunk.getPos().getZStart() + z));
}
}
}
}
#elif MC_VER < MC_1_20_1
this.chunk.getLights().forEach((blockPos) ->
{
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
@@ -652,11 +767,14 @@ public class ChunkWrapper implements IChunkWrapper
return this.blockLightPosList;
}
//endregion
//================//
// base overrides //
//================//
//region
@Override
public String toString() { return this.chunk.getClass().getSimpleName() + this.chunk.getPos(); }
@@ -672,4 +790,8 @@ public class ChunkWrapper implements IChunkWrapper
// return this.blockBiomeHashCode;
//}
//endregion
}
@@ -1,12 +1,19 @@
package com.seibel.distanthorizons.common.wrappers.gui;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.util.text.ITextComponent;
#else
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
#endif
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
#elif MC_VER < MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
#elif MC_VER <= MC_1_21_11
import net.minecraft.client.gui.GuiGraphics;
@@ -16,26 +23,73 @@ import net.minecraft.client.gui.GuiGraphicsExtractor;
import java.util.List;
#if MC_VER <= MC_1_12_2
public class DhScreen extends GuiScreen
#else
public class DhScreen extends Screen
#endif
{
#if MC_VER <= MC_1_12_2
protected ITextComponent title;
#endif
protected DhScreen(Component $$0)
#if MC_VER <= MC_1_12_2
protected DhScreen(ITextComponent title)
{
super($$0);
this.title = title;
}
#else
protected DhScreen(Component title)
{
super(title);
}
#endif
// addRenderableWidget in 1.17 and over
// addButton in 1.16 and below
#if MC_VER <= MC_1_12_2
protected GuiButton addBtn(GuiButton button)
#else
protected Button addBtn(Button button)
#endif
{
#if MC_VER < MC_1_17_1
#if MC_VER <= MC_1_12_2
this.buttonList.add(button);
return button;
#elif MC_VER < MC_1_17_1
return this.addButton(button);
#else
return this.addRenderableWidget(button);
#endif
}
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
@Override
protected void actionPerformed(GuiButton button)
{
OnPressed handler = GuiHelper.HANDLER_BY_BUTTON.get(button);
if (handler != null)
{
handler.pressed(button);
}
}
protected void DhDrawCenteredString(ITextComponent text, int x, int y, int color) {
drawCenteredString(fontRenderer, text.getFormattedText(), x, y, color);
}
protected void DhDrawString(ITextComponent text, int x, int y, int color) {
drawString(fontRenderer, text.getFormattedText(), x, y, color);
}
protected void DhRenderComponentTooltip(List<ITextComponent> list, int x, int y) {
drawHoveringText(list.stream().map(ITextComponent::getFormattedText).toList(), x, y, fontRenderer);
}
protected void DhRenderTooltip(ITextComponent text, int x, int y) {
drawHoveringText(List.of(text.getFormattedText()), x, y, fontRenderer);
}
#elif MC_VER < MC_1_20_1
protected void DhDrawCenteredString(PoseStack guiStack, Font font, Component text, int x, int y, int color)
{
drawCenteredString(guiStack, font, text, x, y, color);
@@ -112,7 +166,4 @@ public class DhScreen extends Screen
guiStack.setTooltipForNextFrame(font, text, x, y);
}
#endif
}
@@ -4,14 +4,22 @@ import com.seibel.distanthorizons.common.wrappers.gui.classicConfig.ClassicConfi
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.coreapi.ModInfo;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.gui.GuiScreen;
#else
import net.minecraft.client.gui.screens.Screen;
#endif
import com.seibel.distanthorizons.core.logging.DhLogger;
public class GetConfigScreen
{
protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER <= MC_1_12_2
public static GuiScreen getScreen(GuiScreen parent)
#else
public static Screen getScreen(Screen parent)
#endif
{
if (ModInfo.IS_DEV_BUILD)
{
@@ -1,11 +1,21 @@
package com.seibel.distanthorizons.common.wrappers.gui;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import java.util.HashMap;
import java.util.Map;
#else
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
#endif
#if MC_VER < MC_1_19_2
#if MC_VER < MC_1_19_2 && MC_VER > MC_1_12_2
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
#endif
@@ -15,57 +25,99 @@ public class GuiHelper
/**
* Helper static methods for versional compat
*/
#if MC_VER <= MC_1_12_2
public static final Map<GuiButton, OnPressed> HANDLER_BY_BUTTON = new HashMap<>();
#endif
#if MC_VER <= MC_1_12_2
public static GuiButton MakeBtn(ITextComponent base, int posX, int posZ, int width, int height, OnPressed action)
#else
public static Button MakeBtn(Component base, int posX, int posZ, int width, int height, Button.OnPress action)
#endif
{
#if MC_VER < MC_1_19_4
#if MC_VER <= MC_1_12_2
GuiButton button = new GuiButton(HANDLER_BY_BUTTON.size(), posX, posZ, width, height, base.getFormattedText());
HANDLER_BY_BUTTON.put(button, action);
return button;
#elif MC_VER < MC_1_19_4
return new Button(posX, posZ, width, height, base, action);
#else
return Button.builder(base, action).bounds(posX, posZ, width, height).build();
#endif
}
#if MC_VER <= MC_1_12_2
public static ITextComponent TextOrLiteral(String text)
#else
public static MutableComponent TextOrLiteral(String text)
#endif
{
#if MC_VER < MC_1_19_2
#if MC_VER <= MC_1_12_2
return new TextComponentString(text);
#elif MC_VER < MC_1_19_2
return new TextComponent(text);
#else
return Component.literal(text);
#endif
}
#if MC_VER <= MC_1_12_2
public static ITextComponent TextOrTranslatable(String text)
#else
public static MutableComponent TextOrTranslatable(String text)
#endif
{
#if MC_VER < MC_1_19_2
#if MC_VER <= MC_1_12_2
return new TextComponentString(text);
#elif MC_VER < MC_1_19_2
return new TextComponent(text);
#else
return Component.translatable(text);
#endif
}
#if MC_VER <= MC_1_12_2
public static ITextComponent Translatable(String text, Object... args)
#else
public static MutableComponent Translatable(String text, Object... args)
#endif
{
#if MC_VER < MC_1_19_2
#if MC_VER <= MC_1_12_2
return new TextComponentTranslation(text, args);
#elif MC_VER < MC_1_19_2
return new TranslatableComponent(text, args);
#else
return Component.translatable(text, args);
#endif
}
public static void SetX(AbstractWidget w, int x)
#if MC_VER <= MC_1_12_2
public static void SetX(GuiButton widget, int x)
#else
public static void SetX(AbstractWidget widget, int x)
#endif
{
#if MC_VER < MC_1_19_4
w.x = x;
widget.x = x;
#else
w.setX(x);
widget.setX(x);
#endif
}
public static void SetY(AbstractWidget w, int y)
#if MC_VER <= MC_1_12_2
public static void SetY(GuiTextField textField, int y) { textField.y = y; }
#endif
#if MC_VER <= MC_1_12_2
public static void SetY(GuiButton widget, int y)
#else
public static void SetY(AbstractWidget widget, int y)
#endif
{
#if MC_VER < MC_1_19_4
w.y = y;
widget.y = y;
#else
w.setY(y);
widget.setY(y);
#endif
}
@@ -1,21 +1,34 @@
package com.seibel.distanthorizons.common.wrappers.gui;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.resources.I18n;
#else
import net.minecraft.client.resources.language.I18n;
#endif
public class LangWrapper implements ILangWrapper
{
public static final LangWrapper INSTANCE = new LangWrapper();
@Override
public boolean langExists(String str)
{
#if MC_VER <= MC_1_12_2
return I18n.hasKey(str);
#else
return I18n.exists(str);
#endif
}
@Override
public String getLang(String str)
{
#if MC_VER <= MC_1_12_2
return I18n.format(str);
#else
return I18n.get(str);
#endif
}
}
@@ -1,17 +1,27 @@
package com.seibel.distanthorizons.common.wrappers.gui;
#if MC_VER <= MC_1_12_2
import org.lwjglx.opengl.Display;
#else
import com.mojang.blaze3d.platform.Window;
#endif
import com.seibel.distanthorizons.core.config.gui.AbstractScreen;
import net.minecraft.client.Minecraft;
#if MC_VER > MC_1_12_2
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.screens.Screen;
#endif
import org.jetbrains.annotations.NotNull;
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
import net.minecraft.client.gui.GuiListExtended;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiSlot;
#elif MC_VER < MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
#elif MC_VER <= MC_1_21_11
import net.minecraft.client.gui.GuiGraphics;
@@ -24,19 +34,29 @@ import java.util.*;
public class MinecraftScreen
{
#if MC_VER <= MC_1_12_2
public static GuiScreen getScreen(GuiScreen parent, AbstractScreen screen, String translationName)
#else
public static Screen getScreen(Screen parent, AbstractScreen screen, String translationName)
#endif
{
return new ConfigScreenRenderer(parent, screen, translationName);
}
private static class ConfigScreenRenderer extends DhScreen
{
#if MC_VER <= MC_1_12_2
private final GuiScreen parent;
#else
private final Screen parent;
#endif
private ConfigListWidget configListWidget;
private AbstractScreen screen;
#if MC_VER < MC_1_19_2
#if MC_VER <= MC_1_12_2
public static net.minecraft.util.text.TextComponentTranslation translate(String str, Object... args)
{ return new net.minecraft.util.text.TextComponentTranslation(str, args); }
#elif MC_VER < MC_1_19_2
public static net.minecraft.network.chat.TranslatableComponent translate(String str, Object... args)
{ return new net.minecraft.network.chat.TranslatableComponent(str, args); }
#else
@@ -44,10 +64,16 @@ public class MinecraftScreen
{ return net.minecraft.network.chat.Component.translatable(str, args); }
#endif
#if MC_VER <= MC_1_12_2
protected ConfigScreenRenderer(GuiScreen parent, AbstractScreen screen, String translationName)
#else
protected ConfigScreenRenderer(Screen parent, AbstractScreen screen, String translationName)
#endif
{
super(translate(translationName));
#if MC_VER < MC_1_21_9
#if MC_VER <= MC_1_12_2
screen.minecraftWindow = Display.getWindow();
#elif MC_VER < MC_1_21_9
screen.minecraftWindow = Minecraft.getInstance().getWindow().getWindow();
#else
screen.minecraftWindow = Minecraft.getInstance().getWindow().handle();
@@ -57,30 +83,53 @@ public class MinecraftScreen
}
@Override
#if MC_VER <= MC_1_12_2
public void initGui()
#else
protected void init()
#endif
{
super.init(); // Init Minecraft's screen
#if MC_VER <= MC_1_12_2
super.initGui();
#else
super.init();
#endif
#if MC_VER <= MC_1_12_2
this.screen.width = Display.getWidth();
this.screen.height = Display.getHeight();
#else
Window mcWindow = this.minecraft.getWindow();
this.screen.width = mcWindow.getWidth();
this.screen.height = mcWindow.getHeight();
#endif
this.screen.scaledWidth = this.width;
this.screen.scaledHeight = this.height;
this.screen.init(); // Init our own config screen
#if MC_VER <= MC_1_12_2
this.configListWidget = new ConfigListWidget(this.mc, this.width, this.height, 0, 0, 25); // Select the area to tint
#else
this.configListWidget = new ConfigListWidget(this.minecraft, this.width, this.height, 0, 0, 25); // Select the area to tint
#endif
#if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
#if MC_VER <= MC_1_12_2
#elif MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
if (this.minecraft != null && this.minecraft.level != null) // Check if in game
{
this.configListWidget.setRenderBackground(false); // Disable from rendering
}
#endif
#if MC_VER > MC_1_12_2
this.addWidget(this.configListWidget); // Add the tint to the things to be rendered
#endif
}
@Override
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
public void drawScreen(int mouseX, int mouseY, float delta)
#elif MC_VER < MC_1_20_1
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
#elif MC_VER <= MC_1_21_11
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
@@ -88,7 +137,9 @@ public class MinecraftScreen
public void extractRenderState(GuiGraphicsExtractor matrices, int mouseX, int mouseY, float delta)
#endif
{
#if MC_VER < MC_1_20_2
#if MC_VER <= MC_1_12_2
this.drawDefaultBackground();
#elif MC_VER < MC_1_20_2
this.renderBackground(matrices); // Render background
#elif MC_VER < MC_1_21_6
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
@@ -96,7 +147,9 @@ public class MinecraftScreen
// background blur is already being rendered, rendering again causes the game to crash
#endif
#if MC_VER <= MC_1_21_11
#if MC_VER <= MC_1_12_2
this.configListWidget.drawScreen(mouseX, mouseY, delta);
#elif MC_VER <= MC_1_21_11
this.configListWidget.render(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
#else
this.configListWidget.extractRenderState(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
@@ -106,55 +159,86 @@ public class MinecraftScreen
this.screen.mouseY = mouseY;
this.screen.render(delta); // Render everything on the main screen
#if MC_VER <= MC_1_21_11
#if MC_VER <= MC_1_12_2
super.drawScreen(mouseX, mouseY, delta); // Render the vanilla stuff (currently only used for the background and tint)
#elif MC_VER <= MC_1_21_11
super.render(matrices, mouseX, mouseY, delta); // Render the vanilla stuff (currently only used for the background and tint)
#else
super.extractRenderState(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
#endif
}
#if MC_VER <= MC_1_21_10
@Override
#if MC_VER <= MC_1_12_2
public void setWorldAndResolution(Minecraft mc, int width, int height)
#elif MC_VER <= MC_1_21_10
public void resize(Minecraft mc, int width, int height)
#else
@Override
public void resize(int width, int height)
#endif
{
// Resize Minecraft's screen
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
super.setWorldAndResolution(mc, width, height);
#elif MC_VER <= MC_1_21_10
super.resize(mc, width, height);
#else
super.resize(width, height);
#endif
#if MC_VER <= MC_1_12_2
this.screen.width = Display.getWidth();
this.screen.height = Display.getHeight();
#else
Window mcWindow = this.minecraft.getWindow();
this.screen.width = mcWindow.getWidth();
this.screen.height = mcWindow.getHeight();
#endif;
this.screen.scaledWidth = this.width;
this.screen.scaledHeight = this.height;
this.screen.onResize(); // Resize our screen
}
@Override
#if MC_VER <= MC_1_12_2
public void updateScreen()
#else
public void tick()
#endif
{
#if MC_VER <= MC_1_12_2
super.updateScreen(); // Tick Minecraft's screen
#else
super.tick(); // Tick Minecraft's screen
#endif
this.screen.tick(); // Tick our screen
if (this.screen.close) // If we decide to close the screen, then actually close the screen
{
#if MC_VER <= MC_1_12_2
this.onGuiClosed();
#else
this.onClose();
#endif
}
}
@Override
#if MC_VER <= MC_1_12_2
public void onGuiClosed()
#else
public void onClose()
#endif
{
this.screen.onClose(); // Close our screen
#if MC_VER <= MC_1_12_2
Objects.requireNonNull(this.mc).displayGuiScreen(this.parent); // Goto the parent screen
#else
Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Goto the parent screen
#endif
}
#if MC_VER > MC_1_12_2
@Override
public void onFilesDrop(@NotNull List<Path> files)
{ this.screen.onFilesDrop(files); }
@@ -163,10 +247,14 @@ public class MinecraftScreen
@Override
public boolean shouldCloseOnEsc()
{ return this.screen.shouldCloseOnEsc; }
#endif
}
#if MC_VER <= MC_1_12_2
public static class ConfigListWidget extends GuiListExtended
#else
public static class ConfigListWidget extends ContainerObjectSelectionList
#endif
{
public ConfigListWidget(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing)
{
@@ -178,6 +266,18 @@ public class MinecraftScreen
this.centerListVertically = false;
}
#if MC_VER <= MC_1_12_2
@Override
protected int getSize()
{
return 0;
}
@Override
public IGuiListEntry getListEntry(int index)
{
return null;
}
#endif
}
}
@@ -0,0 +1,9 @@
package com.seibel.distanthorizons.common.wrappers.gui;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.gui.GuiButton;
public interface OnPressed {
void pressed(GuiButton button);
}
#endif
@@ -19,13 +19,21 @@
package com.seibel.distanthorizons.common.wrappers.gui;
#if MC_VER > MC_1_12_2
import net.minecraft.network.chat.Component;
#endif
#if MC_VER >= MC_1_17_1
import net.minecraft.client.gui.components.Button;
#endif
#if MC_VER < MC_1_17_1
#if MC_VER <= MC_1_12_2
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.util.ResourceLocation;
#elif MC_VER < MC_1_17_1
import net.minecraft.client.gui.components.ImageButton;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
@@ -53,7 +61,9 @@ import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.renderer.RenderPipelines;
#endif
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
import net.minecraft.util.ResourceLocation;
#elif MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
@@ -65,7 +75,10 @@ import net.minecraft.resources.Identifier;
* @author coolGi
* @version 2023-10-03
*/
#if MC_VER < MC_1_20_2
#if MC_VER <= MC_1_12_2
@SuppressWarnings("deprecation") // we use a few deprecated Mojang functions (as expected when running on old MC versions)
public class TexturedButtonWidget extends GuiButton
#elif MC_VER < MC_1_20_2
@SuppressWarnings("deprecation") // we use a few deprecated Mojang functions (as expected when running on old MC versions)
public class TexturedButtonWidget extends ImageButton
#else
@@ -75,7 +88,7 @@ public class TexturedButtonWidget extends Button
{
public final boolean renderBackground;
#if MC_VER >= MC_1_20_2
#if MC_VER >= MC_1_20_2 || MC_VER <= MC_1_12_2
private final int u;
private final int v;
private final int hoveredVOffset;
@@ -90,30 +103,41 @@ public class TexturedButtonWidget extends Button
private final int textureHeight;
#endif
public TexturedButtonWidget(
int x, int y, int width, int height, int u, int v, int hoveredVOffset,
#if MC_VER <= MC_1_21_10 ResourceLocation textureResourceLocation,
#else Identifier textureResourceLocation,
#endif
int textureWidth, int textureHeight, OnPress pressAction, Component text)
#if MC_VER <= MC_1_12_2
public TexturedButtonWidget(int id, int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, String text)
{
this(id, x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, text, true);
}
#elif MC_VER <= MC_1_21_10
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text)
{
this(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text, true);
}
public TexturedButtonWidget(
int x, int y, int width, int height, int u, int v, int hoveredVOffset,
#if MC_VER <= MC_1_21_10 ResourceLocation textureResourceLocation,
#else Identifier textureResourceLocation,
#endif
int textureWidth, int textureHeight, OnPress pressAction, Component text,
boolean renderBackground)
#else
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, Identifier textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text)
{
#if MC_VER < MC_1_20_2
this(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text, true);
}
#endif
#if MC_VER <= MC_1_12_2
public TexturedButtonWidget(int id, int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, String text, boolean renderBackground)
#elif MC_VER <= MC_1_21_10
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground)
#else
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, Identifier textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground)
#endif
{
#if MC_VER <= MC_1_12_2
super(id, x, y, width, height, text);
#elif MC_VER < MC_1_20_2
super(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text);
#else
// We don't pass in the text option since it will render (we normally pass it in for narration)
super(x, y, width, height, Component.empty(), pressAction, DEFAULT_NARRATION);
#endif
#if MC_VER >= MC_1_20_2 || MC_VER <= MC_1_12_2
this.u = u;
this.v = v;
this.hoveredVOffset = hoveredVOffset;
@@ -127,7 +151,27 @@ public class TexturedButtonWidget extends Button
this.renderBackground = renderBackground;
}
#if MC_VER < MC_1_20_2
#if MC_VER <= MC_1_12_2
@Override
public void drawButton(Minecraft mc, int mouseX, int mouseY, float partialTicks) {
if (this.visible) {
//Render vanilla background
mc.getTextureManager().bindTexture(BUTTON_TEXTURES);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
this.hovered = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
int i = this.getHoverState(this.hovered);
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
this.drawTexturedModalRect(this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height);
this.drawTexturedModalRect(this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
//Render DH texture
mc.getTextureManager().bindTexture(textureResourceLocation);
drawModalRectWithCustomSizedTexture(this.x, this.y, this.u, (hoveredVOffset * (i - 1)), this.width, this.height, this.textureWidth, this.textureHeight);
}
}
#elif MC_VER < MC_1_20_2
#if MC_VER < MC_1_19_4
@Override
public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta)
@@ -12,17 +12,24 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigCommentTextPosi
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
import net.minecraft.client.Minecraft;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.gui.*;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.text.ITextComponent;
#else
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
#endif
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
#elif MC_VER < MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.GuiComponent;
#elif MC_VER <= MC_1_21_11
@@ -67,6 +74,7 @@ public class ClassicConfigGUI
//==============//
// Initializers //
//==============//
//region
// Some regexes to check if an input is valid
public static final Pattern INTEGER_ONLY_REGEX = Pattern.compile("(-?[0-9]*)");
@@ -87,25 +95,46 @@ public class ClassicConfigGUI
}
//endregion
//==============//
// GUI handling //
//==============//
//region
/** if you want to get this config gui's screen call this */
#if MC_VER <= MC_1_12_2
public static GuiScreen getScreen(GuiScreen parent, String category)
#else
public static Screen getScreen(Screen parent, String category)
#endif
{ return new DhConfigScreen(parent, category); }
//endregion
//================//
// helper classes //
//================//
//region
#if MC_VER <= MC_1_12_2
public static class ConfigListWidget extends GuiListExtended
#else
public static class ConfigListWidget extends ContainerObjectSelectionList<DhButtonEntry>
#endif
{
#if MC_VER <= MC_1_12_2
public List<DhButtonEntry> children = new ArrayList<>();
#endif
#if MC_VER <= MC_1_12_2
FontRenderer textRenderer;
#else
Font textRenderer;
#endif
public ConfigListWidget(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing)
{
@@ -116,23 +145,105 @@ public class ClassicConfigGUI
#endif
this.centerListVertically = false;
#if MC_VER <= MC_1_12_2
this.textRenderer = minecraftClient.fontRenderer;
#else
this.textRenderer = minecraftClient.font;
#endif
}
public void addButton(DhConfigScreen gui, AbstractConfigBase dhConfigType, AbstractWidget button, AbstractWidget resetButton, AbstractWidget indexButton, Component text)
{ this.addEntry(new DhButtonEntry(gui, dhConfigType, button, text, resetButton, indexButton)); }
#if MC_VER <= MC_1_12_2
@Override
protected int getSize()
{
return this.children.size();
}
@Override
public int getRowWidth() { return 10_000; }
public IGuiListEntry getListEntry(int index)
{
return this.children.get(index);
}
@Override
protected void drawContainerBackground(Tessellator tessellator)
{
if (this.mc.world != null)
{
return; // in-game don't draw dirt background
}
super.drawContainerBackground(tessellator);
}
#endif
#if MC_VER <= MC_1_12_2
public void addButton(DhConfigScreen gui, AbstractConfigBase dhConfigType, Gui button, GuiButton resetButton, GuiButton indexButton, ITextComponent text)
#else
public void addButton(DhConfigScreen gui, AbstractConfigBase dhConfigType, AbstractWidget button, AbstractWidget resetButton, AbstractWidget indexButton, Component text)
#endif
{
#if MC_VER <= MC_1_12_2
this.children.add(new DhButtonEntry(gui, dhConfigType, button, text, resetButton, indexButton));
#else
this.addEntry(new DhButtonEntry(gui, dhConfigType, button, text, resetButton, indexButton));
#endif
}
@Override
#if MC_VER <= MC_1_12_2
public int getListWidth()
#else
public int getRowWidth()
#endif
{ return 10_000; }
#if MC_VER <= MC_1_12_2
public Gui getHoveredButton(double mouseX, double mouseY)
#else
public AbstractWidget getHoveredButton(double mouseX, double mouseY)
#endif
{
#if MC_VER <= MC_1_12_2
for (DhButtonEntry buttonEntry : this.children)
#else
for (DhButtonEntry buttonEntry : this.children())
#endif
{
AbstractWidget button = buttonEntry.button;
if (button != null
&& button.visible)
#if MC_VER <= MC_1_12_2
Gui gui = buttonEntry.button;
if (gui == null) continue;
double minX, minY, maxX, maxY;
if (gui instanceof GuiButton button)
{
if (!button.visible) continue;
minX = button.x;
minY = button.y;
maxX = minX + button.width;
maxY = minY + button.height;
}
else if (gui instanceof GuiTextField field)
{
if (!field.getVisible()) continue;
minX = field.x;
minY = field.y;
maxX = minX + field.width;
maxY = minY + field.height;
}
else
{
continue;
}
if (mouseX >= minX && mouseX < maxX && mouseY >= minY && mouseY < maxY)
{
return gui;
}
#else
AbstractWidget button = (AbstractWidget) buttonEntry.button;
if (button == null || !button.visible) continue;
#if MC_VER < MC_1_19_4
double minX = button.x;
double minY = button.y;
@@ -144,12 +255,11 @@ public class ClassicConfigGUI
double maxX = minX + button.getWidth();
double maxY = minY + button.getHeight();
if (mouseX >= minX && mouseX < maxX
&& mouseY >= minY && mouseY < maxY)
if (mouseX >= minX && mouseX < maxX && mouseY >= minY && mouseY < maxY)
{
return button;
}
}
#endif
}
return null;
@@ -157,32 +267,60 @@ public class ClassicConfigGUI
}
#if MC_VER <= MC_1_12_2
public static class DhButtonEntry implements GuiListExtended.IGuiListEntry
#else
public static class DhButtonEntry extends ContainerObjectSelectionList.Entry<DhButtonEntry>
#endif
{
#if MC_VER <= MC_1_12_2
private static final FontRenderer textRenderer = Minecraft.getMinecraft().fontRenderer;
#else
private static final Font textRenderer = Minecraft.getInstance().font;
private final AbstractWidget button;
#endif
private final DhConfigScreen gui;
private final AbstractWidget resetButton;
#if MC_VER <= MC_1_12_2
public final Gui button;
public final Gui resetButton;
public final Gui indexButton;
#else
private final AbstractWidget indexButton;
private final AbstractWidget resetButton;
private final AbstractWidget button;
#endif
#if MC_VER <= MC_1_12_2
private final ITextComponent text;
#else
private final Component text;
#endif
#if MC_VER <= MC_1_12_2
private final List<Gui> children = new ArrayList<>();
#else
private final List<AbstractWidget> children = new ArrayList<>();
#endif
@NotNull
private final EConfigCommentTextPosition textPosition;
public final AbstractConfigBase dhConfigType;
#if MC_VER <= MC_1_12_2
public static final Map<Gui, ITextComponent> TEXT_BY_WIDGET = new HashMap<>();
public static final Map<Gui, DhButtonEntry> BUTTON_BY_WIDGET = new HashMap<>();
#else
public static final Map<AbstractWidget, Component> TEXT_BY_WIDGET = new HashMap<>();
public static final Map<AbstractWidget, DhButtonEntry> BUTTON_BY_WIDGET = new HashMap<>();
#endif
public DhButtonEntry(
DhConfigScreen gui, AbstractConfigBase dhConfigType,
AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton)
#if MC_VER <= MC_1_12_2
public DhButtonEntry(DhConfigScreen gui, AbstractConfigBase dhConfigType, Gui button, ITextComponent text, GuiButton resetButton, GuiButton indexButton)
#else
public DhButtonEntry(DhConfigScreen gui, AbstractConfigBase dhConfigType, AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton)
#endif
{
TEXT_BY_WIDGET.put(button, text);
BUTTON_BY_WIDGET.put(button, this);
@@ -225,7 +363,9 @@ public class ClassicConfigGUI
@Override
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, boolean isSelected, float tickDelta)
#elif MC_VER < MC_1_20_1
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
#elif MC_VER < MC_1_21_9
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
@@ -250,18 +390,40 @@ public class ClassicConfigGUI
if (this.button != null)
{
#if MC_VER <= MC_1_12_2
if (this.button instanceof GuiButton guiButton)
{
SetY(guiButton, y);
guiButton.drawButton(Minecraft.getMinecraft(), mouseX, mouseY, tickDelta);
}
if (this.button instanceof GuiTextField guiTextField)
{
SetY(guiTextField, y);
guiTextField.drawTextBox();
}
#else
SetY(this.button, y);
{
#if MC_VER <= MC_1_21_11
this.button.render(matrices, mouseX, mouseY, tickDelta);
#else
this.button.extractRenderState(matrices, mouseX, mouseY, tickDelta);
#endif
}
#endif
}
if (this.resetButton != null)
{
#if MC_VER <= MC_1_12_2
SetY((GuiButton) this.resetButton, y);
#else
SetY(this.resetButton, y);
#if MC_VER <= MC_1_21_11
#endif
#if MC_VER <= MC_1_12_2
((GuiButton) this.resetButton).drawButton(Minecraft.getMinecraft(), mouseX, mouseY, tickDelta);
#elif MC_VER <= MC_1_21_11
this.resetButton.render(matrices, mouseX, mouseY, tickDelta);
#else
this.resetButton.extractRenderState(matrices, mouseX, mouseY, tickDelta);
@@ -270,8 +432,15 @@ public class ClassicConfigGUI
if (this.indexButton != null)
{
#if MC_VER <= MC_1_12_2
SetY((GuiButton) this.indexButton, y);
#else
SetY(this.indexButton, y);
#if MC_VER <= MC_1_21_11
#endif
#if MC_VER <= MC_1_12_2
((GuiButton) this.indexButton).drawButton(Minecraft.getMinecraft(), mouseX, mouseY, tickDelta);
#elif MC_VER <= MC_1_21_11
this.indexButton.render(matrices, mouseX, mouseY, tickDelta);
#else
this.indexButton.extractRenderState(matrices, mouseX, mouseY, tickDelta);
@@ -280,7 +449,11 @@ public class ClassicConfigGUI
if (this.text != null)
{
#if MC_VER <= MC_1_12_2
int translatedLength = textRenderer.getStringWidth(this.text.getFormattedText());
#else
int translatedLength = textRenderer.width(this.text);
#endif
int textXPos;
if (this.textPosition == EConfigCommentTextPosition.RIGHT_JUSTIFIED)
@@ -313,8 +486,12 @@ public class ClassicConfigGUI
throw new UnsupportedOperationException("No text position render defined for [" + this.textPosition + "]");
}
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
textRenderer.drawString(
this.text.getFormattedText(),
textXPos, y + 5,
0xFFFFFF);
#elif MC_VER < MC_1_20_1
GuiComponent.drawString(matrices, textRenderer,
this.text,
textXPos, y + 5,
@@ -344,9 +521,25 @@ public class ClassicConfigGUI
}
}
#if MC_VER <= MC_1_12_2
@Override
public void updatePosition(int slotIndex, int x, int y, float partialTicks) { }
@Override
public boolean mousePressed(int slotIndex, int mouseX, int mouseY, int mouseEvent, int relativeX, int relativeY)
{
return false; // handled in DhConfigScreen.mouseClicked
}
@Override
public void mouseReleased(int slotIndex, int x, int y, int mouseEvent, int relativeX, int relativeY) { }
#endif
#if MC_VER > MC_1_12_2
@Override
public @NotNull List<? extends GuiEventListener> children()
{ return this.children; }
#endif
#if MC_VER >= MC_1_17_1
@Override
@@ -358,11 +551,14 @@ public class ClassicConfigGUI
}
//endregion
//================//
// event handling //
//================//
//region
public static class ConfigCoreInterface implements IConfigGui
{
@@ -381,4 +577,5 @@ public class ClassicConfigGUI
}
//endregion
}
@@ -13,14 +13,18 @@ import java.util.regex.Pattern;
import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui;
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
import com.seibel.distanthorizons.common.wrappers.gui.GuiHelper;
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
import com.seibel.distanthorizons.common.wrappers.gui.config.ConfigGuiInfo;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.types.*;
#if MC_VER <= MC_1_12_2
import com.seibel.distanthorizons.common.wrappers.gui.OnPressed;
#else
import com.seibel.distanthorizons.common.wrappers.gui.updater.ChangelogScreen;
#endif
import com.seibel.distanthorizons.core.config.types.enums.EConfigCommentTextPosition;
import com.seibel.distanthorizons.core.config.types.enums.EConfigValidity;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
@@ -30,8 +34,15 @@ import com.seibel.distanthorizons.core.util.AnnotationUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.gui.*;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
#else
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
@@ -40,12 +51,13 @@ import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
#endif
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
#elif MC_VER < MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.GuiComponent;
#elif MC_VER <= MC_1_21_11
@@ -58,14 +70,18 @@ import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.narration.NarratableEntry;
#endif
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
import net.minecraft.util.ResourceLocation;
#elif MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
#endif
import org.lwjgl.glfw.GLFW;
#if MC_VER > MC_1_12_2
import com.mojang.blaze3d.platform.InputConstants;
#endif
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.Translatable;
@@ -79,21 +95,34 @@ class DhConfigScreen extends DhScreen
private static final MinecraftClientWrapper MC_CLIENT = MinecraftClientWrapper.INSTANCE;
#if MC_VER <= MC_1_12_2
private final GuiScreen parent;
#else
private final Screen parent;
#endif
private final String category;
private ClassicConfigGUI.ConfigListWidget configListWidget;
private boolean reload = false;
#if MC_VER <= MC_1_12_2
private GuiButton doneButton;
#else
private Button doneButton;
#endif
//=============//
// constructor //
//=============//
//region
#if MC_VER <= MC_1_12_2
protected DhConfigScreen(GuiScreen parent, String category)
#else
protected DhConfigScreen(Screen parent, String category)
#endif
{
super(Translatable(
LANG_WRAPPER.langExists(ModInfo.ID + ".config" + (category.isEmpty() ? "." + category : "") + ".title") ?
@@ -104,25 +133,50 @@ class DhConfigScreen extends DhScreen
this.category = category;
}
//endregion
//===================//
// menu UI lifecycle //
//===================//
//region
@Override
#if MC_VER <= MC_1_12_2
public void updateScreen() { super.updateScreen(); }
#else
public void tick() { super.tick(); }
#endif
//endregion
//==================//
// menu UI creation //
//==================//
//region
@Override
#if MC_VER <= MC_1_12_2
public void initGui()
#else
protected void init()
#endif
{
#if MC_VER <= MC_1_12_2
super.initGui();
#else
super.init();
#endif
if (!this.reload)
{
ConfigHandler.INSTANCE.configFileHandler.loadFromFile();
}
#if MC_VER > MC_1_12_2
// Changelog button
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get()
// we only have changelogs for stable builds
@@ -161,6 +215,7 @@ class DhConfigScreen extends DhScreen
Translatable(ModInfo.ID + ".updater.title")
));
}
#endif
// back button
@@ -170,7 +225,11 @@ class DhConfigScreen extends DhScreen
(button) ->
{
ConfigHandler.INSTANCE.configFileHandler.loadFromFile();
#if MC_VER <= MC_1_12_2
Objects.requireNonNull(this.mc).displayGuiScreen(this.parent);
#else
Objects.requireNonNull(this.minecraft).setScreen(this.parent);
#endif
}));
// done/close button
@@ -181,19 +240,30 @@ class DhConfigScreen extends DhScreen
(button) ->
{
ConfigHandler.INSTANCE.configFileHandler.saveToFile();
#if MC_VER <= MC_1_12_2
Objects.requireNonNull(this.mc).displayGuiScreen(this.parent);
#else
Objects.requireNonNull(this.minecraft).setScreen(this.parent);
#endif
}));
#if MC_VER <= MC_1_12_2
this.configListWidget = new ClassicConfigGUI.ConfigListWidget(this.mc, this.width * 2, this.height, 32, 32, 25);
#else
this.configListWidget = new ClassicConfigGUI.ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, 32, 25);
#endif
#if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
#if MC_VER <= MC_1_12_2
#elif MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
if (this.minecraft != null && this.minecraft.level != null)
{
this.configListWidget.setRenderBackground(false);
}
#endif
#if MC_VER > MC_1_12_2
this.addWidget(this.configListWidget);
#endif
for (AbstractConfigBase<?> configEntry : ConfigHandler.INSTANCE.configBaseList)
{
@@ -356,18 +426,35 @@ class DhConfigScreen extends DhScreen
private static void setupBooleanMenuOption(ConfigEntry<Boolean> booleanConfigEntry)
{
// For boolean
#if MC_VER <= MC_1_12_2
Function<Object, ITextComponent> func = value -> Translatable("distanthorizons.general."+((Boolean) value ? "true" : "false")).setStyle(new Style().setColor((Boolean) value ? TextFormatting.GREEN : TextFormatting.RED));
#else
Function<Object, Component> func = value -> Translatable("distanthorizons.general." + ((Boolean) value ? "true" : "false")).withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
#endif
final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) booleanConfigEntry.guiValue);
configGuiInfo.buttonOptionMap =
#if MC_VER <= MC_1_12_2
new AbstractMap.SimpleEntry<OnPressed, Function<Object, ITextComponent>>(
#else
new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(
#endif
(button) ->
{
#if MC_VER <= MC_1_12_2
button.enabled = !booleanConfigEntry.apiIsOverriding();
#else
button.active = !booleanConfigEntry.apiIsOverriding();
#endif
booleanConfigEntry.uiSetWithoutSaving(!booleanConfigEntry.get());
#if MC_VER <= MC_1_12_2
button.displayString = func.apply(booleanConfigEntry.get()).getFormattedText();
#else
button.setMessage(func.apply(booleanConfigEntry.get()));
#endif
}, func);
}
private static void setupEnumMenuOption(ConfigEntry<Enum<?>> enumConfigEntry, Class<? extends Enum<?>> enumClass)
@@ -376,20 +463,29 @@ class DhConfigScreen extends DhScreen
final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) enumConfigEntry.guiValue);
#if MC_VER <= MC_1_12_2
Function<Object, ITextComponent > getEnumTranslatableFunc = (value) -> Translatable(TRANSLATION_PREFIX + "enum." + enumClass.getSimpleName() + "." + enumConfigEntry.get().toString());
#else
Function<Object, Component> getEnumTranslatableFunc = (value) -> Translatable(TRANSLATION_PREFIX + "enum." + enumClass.getSimpleName() + "." + enumConfigEntry.get().toString());
#endif
configGuiInfo.buttonOptionMap =
#if MC_VER <= MC_1_12_2
new AbstractMap.SimpleEntry<OnPressed, Function<Object, ITextComponent>>(
#else
new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(
#endif
(button) ->
{
// get the currently selected enum and enum index
int startingIndex = enumList.indexOf(enumConfigEntry.get());
Enum<?> enumValue = enumList.get(startingIndex);
boolean shiftPressed =
InputConstants.isKeyDown(MC_CLIENT.getGlfwWindowId(), GLFW.GLFW_KEY_LEFT_SHIFT)
|| InputConstants.isKeyDown(MC_CLIENT.getGlfwWindowId(), GLFW.GLFW_KEY_RIGHT_SHIFT);
#if MC_VER <= MC_1_12_2
boolean shiftPressed = GuiScreen.isShiftKeyDown();
#else
boolean shiftPressed = InputConstants.isKeyDown(MC_CLIENT.getGlfwWindowId(), GLFW.GLFW_KEY_LEFT_SHIFT) || InputConstants.isKeyDown(MC_CLIENT.getGlfwWindowId(), GLFW.GLFW_KEY_RIGHT_SHIFT);
#endif
// move forward or backwards depending on if the shift key is pressed
int index = shiftPressed ? startingIndex - 1 : startingIndex + 1;
@@ -441,9 +537,13 @@ class DhConfigScreen extends DhScreen
enumConfigEntry.uiSetWithoutSaving(enumValue);
#if MC_VER <= MC_1_12_2
button.enabled = !enumConfigEntry.apiIsOverriding();
button.displayString = getEnumTranslatableFunc.apply(enumConfigEntry.get()).getFormattedText();
#else
button.active = !enumConfigEntry.apiIsOverriding();
button.setMessage(getEnumTranslatableFunc.apply(enumConfigEntry.get()));
#endif
}, getEnumTranslatableFunc);
}
@@ -459,12 +559,18 @@ class DhConfigScreen extends DhScreen
//==============//
// reset button //
//==============//
//region
Button.OnPress btnAction = (button) ->
#if MC_VER <= MC_1_12_2 OnPressed #else Button.OnPress #endif btnAction = (button) ->
{
configEntry.uiSetWithoutSaving(configEntry.getDefaultValue());
this.reload = true;
Objects.requireNonNull(this.minecraft).setScreen(this);
#if MC_VER <= MC_1_12_2
Objects.requireNonNull(this.mc).displayGuiScreen(ClassicConfigGUI.getScreen(this.parent, this.category));
#else
Objects.requireNonNull(this.minecraft).setScreen(this.parent);
#endif
};
int resetButtonPosX = this.width
@@ -472,29 +578,60 @@ class DhConfigScreen extends DhScreen
- ClassicConfigGUI.ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN;
int resetButtonPosZ = 0;
Button resetButton = MakeBtn(
#if MC_VER <= MC_1_12_2 GuiButton #else Button #endif resetButton = MakeBtn(
#if MC_VER <= MC_1_12_2
Translatable("distanthorizons.general.reset").setStyle(new Style().setColor(TextFormatting.RED)),
#else
Translatable("distanthorizons.general.reset").withStyle(ChatFormatting.RED),
#endif
resetButtonPosX, resetButtonPosZ,
ClassicConfigGUI.ConfigScreenConfigs.RESET_BUTTON_WIDTH, ClassicConfigGUI.ConfigScreenConfigs.RESET_BUTTON_HEIGHT,
btnAction);
if (configEntry.apiIsOverriding())
if (configEntry.mcVersionOverridePresent())
{
#if MC_VER <= MC_1_12_2
resetButton.enabled = false;
resetButton.displayString = Translatable("distanthorizons.general.unsupportedMcVersion").setStyle(new Style().setColor(TextFormatting.DARK_GRAY)).getFormattedText();
#else
resetButton.active = false;
resetButton.setMessage(Translatable("distanthorizons.general.unsupportedMcVersion").withStyle(ChatFormatting.DARK_GRAY));
#endif
}
else if (configEntry.apiIsOverriding())
{
#if MC_VER <= MC_1_12_2
resetButton.enabled = false;
resetButton.displayString = Translatable("distanthorizons.general.apiOverride").setStyle(new Style().setColor(TextFormatting.DARK_GRAY)).getFormattedText();
#else
resetButton.active = false;
resetButton.setMessage(Translatable("distanthorizons.general.apiOverride").withStyle(ChatFormatting.DARK_GRAY));
#endif
}
else
{
#if MC_VER <= MC_1_12_2
resetButton.enabled = true;
#else
resetButton.active = true;
#endif
}
//endregion
//==============//
// option field //
//==============//
//region
#if MC_VER <= MC_1_12_2
ITextComponent textComponent = this.GetTranslatableTextComponentForConfig(configEntry);
#else
Component textComponent = this.GetTranslatableTextComponentForConfig(configEntry);
#endif
int optionFieldPosX = this.width
- ClassicConfigGUI.ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN
@@ -506,21 +643,40 @@ class DhConfigScreen extends DhScreen
if (configGuiInfo.buttonOptionMap != null)
{
// enum/multi option input button
#if MC_VER <= MC_1_12_2
Map.Entry<OnPressed, Function<Object,ITextComponent>> widget = configGuiInfo.buttonOptionMap;
#else
Map.Entry<Button.OnPress, Function<Object, Component>> widget = configGuiInfo.buttonOptionMap;
#endif
if (configEntry.getType().isEnum())
{
widget.setValue((value) -> Translatable(TRANSLATION_PREFIX + "enum." + configEntry.getType().getSimpleName() + "." + configEntry.get().toString()));
}
#if MC_VER <= MC_1_12_2
GuiButton button = MakeBtn(
#else
Button button = MakeBtn(
#endif
widget.getValue().apply(configEntry.get()),
optionFieldPosX, optionFieldPosZ,
ClassicConfigGUI.ConfigScreenConfigs.OPTION_FIELD_WIDTH, ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT,
widget.getKey());
// deactivate the button if the API is overriding it
button.active = !configEntry.apiIsOverriding();
// or the MC version doesn't support it
if (configEntry.mcVersionOverridePresent()
|| configEntry.apiIsOverriding())
{
#if MC_VER <= MC_1_12_2
button.enabled = false;
#else
button.active = false;
#endif
}
this.configListWidget.addButton(this, configEntry,
@@ -534,16 +690,25 @@ class DhConfigScreen extends DhScreen
else
{
// text box input
#if MC_VER <= MC_1_12_2
GuiTextField widget = new GuiTextField(0, this.fontRenderer,
optionFieldPosX, optionFieldPosZ,
ClassicConfigGUI.ConfigScreenConfigs.OPTION_FIELD_WIDTH - 4, ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT);
widget.setMaxStringLength(3_000_000); // hopefully 3 million characters should be enough for any normal use-case, lol
widget.setText(String.valueOf(configEntry.get()));
#else
EditBox widget = new EditBox(this.font,
optionFieldPosX, optionFieldPosZ,
ClassicConfigGUI.ConfigScreenConfigs.OPTION_FIELD_WIDTH - 4, ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT,
Translatable(""));
widget.setMaxLength(3_000_000); // hopefully 3 million characters should be enough for any normal use-case, lol
widget.insertText(String.valueOf(configEntry.get()));
#endif
Predicate<String> processor = configGuiInfo.tooltipFunction.apply(widget, this.doneButton);
#if MC_VER <= MC_1_21_11
#if MC_VER <= MC_1_12_2
widget.setValidator(processor::test);
#elif MC_VER <= MC_1_21_11
widget.setFilter(processor);
#else
widget.setResponder(processor::test);
@@ -553,6 +718,8 @@ class DhConfigScreen extends DhScreen
return true;
}
//endregion
}
return false;
@@ -563,18 +730,31 @@ class DhConfigScreen extends DhScreen
{
ConfigCategory configCategory = (ConfigCategory) configType;
#if MC_VER <= MC_1_12_2
ITextComponent textComponent = this.GetTranslatableTextComponentForConfig(configCategory);
#else
Component textComponent = this.GetTranslatableTextComponentForConfig(configCategory);
#endif
int categoryPosX = this.width - ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH - ClassicConfigGUI.ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN;
int categoryPosZ = this.height - ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT; // Note: the posZ value here seems to be ignored
Button widget = MakeBtn(textComponent,
#if MC_VER <= MC_1_12_2
GuiButton widget = MakeBtn(
#else
Button widget = MakeBtn(
#endif
textComponent,
categoryPosX, categoryPosZ,
ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH, ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT,
((button) ->
{
ConfigHandler.INSTANCE.configFileHandler.saveToFile();
#if MC_VER <= MC_1_12_2
Objects.requireNonNull(this.mc).displayGuiScreen(ClassicConfigGUI.getScreen(this, configCategory.getDestination()));
#else
Objects.requireNonNull(this.minecraft).setScreen(ClassicConfigGUI.getScreen(this, configCategory.getDestination()));
#endif
}));
this.configListWidget.addButton(this, configType, widget, null, null, null);
@@ -589,11 +769,20 @@ class DhConfigScreen extends DhScreen
{
ConfigUIButton configUiButton = (ConfigUIButton) configType;
#if MC_VER <= MC_1_12_2
ITextComponent textComponent = this.GetTranslatableTextComponentForConfig(configUiButton);
#else
Component textComponent = this.GetTranslatableTextComponentForConfig(configUiButton);
#endif
int buttonPosX = this.width - ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH - ClassicConfigGUI.ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN;
Button widget = MakeBtn(textComponent,
#if MC_VER <= MC_1_12_2
GuiButton widget = MakeBtn(
#else
Button widget = MakeBtn(
#endif
textComponent,
buttonPosX, this.height - 28,
ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH, ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT,
(button) -> ((ConfigUIButton) configType).runAction());
@@ -610,7 +799,11 @@ class DhConfigScreen extends DhScreen
{
ConfigUIComment configUiComment = (ConfigUIComment) configType;
#if MC_VER <= MC_1_12_2
ITextComponent textComponent = this.GetTranslatableTextComponentForConfig(configUiComment);
#else
Component textComponent = this.GetTranslatableTextComponentForConfig(configUiComment);
#endif
if (configUiComment.parentConfigPath != null)
{
textComponent = Translatable(TRANSLATION_PREFIX + configUiComment.parentConfigPath);
@@ -627,7 +820,12 @@ class DhConfigScreen extends DhScreen
{
if (configType instanceof ConfigUISpacer)
{
Button spacerButton = MakeBtn(Translatable("distanthorizons.general.spacer"),
#if MC_VER <= MC_1_12_2
GuiButton spacerButton = MakeBtn(
#else
Button spacerButton = MakeBtn(
#endif
Translatable("distanthorizons.general.spacer"),
10, 10, // having too small of a size causes division by 0 errors in older MC versions (IE 1.20.1)
1, 1,
(button) -> { });
@@ -652,25 +850,36 @@ class DhConfigScreen extends DhScreen
return false;
}
#if MC_VER <= MC_1_12_2
private ITextComponent GetTranslatableTextComponentForConfig(AbstractConfigBase<?> configType)
#else
private Component GetTranslatableTextComponentForConfig(AbstractConfigBase<?> configType)
#endif
{ return Translatable(TRANSLATION_PREFIX + configType.getNameAndCategory()); }
//endregion
//===========//
// rendering //
//===========//
//region
@Override
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
public void drawScreen(int mouseX, int mouseY, float delta)
#elif MC_VER < MC_1_20_1
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
#elif MC_VER <= MC_1_21_11
#elif MC_VER <= MC_1_21_11
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#else
#else
public void extractRenderState(GuiGraphicsExtractor matrices, int mouseX, int mouseY, float delta)
#endif
{
#if MC_VER < MC_1_20_2 // 1.20.2 now enables this by default in the `this.list.render` function
#if MC_VER <= MC_1_12_2
this.drawDefaultBackground();
#elif MC_VER < MC_1_20_2 // 1.20.2 now enables this by default in the `this.list.render` function
this.renderBackground(matrices);
#elif MC_VER <= MC_1_21_11
super.render(matrices, mouseX, mouseY, delta);
@@ -679,7 +888,9 @@ class DhConfigScreen extends DhScreen
#endif
// Render buttons
#if MC_VER <= MC_1_21_11
#if MC_VER <= MC_1_12_2
this.configListWidget.drawScreen(mouseX, mouseY, delta);
#elif MC_VER <= MC_1_21_11
this.configListWidget.render(matrices, mouseX, mouseY, delta);
#else
this.configListWidget.extractRenderState(matrices, mouseX, mouseY, delta);
@@ -687,51 +898,74 @@ class DhConfigScreen extends DhScreen
// Render config title
this.DhDrawCenteredString(matrices, this.font, this.title,
this.DhDrawCenteredString(
#if MC_VER > MC_1_12_2
matrices, this.font,
#endif
this.title,
this.width / 2, 15,
#if MC_VER < MC_1_21_6
0xFFFFFF // RGB white
#else
0xFFFFFFFF // ARGB white
#endif );
#endif);
// render DH version
this.DhDrawString(matrices, this.font, TextOrLiteral(ModInfo.VERSION), 2, this.height - 10,
this.DhDrawString(
#if MC_VER > MC_1_12_2
matrices, this.font,
#endif
TextOrLiteral(ModInfo.VERSION), 2, this.height - 10,
#if MC_VER < MC_1_21_6
0xAAAAAA // RGB white
#else
0xFFAAAAAA // ARGB white
#endif );
#endif);
// If the update is pending, display this message to inform the user that it will apply when the game restarts
if (SelfUpdater.deleteOldJarOnJvmShutdown)
{
this.DhDrawString(matrices, this.font, Translatable(ModInfo.ID + ".updater.waitingForClose"), 4, this.height - 42,
this.DhDrawString(
#if MC_VER > MC_1_12_2
matrices, this.font,
#endif
Translatable(ModInfo.ID + ".updater.waitingForClose"), 4, this.height - 42,
#if MC_VER < MC_1_21_6
0xFFFFFF // RGB white
#else
0xFFFFFFFF // ARGB white
#endif );
#endif);
}
#if MC_VER <= MC_1_12_2
this.renderTooltip(mouseX, mouseY, delta);
#else
this.renderTooltip(matrices, mouseX, mouseY, delta);
#endif
#if MC_VER < MC_1_20_2
#if MC_VER <= MC_1_12_2
super.drawScreen(mouseX, mouseY, delta);
#elif MC_VER < MC_1_20_2
super.render(matrices, mouseX, mouseY, delta);
#endif
}
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
private void renderTooltip(int mouseX, int mouseY, float delta)
#elif MC_VER < MC_1_20_1
private void renderTooltip(PoseStack matrices, int mouseX, int mouseY, float delta)
#elif MC_VER <= MC_1_21_11
private void renderTooltip(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#else
#else
private void renderTooltip(GuiGraphicsExtractor matrices, int mouseX, int mouseY, float delta)
#endif
{
#if MC_VER <= MC_1_12_2
Gui hoveredWidget = this.configListWidget.getHoveredButton(mouseX, mouseY);
#else
AbstractWidget hoveredWidget = this.configListWidget.getHoveredButton(mouseX, mouseY);
#endif
if (hoveredWidget == null)
{
return;
@@ -747,14 +981,21 @@ class DhConfigScreen extends DhScreen
button.dhConfigType;
boolean apiOverrideActive = false;
boolean unsupportedMcVersion = false;
if (configBase instanceof ConfigEntry)
{
apiOverrideActive = ((ConfigEntry<?>) configBase).apiIsOverriding();
unsupportedMcVersion = ((ConfigEntry<?>) configBase).mcVersionOverridePresent();
}
String key = TRANSLATION_PREFIX + (configBase.category.isEmpty() ? "" : configBase.category + ".") + configBase.getName() + ".@tooltip";
if (apiOverrideActive)
if (unsupportedMcVersion)
{
key = "distanthorizons.general.unsupportedMcVersion.@tooltip";
}
else if (apiOverrideActive)
{
key = "distanthorizons.general.disabledByApi.@tooltip";
}
@@ -763,37 +1004,116 @@ class DhConfigScreen extends DhScreen
final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) configBase.guiValue);
if (configGuiInfo.errorMessage != null)
{
#if MC_VER <= MC_1_12_2
this.DhRenderTooltip(configGuiInfo.errorMessage, mouseX, mouseY);
#else
this.DhRenderTooltip(matrices, this.font, configGuiInfo.errorMessage, mouseX, mouseY);
#endif
}
// display the tooltip if present
else if (LANG_WRAPPER.langExists(key))
{
#if MC_VER <= MC_1_12_2
List<ITextComponent> list = new ArrayList<>();
#else
List<Component> list = new ArrayList<>();
#endif
String lang = LANG_WRAPPER.getLang(key);
for (String langLine : lang.split("\n"))
{
list.add(TextOrTranslatable(langLine));
}
#if MC_VER <= MC_1_12_2
this.DhRenderComponentTooltip(list, mouseX, mouseY);
#else
this.DhRenderComponentTooltip(matrices, this.font, list, mouseX, mouseY);
#endif
}
}
#if MC_VER <= MC_1_12_2
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws java.io.IOException
{
super.mouseClicked(mouseX, mouseY, mouseButton);
if (mouseY >= this.configListWidget.top && mouseY <= this.configListWidget.bottom)
{
for (ClassicConfigGUI.DhButtonEntry entry : this.configListWidget.children)
{
if (entry.button instanceof GuiButton btn && btn.visible)
{
if (btn.mousePressed(this.mc, mouseX, mouseY))
{
btn.playPressSound(this.mc.getSoundHandler());
OnPressed handler = GuiHelper.HANDLER_BY_BUTTON.get(btn);
if (handler != null) handler.pressed(btn);
}
}
else if (entry.button instanceof GuiTextField field && field.getVisible())
{
field.mouseClicked(mouseX, mouseY, mouseButton);
}
if (entry.resetButton instanceof GuiButton reset && reset.visible)
{
if (reset.mousePressed(this.mc, mouseX, mouseY))
{
reset.playPressSound(this.mc.getSoundHandler());
OnPressed handler = GuiHelper.HANDLER_BY_BUTTON.get(reset);
if (handler != null) handler.pressed(reset);
}
}
}
}
}
@Override
protected void keyTyped(char typedChar, int keyCode) throws java.io.IOException
{
super.keyTyped(typedChar, keyCode);
for (ClassicConfigGUI.DhButtonEntry entry : this.configListWidget.children)
{
if (entry.button instanceof GuiTextField field)
{
field.textboxKeyTyped(typedChar, keyCode);
}
}
}
@Override
public void handleMouseInput() throws java.io.IOException
{
super.handleMouseInput();
this.configListWidget.handleMouseInput();
}
#endif
//endregion
//==========//
// shutdown //
//==========//
//region
/** When you close it, it goes to the previous screen and saves */
@Override
#if MC_VER <= MC_1_12_2
public void onGuiClosed()
#else
public void onClose()
#endif
{
ConfigHandler.INSTANCE.configFileHandler.saveToFile();
#if MC_VER > MC_1_12_2
Objects.requireNonNull(this.minecraft).setScreen(this.parent);
#endif
ClassicConfigGUI.CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
}
//endregion
}
@@ -2,9 +2,16 @@ package com.seibel.distanthorizons.common.wrappers.gui.config;
import com.seibel.distanthorizons.core.config.gui.IConfigGuiInfo;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
#if MC_VER <= MC_1_12_2
import com.seibel.distanthorizons.common.wrappers.gui.OnPressed;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.util.text.ITextComponent;
#else
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.network.chat.Component;
#endif
import org.jetbrains.annotations.Nullable;
import java.util.AbstractMap;
@@ -23,11 +30,25 @@ public class ConfigGuiInfo implements IConfigGuiInfo
* Used to display validation errors.
* Null if no error is present.
*/
@Nullable
public Component errorMessage;
public BiFunction<EditBox, Button, Predicate<String>> tooltipFunction;
@Nullable
#if MC_VER <= MC_1_12_2
public ITextComponent errorMessage;
#else
public Component errorMessage;
#endif
#if MC_VER <= MC_1_12_2
public BiFunction<GuiTextField, GuiButton, Predicate<String>> tooltipFunction;
#else
public BiFunction<EditBox, Button , Predicate<String>> tooltipFunction;
#endif
/** determines which options the button will show */
#if MC_VER <= MC_1_12_2
public AbstractMap.SimpleEntry<OnPressed, Function<Object, ITextComponent>> buttonOptionMap;
#else
public AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>> buttonOptionMap;
#endif
}
@@ -1,5 +1,5 @@
package com.seibel.distanthorizons.common.wrappers.gui.updater;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -297,3 +297,4 @@ public class ChangelogScreen extends DhScreen
}
}
#endif
@@ -1,5 +1,5 @@
package com.seibel.distanthorizons.common.wrappers.gui.updater;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
@@ -222,3 +222,4 @@ public class UpdateModScreen extends DhScreen
}
}
#endif
@@ -1,20 +1,43 @@
package com.seibel.distanthorizons.common.wrappers.level;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.multiplayer.WorldClient;
#else
import net.minecraft.client.multiplayer.ClientLevel;
#endif
import net.minecraft.client.Minecraft;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
public class KeyedClientLevelManager implements IKeyedClientLevelManager
{
public static final KeyedClientLevelManager INSTANCE = new KeyedClientLevelManager();
/** This is set and managed by the ClientApi for servers with support for DH. */
@Nullable
private IServerKeyedClientLevel serverKeyedLevel = null;
private static class KeyInfo {
public final String serverKey;
public final String levelKey;
public KeyInfo(String serverKey, String levelKey) {
this.serverKey = serverKey;
this.levelKey = levelKey;
}
}
/** Stores the server-provided keys indexed by dimension name for persistence. */
private final Map<String, KeyInfo> keysByDimensionName = new ConcurrentHashMap<>();
/** Cache for already keyed level wrappers to maintain object identity. */
private final Map<#if MC_VER > MC_1_12_2 ClientLevel #else WorldClient #endif, IServerKeyedClientLevel> keyedLevelsCache = Collections.synchronizedMap(new WeakHashMap<>());
/** Allows to keep level manager enabled between loading different keyed levels */
private boolean enabled = false;
private volatile boolean enabled = false;
@@ -22,34 +45,131 @@ public class KeyedClientLevelManager implements IKeyedClientLevelManager
//=============//
// constructor //
//=============//
//region
private KeyedClientLevelManager() { }
//endregion
//======================//
// level override logic //
//======================//
//region
@Override
@Nullable
public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; }
public IServerKeyedClientLevel getServerKeyedLevel()
{
#if MC_VER > MC_1_12_2
return this.getServerKeyedLevel(Minecraft.getInstance().level);
#else
return this.getServerKeyedLevel(Minecraft.getMinecraft().world);
#endif
}
@Nullable
public IServerKeyedClientLevel getServerKeyedLevel(@Nullable #if MC_VER > MC_1_12_2 ClientLevel #else WorldClient #endif level)
{
if (level == null)
{
return null;
}
// We synchronize on the cache map to ensure atomicity of the lookup-and-populate sequence.
// This prevents multiple threads from creating duplicate wrappers for the same level.
synchronized (this.keyedLevelsCache)
{
// Check the cache first
IServerKeyedClientLevel cached = this.keyedLevelsCache.get(level);
if (cached != null)
{
return cached;
}
// Determine the dimension name for this level
// We use bypassLevelKeyManager=true to avoid recursion back into this manager
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level, true);
if (wrappedLevel == null)
{
return null;
}
String dimensionName = wrappedLevel.getDimensionName();
KeyInfo info = this.keysByDimensionName.get(dimensionName);
if (info == null)
{
return null;
}
// Create and cache a new keyed wrapper
IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevelWrapper(level, info.serverKey, info.levelKey);
this.keyedLevelsCache.put(level, keyedLevel);
return keyedLevel;
}
}
@Override
public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String serverKey, String levelKey)
{
IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevelWrapper((ClientLevel) clientLevel.getWrappedMcObject(), serverKey, levelKey);
this.serverKeyedLevel = keyedLevel;
// 1. Determine the target dimension name
String targetDimensionName = clientLevel.getDimensionName();
int separatorIndex = levelKey.lastIndexOf("@");
if (separatorIndex != -1)
{
targetDimensionName = levelKey.substring(separatorIndex + 1);
}
final String finalTargetDimensionName = targetDimensionName;
// 2. Store the key for this dimension
this.keysByDimensionName.put(finalTargetDimensionName, new KeyInfo(serverKey, levelKey));
this.enabled = true;
return keyedLevel;
// 3. Clear the cache for this dimension to ensure new wrappers are created with the new key
// (though in practice keys shouldn't change mid-session)
//
// We synchronize manually on the map to ensure atomicity of the compound removal operation
// and to prevent race conditions or deadlocks with other threads accessing the map.
// We avoid calling ClientLevelWrapper.getWrapper() inside the lock to prevent circular lock dependencies.
synchronized (this.keyedLevelsCache)
{
this.keyedLevelsCache.keySet().removeIf(level -> {
#if MC_VER <= MC_1_12_2
String levelDim = level.provider.getDimensionType().getName() + ":" + level.provider.getDimension();
#elif MC_VER <= MC_1_21_10
String levelDim = level.dimension().location().toString();
#else
String levelDim = level.dimension().identifier().toString();
#endif
return levelDim.equals(finalTargetDimensionName);
});
}
// 4. Return the keyed wrapper for whatever level the core passed us,
// but only if it matches the dimension we just keyed.
return this.getServerKeyedLevel((#if MC_VER > MC_1_12_2 ClientLevel #else WorldClient #endif) clientLevel.getWrappedMcObject());
}
@Override
public void clearKeyedLevel() { this.serverKeyedLevel = null; }
public void clearKeyedLevel()
{
synchronized (this.keyedLevelsCache)
{
this.keyedLevelsCache.clear();
this.keysByDimensionName.clear();
}
}
@Override
public boolean isEnabled() { return this.enabled; }
@Override
public void disable() { this.enabled = false; }
@Override
public void disable()
{
this.enabled = false;
this.clearKeyedLevel();
}
}
@@ -2,7 +2,11 @@ package com.seibel.distanthorizons.common.wrappers.level;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.multiplayer.WorldClient;
#else
import net.minecraft.client.multiplayer.ClientLevel;
#endif
public class ServerKeyedClientLevelWrapper extends ClientLevelWrapper implements IServerKeyedClientLevel
{
@@ -17,21 +21,30 @@ public class ServerKeyedClientLevelWrapper extends ClientLevelWrapper implements
//=============//
// constructor //
//=============//
//region
#if MC_VER <= MC_1_12_2
public ServerKeyedClientLevelWrapper(WorldClient level, String serverKey, String serverLevelKey)
#else
public ServerKeyedClientLevelWrapper(ClientLevel level, String serverKey, String serverLevelKey)
#endif
{
super(level);
this.serverKey = serverKey;
this.serverLevelKey = serverLevelKey;
}
//endregion
@Override
public String getServerKey() { return this.serverKey; }
//======================//
// level identification //
//======================//
//region
@Override
public String getServerKey() { return this.serverKey; }
@Override
public String getServerLevelKey() { return this.serverLevelKey; }
@@ -39,6 +52,8 @@ public class ServerKeyedClientLevelWrapper extends ClientLevelWrapper implements
@Override
public String getDhIdentifier() { return this.getServerLevelKey(); }
//endregion
}
@@ -20,13 +20,20 @@
package com.seibel.distanthorizons.common.wrappers.minecraft;
import java.io.File;
import java.util.Arrays;
#if MC_VER > MC_1_12_2
import com.mojang.blaze3d.platform.Window;
#endif
import com.seibel.distanthorizons.common.wrappers.gui.NativeDialogUtil;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IImmersivePortalsAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
@@ -36,19 +43,35 @@ import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.logging.DhLogger;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ServerData;
#if MC_VER <= MC_1_12_2
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.crash.CrashReport;
import net.minecraft.profiler.Profiler;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.world.DimensionType;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.DimensionManager;
#else
import net.minecraft.CrashReport;
import net.minecraft.client.CloudStatus;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
#endif
import org.jetbrains.annotations.Nullable;
#if MC_VER < MC_1_19_2
#if MC_VER < MC_1_19_2 && MC_VER > MC_1_12_2
import net.minecraft.network.chat.TextComponent;
#endif
@@ -57,11 +80,26 @@ import net.minecraft.network.chat.TextComponent;
import net.minecraft.util.profiling.Profiler;
#endif
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_21_10 && MC_VER > MC_1_12_2
import net.minecraft.client.GraphicsStatus;
#else
#endif
#if MC_VER <= MC_1_12_2
import net.minecraft.util.ResourceLocation;
#elif MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
#endif
#if MC_VER > MC_1_19_2
import net.minecraft.core.registries.Registries;
#elif MC_VER > MC_1_12_2
import net.minecraft.core.Registry;
#endif
/**
* A singleton that wraps the Minecraft object.
*
@@ -70,13 +108,23 @@ import net.minecraft.client.GraphicsStatus;
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER <= MC_1_12_2
private static final Minecraft MINECRAFT = Minecraft.getMinecraft();
#else
private static final Minecraft MINECRAFT = Minecraft.getInstance();
#endif
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
private ProfilerWrapper profilerWrapper;
// Need to classload this field later because otherwise it will be null even when Immersive Portals is present.
public static class Late {
private static final IImmersivePortalsAccessor IMMERSIVE_PORTALS = ModAccessorInjector.INSTANCE.get(IImmersivePortalsAccessor.class);
}
//======================//
@@ -85,20 +133,37 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
//region
@Override
public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); }
public boolean hasSinglePlayerServer()
{
#if MC_VER <= MC_1_12_2
return MINECRAFT.isSingleplayer();
#else
return MINECRAFT.hasSingleplayerServer();
#endif
}
@Override
public boolean clientConnectedToDedicatedServer()
{
return MINECRAFT.getCurrentServer() != null
return this.hasServerConnection()
&& !this.hasSinglePlayerServer();
}
@Override
public boolean connectedToReplay()
{
return MINECRAFT.getCurrentServer() == null
return !this.hasServerConnection()
&& !this.hasSinglePlayerServer() ;
}
private boolean hasServerConnection()
{
#if MC_VER <= MC_1_12_2
return MINECRAFT.getCurrentServerData() != null;
#else
return MINECRAFT.getCurrentServer() != null;
#endif
}
@Override
public String getCurrentServerName()
{
@@ -108,8 +173,13 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
}
else
{
#if MC_VER <= MC_1_12_2
ServerData server = MINECRAFT.getCurrentServerData();
return (server != null) ? server.serverName : "NULL";
#else
ServerData server = MINECRAFT.getCurrentServer();
return (server != null) ? server.name : "NULL";
#endif
}
}
@Override
@@ -121,15 +191,44 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
}
else
{
ServerData server = MINECRAFT.getCurrentServer();
return (server != null) ? server.ip : "NA";
ServerData server = getCurrentServerData();
return getServerIp(server);
}
}
@Override
public String getCurrentServerVersion()
{
ServerData server = MINECRAFT.getCurrentServer();
return (server != null) ? server.version.getString() : "UNKOWN";
ServerData server = getCurrentServerData();
return getServerVersion(server);
}
private ServerData getCurrentServerData()
{
#if MC_VER <= MC_1_12_2
return MINECRAFT.getCurrentServerData();
#else
return MINECRAFT.getCurrentServer();
#endif
}
private String getServerIp(ServerData server)
{
if (server == null) { return "NA"; }
#if MC_VER <= MC_1_12_2
return server.serverIP;
#else
return server.ip;
#endif
}
private String getServerVersion(ServerData server)
{
if (server == null) { return "UNKOWN"; }
#if MC_VER <= MC_1_12_2
return server.gameVersion;
#else
return server.version.getString();
#endif
}
//endregion
@@ -141,7 +240,11 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
//=================//
//region
#if MC_VER <= MC_1_12_2
public EntityPlayerSP getPlayer() { return MINECRAFT.player; }
#else
public LocalPlayer getPlayer() { return MINECRAFT.player; }
#endif
@Override
public boolean playerExists() { return MINECRAFT.player != null; }
@@ -149,26 +252,52 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public DhBlockPos getPlayerBlockPos()
{
#if MC_VER <= MC_1_12_2
EntityPlayerSP player = this.getPlayer();
#else
LocalPlayer player = this.getPlayer();
#endif
if (player == null)
{
return new DhBlockPos(0, 0, 0);
}
if (Late.IMMERSIVE_PORTALS != null)
{
DhBlockPos pos = Late.IMMERSIVE_PORTALS.getOriginalPlayerBlockPos();
if (pos != null) return pos;
}
#if MC_VER <= MC_1_12_2
BlockPos playerPos = player.getPosition();
#else
BlockPos playerPos = player.blockPosition();
#endif
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
}
@Override
public DhChunkPos getPlayerChunkPos()
{
#if MC_VER <= MC_1_12_2
EntityPlayerSP player = this.getPlayer();
#else
LocalPlayer player = this.getPlayer();
#endif
if (player == null)
{
return new DhChunkPos(0, 0);
}
#if MC_VER < MC_1_17_1
if (Late.IMMERSIVE_PORTALS != null)
{
DhChunkPos pos = Late.IMMERSIVE_PORTALS.getOriginalPlayerChunkPos();
if (pos != null) return pos;
}
#if MC_VER <= MC_1_12_2
ChunkPos playerPos = new ChunkPos(player.getPosition());
#elif MC_VER < MC_1_17_1
ChunkPos playerPos = new ChunkPos(player.blockPosition());
#else
ChunkPos playerPos = player.chunkPosition();
@@ -198,7 +327,16 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Nullable
public IClientLevelWrapper getWrappedClientLevel(boolean bypassLevelKeyManager)
{
if (!bypassLevelKeyManager && Late.IMMERSIVE_PORTALS != null)
{
IClientLevelWrapper level = Late.IMMERSIVE_PORTALS.getOriginalClientLevelWrapper();
if (level != null) return level;
}
#if MC_VER <= MC_1_12_2
WorldClient level = MINECRAFT.world;
#else
ClientLevel level = MINECRAFT.level;
#endif
if (level == null)
{
return null;
@@ -219,13 +357,19 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public void sendChatMessage(String string)
{
#if MC_VER <= MC_1_12_2
EntityPlayerSP player = this.getPlayer();
#else
LocalPlayer player = this.getPlayer();
#endif
if (player == null)
{
return;
}
#if MC_VER < MC_1_19_2
#if MC_VER <= MC_1_12_2
player.sendMessage(new TextComponentString(string));
#elif MC_VER < MC_1_19_2
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
#elif MC_VER < MC_1_21_9
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
@@ -245,13 +389,19 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public void sendOverlayMessage(String string)
{
#if MC_VER <= MC_1_12_2
EntityPlayerSP player = this.getPlayer();
#else
LocalPlayer player = this.getPlayer();
#endif
if (player == null)
{
return;
}
#if MC_VER < MC_1_19_2
#if MC_VER <= MC_1_12_2
MINECRAFT.ingameGUI.setOverlayMessage(string, /*animateColor*/false);
#elif MC_VER < MC_1_19_2
player.displayClientMessage(new TextComponent(string), /*isOverlay*/true);
#elif MC_VER <= MC_1_21_11
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/true);
@@ -273,7 +423,9 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
{
LOGGER.info("Disabling vanilla clouds... This is done to prevent vanilla clouds from rendering on top of Distant Horizons LODs.");
#if MC_VER <= MC_1_18_2
#if MC_VER <= MC_1_12_2
MINECRAFT.gameSettings.clouds = 0;
#elif MC_VER <= MC_1_18_2
MINECRAFT.options.renderClouds = CloudStatus.OFF;
#else
MINECRAFT.options.cloudStatus().set(CloudStatus.OFF);
@@ -282,11 +434,13 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
public void disableVanillaChunkFadeIn()
{
LOGGER.info("Disabling vanilla chunk fade in... This is done to prevent vanilla chunks from flashing on the Distant Horizons boarder when moving (which is distracting).");
String message = "Disabling vanilla chunk fade in... This is done to prevent vanilla chunks from flashing on the Distant Horizons boarder when moving (which is distracting).";
#if MC_VER <= MC_1_21_10
// chunk fade in was added MC 1.21.11
#else
LOGGER.info(message);
MINECRAFT.options.chunkSectionFadeInTime().set(0.0);
#endif
}
@@ -295,7 +449,9 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
{
String reasoning = "This is done to fix vanilla chunks (specifically water blocks) not fading into Distant Horizons LODs when DH's 'Vanilla Fade' option is enabled.";
#if MC_VER <= MC_1_18_2
#if MC_VER <= MC_1_12_2
// fabulous graphics was added in MC 1.16
#elif MC_VER <= MC_1_18_2
LOGGER.info("Disabling fabulous graphics... "+reasoning);
GraphicsStatus oldGraphicsStatus = MINECRAFT.options.graphicsMode;
@@ -331,6 +487,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
* no override and not included in {@link IMinecraftClientWrapper}
* since this would only be used in common/client, not core.
*/
#if MC_VER > MC_1_12_2
public
#if MC_VER < MC_1_21_9 long
#else Window
@@ -344,12 +501,20 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
return MINECRAFT.getWindow();
#endif
}
#endif
@Override
public IProfilerWrapper getProfiler()
{
#if MC_VER <= MC_1_12_2
Profiler profiler;
#else
ProfilerFiller profiler;
#if MC_VER < MC_1_21_3
#endif
#if MC_VER <= MC_1_12_2
profiler = MINECRAFT.profiler;
#elif MC_VER < MC_1_21_3
profiler = MINECRAFT.getProfiler();
#else
profiler = Profiler.get();
@@ -378,7 +543,9 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
executeOnRenderThread(() ->
{
CrashReport report = new CrashReport(errorMessage, exception);
#if MC_VER < MC_1_20_4
#if MC_VER <= MC_1_12_2
MINECRAFT.crashed(report);
#elif MC_VER < MC_1_20_4
Minecraft.crash(report);
#else
MINECRAFT.delayCrash(report);
@@ -387,7 +554,14 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
}
@Override
public void executeOnRenderThread(Runnable runnable) { MINECRAFT.execute(runnable); }
public void executeOnRenderThread(Runnable runnable)
{
#if MC_VER <= MC_1_12_2
MINECRAFT.addScheduledTask(runnable);
#else
MINECRAFT.execute(runnable);
#endif
}
@Override
public void showDialog(String title, String message, String dialogType, String iconType)
@@ -403,7 +577,14 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
//region
@Override
public Object getOptionsObject() { return MINECRAFT.options; }
public Object getOptionsObject()
{
#if MC_VER <= MC_1_12_2
return MINECRAFT.gameSettings;
#else
return MINECRAFT.options;
#endif
}
//endregion
@@ -418,22 +599,75 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
public boolean isDedicatedServer() { return false; }
@Override
public File getInstallationDirectory() { return MINECRAFT.gameDirectory; }
public File getInstallationDirectory()
{
#if MC_VER <= MC_1_12_2
return MINECRAFT.gameDir;
#else
return MINECRAFT.gameDirectory;
#endif
}
@Override
public int getPlayerCount()
{
// can be null if the server hasn't finished booting up yet
#if MC_VER <= MC_1_12_2
if (MINECRAFT.getIntegratedServer() == null)
#else
if (MINECRAFT.getSingleplayerServer() == null)
#endif
{
return 1;
}
else
{
#if MC_VER <= MC_1_12_2
return MINECRAFT.getIntegratedServer().getCurrentPlayerCount();
#else
return MINECRAFT.getSingleplayerServer().getPlayerCount();
#endif
}
}
@Override
public IServerLevelWrapper getWrappedServerLevel(String levelKey)
{
if (!hasSinglePlayerServer()) return null;
#if MC_VER <= MC_1_12_2
int dimensionID;
try
{
dimensionID = Integer.parseInt(levelKey.substring(levelKey.indexOf(":")+1));
}
catch (NumberFormatException ignored)
{
return null;
}
#else
#if MC_VER <= MC_1_21_10
ResourceLocation levelID = ResourceLocation.tryParse(levelKey);
#else
Identifier levelID = Identifier.tryParse(levelKey);
#endif
if (levelID == null) return null;
#if MC_VER > MC_1_19_2
ResourceKey<Level> resourceKey = ResourceKey.create(Registries.DIMENSION, levelID);
#else
ResourceKey<Level> resourceKey = ResourceKey.create(Registry.DIMENSION_REGISTRY, levelID);
#endif
#endif
#if MC_VER <= MC_1_12_2
WorldServer level = MINECRAFT.getIntegratedServer().getWorld(dimensionID);
#else
ServerLevel level = MINECRAFT.getSingleplayerServer().getLevel(resourceKey);
#endif
return ServerLevelWrapper.getWrapper(level);
}
//endregion
@@ -19,7 +19,9 @@
package com.seibel.distanthorizons.common.wrappers.minecraft;
#if MC_VER < MC_1_21_5
#if MC_VER <= MC_1_12_2
import net.minecraft.client.renderer.GlStateManager;
#elif MC_VER < MC_1_21_5
import com.mojang.blaze3d.platform.GlStateManager;
#else
import com.mojang.blaze3d.opengl.GlStateManager;
@@ -69,13 +71,17 @@ public class MinecraftGLWrapper
public void enableScissorTest()
{
GL32.glEnable(GL32.GL_SCISSOR_TEST);
#if MC_VER > MC_1_12_2
GlStateManager._enableScissorTest();
#endif
}
/** @see GL32#GL_SCISSOR_TEST */
public void disableScissorTest()
{
GL32.glDisable(GL32.GL_SCISSOR_TEST);
#if MC_VER > MC_1_12_2
GlStateManager._disableScissorTest();
#endif
}
@@ -93,33 +99,53 @@ public class MinecraftGLWrapper
public void enableDepthTest()
{
GL32.glEnable(GL32.GL_DEPTH_TEST);
#if MC_VER <= MC_1_12_2
GlStateManager.enableDepth();
#else
GlStateManager._enableDepthTest();
#endif
}
/** @see GL32#GL_DEPTH_TEST */
public void disableDepthTest()
{
GL32.glDisable(GL32.GL_DEPTH_TEST);
#if MC_VER <= MC_1_12_2
GlStateManager.disableDepth();
#else
GlStateManager._disableDepthTest();
#endif
}
/** @see GL32#glDepthFunc(int) */
public void glDepthFunc(int func)
{
GL32.glDepthFunc(func);
#if MC_VER <= MC_1_12_2
GlStateManager.depthFunc(func);
#else
GlStateManager._depthFunc(func);
#endif
}
/** @see GL32#glDepthMask(boolean) */
public void enableDepthMask()
{
GL32.glDepthMask(true);
#if MC_VER <= MC_1_12_2
GlStateManager.depthMask(true);
#else
GlStateManager._depthMask(true);
#endif
}
/** @see GL32#glDepthMask(boolean) */
public void disableDepthMask()
{
GL32.glDepthMask(false);
#if MC_VER <= MC_1_12_2
GlStateManager.depthMask(false);
#else
GlStateManager._depthMask(false);
#endif
}
@@ -129,21 +155,30 @@ public class MinecraftGLWrapper
public void enableBlend()
{
GL32.glEnable(GL32.GL_BLEND);
#if MC_VER <= MC_1_12_2
GlStateManager.enableBlend();
#else
GlStateManager._enableBlend();
#endif
}
/** @see GL32#GL_BLEND */
public void disableBlend()
{
GL32.glDisable(GL32.GL_BLEND);
#if MC_VER <= MC_1_12_2
GlStateManager.disableBlend();
#else
GlStateManager._disableBlend();
#endif
}
/** @see GL32#glBlendFunc */
public void glBlendFunc(int sfactor, int dfactor)
{
GL32.glBlendFunc(sfactor, dfactor);
#if MC_VER < MC_1_21_5
#if MC_VER <= MC_1_12_2
GlStateManager.blendFunc(sfactor, dfactor);
#elif MC_VER < MC_1_21_5
GlStateManager._blendFunc(sfactor, dfactor);
#endif
}
@@ -151,7 +186,11 @@ public class MinecraftGLWrapper
public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha)
{
GL32.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
#if MC_VER <= MC_1_12_2
GlStateManager.tryBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
#else
GlStateManager._blendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
#endif
}
@@ -161,7 +200,9 @@ public class MinecraftGLWrapper
public void glBindFramebuffer(int target, int framebuffer)
{
GL32.glBindFramebuffer(target, framebuffer);
#if MC_VER > MC_1_12_2
GlStateManager._glBindFramebuffer(target, framebuffer);
#endif
}
@@ -190,28 +231,54 @@ public class MinecraftGLWrapper
public void enableFaceCulling()
{
GL32.glEnable(GL32.GL_CULL_FACE);
#if MC_VER <= MC_1_12_2
GlStateManager.enableCull();
#else
GlStateManager._enableCull();
#endif
}
/** @see GL32#GL_CULL_FACE */
public void disableFaceCulling()
{
GL32.glDisable(GL32.GL_CULL_FACE);
#if MC_VER <= MC_1_12_2
GlStateManager.disableCull();
#else
GlStateManager._disableCull();
#endif
}
// textures //
/** @see GL32#glGenTextures() */
public int glGenTextures() { return GlStateManager._genTexture(); }
public int glGenTextures()
{
#if MC_VER <= MC_1_12_2
return GlStateManager.generateTexture();
#else
return GlStateManager._genTexture();
#endif
}
/** @see GL32#glDeleteTextures(int) */
public void glDeleteTextures(int texture) { GlStateManager._deleteTexture(texture); }
public void glDeleteTextures(int texture)
{
#if MC_VER <= MC_1_12_2
GlStateManager.deleteTexture(texture);
#else
GlStateManager._deleteTexture(texture);
#endif
}
/** @see GL32#glActiveTexture(int) */
public void glActiveTexture(int textureId)
{
GL32.glActiveTexture(textureId);
#if MC_VER <= MC_1_12_2
GlStateManager.setActiveTexture(textureId);
#else
GlStateManager._activeTexture(textureId);
#endif
}
public int getActiveTexture() { return GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); }
@@ -222,7 +289,11 @@ public class MinecraftGLWrapper
public void glBindTexture(int texture)
{
GL32.glBindTexture(GL32.GL_TEXTURE_2D, texture);
#if MC_VER <= MC_1_12_2
GlStateManager.bindTexture(texture);
#else
GlStateManager._bindTexture(texture);
#endif
}
@@ -20,27 +20,34 @@
package com.seibel.distanthorizons.common.wrappers.minecraft;
import java.awt.Color;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.ConcurrentHashMap;
#if MC_VER > MC_1_12_2
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.NativeImage;
#endif
import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
#if MC_VER < MC_1_17_1
#elif MC_VER < MC_1_21_6
#elif MC_VER < MC_1_21_3
import net.minecraft.client.renderer.FogRenderer;
import com.mojang.blaze3d.systems.RenderSystem;
#elif MC_VER < MC_1_21_6
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import net.minecraft.client.renderer.FogRenderer;
#else
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import net.minecraft.client.renderer.fog.FogData;
import net.minecraft.client.renderer.fog.FogRenderer;
#endif
@@ -51,27 +58,38 @@ import org.joml.Vector3f;
#else
#endif
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IImmersivePortalsAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
#if MC_VER <= MC_1_12_2
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.fluids.IFluidBlock;
import net.minecraft.init.MobEffects;
import net.minecraft.client.renderer.entity.RenderManager;
#else
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.phys.Vec3;
#endif
import net.minecraft.client.Minecraft;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector4f;
#if MC_VER < MC_1_17_1
#if MC_VER <= MC_1_12_2
import org.lwjgl.opengl.GL15;
#elif MC_VER < MC_1_17_1
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.material.FluidState;
@@ -100,9 +118,20 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper();
private static final IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER <= MC_1_12_2
private static final Minecraft MC = Minecraft.getMinecraft();
#else
private static final Minecraft MC = Minecraft.getInstance();
#endif
// Need to classload this field later because otherwise it will be null even when Immersive Portals is present.
public static class Late {
private static final IImmersivePortalsAccessor IMMERSIVE_PORTALS = ModAccessorInjector.INSTANCE.get(IImmersivePortalsAccessor.class);
}
/**
* In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps. <br>
@@ -133,7 +162,10 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public Vec3f getLookAtVector()
{
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
net.minecraft.util.math.Vec3d lookVector = (MC.getRenderViewEntity().getLook(MC.getRenderPartialTicks()));
return new Vec3f((float) lookVector.x, (float) lookVector.y, (float) lookVector.z);
#elif MC_VER <= MC_1_21_10
Camera camera = MC.gameRenderer.getMainCamera();
return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z());
#else
@@ -153,37 +185,57 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
{
return false;
}
else if (MC.player.getActiveEffectsMap() == null)
#if MC_VER <= MC_1_12_2
if (MC.player.getActivePotionMap() == null)
{
return false;
}
else
return MC.player.getActivePotionEffect(MobEffects.BLINDNESS) != null;
#else
if (MC.player.getActiveEffectsMap() == null)
{
return false;
}
return MC.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null
#if MC_VER >= MC_1_19_2
|| MC.player.getActiveEffectsMap().get(MobEffects.DARKNESS) != null // Deep dark effect
#endif
;
}
#endif
}
@Override
public Vec3d getCameraExactPosition()
{
if (Late.IMMERSIVE_PORTALS != null)
{
Vec3d cameraPos = Late.IMMERSIVE_PORTALS.getOriginalCameraPos();
if (cameraPos != null) return cameraPos;
}
#if MC_VER <= MC_1_12_2
RenderManager rm = MC.getRenderManager();
return new Vec3d(rm.viewerPosX, rm.viewerPosY, rm.viewerPosZ);
#else
Camera camera = MC.gameRenderer.getMainCamera();
#if MC_VER <= MC_1_21_10
Vec3 projectedView = camera.getPosition();
#else
Vec3 projectedView = camera.position();
#endif
return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
#endif
}
@Override
public float getPartialTickTime()
{
#if MC_VER < MC_1_21_1
#if MC_VER <= MC_1_12_2
return MC.getRenderPartialTicks();
#elif MC_VER < MC_1_21_1
return MC.getFrameTime();
#elif MC_VER < MC_1_21_3
return MC.getTimer().getRealtimeDeltaTicks();
@@ -296,9 +348,17 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public Color getSkyColor()
{
#if MC_VER <= MC_1_12_2
if (MC.world.provider.hasSkyLight())
#else
if (MC.level.dimensionType().hasSkyLight())
#endif
{
#if MC_VER < MC_1_17_1
#if MC_VER <= MC_1_12_2
float frameTime = this.getPartialTickTime();
net.minecraft.util.math.Vec3d colorValues = MC.world.getSkyColor(MC.getRenderViewEntity(), frameTime);
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
#elif MC_VER < MC_1_17_1
float frameTime = this.getPartialTickTime();
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), frameTime);
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
@@ -325,7 +385,9 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public int getRenderDistance()
{
#if MC_VER <= MC_1_17_1
#if MC_VER <= MC_1_12_2
return MC.gameSettings.renderDistanceChunks;
#elif MC_VER <= MC_1_17_1
return MC.options.renderDistance;
#else
return MC.options.getEffectiveRenderDistance();
@@ -335,14 +397,18 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public int getFrameLimit()
{
#if MC_VER <= MC_1_18_2
#if MC_VER <= MC_1_12_2
return MC.gameSettings.limitFramerate;
#elif MC_VER <= MC_1_18_2
return MC.options.framerateLimit;
#else
return MC.options.framerateLimit().get();
#endif
}
#if MC_VER > MC_1_12_2
protected RenderTarget getRenderTarget() { return MC.getMainRenderTarget(); }
#endif
@Override
public boolean mcRendersToFrameBuffer()
@@ -373,7 +439,9 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return this.finalLevelFrameBufferId;
}
#if MC_VER < MC_1_21_5
#if MC_VER <= MC_1_12_2
return MC.getFramebuffer().framebufferObject;
#elif MC_VER < MC_1_21_5
return this.getRenderTarget().frameBufferId;
#else
// MC renders to a texture and then directly to the default FBO now
@@ -388,7 +456,10 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public int getDepthTextureId()
{
#if MC_VER < MC_1_21_5
#if MC_VER <= MC_1_12_2
//1.12.2 is using renderbuffer instead of framebuffer for depth texture
return -1;
#elif MC_VER < MC_1_21_5
return this.getRenderTarget().getDepthTextureId();
#else
try
@@ -418,7 +489,9 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public int getColorTextureId()
{
#if MC_VER < MC_1_21_5
#if MC_VER <= MC_1_12_2
return MC.getFramebuffer().framebufferTexture;
#elif MC_VER < MC_1_21_5
return this.getRenderTarget().getColorTextureId();
#else
try
@@ -448,7 +521,9 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public int getTargetFramebufferViewportWidth()
{
#if MC_VER < MC_1_21_9
#if MC_VER <= MC_1_12_2
return MC.getFramebuffer().framebufferWidth;
#elif MC_VER < MC_1_21_9
return this.getRenderTarget().viewWidth;
#else
return this.getRenderTarget().width;
@@ -458,20 +533,23 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public int getTargetFramebufferViewportHeight()
{
#if MC_VER < MC_1_21_9
#if MC_VER <= MC_1_12_2
return MC.getFramebuffer().framebufferHeight;
#elif MC_VER < MC_1_21_9
return this.getRenderTarget().viewHeight;
#else
return this.getRenderTarget().height;
#endif
}
@Override
public ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
@Override
public boolean isFogStateSpecial()
{
#if MC_VER < MC_1_17_1
#if MC_VER <= MC_1_12_2
BlockPos blockPos = new BlockPos(MC.getRenderViewEntity().getPositionEyes(MC.getRenderPartialTicks()));
IBlockState fluidState = MC.getRenderViewEntity().world.getBlockState(blockPos);
return this.playerHasBlindingEffect() || fluidState.getMaterial().isLiquid() || fluidState.getBlock() instanceof IFluidBlock;
#elif MC_VER < MC_1_17_1
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
FluidState fluidState = camera.getFluidInCamera();
Entity entity = camera.getEntity();
@@ -485,87 +563,88 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
#endif
}
//==========//
// lightmap //
//==========//
//region
@Override
public ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
/**
* It's better to use {@link MinecraftRenderWrapper#setLightmapId(int, IClientLevelWrapper)} if possible,
* It's better to use {@link MinecraftRenderWrapper#setLightmapId(int)} if possible,
* however old MC versions don't support it.
*/
public void updateLightmap(NativeImage lightPixels, IClientLevelWrapper level)
#if MC_VER > MC_1_12_2
public void updateLightmap(NativeImage lightPixels)
{
IClientLevelWrapper clientLevel = getLightmapClientLevelWrapper();
if (clientLevel == null)
{
return;
}
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
// object for the same MC level and/or the same hash,
// so this will have to do for now
IDimensionTypeWrapper dimensionType = level.getDimensionType();
IDimensionTypeWrapper dimensionType = clientLevel.getDimensionType();
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
wrapper.uploadLightmap(lightPixels);
}
public void setLightmapId(int tetxureId, IClientLevelWrapper level)
#endif
public void setLightmapId(int textureId)
{
IClientLevelWrapper clientLevel = getLightmapClientLevelWrapper();
if (clientLevel == null)
{
return;
}
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
// object for the same MC level and/or the same hash,
// so this will have to do for now
IDimensionTypeWrapper dimensionType = level.getDimensionType();
IDimensionTypeWrapper dimensionType = clientLevel.getDimensionType();
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
wrapper.setLightmapId(tetxureId);
wrapper.setLightmapId(textureId);
}
#if MC_VER <= MC_1_21_10
#else
public void setLightmapGpuTexture(GpuTexture gpuTexture, IClientLevelWrapper level)
public void setLightmapGpuTexture(GpuTexture gpuTexture)
{
IClientLevelWrapper clientLevel = getLightmapClientLevelWrapper();
if (clientLevel == null)
{
return;
}
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
// object for the same MC level and/or the same hash,
// so this will have to do for now
IDimensionTypeWrapper dimensionType = level.getDimensionType();
IDimensionTypeWrapper dimensionType = clientLevel.getDimensionType();
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
wrapper.setLightmapGpuTexture(gpuTexture);
}
#endif
@Override
public float getShade(EDhDirection lodDirection)
private static @Nullable IClientLevelWrapper getLightmapClientLevelWrapper()
{
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.Quality.lodShading.get();
switch (lodShading)
IClientLevelWrapper clientLevel = ClientApi.RENDER_STATE.clientLevelWrapper;
if (clientLevel == null)
{
default:
case AUTO:
if (MC.level != null)
{
Direction mcDir = McObjectConverter.Convert(lodDirection);
#if MC_VER <= MC_1_21_11
return MC.level.getShade(mcDir, true);
#else
return MC.level.cardinalLighting().byFace(mcDir);
#endif
}
else
{
return 0.0f;
clientLevel = MC_CLIENT.getWrappedClientLevel();
}
case ENABLED:
switch (lodDirection)
{
case DOWN:
return 0.5F;
default:
case UP:
return 1.0F;
case NORTH:
case SOUTH:
return 0.8F;
case WEST:
case EAST:
return 0.6F;
return clientLevel;
}
case DISABLED:
return 1.0F;
}
}
//endregion
@@ -1,9 +1,32 @@
package com.seibel.distanthorizons.common.wrappers.minecraft;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.server.dedicated.DedicatedServer;
import org.jetbrains.annotations.Nullable;
#if MC_VER > MC_1_12_2
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
#else
import net.minecraft.world.WorldServer;
#endif
#if MC_VER <= MC_1_12_2
#elif MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
#endif
#if MC_VER > MC_1_19_2
import net.minecraft.core.registries.Registries;
#elif MC_VER > MC_1_12_2
import net.minecraft.core.Registry;
#endif
import java.io.File;
public class MinecraftServerWrapper implements IMinecraftSharedWrapper
@@ -39,7 +62,9 @@ public class MinecraftServerWrapper implements IMinecraftSharedWrapper
throw new IllegalStateException("Trying to get Installation Direction before dedicated server completed initialization!");
}
#if MC_VER < MC_1_21_1
#if MC_VER <= MC_1_12_2
return this.dedicatedServer.getDataDirectory();
#elif MC_VER < MC_1_21_1
return this.dedicatedServer.getServerDirectory();
#else
return this.dedicatedServer.getServerDirectory().toFile();
@@ -54,9 +79,49 @@ public class MinecraftServerWrapper implements IMinecraftSharedWrapper
throw new IllegalStateException("Trying to get player count before dedicated server completed initialization!");
}
#if MC_VER <= MC_1_12_2
return this.dedicatedServer.getCurrentPlayerCount();
#else
return this.dedicatedServer.getPlayerCount();
#endif
}
@Override
public IServerLevelWrapper getWrappedServerLevel(String levelKey)
{
#if MC_VER <= MC_1_12_2
int dimensionID;
try
{
dimensionID = Integer.parseInt(levelKey.substring(levelKey.indexOf(":")+1));
}
catch (NumberFormatException ignored)
{
return null;
}
#else
#if MC_VER <= MC_1_21_10
ResourceLocation levelID = ResourceLocation.tryParse(levelKey);
#else
Identifier levelID = Identifier.tryParse(levelKey);
#endif
if (levelID == null) return null;
#if MC_VER > MC_1_19_2
ResourceKey<Level> resourceKey = ResourceKey.create(Registries.DIMENSION, levelID);
#else
ResourceKey<Level> resourceKey = ResourceKey.create(Registry.DIMENSION_REGISTRY, levelID);
#endif
#endif
#if MC_VER > MC_1_12_2
ServerLevel level = dedicatedServer.getLevel(resourceKey);
#else
WorldServer level = dedicatedServer.getWorld(dimensionID);
#endif
return ServerLevelWrapper.getWrapper(level);
}
}
@@ -21,25 +21,50 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
#if MC_VER <= MC_1_12_2
import net.minecraft.profiler.Profiler;
#else
import net.minecraft.util.profiling.ProfilerFiller;
#endif
public class ProfilerWrapper implements IProfilerWrapper
{
#if MC_VER <= MC_1_12_2
public Profiler profiler;
#else
public ProfilerFiller profiler;
#endif
#if MC_VER <= MC_1_12_2
public ProfilerWrapper(Profiler newProfiler)
#else
public ProfilerWrapper(ProfilerFiller newProfiler)
#endif
{ this.profiler = newProfiler; }
public ProfilerWrapper(ProfilerFiller newProfiler) { this.profiler = newProfiler; }
@Override
public IProfileBlock push(String newSection)
{
#if MC_VER <= MC_1_12_2
this.profiler.startSection(newSection);
#else
this.profiler.push(newSection);
#endif
return new ProfileBlock(this.profiler);
}
@Override
public void popPush(String newSection)
{
this.profiler.popPush(newSection);
#if MC_VER <= MC_1_12_2
this.profiler.endStartSection(newSection);
#else
this.profiler.popPush(newSection) ;
#endif
}
@@ -51,14 +76,27 @@ public class ProfilerWrapper implements IProfilerWrapper
public static class ProfileBlock implements IProfileBlock
{
private final ProfilerFiller profiler;
public ProfileBlock(ProfilerFiller newProfiler) { this.profiler = newProfiler; }
#if MC_VER <= MC_1_12_2
public Profiler profiler;
#else
public ProfilerFiller profiler;
#endif
#if MC_VER <= MC_1_12_2
public ProfileBlock(Profiler newProfiler)
#else
public ProfileBlock(ProfilerFiller newProfiler)
#endif
{ this.profiler = newProfiler; }
@Override
public void close()
{
#if MC_VER <= MC_1_12_2
this.profiler.endSection();
#else
this.profiler.pop();
#endif
}
}
@@ -1,12 +1,21 @@
package com.seibel.distanthorizons.common.wrappers.misc;
#if MC_VER <= MC_1_12_2
import net.minecraft.world.WorldServer;
#else
import net.minecraft.server.level.ServerLevel;
#endif
import org.jetbrains.annotations.Nullable;
public interface IMixinServerPlayer
{
@Nullable
#if MC_VER <= MC_1_12_2
WorldServer distantHorizons$getDimensionChangeDestination();
#else
ServerLevel distantHorizons$getDimensionChangeDestination();
#endif
#if MC_VER == MC_1_16_5
void distantHorizons$setDimensionChangeDestination(ServerLevel dimensionChangeDestination);
@@ -19,9 +19,13 @@
package com.seibel.distanthorizons.common.wrappers.misc;
#if MC_VER > MC_1_12_2
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper;
#endif
import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.distanthorizons.core.logging.DhLogger;
@@ -58,7 +62,6 @@ public class LightMapWrapper implements ILightMapWrapper
private final BlazeTextureViewWrapper lightmapTextureWrapper = new BlazeTextureViewWrapper();
//==============//
// constructors //
//==============//
@@ -75,6 +78,7 @@ public class LightMapWrapper implements ILightMapWrapper
//==================//
//region
#if MC_VER > MC_1_12_2
public void uploadLightmap(NativeImage image)
{
#if MC_VER < MC_1_21_3
@@ -110,6 +114,7 @@ public class LightMapWrapper implements ILightMapWrapper
throw new UnsupportedOperationException("setLightmapId should be used for MC versions after 1.21.3");
#endif
}
#endif
public void setLightmapId(int minecraftLightmapTextureId)
{
@@ -126,6 +131,7 @@ public class LightMapWrapper implements ILightMapWrapper
}
#endif
//endregion
@@ -1,7 +1,11 @@
package com.seibel.distanthorizons.common.wrappers.misc;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
#if MC_VER <= MC_1_12_2
import net.minecraft.util.math.BlockPos;
#else
import net.minecraft.core.BlockPos;
#endif
public class MutableBlockPosWrapper implements IMutableBlockPosWrapper
{
@@ -12,19 +16,27 @@ public class MutableBlockPosWrapper implements IMutableBlockPosWrapper
//=============//
// constructor //
//=============//
//region
public MutableBlockPosWrapper()
{
this.pos = new BlockPos.MutableBlockPos();
}
//endregion
//===========//
// overrides //
//===========//
//region
@Override
public Object getWrappedMcObject() { return this.pos; }
//endregion
}
@@ -6,53 +6,114 @@ import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import com.seibel.distanthorizons.core.util.math.Vec3d;
#if MC_VER <= MC_1_12_2
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.NetHandlerPlayServer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldServer;
#else
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.phys.Vec3;
#endif
import java.net.SocketAddress;
import java.util.concurrent.ConcurrentMap;
#if MC_VER <= MC_1_12_2
/**
* This wrapper transparently ensures that the underlying {@link EntityPlayerMP} is always valid,
* unless the player has disconnected.
*/
#else
/**
* This wrapper transparently ensures that the underlying {@link ServerPlayer} is always valid,
* unless the player has disconnected.
*/
#endif
public class ServerPlayerWrapper implements IServerPlayerWrapper
{
#if MC_VER <= MC_1_12_2
private static final ConcurrentMap<NetHandlerPlayServer, ServerPlayerWrapper> serverPlayerWrapperMap = new MapMaker().weakKeys().weakValues().makeMap();
#else
private static final ConcurrentMap<ServerGamePacketListenerImpl, ServerPlayerWrapper> serverPlayerWrapperMap = new MapMaker().weakKeys().weakValues().makeMap();
#endif
#if MC_VER <= MC_1_12_2
private final NetHandlerPlayServer connection;
#else
private final ServerGamePacketListenerImpl connection;
#endif
//=============//
// constructor //
//=============//
//region
#if MC_VER <= MC_1_12_2
public static ServerPlayerWrapper getWrapper(EntityPlayerMP serverPlayer)
#else
public static ServerPlayerWrapper getWrapper(ServerPlayer serverPlayer)
#endif
{ return serverPlayerWrapperMap.computeIfAbsent(serverPlayer.connection, ignored -> new ServerPlayerWrapper(serverPlayer.connection)); }
private ServerPlayerWrapper(ServerGamePacketListenerImpl connection) { this.connection = connection; }
#if MC_VER <= MC_1_12_2
private ServerPlayerWrapper(NetHandlerPlayServer connection)
#else
private ServerPlayerWrapper(ServerGamePacketListenerImpl connection)
#endif
{ this.connection = connection; }
//endregion
//=========//
// getters //
//=========//
//region
private ServerPlayer getServerPlayer() { return this.connection.player; }
#if MC_VER <= MC_1_12_2
private EntityPlayerMP getServerPlayer()
#else
private ServerPlayer getServerPlayer()
#endif
{ return this.connection.player; }
@Override
public String getName() { return this.getServerPlayer().getName().getString(); }
public String getName()
{
#if MC_VER <= MC_1_12_2
return this.getServerPlayer().getName();
#else
return this.getServerPlayer().getName().getString();
#endif
}
@Override
public IServerLevelWrapper getLevel()
{
#if MC_VER <= MC_1_12_2
WorldServer level = null;
if (this.getServerPlayer() instanceof IMixinServerPlayer mixinPlayer)
{
level = mixinPlayer.distantHorizons$getDimensionChangeDestination();
}
#else
ServerLevel level = ((IMixinServerPlayer) this.getServerPlayer()).distantHorizons$getDimensionChangeDestination();
#endif
if (level == null)
{
#if MC_VER < MC_1_20_1
#if MC_VER <= MC_1_12_2
MinecraftServer server = this.getServerPlayer().getServer();
level = (server != null) ? server.getWorld(this.getServerPlayer().dimension) : this.getServerPlayer().getServerWorld();
#elif MC_VER < MC_1_20_1
level = this.getServerPlayer().getLevel();
#elif MC_VER < MC_1_21_6
level = this.getServerPlayer().serverLevel();
@@ -67,15 +128,23 @@ public class ServerPlayerWrapper implements IServerPlayerWrapper
@Override
public Vec3d getPosition()
{
#if MC_VER <= MC_1_12_2
BlockPos position = this.getServerPlayer().getPosition();
return new Vec3d(position.getX(), position.getY(), position.getZ());
#else
Vec3 position = this.getServerPlayer().position();
return new Vec3d(position.x, position.y, position.z);
#endif
}
//endregion
//================//
// base overrides //
//================//
//region
@Override
public Object getWrappedMcObject() { return this.getServerPlayer(); }
@@ -101,4 +170,8 @@ public class ServerPlayerWrapper implements IServerPlayerWrapper
@Override
public int hashCode() { return Objects.hashCode(this.connection); }
//endregion
}
@@ -0,0 +1,144 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.modAccessor;
#if MC_VER > MC_1_12_2
import com.google.common.base.Suppliers;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ImmersivePortalsAbstractAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
#if MC_VER > MC_1_19_2
import org.joml.Matrix4f;
#else
import com.mojang.math.Matrix4f;
import com.seibel.distanthorizons.core.util.math.Mat4f;
#endif
#if MC_VER < MC_1_17_1
import java.lang.reflect.Field;
#endif
import java.util.function.Supplier;
public abstract class ImmersivePortalsAccessorCommon extends ImmersivePortalsAbstractAccessor
{
// We don't use the fields in RenderStates because they are not volatile.
@Nullable
public static volatile ClientLevel originalLevel;
@Nullable
public static volatile DhBlockPos originalBlockPos;
@Nullable
public static volatile DhChunkPos originalChunkPos;
@Nullable
public static volatile Vec3d originalCameraPos;
@Override
protected Object getClientLevel() { return Minecraft.getInstance().level; }
@Override
protected Class<?> getLevelClass() { return Level.class; }
@Override
protected Iterable<?> getEntitiesForRendering() { return Minecraft.getInstance().level.entitiesForRendering(); }
#if MC_VER < MC_1_21_6
private static Matrix4f getProjectionMatrix()
{
#if MC_VER > MC_1_16_5
return RenderSystem.getProjectionMatrix();
#else
try
{
Class<?> renderStates = Class.forName("com.qouteall.immersive_portals.render.context_management.RenderStates");
Field projectionMatrix = renderStates.getField("projectionMatrix");
return (Matrix4f) projectionMatrix.get(null);
}
catch (Throwable e)
{
throw new RuntimeException(e);
}
#endif
}
#endif
#if MC_VER <= MC_1_19_2
protected abstract Matrix4f convert(Mat4f matrix);
#endif
@Override
protected Supplier<?> getFrustumSupplier()
{
#if MC_VER < MC_1_21_6 // TODO Fix this for 1.21.6+ when a more modern version of Immersive Portals is available.
return Suppliers.memoize(() -> {
Frustum frustum = new Frustum(
#if MC_VER > MC_1_19_2
ClientApi.RENDER_STATE.mcModelViewMatrix.createJomlMatrix(),
#else
convert(ClientApi.RENDER_STATE.mcModelViewMatrix),
#endif
getProjectionMatrix()
);
Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition();
frustum.prepare(cameraPos.x, cameraPos.y, cameraPos.z);
return frustum;
});
#else
return null;
#endif
}
@Override
@Nullable
public DhBlockPos getOriginalPlayerBlockPos() {
return originalBlockPos;
}
@Override
@Nullable
public DhChunkPos getOriginalPlayerChunkPos() {
return originalChunkPos;
}
@Override
@Nullable
public IClientLevelWrapper getOriginalClientLevelWrapper() {
return ClientLevelWrapper.getWrapper(originalLevel, false);
}
@Override
@Nullable
public Vec3d getOriginalCameraPos() {
return originalCameraPos;
}
}
#endif
@@ -1,31 +1,48 @@
package com.seibel.distanthorizons.common.wrappers.world;
import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.ClientBlockStateColorCache;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.level.KeyedClientLevelManager;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.level.*;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.client.Minecraft;
#if MC_VER <= MC_1_12_2
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.WorldServer;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.block.state.IBlockState;
#else
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
#endif
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -33,19 +50,20 @@ import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
#if MC_VER <= MC_1_20_4
#if MC_VER <= MC_1_12_2
#elif MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
#if MC_VER < MC_1_21_3
#if MC_VER <= MC_1_12_2
#elif MC_VER < MC_1_21_3
import net.minecraft.world.phys.Vec3;
#else
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
@@ -65,22 +83,42 @@ public class ClientLevelWrapper implements IClientLevelWrapper
* where, upon world closure, some levels aren't shutdown/removed properly
* and/or for servers were the level object isn't consistent
*/
private static final Map<ClientLevel, WeakReference<ClientLevelWrapper>> LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL = Collections.synchronizedMap(new WeakHashMap<>());
private static final Map<
#if MC_VER <= MC_1_12_2 WorldClient #else ClientLevel #endif,
WeakReference<ClientLevelWrapper>> LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL = Collections.synchronizedMap(new WeakHashMap<>());
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
#if MC_VER <= MC_1_12_2
private static final Minecraft MINECRAFT = Minecraft.getMinecraft();
#else
private static final Minecraft MINECRAFT = Minecraft.getInstance();
#endif
private static final Timer CLIENT_CLEANUP_TIMER = TimerUtil.CreateTimer("ClientLevelTickCleanup");
private static final TimerTask CLIENT_CLEANUP_TASK = TimerUtil.createTimerTask(ClientLevelWrapper::tickCleanup);
#if MC_VER <= MC_1_12_2
private final WorldClient level;
private final ConcurrentHashMap<IBlockState, ClientBlockStateColorCache> blockColorCacheByBlockState = new ConcurrentHashMap<>();
#else
private final ClientLevel level;
private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockColorCacheByBlockState = new ConcurrentHashMap<>();
#endif
/** cached method reference to reduce GC overhead */
private final Function<BlockState, ClientBlockStateColorCache> createCachedBlockColorCacheFunc = (blockState) -> new ClientBlockStateColorCache(blockState, this);
private final Function<
#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif,
ClientBlockStateColorCache> createCachedBlockColorCacheFunc
= (blockState) -> new ClientBlockStateColorCache(blockState, this);
private boolean cloudColorFailLogged = false;
private BlockStateWrapper dirtBlockWrapper;
private IDhLevel dhLevel;
private volatile BlockStateWrapper dirtBlockWrapper;
private volatile IDhLevel dhLevel;
private volatile long lastAccessTime = System.currentTimeMillis();
@@ -89,7 +127,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//=============//
//region
protected ClientLevelWrapper(ClientLevel level) { this.level = level; }
protected ClientLevelWrapper(#if MC_VER <= MC_1_12_2 WorldClient #else ClientLevel #endif level) { this.level = level; }
//endregion
@@ -100,17 +138,103 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//==================//
//region
@Override
public synchronized void markAccessed() { this.lastAccessTime = System.currentTimeMillis(); }
public synchronized long getLastAccessTime() { return this.lastAccessTime; }
static
{
// 20 ticks per second (50ms interval)
CLIENT_CLEANUP_TIMER.scheduleAtFixedRate(CLIENT_CLEANUP_TASK, 0, 1000 / 20);
}
private void unload()
{
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
if (world == null || !world.unloadLevel(this))
{
this.onUnload();
}
}
private static #if MC_VER > MC_1_12_2 ClientLevel #else WorldClient #endif clientLevel() {
return #if MC_VER > MC_1_12_2 MINECRAFT.level #else MINECRAFT.world #endif;
}
public static void tickCleanup()
{
if (clientLevel() == null)
{
return;
}
long currentTime = System.currentTimeMillis();
long timeout = 30 * 1000;
ArrayList<ClientLevelWrapper> toUnload = new ArrayList<>();
synchronized(LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL)
{
for (WeakReference<ClientLevelWrapper> ref : LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.values())
{
ClientLevelWrapper wrapper = ref.get();
if (wrapper != null
&& wrapper.level != clientLevel())
{
// We use the synchronized getter to prevent race conditions with markAccessed()
if (currentTime - wrapper.getLastAccessTime() > timeout)
{
toUnload.add(wrapper);
}
}
}
}
for (ClientLevelWrapper wrapper : toUnload)
{
// Re-verify all conditions inside a synchronized block on the wrapper
// to ensure atomicity with respect to markAccessed()
synchronized(wrapper)
{
if (wrapper.level != clientLevel()
&& currentTime - wrapper.getLastAccessTime() > timeout)
{
LOGGER.debug("Unloading level [" + wrapper.getDhIdentifier() + "] due to inactivity");
wrapper.unload();
}
}
}
}
/**
* can be used when speed is important and the same level is likely to be passed in,
* IE rendering.
*/
@Nullable
public static IClientLevelWrapper getWrapperIfDifferent(@Nullable IClientLevelWrapper levelWrapper, @NotNull ClientLevel level)
public static IClientLevelWrapper getWrapperIfDifferent(
@Nullable IClientLevelWrapper levelWrapper,
@NotNull #if MC_VER <= MC_1_12_2 WorldClient #else ClientLevel #endif level)
{
if (KEYED_CLIENT_LEVEL_MANAGER.isEnabled() && KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel() != levelWrapper)
if (KEYED_CLIENT_LEVEL_MANAGER.isEnabled())
{
IServerKeyedClientLevel keyedLevel = null;
if (KEYED_CLIENT_LEVEL_MANAGER instanceof KeyedClientLevelManager)
{
keyedLevel = ((KeyedClientLevelManager) KEYED_CLIENT_LEVEL_MANAGER).getServerKeyedLevel(level);
}
else
{
// FIXME: If the implementation is not KeyedClientLevelManager,
// this fallback may return the key for the wrong dimension in multiverse scenarios.
keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
}
if (keyedLevel != levelWrapper)
{
return getWrapper(level);
}
}
ClientLevelWrapper clientLevelWrapper = (ClientLevelWrapper)levelWrapper;
if (clientLevelWrapper == null
@@ -123,10 +247,13 @@ public class ClientLevelWrapper implements IClientLevelWrapper
}
@Nullable
public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level) { return getWrapper(level, false); }
public static IClientLevelWrapper getWrapper(
@NotNull #if MC_VER <= MC_1_12_2 WorldClient #else ClientLevel #endif level)
{ return getWrapper(level, false); }
@Nullable
public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level, boolean bypassLevelKeyManager)
public static IClientLevelWrapper getWrapper(
@Nullable #if MC_VER <= MC_1_12_2 WorldClient #else ClientLevel #endif level, boolean bypassLevelKeyManager)
{
if (!bypassLevelKeyManager)
{
@@ -136,9 +263,31 @@ public class ClientLevelWrapper implements IClientLevelWrapper
}
// used if the client is connected to a server that defines the currently loaded level
IServerKeyedClientLevel overrideLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
IServerKeyedClientLevel overrideLevel = null;
if (KEYED_CLIENT_LEVEL_MANAGER instanceof KeyedClientLevelManager)
{
overrideLevel = ((KeyedClientLevelManager) KEYED_CLIENT_LEVEL_MANAGER).getServerKeyedLevel(level);
}
else
{
// FIXME: If the implementation is not KeyedClientLevelManager,
// this fallback may return the key for the wrong dimension in multiverse scenarios.
overrideLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
}
if (overrideLevel != null)
{
WeakReference<ClientLevelWrapper> levelRef = LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.get(level);
if (levelRef != null && levelRef.get() != overrideLevel)
{
ClientLevelWrapper l = levelRef.get();
if (l != null) l.unload();
levelRef = null;
}
if (levelRef == null && overrideLevel instanceof ClientLevelWrapper)
{
LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.put(level, new WeakReference<>((ClientLevelWrapper) overrideLevel));
}
return overrideLevel;
}
}
@@ -177,19 +326,27 @@ public class ClientLevelWrapper implements IClientLevelWrapper
try
{
// this method only makes sense if we are running a single-player server
if (MINECRAFT.getSingleplayerServer() == null)
if (MINECRAFT.#if MC_VER <= MC_1_12_2 getIntegratedServer() #else getSingleplayerServer() #endif == null)
{
return null;
}
#if MC_VER <= MC_1_12_2
WorldServer[] serverLevels = MINECRAFT.getIntegratedServer().worlds;
#else
Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels();
#endif
// attempt to find the server level with the same dimension type
// Note: this assumes only one level per dimension type, multiverse servers may not behave correctly
ServerLevelWrapper foundLevelWrapper = null;
for (ServerLevel serverLevel : serverLevels)
for (#if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif serverLevel : serverLevels)
{
#if MC_VER <= MC_1_12_2
if (serverLevel.provider.getDimension() == this.level.provider.getDimension())
#else
if (serverLevel.dimension() == this.level.dimension())
#endif
{
foundLevelWrapper = ServerLevelWrapper.getWrapper(serverLevel);
break;
@@ -262,7 +419,9 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return this.dimensionTypeWrapper;
}
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
this.dimensionTypeWrapper = DimensionTypeWrapper.getDimensionTypeWrapper(this.level.provider.getDimensionType());
#elif MC_VER <= MC_1_21_10
this.dimensionTypeWrapper = DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
#else
this.dimensionTypeWrapper = DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType(), this.getDimensionName());
@@ -281,8 +440,9 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return this.dimensionName;
}
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
this.dimensionName = this.level.provider.getDimensionType().getName() + ":" + this.level.provider.getDimension();
#elif MC_VER <= MC_1_21_10
this.dimensionName = this.level.dimension().location().toString();
#else
this.dimensionName = this.level.dimension().identifier().toString();
@@ -291,7 +451,14 @@ public class ClientLevelWrapper implements IClientLevelWrapper
}
@Override
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
public long getHashedSeed()
{
#if MC_VER <= MC_1_12_2
return this.level.getSeed();
#else
return this.level.getBiomeManager().biomeZoomSeed;
#endif
}
@Override
public String getDhIdentifier() { return this.getHashedSeedEncoded() + "@" + this.getDimensionName(); }
@@ -299,7 +466,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
@Override
public EDhApiLevelType getLevelType() { return EDhApiLevelType.CLIENT_LEVEL; }
public ClientLevel getLevel() { return this.level; }
public #if MC_VER <= MC_1_12_2 WorldClient #else ClientLevel #endif getLevel() { return this.level; }
private Boolean dimHasCeiling = null;
@Override
@@ -312,8 +479,12 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return this.dimHasCeiling;
}
#if MC_VER <= MC_1_12_2
// 1.12.2 has no hasCeiling() - only the nether has a ceiling in vanilla
this.dimHasCeiling = this.level.provider.isNether();
#else
this.dimHasCeiling = this.level.dimensionType().hasCeiling();
#endif
return this.dimHasCeiling;
}
@@ -328,7 +499,11 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return this.dimHasSkyLight;
}
#if MC_VER <= MC_1_12_2
this.dimHasSkyLight = this.level.provider.hasSkyLight();
#else
this.dimHasSkyLight = this.level.dimensionType().hasSkyLight();
#endif
return this.dimHasSkyLight;
}
@@ -370,7 +545,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
}
@Override
public ClientLevel getWrappedMcObject() { return this.level; }
public #if MC_VER <= MC_1_12_2 WorldClient #else ClientLevel #endif getWrappedMcObject() { return this.level; }
@Override
public void onUnload()
@@ -419,10 +594,20 @@ public class ClientLevelWrapper implements IClientLevelWrapper
public Color getCloudColor(float tickDelta)
{
#if MC_VER < MC_1_21_3
#if MC_VER <= MC_1_12_2
Vec3d colorVec3 = null;
#else
Vec3 colorVec3 = null;
#endif
try
{
#if MC_VER <= MC_1_12_2
colorVec3 = this.level.getCloudColour(tickDelta);
#else
colorVec3 = this.level.getCloudColor(tickDelta);
#endif
return new Color((float)colorVec3.x, (float)colorVec3.y, (float)colorVec3.z);
}
catch (Exception e)
@@ -484,6 +669,45 @@ public class ClientLevelWrapper implements IClientLevelWrapper
#endif
}
@Override
public float getShade(EDhDirection lodDirection)
{
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.Quality.lodShading.get();
switch (lodShading)
{
default:
case AUTO:
#if MC_VER <= MC_1_12_2
// 1.12.2 level doesn't have a getShade method, fall through to ENABLED
#else
Direction mcDir = McObjectConverter.Convert(lodDirection);
#if MC_VER <= MC_1_21_11
return this.level.getShade(mcDir, true);
#else
return this.level.cardinalLighting().byFace(mcDir);
#endif
#endif
case ENABLED:
switch (lodDirection)
{
case DOWN:
return 0.5F;
default:
case UP:
return 1.0F;
case NORTH:
case SOUTH:
return 0.8F;
case WEST:
case EAST:
return 0.6F;
}
case DISABLED:
return 1.0F;
}
}
//endregion
@@ -24,7 +24,11 @@ import java.util.concurrent.ConcurrentMap;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
#if MC_VER <= MC_1_12_2
import net.minecraft.world.DimensionType;
#else
import net.minecraft.world.level.dimension.DimensionType;
#endif
public class DimensionTypeWrapper implements IDimensionTypeWrapper
{
@@ -86,7 +90,9 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
}
private static String determineName(DimensionType dimensionType)
{
#if MC_VER <= MC_1_16_5
#if MC_VER <= MC_1_12_2
return dimensionType.getName();
#elif MC_VER <= MC_1_16_5
// effectsLocation() is marked as client only, so using the backing field directly
return dimensionType.effectsLocation.getPath();
#elif MC_VER <= MC_1_21_10
@@ -108,10 +114,26 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
public String getName() { return this.name; }
@Override
public boolean hasCeiling() { return this.dimensionType.hasCeiling(); }
public boolean hasCeiling()
{
#if MC_VER <= MC_1_12_2
// 1.12.2 has no hasCeiling() - only the nether has a ceiling in vanilla
return this.dimensionType.getId() == -1;
#else
return this.dimensionType.hasCeiling();
#endif
}
@Override
public boolean hasSkyLight() { return this.dimensionType.hasSkyLight(); }
public boolean hasSkyLight()
{
#if MC_VER <= MC_1_12_2
// 1.12.2 DimensionType doesn't store hasSkyLight, it's in the WorldProvider instead
return this.dimensionType != DimensionType.NETHER;
#else
return this.dimensionType.hasSkyLight();
#endif
}
@Override
public Object getWrappedMcObject() { return this.dimensionType; }
@@ -120,7 +142,15 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
public boolean isTheEnd() { return this.getName().equalsIgnoreCase("the_end"); }
@Override
public double getCoordinateScale() { return this.dimensionType.coordinateScale(); }
public double getCoordinateScale()
{
#if MC_VER <= MC_1_12_2
// 1.12.2 DimensionType doesn't store coordinate scale, it's in the WorldProvider instead
return this.dimensionType == net.minecraft.world.DimensionType.NETHER ? 8.0 : 1.0;
#else
return this.dimensionType.coordinateScale();
#endif
}
@@ -38,12 +38,17 @@ import com.seibel.distanthorizons.core.world.EWorldEnvironment;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
#if MC_VER <= MC_1_12_2
import net.minecraft.world.WorldServer;
#else
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
#endif
#if MC_VER <= MC_1_20_4
#if MC_VER <= MC_1_12_2
#elif MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
@@ -59,9 +64,9 @@ public class ServerLevelWrapper implements IServerLevelWrapper
* weak references are to prevent rare issues
* where, upon world closure, some levels aren't shutdown/removed properly
*/
private static final Map<ServerLevel, WeakReference<ServerLevelWrapper>> LEVEL_WRAPPER_REF_BY_SERVER_LEVEL = Collections.synchronizedMap(new WeakHashMap<>());
private static final Map<#if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif, WeakReference<ServerLevelWrapper>> LEVEL_WRAPPER_REF_BY_SERVER_LEVEL = Collections.synchronizedMap(new WeakHashMap<>());
private final ServerLevel level;
private final #if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif level;
private IDhLevel dhLevel;
/**
@@ -76,7 +81,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper
// constructors //
//==============//
public static ServerLevelWrapper getWrapper(ServerLevel level)
public static ServerLevelWrapper getWrapper(#if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif level)
{
return LEVEL_WRAPPER_REF_BY_SERVER_LEVEL.compute(level, (newLevel, levelRef) ->
{
@@ -93,7 +98,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper
}).get();
}
public ServerLevelWrapper(ServerLevel level)
public ServerLevelWrapper(#if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif level)
{
this.level = level;
this.KeyedLevelDimensionName = this.createKeyedLevelDimensionName();
@@ -108,7 +113,9 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@Override
public File getMcSaveFolder()
{
#if MC_VER < MC_1_21_3
#if MC_VER <= MC_1_12_2
return new File(this.level.getChunkSaveLocation(), "data");
#elif MC_VER < MC_1_21_3
return this.level.getChunkSource().getDataStorage().dataFolder;
#else
return this.level.getChunkSource().getDataStorage().dataFolder.toFile();
@@ -157,7 +164,9 @@ public class ServerLevelWrapper implements IServerLevelWrapper
{
// We use the overworld since it's the only dimension that is stored in the server root folder
#if MC_VER >= MC_1_21_3
#if MC_VER <= MC_1_12_2
return this.level.getMinecraftServer().getWorld(0).getSaveHandler().getWorldDirectory().getParentFile().getName();
#elif MC_VER >= MC_1_21_3
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParent().getFileName().toString();
#else // <= 1.21.3
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParentFile().getName();
@@ -174,7 +183,9 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@Override
public DimensionTypeWrapper getDimensionType()
{
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.provider.getDimensionType());
#elif MC_VER <= MC_1_21_10
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
#else
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType(), this.getDimensionName());
@@ -184,7 +195,9 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@Override
public String getDimensionName()
{
#if MC_VER <= MC_1_21_10
#if MC_VER <= MC_1_12_2
return this.level.provider.getDimensionType().getName() + ":" + this.level.provider.getDimension();
#elif MC_VER <= MC_1_21_10
return this.level.dimension().location().toString();
#else
return this.level.dimension().identifier().toString();
@@ -192,7 +205,14 @@ public class ServerLevelWrapper implements IServerLevelWrapper
}
@Override
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
public long getHashedSeed()
{
#if MC_VER <= MC_1_12_2
return this.level.getSeed();
#else
return this.level.getBiomeManager().biomeZoomSeed;
#endif
}
@Override
public String getDhIdentifier() { return this.getDimensionName(); }
@@ -200,13 +220,28 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@Override
public EDhApiLevelType getLevelType() { return EDhApiLevelType.SERVER_LEVEL; }
public ServerLevel getLevel() { return this.level; }
public #if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif getLevel() { return this.level; }
@Override
public boolean hasCeiling() { return this.level.dimensionType().hasCeiling(); }
public boolean hasCeiling()
{
#if MC_VER <= MC_1_12_2
// 1.12.2 has no hasCeiling() - only the nether has a ceiling in vanilla
return this.level.provider.isNether();
#else
return this.level.dimensionType().hasCeiling();
#endif
}
@Override
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
public boolean hasSkyLight()
{
#if MC_VER <= MC_1_12_2
return this.level.provider.hasSkyLight();
#else
return this.level.dimensionType().hasSkyLight();
#endif
}
@Override
public int getMaxHeight() { return this.level.getHeight(); }
@@ -224,7 +259,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper
}
@Override
public ServerLevel getWrappedMcObject() { return this.level; }
public #if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif getWrappedMcObject() { return this.level; }
@Override
public void onUnload() { LEVEL_WRAPPER_REF_BY_SERVER_LEVEL.remove(this.level); }
@@ -26,8 +26,10 @@ import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGenerat
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling.ChunkFileReader;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.*;
#endif
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.api.internal.chunkUpdating.ChunkUpdateQueueManager;
@@ -57,18 +59,26 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepBiomes;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepFeatures;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepNoise;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepStructureReference;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepStructureStart;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepSurface;
#endif
#if MC_VER <= MC_1_12_2
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.ForgeChunkManager;
#else
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.*;
import net.minecraft.world.level.levelgen.DebugLevelSource;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
#endif
#if MC_VER <= MC_1_17_1
#elif MC_VER <= MC_1_19_2
@@ -80,12 +90,14 @@ import net.minecraft.core.registries.Registries;
#else
#endif
#if MC_VER <= MC_1_20_4
#if MC_VER <= MC_1_12_2
#elif MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
import org.jetbrains.annotations.Nullable;
public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironmentWrapper
{
@@ -122,7 +134,10 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
private final ChunkUpdateQueueManager updateManager;
public final InternalServerGenerator internalServerGenerator;
#if MC_VER > MC_1_12_2
public final ChunkFileReader chunkFileReader;
#endif
private final Timer chunkSaveIgnoreTimer = TimerUtil.CreateTimer("ChunkSaveIgnoreTimer");
@@ -131,12 +146,14 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
public final LinkedBlockingQueue<GenerationEvent> generationEventQueue = new LinkedBlockingQueue<>();
public final GlobalWorldGenParams globalParams;
#if MC_VER > MC_1_12_2
public final StepStructureStart stepStructureStart = new StepStructureStart(this);
public final StepStructureReference stepStructureReference = new StepStructureReference(this);
public final StepBiomes stepBiomes = new StepBiomes(this);
public final StepNoise stepNoise = new StepNoise(this);
public final StepSurface stepSurface = new StepSurface(this);
public final StepFeatures stepFeatures = new StepFeatures(this);
#endif
public boolean unsafeThreadingRecorded = false;
public boolean generatedChunkWithoutBiomeWarningLogged = false;
@@ -154,22 +171,12 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
static
{
boolean isTerraFirmaCraftPresent = false;
try
{
Class.forName("net.dries007.tfc.world.TFCChunkGenerator");
isTerraFirmaCraftPresent = true;
LOGGER.info("TerraFirmaCraft detected.");
}
catch (ClassNotFoundException ignore) { }
ImmutableMap.Builder<EDhApiWorldGenerationStep, Integer> builder = ImmutableMap.builder();
builder.put(EDhApiWorldGenerationStep.EMPTY, 1);
builder.put(EDhApiWorldGenerationStep.STRUCTURE_START, 0);
builder.put(EDhApiWorldGenerationStep.STRUCTURE_REFERENCE, 0);
builder.put(EDhApiWorldGenerationStep.BIOMES, isTerraFirmaCraftPresent ? 1 : 0);
builder.put(EDhApiWorldGenerationStep.NOISE, isTerraFirmaCraftPresent ? 1 : 0);
builder.put(EDhApiWorldGenerationStep.BIOMES, 0);
builder.put(EDhApiWorldGenerationStep.NOISE, 0);
builder.put(EDhApiWorldGenerationStep.SURFACE, 0);
builder.put(EDhApiWorldGenerationStep.CARVERS, 0);
builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0);
@@ -191,6 +198,8 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
this.updateManager = WorldChunkUpdateManager.INSTANCE.getByLevelWrapper(this.dhServerLevel.getServerLevelWrapper());
this.globalParams = new GlobalWorldGenParams(dhServerLevel);
this.internalServerGenerator = new InternalServerGenerator(this.globalParams, this.dhServerLevel);
#if MC_VER > MC_1_12_2
this.chunkFileReader = new ChunkFileReader(this.globalParams);
ChunkGenerator generator = ((ServerLevelWrapper) (dhServerLevel.getServerLevelWrapper())).getLevel().getChunkSource().getGenerator();
@@ -205,17 +214,13 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
LOGGER.info("TerraForge Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it.");
LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
}
else if (generator.getClass().toString().equals("class net.dries007.tfc.world.TFCChunkGenerator"))
{
LOGGER.info("TerraFirmaCraft Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it.");
LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
}
else
{
LOGGER.warn("Unknown Chunk Generator detected: [" + generator.getClass() + "], Distant Generation May Fail!");
LOGGER.warn("If it does crash, disable Distant Generation or set the Generation Mode to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
}
}
#endif
}
@@ -324,6 +329,11 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
// variable setup //
//================//
#if MC_VER <= MC_1_12_2
// MC 1.12 can only run world gen on the main thread
this.internalServerGenerator.generateChunksViaInternalServer(genEvent);
#else
int borderSize = MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
// genEvent.size - 1 converts the even width size to an odd number for MC compatability
int refSize = (genEvent.widthInChunks - 1) + (borderSize * 2);
@@ -573,12 +583,14 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
{
LOGGER.error("Unexpected error during world gen for min chunk pos ["+genEvent.minPos+"], error: ["+e.getMessage()+"].", e);
}
#endif
}
// direct generation //
#if MC_VER > MC_1_12_2
public void generateDirect(
GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunkWrappersToGenerate,
DhLitWorldGenRegion region) throws InterruptedException
@@ -743,7 +755,7 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
}
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border) { return new ArrayGridList<>(total, border, total.gridSize - border); }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.get(step)); }
#endif
// queue task //
@@ -784,8 +796,9 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
}
#if MC_VER > MC_1_12_2
this.chunkFileReader.close();
#endif
}
@@ -813,12 +826,12 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
// helper classes //
//================//
#if MC_VER > MC_1_12_2
@FunctionalInterface
public interface IEmptyChunkRetrievalFunc
{
ChunkAccess getChunk(int chunkPosX, int chunkPosZ);
}
#endif
}
@@ -1,6 +1,10 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
#if MC_VER <= MC_1_12_2
import net.minecraft.util.math.ChunkPos;
#else
import net.minecraft.world.level.ChunkPos;
#endif
import java.util.Iterator;
import java.util.Spliterator;
@@ -25,7 +25,9 @@ import java.util.function.Consumer;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
#endif
import com.seibel.distanthorizons.core.util.ExceptionUtil;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
@@ -43,7 +45,9 @@ public final class GenerationEvent
/** can be used for troubleshooting */
public final int id;
#if MC_VER > MC_1_12_2
public final ThreadWorldGenParams threadedParam;
#endif
public final DhChunkPos minPos;
public final int widthInChunks;
public final EDhApiWorldGenerationStep targetGenerationStep;
@@ -67,7 +71,9 @@ public final class GenerationEvent
this.widthInChunks = widthInChunks;
this.targetGenerationStep = targetGenerationStep;
this.generatorMode = generatorMode;
#if MC_VER > MC_1_12_2
this.threadedParam = ThreadWorldGenParams.getOrMake(generationGroup.globalParams);
#endif
this.future = new CompletableFuture<>();
this.resultConsumer = resultConsumer;
}
@@ -12,7 +12,6 @@ import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.level.DhServerLevel;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -25,14 +24,23 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccess
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.jetbrains.annotations.Nullable;
#if MC_VER <= MC_1_12_2
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.gen.ChunkProviderServer;
import net.minecraftforge.common.ForgeChunkManager;
#else
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
#endif
#if MC_VER <= MC_1_20_4
#if MC_VER <= MC_1_12_2
#elif MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
@@ -66,7 +74,8 @@ public class InternalServerGenerator
*/
private static final int MS_TO_IGNORE_CHUNK_AFTER_COMPLETION = 5_000;
#if MC_VER < MC_1_21_5
#if MC_VER <= MC_1_12_2
#elif MC_VER < MC_1_21_5
private static final TicketType<ChunkPos> DH_SERVER_GEN_TICKET = TicketType.create("dh_server_gen_ticket", Comparator.comparingLong(ChunkPos::toLong));
#elif MC_VER < MC_1_21_9
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* persist */ false, TicketType.TicketUse.LOADING);
@@ -112,14 +121,25 @@ public class InternalServerGenerator
// create gen requests //
//=====================//
#if MC_VER <= MC_1_12_2
ArrayList<CompletableFuture<Chunk>> getChunkFutureList = new ArrayList<>();
#else
ArrayList<CompletableFuture<ChunkAccess>> getChunkFutureList = new ArrayList<>();
#endif
{
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
while (chunkPosIterator.hasNext())
{
ChunkPos chunkPos = chunkPosIterator.next();
CompletableFuture<ChunkAccess> requestChunkFuture =
#if MC_VER <= MC_1_12_2
CompletableFuture<Chunk> requestChunkFuture;
#else
CompletableFuture<ChunkAccess> requestChunkFuture;
#endif
requestChunkFuture =
this.requestChunkFromServerAsync(chunkPos)
// log errors if necessary
.whenCompleteAsync(
@@ -162,8 +182,16 @@ public class InternalServerGenerator
ArrayList<IChunkWrapper> chunkWrappers = new ArrayList<>();
for (int i = 0; i < getChunkFutureList.size(); i++)
{
CompletableFuture<ChunkAccess> getChunkFuture = getChunkFutureList.get(i);
ChunkAccess chunk = getChunkFuture.join();
#if MC_VER <= MC_1_12_2
CompletableFuture<Chunk> getChunkFuture;
Chunk chunk;
#else
CompletableFuture<ChunkAccess> getChunkFuture;
ChunkAccess chunk;
#endif
getChunkFuture = getChunkFutureList.get(i);
chunk = getChunkFuture.join();
if (chunk != null)
{
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.dhServerLevel.getLevelWrapper());
@@ -195,12 +223,22 @@ public class InternalServerGenerator
}
finally
{
ArrayList<CompletableFuture<Void>> releaseFutures = new ArrayList<>();
// release all chunks from the server to prevent out of memory issues
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
while (chunkPosIterator.hasNext())
{
ChunkPos chunkPos = chunkPosIterator.next();
this.releaseChunkFromServer(this.params.mcServerLevel, this.params.dhServerLevel, chunkPos);
releaseFutures.add(this.releaseChunkFromServerAsync(this.params.mcServerLevel, chunkPos));
}
// wait for all release futures to finish to prevent an issue where DH queues
// tickets faster than MC can clear them out
for (int i = 0; i < releaseFutures.size(); i++)
{
CompletableFuture<Void> releaseFuture = releaseFutures.get(i);
releaseFuture.join();
}
}
}
@@ -237,8 +275,47 @@ public class InternalServerGenerator
LOGGER.warn(c2meWarning);
}
}
#if MC_VER <= MC_1_12_2
private CompletableFuture<Chunk> requestChunkFromServerAsync(ChunkPos chunkPos)
#else
private CompletableFuture<ChunkAccess> requestChunkFromServerAsync(ChunkPos chunkPos)
#endif
{
#if MC_VER <= MC_1_12_2
WorldServer level = this.params.mcServerLevel;
// ignore chunk update events for this position
if (this.updateManager != null)
{
this.updateManager.addPosToIgnore(McObjectConverter.Convert(chunkPos));
}
CompletableFuture<Chunk> future = new CompletableFuture<>();
level.getMinecraftServer().addScheduledTask(() ->
{
ChunkProviderServer provider = level.getChunkProvider();
// load neighbors first so the target chunk can fully populate
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
if (i != 0 || j != 0)
{
if (this.updateManager != null)
{
this.updateManager.addPosToIgnore(new DhChunkPos(chunkPos.x + i, chunkPos.z + j));
}
provider.provideChunk(chunkPos.x + i, chunkPos.z + j);
}
}
}
Chunk chunk = provider.provideChunk(chunkPos.x, chunkPos.z);
future.complete(chunk);
});
return future;
#else
return CompletableFuture.supplyAsync(() ->
{
ServerLevel level = this.params.mcServerLevel;
@@ -281,25 +358,65 @@ public class InternalServerGenerator
}, this.params.mcServerLevel.getChunkSource().chunkMap.mainThreadExecutor)
.thenCompose(Function.identity());
#endif
}
/**
* mitigates out of memory issues in the vanilla chunk system. <br>
* See: https://github.com/pop4959/Chunky/pull/383
*/
private void releaseChunkFromServer(ServerLevel level, IDhServerLevel dhLevel, ChunkPos chunkPos)
#if MC_VER <= MC_1_12_2
private CompletableFuture<Void> releaseChunkFromServerAsync(WorldServer level, ChunkPos chunkPos)
#else
private CompletableFuture<Void> releaseChunkFromServerAsync(ServerLevel level, ChunkPos chunkPos)
#endif
{
CompletableFuture<Void> removeTicketFuture = new CompletableFuture<>();
#if MC_VER <= MC_1_12_2
level.getMinecraftServer().addScheduledTask(() ->
#else
level.getChunkSource().chunkMap.mainThreadExecutor.execute(() ->
#endif
{
try
{
#if MC_VER < MC_1_21_5
#if MC_VER <= MC_1_12_2
for (int difX = -1; difX <= 1; difX++)
{
for (int difZ = -1; difZ <= 1; difZ++)
{
if (difX != 0 || difZ != 0)
{
final int finalDifX = difX;
final int finalDifZ = difZ;
this.chunkSaveIgnoreTimer.schedule(new TimerTask()
{
@Override
public void run()
{
if (InternalServerGenerator.this.updateManager != null)
{
InternalServerGenerator.this.updateManager.removePosToIgnore(
new DhChunkPos(
chunkPos.x + finalDifX,
chunkPos.z + finalDifZ
)
);
}
}
}, MS_TO_IGNORE_CHUNK_AFTER_COMPLETION);
}
}
}
#elif MC_VER < MC_1_21_5
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
level.getChunkSource().distanceManager.removeTicket(DH_SERVER_GEN_TICKET, chunkPos, chunkLevel, chunkPos);
#else
level.getChunkSource().removeTicketWithRadius(DH_SERVER_GEN_TICKET, chunkPos, 0);
#endif
#if MC_VER > MC_1_12_2
level.getChunkSource().chunkMap.tick(() -> false);
#endif
#if MC_VER > MC_1_16_5
level.entityManager.tick();
@@ -323,9 +440,14 @@ public class InternalServerGenerator
}
catch (Exception e)
{
LOGGER.warn("Failed to release chunk back to internal server. Error: ["+e.getMessage()+"]", e);
LOGGER.warn("Failed to release chunk ["+chunkPos+"] back to internal server. Error: ["+e.getMessage()+"]", e);
}
finally
{
removeTicketFuture.complete(null);
}
});
return removeTicketFuture;
}
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
#if MC_VER > MC_1_12_2
import com.mojang.serialization.Codec;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -749,3 +750,4 @@ public class ChunkCompoundTagParser
}
#endif
@@ -1,5 +1,6 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.RegionFileStorageExternalCache;
@@ -341,3 +342,4 @@ public class ChunkFileReader implements AutoCloseable
}
#endif
@@ -1,5 +1,6 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
#if MC_VER > MC_1_12_2
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import org.jetbrains.annotations.Nullable;
@@ -146,3 +147,4 @@ public class CompoundTagUtil
}
#endif
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
#if MC_VER > MC_1_12_2
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
@@ -489,3 +490,4 @@ public class DhLitWorldGenRegion extends WorldGenRegion
}
}
#endif
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
#if MC_VER > MC_1_12_2
import net.minecraft.world.level.lighting.*;
import org.jetbrains.annotations.Nullable;
@@ -97,3 +98,4 @@ public class DummyLightEngine extends LevelLightEngine
#endif
}
#endif
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IStarlightAccessor;
@@ -80,3 +81,4 @@ public class LightGetterAdaptor implements LightChunkGetter
}
#endif
}
#endif
@@ -1,5 +1,6 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
#if MC_VER > MC_1_12_2
import com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling.ChunkFileReader;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.nbt.CompoundTag;
@@ -250,3 +251,4 @@ public class RegionFileStorageExternalCache implements AutoCloseable
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More