Compare commits

...

383 Commits

Author SHA1 Message Date
Ran 0c8b3f3cda Merge remote-tracking branch 'origin/java_omega' into java_omega
# Conflicts:
#	build.gradle
#	coreSubProjects
#	fabric/build.gradle
#	forge/build.gradle
2024-07-26 00:21:34 +10:00
Ran a9a6a19307 Attempt to migrate most of the codebase to newer versions of Java 2024-07-26 00:17:15 +10:00
Cutiepie 208fc69eb7 Rebase 2024-07-25 23:00:43 +10:00
Cutiepie eb1a062934 Rebase 2024-07-25 22:59:16 +10:00
James Seibel 132251341f Fix replay mod not showing LODs 2024-07-21 20:06:40 -05:00
James Seibel 2bac5f933a remove unused clientLevelWrapper.getGameDirectory() 2024-07-21 19:29:01 -05:00
James Seibel 2e565aa83a Improve cave culling and add config for ignored/cave blocks 2024-07-21 17:27:26 -05:00
James Seibel 4e9d0f4861 Fix ConfigEntry String value saving 2024-07-21 16:13:55 -05:00
James Seibel 7216b193e8 Fix API chunk world gen 2024-07-20 17:58:39 -05:00
James Seibel c33a5bf814 Add IDhApiWrapperFactory resourceLocation string methods for block/biomes 2024-07-20 11:21:01 -05:00
James Seibel 97756a5196 Add AbstractDhApiChunkWorldGenerator.generateApiChunk() 2024-07-20 10:45:55 -05:00
James Seibel 377f7d23e3 Remove render param from DhApiAfterRenderEvent 2024-07-14 09:31:03 -05:00
James Seibel 7005202384 Add a optional memory cache to the IDhApiTerrainDataRepo 2024-07-14 08:41:22 -05:00
James Seibel 99e8f57bac add missing genericRendering to IDhApiGraphicsConfig 2024-07-14 07:25:49 -05:00
James Seibel afddf4168e Change some chunk deserialization errors to warnings 2024-07-13 12:59:43 -05:00
James Seibel fbffdc0c9f Fix fog for Mac and remove near fog limitation 2024-07-13 12:17:30 -05:00
James Seibel e6d3647490 Increase default fog start distance 2024-07-13 08:14:33 -05:00
James Seibel 13363ff363 make clouds smaller and thinner 2024-07-12 21:47:16 -05:00
James Seibel 7f98e4b1eb Fix potential chunkWrapper null pointer 2024-07-12 21:31:12 -05:00
James Seibel 408460b0ae Fix missing imports for MC 1.19 and below 2024-07-12 20:31:36 -05:00
James Seibel b69ef5835d Fix repo connections not getting closed 2024-07-12 20:22:02 -05:00
James Seibel 0428fa0912 Clone API event parameters to reduce listener contamination 2024-07-12 19:22:25 -05:00
James Seibel 9f3124fa56 Add renderEventParam to generic rendering shader binding by IMS request 2024-07-12 17:27:32 -05:00
James Seibel fbbdab73c6 Attempt to fix lag spikes when right clicking blocks 2024-07-12 17:24:45 -05:00
James Seibel ee9441c521 Fix world gen not skipping already complete stages 2024-07-12 07:41:18 -05:00
James Seibel a9e0fd5d9b Add generic object setup/cleanup events 2024-07-12 07:16:06 -05:00
James Seibel 98464889ca Fix material typo 2 2024-07-11 22:51:47 -05:00
James Seibel eed5fd60c6 Fix material typo 2024-07-11 22:07:15 -05:00
James Seibel ac43cd5496 Add generic object materials 2024-07-11 18:13:07 -05:00
James Seibel 1f16a7c808 Fix generic rendering and add EDhApiBlockMaterial 2024-07-11 17:58:05 -05:00
James Seibel 39e4c70754 Add api for generic rendering config 2024-07-11 17:39:01 -05:00
James Seibel 82eb27af4c Add DhApiBeforeGenericObjectRenderEvent 2024-07-11 17:32:26 -05:00
James Seibel 07a0779ca4 Fix potential light map crashing and memory leak 2024-07-10 18:57:09 -05:00
James Seibel 2adba02a38 Add "IP Only" to multiplayer tooltip 2024-07-10 07:45:12 -05:00
James Seibel 9dd76db3fc Fix generic rendering at extreme distances 2024-07-10 07:37:18 -05:00
James Seibel 97dacf2429 Add toggleable logging for GL Buffer garbage collection
Will need to be tested by someone who is experiencing issue #718, so far I've been unable to reproduce anything meaningful.
2024-07-09 17:40:27 -05:00
James Seibel 1c189e162a fix sub MC 1.20.1 compiling 2024-07-09 16:39:29 -05:00
James Seibel f7a0fff869 Move IBlockStateWrapper constants into LodUtil 2024-07-09 16:39:04 -05:00
James Seibel 2f985d0926 Add beacon colors 2024-07-09 07:33:30 -05:00
James Seibel 2a3c544fba Increase cloud rendering performance 2024-07-08 19:56:29 -05:00
James Seibel 09d133b994 Add generic rendering localization 2024-07-08 07:45:03 -05:00
James Seibel 26a4223ecf Fix double unloading beacons 2024-07-07 19:54:25 -05:00
James Seibel e2943fdcaf Fix beacons un-rendering when unloading LODs 2024-07-07 19:45:47 -05:00
James Seibel f1053251b4 Add missing generic rendering config options 2024-07-07 18:13:58 -05:00
James Seibel be1dcaf43c Add cloud rendering 2024-07-07 18:03:11 -05:00
James Seibel a899d988fc Fix concurrent modification for GenericObjectRenderer 2024-07-04 21:43:15 -05:00
James Seibel 06b5b2c514 Fix potential null pointer in auto updater 2024-07-04 17:37:30 -05:00
James Seibel 864a19b79f Remove useless IServerLevelWrapper.tryGetClientLevelWrapper() 2024-07-04 16:31:04 -05:00
James Seibel 8974323406 Fix Api client level not containing the generic renderer 2024-07-04 16:15:51 -05:00
James Seibel 46c9e0103a Improve world gen timeout warning message 2024-07-04 16:01:33 -05:00
James Seibel 02203466ed Move generic rendering to the level API 2024-07-03 22:38:14 -05:00
James Seibel 87b22ea1cc Add a config to use pre-existing lighting 2024-07-03 20:30:56 -05:00
James Seibel d26327a930 fix max chunk Y position for empty chunks 2024-07-03 19:14:47 -05:00
James Seibel 469d2bdcb7 Add improved beacon logic 2024-07-02 17:51:26 -05:00
James Seibel 5516603a0c Add temporary proof-of-concept beacon rendering 2024-06-30 18:08:55 -05:00
James Seibel b737adc3da Up API version 2.1.0 -> 3.0.0 2024-06-30 16:36:49 -05:00
James Seibel f3a8afeee3 Up version 2.1.2 -> 2.1.3-dev 2024-06-25 19:25:35 -05:00
James Seibel a4501f86e9 Update coreSubProjects 2024-06-25 19:24:13 -05:00
James Seibel 095fff96ff Up version 2.1.1-dev -> 2.1.2 2024-06-24 20:53:45 -05:00
James Seibel a23211d061 Fix NeoForge not running 2024-06-24 20:52:14 -05:00
James Seibel b57ea41686 neoforge build script cleanup 2024-06-23 08:52:55 -05:00
James Seibel 62fb5ffb73 Add DB file lock checking 2024-06-23 08:36:48 -05:00
James Seibel 99c713967b Temporary spongepowered.vanillagradle fix/workaround 2024-06-22 16:21:19 -05:00
James Seibel 9f3de07bd8 Increase default world gen timeout to 3 minutes (from 60 sec) 2024-06-18 07:12:01 -05:00
James Seibel cd74117de3 Fix file handler tooltip 2024-06-17 07:42:35 -05:00
James Seibel e7d7033548 Improve F3 menu logic and visuals 2024-06-15 19:20:25 -05:00
James Seibel 34db7c9dac Lower the default CPU presets 2024-06-15 11:26:05 -05:00
James Seibel 272841aae9 Add a startup low memory warning 2024-06-15 11:05:10 -05:00
James Seibel 389b09a5cd Prevent creating LODs for already processed chunks 2024-06-15 09:42:49 -05:00
James Seibel 84bd876c71 Refactor ChunkWrapper 2024-06-15 08:11:26 -05:00
James Seibel 7e45051ffd Fix more MC version compiles 2024-06-14 22:21:52 -05:00
James Seibel 5570f3a313 Fix some compiling issues 2024-06-14 19:31:21 -05:00
James Seibel f4e71f7012 Add NeoForge 1.21 2024-06-14 19:05:45 -05:00
James Seibel 601d4e6e3a Fix CI not picking up 1.21 2024-06-14 07:40:29 -05:00
James Seibel a12092c1a1 Add fabric 1.21 support 2024-06-14 07:36:25 -05:00
James Seibel 94ad118c5d Minor memory optimization thanks to littlewolf 2024-06-13 07:30:42 -05:00
James Seibel 48e2978438 Fixes #713 (Forge/Neo level unload events not being called) 2024-06-13 07:15:11 -05:00
James Seibel 96b4c1a9e8 Use existing lighting for pre-generated chunks 2024-06-11 20:22:13 -05:00
James Seibel cc4a69c10c Move shared ChunkWrapper code form Main to Core 2024-06-11 18:35:02 -05:00
James Seibel 7293677ddb Batch Generation Environment refactoring 2024-06-10 21:32:14 -05:00
James Seibel 0f2ff20375 Re-arange ChunkLoader 2024-06-10 21:17:57 -05:00
James Seibel 7706240acb Remove OpenGL multithreading 2024-06-08 12:49:17 -05:00
James Seibel 4cf48fd997 Try changing LZMA preset from 4 -> 3 (faster, less compressed)
won't require any lod regeneration since the decompressor is the same
2024-06-08 11:06:42 -05:00
James Seibel 2708c1ee11 Improve config comment spacing 2024-06-08 08:33:41 -05:00
James Seibel ebb0f6ebad Up the manifold version 2023.1.17 -> 2024.1.15 2024-06-08 08:12:03 -05:00
James Seibel 2c263a2549 Up the API version 2.0.0 -> 2.1.0 2024-06-08 08:11:48 -05:00
James Seibel 955524c632 Remove blendium from the list of suggested fabric mods 2024-06-08 08:11:34 -05:00
James Seibel 564e0d3263 Add update branch config "auto" 2024-06-08 08:11:26 -05:00
James Seibel c533b2e8ea Fix config screen blur on 1.20.6 2024-06-08 07:19:50 -05:00
James Seibel 6073d8122a Up the version number 2.1.0-a -> 2.1.1-a-dev 2024-06-07 17:42:46 -05:00
James Seibel 71ca26bba9 Up the version number 2.0.4-a-dev -> 2.1.0-a 2024-05-30 20:14:04 -05:00
James Seibel 75a51be28c remove unused lightmapBindingIndex in DhApiRenderParam 2024-05-27 17:57:58 -05:00
James Seibel a66e4ba157 Potentially fix memory leaks when rendering is disabled 2024-05-27 17:38:00 -05:00
Cutiepie 55d5bca76d Downgrade to the minimum Java version required by the selected Minecraft version 2024-05-22 22:17:59 +10:00
Cutiepie f81026c707 Bump JVM Downgrader 2024-05-22 21:51:46 +10:00
Cutiepie e86af817e0 Java Ω & cleanup core's code 2024-05-22 21:06:36 +10:00
Cutiepie e0fd2b2f7a Java Ω & cleanup core's code 2024-05-22 21:03:38 +10:00
James Seibel f2b9e428d3 Re-add a missing import to fix compiling 2024-05-21 18:24:30 -05:00
James Seibel 5b2497b9d4 Minor MixinMinecraft reformatting 2024-05-21 17:16:36 -05:00
Yeshi0 e78424def4 typo 2024-05-21 20:58:44 +02:00
Yeshi0 e2c94de6e6 fix blurry text on auto update screen 2024-05-21 20:57:12 +02:00
Yeshi0 daa3caf684 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-05-21 15:12:52 +02:00
James Seibel 5991aa42d9 Revert "Add JVM Downgrader (DH now uses Java version Ω)"
This reverts commit af6dca6e5e.
2024-05-21 07:45:33 -05:00
James Seibel ff6a5aae69 Revert "Set the core to use Java version Ω"
This reverts commit c4a9e7a2a7.
2024-05-21 07:45:25 -05:00
James Seibel 80d9b4540b Fix LZ4 in retail MC 2024-05-21 07:43:45 -05:00
James Seibel 4998991ebe Fix option button in 1.20.6 being on the wrong side 2024-05-21 07:12:04 -05:00
James Seibel 14343569fe Fix neoforge config button position 2024-05-21 06:56:27 -05:00
James Seibel be6cc5ff4e Fix some old MC version compiling 2024-05-20 22:19:04 -05:00
James Seibel 0ad3391bea Put config button hide option only in file 2024-05-20 22:15:45 -05:00
James Seibel 582d998e2e Fix GuiHelper rename for MC 1.19.4 and below 2024-05-20 22:12:09 -05:00
James Seibel c00ee26075 Properly shade libraries when using Java version Ω 2024-05-20 22:10:48 -05:00
James Seibel 4c9f70a52f Merge branch 'main' of gitlab.com:jeseibel/distant-horizons 2024-05-20 22:06:59 -05:00
James Seibel 29481bc123 Fix 1.20.6 config page and config button 2024-05-20 22:06:25 -05:00
Cutiepie e274c9e004 Properly shade libraries when using Java version Ω 2024-05-21 11:53:00 +10:00
Yeshi 73988f0308 Merge branch distant-horizons:main into main 2024-05-20 20:06:23 +00:00
Cutiepie c4a9e7a2a7 Set the core to use Java version Ω 2024-05-21 01:56:33 +10:00
Cutiepie af6dca6e5e Add JVM Downgrader (DH now uses Java version Ω) 2024-05-21 01:26:03 +10:00
Yeshi0 a49720a221 fix gradle.properties typos (it was bothering me) 2024-05-20 17:04:10 +02:00
Yeshi0 12a66e70c9 remove unnecessary references to zstd 2024-05-20 17:01:45 +02:00
James Seibel 00d8aa356b minor ClassicConfigGUI reformat 2024-05-20 07:52:46 -05:00
James Seibel d40d94a565 Add probably broken AT OptionsScreen code
Will probably break 1.20.2 and 1.20.4
2024-05-20 07:52:38 -05:00
James Seibel c1f798793e roll back manifold version to fix mystery compiler issues 2024.1.15 -> 2023.1.17 2024-05-19 21:25:26 -05:00
James Seibel 8fe4ad454c update CI JDK 17 -> 21 2024-05-19 20:31:51 -05:00
James Seibel 17022f2df2 Document GuiHelper args 2024-05-19 14:26:27 -05:00
James Seibel 7fa4bc35f6 remove forge from 1.20.6 to fix CI/CD 2024-05-19 14:16:40 -05:00
James Seibel 85df9c5ef4 Add 1.20.6 to the CI build script 2024-05-18 20:32:33 -05:00
James Seibel 45d4f390a9 Fix a bunch of compiler errors 2024-05-18 20:31:15 -05:00
James Seibel e7b60b7562 Fix neoforge 1.20.6 compiling 2024-05-18 11:14:13 -05:00
James Seibel 2615177907 Fix fabric 1.20.6 compiling 2024-05-18 08:07:48 -05:00
James Seibel a83d7e2a26 Replace DhSectionPos with long's for GC performance
What could possibly go wrong?
2024-05-16 22:15:43 -05:00
James Seibel 8a2182e238 Merge branch 'main' of gitlab.com:jeseibel/distant-horizons 2024-05-15 20:40:11 -05:00
James Seibel d45455092c Replace QuadTree iterator linked list with ArrayDeque
Thanks JustALittleWolf!
2024-05-15 07:36:42 -04:00
James Seibel da18469fd4 Fix resource locations in biome/block wrappers 2024-05-15 07:24:08 -04:00
James Seibel 6b5bae9bee Cache block and biome wrapper deserialization values 2024-05-13 20:26:47 -04:00
James Seibel e29a7786e4 Potentially fix LODs not loading in 2024-05-11 16:23:50 -05:00
James Seibel 55a837ca5e Attempt to prevent thread starvation due to world gen 2024-05-10 22:27:29 -05:00
James Seibel 94cba6cf67 Fix compiling 2024-05-10 17:25:52 -05:00
James Seibel 294685df00 Remove indium recommended dependency
A lot of people were reading fabric's warning as a required dependency
2024-05-10 07:05:40 -05:00
James Seibel 2642b7a9a4 disable sql timeout 2024-05-09 23:22:50 -05:00
James Seibel 45594e4e47 Handle missing/corrupted block/biome ID's in the full data 2024-05-09 19:46:33 -05:00
James Seibel 54cd1a2e48 Fix monoliths due to duplicate IDs 2024-05-09 19:45:50 -05:00
James Seibel a20fb982ec Potential fix for NaN multiverse similarity 2024-05-09 07:35:00 -05:00
James Seibel 184d61e637 Up the API version 1.1.0 -> 2.0.0
There were several breaking changes and I forgot to up the major version number appropriately.
2024-05-04 18:17:10 -05:00
James Seibel 06ea56767f Up manifold version 2024.1.12 -> 2024.1.13 2024-05-04 15:37:20 -05:00
James Seibel 1f6e137759 Fix F3 levels not closing with multiverse 2024-05-04 15:36:51 -05:00
James Seibel c7cf7885ae Fix #670 Remove outdated world gen options from tooltip 2024-05-04 09:48:40 -05:00
James Seibel 8e98444887 Update coreSubProjects 2024-05-04 09:23:02 -05:00
James Seibel 9bfe2e8233 Up 1.20 fabric loader versions 0.14.24/0.15.1 -> 0.15.6 2024-05-04 09:22:44 -05:00
James Seibel 21f4adc769 Minor 1.20.6 preprocessor updates 2024-05-02 17:28:30 -05:00
James Seibel 3b10ca5809 Update arch loom 1.5-snapshot -> 1.6-snapshot 2024-05-02 17:27:04 -05:00
James Seibel 6cc8284747 Start adding 1.20.6 2024-05-01 07:45:23 -05:00
James Seibel 6254f7156f Improve nightly build and migration messages 2024-04-30 21:59:17 -05:00
James Seibel 0fa03701a4 Fix debug wireframes rendering on top of LODs 2024-04-30 21:24:08 -05:00
James Seibel 49125cae47 Remove ZStd compression option
Any ZStd data will be automatically deleted and re-generated
2024-04-30 21:17:54 -05:00
James Seibel 3298857d0c Remove references to FastUtil 8.5.13 2024-04-30 20:30:51 -05:00
James Seibel d939cbeb96 remove unused MixinWorldupgrader files 2024-04-30 19:44:44 -05:00
James Seibel 54d254be73 Fix optifine 1.16 support 2024-04-30 19:44:23 -05:00
James Seibel d433fdea62 Fix white grass/water if the biome is null 2024-04-28 17:35:10 -05:00
James Seibel ffa1c54ff3 Fix warning about BiomeWrapper null level on startup 2024-04-28 16:08:08 -05:00
James Seibel 019ac6dec3 Add corrupt data read handling 2024-04-28 15:52:11 -05:00
James Seibel 08d3da47f4 Fix fastutil relocation issues with world gen 2024-04-27 16:46:22 -05:00
James Seibel 348ac2b734 Closes #638 (optifine not rendering on 1.16 + 1.17) 2024-04-27 13:20:18 -05:00
James Seibel fe014b4985 revert b1c6a5c1 2024-04-27 12:56:20 -05:00
James Seibel b7f6f3b900 Remove (hopefully) unused MixinThreadingDetector 2024-04-27 11:55:27 -05:00
James Seibel 3c76ed71d8 Fix some lib shading issues 2024-04-27 11:35:16 -05:00
James Seibel 5de1998913 up the version number 2.0.3 -> 2.0.4 2024-04-26 07:33:48 -05:00
James Seibel 05c0f030cb Fix issues with compressors not appearing at runtime 2024-04-26 07:33:26 -05:00
James Seibel bd85329589 Merge Data_source_rewrite into main 2024-04-26 07:22:03 -05:00
James Seibel da0f4ae326 shade in apache.logging for the standalone jar 2024-04-25 22:09:56 -05:00
James Seibel b37e568372 Change Initializer compressor test class 2024-04-25 21:52:56 -05:00
James Seibel b1c6a5c1d4 Move most libraries from the main script to core 2024-04-25 21:52:04 -05:00
James Seibel 8222126e8f minor MixinMinecraft reformat 2024-04-25 21:34:39 -05:00
James Seibel b5b888c69f Remove duplicate remapJar in fabric gradle 2024-04-25 21:10:05 -05:00
James Seibel 85f6b8320b Fix Fabric compiling 2024-04-25 21:09:42 -05:00
James Seibel 6c4740e8aa Remove Unimined and restore Architectury build system 2024-04-24 22:05:59 -05:00
Cutiepie ffda83c25d Add core to gradle 2024-04-22 18:53:32 +10:00
James Seibel 18859d22a8 Fix getChunk null assertion 2024-04-20 15:36:42 -05:00
James Seibel befa3b375e Fix compiling 2024-04-20 12:36:33 -05:00
James Seibel a96c08cad4 Clean up DhLitWorldGenRegion 2024-04-20 12:16:33 -05:00
James Seibel d7f789c402 Fix fastutil shading
fastutil is already in MC and used by some world gen so shading it causes issues.
2024-04-20 11:23:47 -05:00
James Seibel ed28bcd0ba fix forge issues with already deleted lib folders 2024-04-18 21:07:09 -05:00
James Seibel 982ae0c0a0 speed up initial LOD loading 2024-04-18 21:06:32 -05:00
James Seibel d17897f276 Fix transparent blocks glowing (thanks IMS) 2024-04-18 07:47:47 -05:00
James Seibel c0ccef7e82 Fix common complaining about missing "prepareWorkspace" task 2024-04-16 07:32:21 -05:00
James Seibel 55e5c64c68 update manifold 2024.1.0 -> 2024.1.9 2024-04-14 21:17:15 -05:00
James Seibel da72f783ed minor forge modinfo fix 2024-04-14 21:17:02 -05:00
James Seibel 8a9bfa3d33 fix unnecessary issues with javax and jetbrain compile time annotations 2024-04-14 21:16:50 -05:00
James Seibel 82937d840a Merge branch 'removeArch' into Data_Source_Rewrite 2024-04-14 16:41:28 -05:00
James Seibel c1bd358502 fix fastutil not being relocated 2024-04-13 21:24:58 -05:00
James Seibel a8a22fd9fe Fix Iris incompatibility check for 1.20.4 and add for 1.20.2 and lower 2024-04-13 21:15:37 -05:00
James Seibel adb70857fe Fix migration messages 2024-04-13 17:53:32 -05:00
James Seibel a680596b3e log migration status in F3 menu and chat 2024-04-13 14:25:34 -05:00
James Seibel b2986ec782 Fix fabric unnecessarily unloading levels when respawning 2024-04-13 13:35:22 -05:00
James Seibel f40f7afab3 speed up initial loading when DB migration is necessary 2024-04-13 12:14:59 -05:00
James Seibel 59555d1ca8 Update coreSubProjects 2024-04-13 11:19:11 -05:00
cola98765 6e2eb7d1ac I may be dumb 2024-04-10 13:42:15 +00:00
cola98765 69483067b4 Made leaves color like if they are in fancy setting. Still no config, but with all other changes to colors this might fit better. 2024-04-10 13:40:32 +00:00
James Seibel c815591565 Fix Z-fighting at very high heights 2024-04-10 07:11:30 -05:00
James Seibel f82b7ec608 Improve file handler speed 2024-04-09 07:48:39 -05:00
James Seibel 23a107682c Mark Iris 1.7.0 and older as incompatible 2024-04-08 07:23:49 -05:00
James Seibel 26da69c875 Remove deprecated DhApiScreenResizeEvent 2024-04-08 07:17:30 -05:00
James Seibel 8f2df2396d Improve grass side rendering and add a config 2024-04-07 21:42:39 -05:00
James Seibel dbc9cbb418 Have grass fade to dirt for walls 2024-04-06 12:37:44 -05:00
James Seibel 09c788e495 Fix removed Screen imports 2024-04-06 10:28:50 -05:00
James Seibel 9af71ac0ea Fix API enums missing "DhApi" prefix and remove unused Enums/code 2024-04-06 10:06:09 -05:00
James Seibel 2db20f8f24 Add Sqlite Write Ahead Log to Sqlite 2024-04-06 09:53:34 -05:00
James Seibel 2c9827c227 Merge branch 'distant-horizons-main' into Data_Source_Rewrite 2024-04-06 08:54:19 -05:00
James Seibel 825b1ab4db Re-add a couple readme breakpoints and remove a unneeded note 2024-04-06 08:51:06 -05:00
James Seibel c61faac06c Merge branch 'distant-horizons-main' into Data_Source_Rewrite 2024-04-06 08:46:59 -05:00
James Seibel 6b442f03c1 Update ELodShading names and descriptions 2024-04-06 08:46:47 -05:00
James Seibel 10ab638643 Change Sqlite Journaling to WAL to potentially improve concurrent performance 2024-04-04 07:50:21 -05:00
James Seibel d349d0c453 Make rendering speed worse but improve LOD loading speed 2024-04-04 07:09:56 -05:00
James Seibel e5c948ce9c Improve initial LOD loading speed 2024-04-03 22:07:29 -05:00
James Seibel c51255f379 Add fastutil to gradle (specifically to fix MC 1.16) 2024-04-03 07:43:12 -05:00
James Seibel 5bbeceee56 Add data source pooling 2024-04-01 20:28:38 -05:00
cola98765 ee78920a88 Merge branch 'fix_glass_transparency' into 'Data_Source_Rewrite'
Rework to "calculateColorFromTexture"

See merge request jeseibel/distant-horizons!55
2024-03-29 14:54:06 +00:00
cola98765 138972cf18 Rework to "calculateColorFromTexture" 2024-03-29 14:54:06 +00:00
James Seibel 1bda767cd7 Add Cola's RenderDataTransformer fixes 2024-03-28 17:14:56 -05:00
James Seibel cf1402edb9 Remove the DhRenderData DB table 2024-03-28 07:47:17 -05:00
James Seibel 778144a553 Add WorldCompressionMode config, default to VISUALLY_EQUAL 2024-03-28 07:43:32 -05:00
James Seibel fcca51a8d9 Fix missing indium messy crash error when java.awt is headless 2024-03-24 11:53:46 -05:00
James Seibel 3cc663ee95 remove render data file handling 2024-03-23 18:09:10 -05:00
James Seibel 52f15a86fd add todo about error in ClientBlockStateCache 2024-03-23 17:56:29 -05:00
James Seibel 734bb4afb8 minor biome/block wrapper changes 2024-03-23 16:23:40 -05:00
James Seibel 9cd48fb5d7 Comment out trace logs
These logs aren't printed and will just increase GC pressure for strings
2024-03-23 16:18:40 -05:00
James Seibel cd5c3d9f13 Compress the column gen step in the database 2024-03-20 07:25:42 -05:00
James Seibel 73ba1c8b56 Fix transactionScript auto update variable flipped 2024-03-19 21:10:31 -05:00
James Seibel 8199b4408a Reduce string concatenations in assertions 2024-03-19 20:53:15 -05:00
James Seibel c00e3d7393 Change the SQLite journal mode DELETE -> TRUNCATE 2024-03-19 20:07:35 -05:00
James Seibel 5d60251da0 Cache the blockState and biome wrapper hashCodes 2024-03-19 17:36:22 -05:00
Yeshi 855d707b3e readme improvements 2024-03-19 18:55:05 +01:00
James Seibel 592a1c3601 up the version number 2.0.2 -> 2.0.3 2024-03-19 06:48:31 -05:00
James Seibel da280db0f8 Fix lighting in superflat worlds 2024-03-18 21:28:27 -05:00
James Seibel 0ee6673e68 update todo comments 2024-03-17 16:15:27 -05:00
James Seibel 8bedb3dbaa Remove Seamless Overdraw Test
The result wasn't very good due to rendering issues with entities
2024-03-16 21:03:27 -05:00
James Seibel ea0e24b430 Add multiple compression options and unit tests 2024-03-16 17:25:29 -05:00
Tatounee cd35461df6 Fix cd command in compiling instructions 2024-03-10 02:38:31 +01:00
James Seibel 40580e81c2 Fix ChunkWrapper returning block light 15 for out of bound positions
Now it will return 0, which is more accurate
2024-03-03 14:54:11 -06:00
James Seibel 1a279b90be rename ThreadPools -> ThreadPoolUtil 2024-03-03 14:53:40 -06:00
James Seibel e34203fe3d Fix potential null pointer in BiomeWrapper 2024-03-02 11:45:14 -06:00
James Seibel 870c0f68d3 temporary multiverse null pointer fix 2024-02-22 07:05:21 -06:00
coolGi 004059dd9f Removed mixins from neoforge's mods.toml 2024-02-22 22:10:34 +10:30
coolGi 4290cdf8c6 Changed run dir to the root of the project 2024-02-20 00:51:41 +10:30
coolGi 4d038fc5e6 Fixed up AT 2024-02-20 00:51:25 +10:30
coolGi 0146d62c2a Moved AT to only appear in final build, not in the source code 2024-02-19 23:44:53 +10:30
coolGi 4e6255dc2b Re-added jank file deleting again for now 2024-02-18 21:38:20 +10:30
coolGi 4d73c3ecb8 Moved common to Unimined 2024-02-18 21:09:00 +10:30
coolGi d0dc3ec9bc Added the new unimined flag to fix fabric's access wideners 2024-02-18 20:11:15 +10:30
coolGi ee922236a0 Fixed forge version in readme for this branch 2024-02-18 19:13:20 +10:30
coolGi bf088ab29c Merge branch 'main' into removeArch 2024-02-18 19:11:47 +10:30
coolGi 40102521a1 Updated readme with new mc version 2024-02-18 19:09:54 +10:30
coolGi eb491144f3 Refactored logos into assets/distanthorizons 2024-02-18 19:04:29 +10:30
coolGi a14748ac75 Fixed most of the remaining issues 2024-02-18 18:42:44 +10:30
coolGi 0c5f38a00b Removed un-needed stuff 2024-02-18 16:07:51 +10:30
James Seibel feb6dd41b7 Merge branch 'removeArch' of gitlab.com:jeseibel/distant-horizons into removeArch 2024-02-17 22:37:10 -06:00
James Seibel a093524939 Fix Forge 1.20.4 gradle run 2024-02-17 22:34:18 -06:00
James Seibel 5cc7bebbe5 Update forge 1.20.4 49.0.16 -> 49.0.30 2024-02-17 22:34:06 -06:00
coolGi 32a256619f Fixed using wrong implementation for joml 2024-02-18 14:00:34 +10:30
coolGi f93e648f69 Added a small error if the user forgot to clone the sub-project, and some extra comments 2024-02-18 13:43:11 +10:30
coolGi 41f022df99 Merge branch 'main' into removeArch
# Conflicts:
#	build.gradle
2024-02-18 12:01:02 +10:30
coolGi c84aac7e45 Fixed resources in forge 2024-02-18 11:59:02 +10:30
James Seibel 4e4fbbe48c Merge branch 'main' into removeArch 2024-02-17 09:25:34 -06:00
James Seibel 7265e2b631 add chunk read timeout config and remove duplicate RegionFileStorageExternalCache 2024-02-16 20:04:46 -06:00
James Seibel 0a11f310cf Revert Fog near end/far start distance to pre-RenderUtil fix 2024-02-15 21:54:33 -06:00
James Seibel a6143780fa Fix C2ME file loading 2024-02-15 21:45:11 -06:00
James Seibel 761f802ced Fix fog rendering 2024-02-15 20:36:41 -06:00
James Seibel 2e7cc9f4b6 Temp fix for near clip plane going too far in some cases 2024-02-13 07:53:08 -06:00
James Seibel 4e6e727799 Fix ocean floors showing at very low detail levels
Partially fixes #632
2024-02-11 21:32:08 -06:00
James Seibel 2da444d03c Fix BiomeWrapper warning about null level on startup 2024-02-11 20:24:36 -06:00
James Seibel f4f234a159 Fix NeverCullFrustum casting error 2024-02-11 18:29:51 -06:00
James Seibel e87823aa29 Close #615 (lag when rapidly breaking/placing blocks) 2024-02-11 16:39:29 -06:00
James Seibel 56167a137e Default to no culling for the shadow pass 2024-02-11 14:56:16 -06:00
James Seibel 91ac4309df Fix the API seeing the wrong far clip plane 2024-02-10 22:02:19 -06:00
James Seibel 8a5bca3136 Fix using the wrong near clip plane 2024-02-10 21:58:24 -06:00
James Seibel ec8d1b5538 Fix Lava Iris block material ID 2024-02-10 21:38:55 -06:00
James Seibel 0ebe8db268 Add IDhApiShadowCullingFrustum and a config for shadow culling 2024-02-10 21:38:44 -06:00
coolGi 3bba08723f joml is included in vanilla 1.19.4 2024-02-08 10:28:33 +00:00
coolGi 6a6a87a3f7 Fixed joml being embedded on versions where mc already embeds it 2024-02-08 17:47:13 +10:30
James Seibel 0ccdcfbb6d Fix 1.20.4 compling 2024-02-07 18:19:41 -06:00
James Seibel aa084c885d Attempt to reduce queuing duplicate world gen tasks 2024-02-07 07:34:49 -06:00
coolGi e3a8a7782e Merge branch 'main' into removeArch
# Conflicts:
#	build.gradle
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java
#	forge/src/main/java/com/seibel/distanthorizons/forge/ForgeMain.java
2024-02-07 23:44:24 +10:30
James Seibel c578ae0fa4 Add Lod Shading to the api config 2024-02-07 07:06:39 -06:00
coolGi f17bc1eccd Fixed JOML not being shadowed (not being included in the jar) 2024-02-07 23:32:33 +10:30
coolGi 08c31e5999 Fixed mod auto-updating on quilt 2024-02-07 23:23:15 +10:30
James Seibel dd341c9a22 Add frustum culling config control to the API 2024-02-06 21:45:12 -06:00
James Seibel 733fb8e871 Merge branch 'minecraft-lod-mod-shadow.frustum.culling' 2024-02-06 19:23:56 -06:00
James Seibel 4764f0969a Fix assertion errors in the end 2024-02-06 17:49:08 -06:00
James Seibel 41f8c8cfa4 Fix compiling on MC 1.19.2 and below 2024-02-06 07:17:43 -06:00
James Seibel 42bcc28d3e Merge branch 'minecraft-lod-mod-frustum.culling' 2024-02-05 21:38:46 -06:00
James Seibel b878faac96 Improve StepFeatures logging 2024-02-05 20:31:05 -06:00
James Seibel 32c1cc29f8 Fix Render Buffer count F3 menu not closing 2024-02-05 19:21:52 -06:00
James Seibel 838d82589b Fix Forge 1.20.4 compiling, but not gradle running 2024-02-04 21:27:22 -06:00
James Seibel b62af66f4b rename shadowMe ->shadowCore and shade ->shadowMc 2024-02-04 16:30:25 -06:00
James Seibel 794f524ae3 gradle commenting and minor refactoring 2024-02-04 15:52:27 -06:00
Cutiepie a38551b3d0 Disable tests for modloader projects 2024-02-04 13:28:52 +11:00
James Seibel 03c4926b09 update to latest core 2024-02-03 19:45:29 -06:00
James Seibel 982bf951e1 Fix 1.19.2 compiling 2024-02-01 07:39:16 -06:00
James Seibel a887e35285 Add Iris API events 2024-01-31 21:49:59 -06:00
Cutiepie 172f2b088d Attempt to move over to Unimined 2024-01-30 17:45:47 +11:00
Cutiepie 6d7b557c36 Attempt to move over to Unimined 2024-01-30 17:44:50 +11:00
James Seibel 9959ebc196 Merge branch 'distant-horizons-fixes' 2024-01-27 13:37:50 -06:00
James Seibel d868b8fc72 Fix missing imports 2024-01-27 13:32:35 -06:00
IMS212 297c8a1a1e Actually fix 1.16 2024-01-23 07:47:43 -08:00
IMS212 e809429a8c Fix 1.16 support 2024-01-22 17:59:59 -08:00
IMS212 200ad05f4c Fix suspicious frame time code 2024-01-22 17:52:51 -08:00
IMS212 9b4276c29b Add mushroom 2024-01-22 08:49:00 -08:00
IMS212 038073d34d Minor fixes for translucency and block ID's 2024-01-22 08:37:27 -08:00
James Seibel 8d32ab9bdb Fix method names in neoForge mixinLevelRenderer 2024-01-21 21:48:06 -06:00
James Seibel 9390b8bc4d Fix several Block materials 2024-01-21 21:36:12 -06:00
James Seibel 0d165860fb Merge branch 'distant-horizons-main' 2024-01-21 19:24:42 -06:00
James Seibel 4135fa6211 Update coreSubProjects 2024-01-20 12:24:10 -06:00
James Seibel 1d89467022 Fix Forge 1.20.1 launching (although crashes due to shadowJar fail) 2024-01-20 11:12:08 -06:00
coolGi e21ac626b3 Merge remote-tracking branch 'origin/main' 2024-01-18 19:47:23 +10:30
James Seibel aa73a30ac4 Fix C2ME throwing errors when attempting to get region file 2024-01-17 20:57:10 -06:00
Cutiepie 9a8f14c7d3 Attempt to remove arch and add AWToAt.java 2024-01-17 23:47:58 +06:00
James Seibel d7618d73c3 Fixes #613 (multiplayer failing due to folder not existing) 2024-01-17 07:45:08 -06:00
James Seibel 5aca47b357 Fix render data holes on world gen 2024-01-17 07:15:47 -06:00
coolGi 666ab1319b Fixed 1.17.1 2024-01-17 17:24:52 +10:30
coolGi 50663edc76 Fixed compilation for pre 1.19.4 2024-01-16 17:28:42 +10:30
James Seibel 1fbc37f8e7 Initializer code reduction and reformatting 2024-01-14 16:55:55 -06:00
James Seibel 5f437f8a4e Merge branch 'minecraft-lod-mod-refactor/initialization' 2024-01-14 16:10:40 -06:00
James Seibel 6130c65c48 Add messages to chunk loading about world optimization 2024-01-14 15:53:21 -06:00
James Seibel 1e19dfd6e8 minor lightMapWrapper reformat 2024-01-14 15:08:29 -06:00
James Seibel f866243d5c Attempt to fix File handler repo closed issues 2024-01-14 15:07:35 -06:00
James Seibel af04c6d995 Fix light engine out of bounds for MC 1.16 and 1.17 2024-01-14 13:27:14 -06:00
James Seibel 46bf8d0188 Update coreSubProjects 2024-01-13 23:12:19 -06:00
coolGi fc62c78136 Updated core sub-module 2024-01-13 16:19:54 +10:30
coolGi 93c2bf530f Fixed neoforge crash with merged jar 2024-01-13 16:19:10 +10:30
James Seibel 51b543a23e Fix Optifine not running in dev environment
For some reason loading the net.minecraft.client.ClientBrandRetriever class causes mixin issues
2024-01-12 07:50:49 -06:00
James Seibel dab5373231 Fix Bclib sometimes refusing to download in 1.20.1 2024-01-11 22:13:11 -06:00
coolGi 17f274a7b4 Renamed many neoforged stuff to neoforge (removed "d" at the end) 2024-01-09 19:26:00 +10:30
coolGi 841e5ba492 Fixed neoforge and updated some dependencies 2024-01-09 19:12:26 +10:30
s809 50339c94e7 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into refactor/initialization 2024-01-08 14:52:26 +05:00
James Seibel d2ad35ad05 Overhaul file handlers 2024-01-07 16:06:09 -06:00
James Seibel 0d65578e6a Fix chunk break/place events not triggering fabric updates post MC 1.17 2024-01-07 15:00:53 -06:00
s809 526df4f184 Refactor initializer code 2024-01-07 21:50:50 +05:00
coolGi aa6cbd1b7d Renamed forge classes to neoforge 2023-12-25 18:50:24 +10:30
coolGi 92f0703723 Removed depricated neoforged code 2023-12-25 18:48:18 +10:30
James Seibel 064241333b Merge branch 'fix-url' into 'main'
Fix clone URL

See merge request jeseibel/distant-horizons!47
2023-12-24 15:17:55 +00:00
Rózsa Péter 39b77c783b Fix clone URL 2023-12-24 13:47:11 +00:00
coolGi 0c8717a0da Fixed implNote in JavaDocs (as it "technically" part of the official Java standard) 2023-12-24 21:42:17 +10:30
coolGi 7f89a1a2cc Fixed 1.16.5 & 1.17.1 compilation 2023-12-24 21:32:21 +10:30
coolGi 5f16f81d58 Merged jar, now for fabric (works on quilt as well), forge and neoforged 2023-12-24 21:16:10 +10:30
coolGi 10bbcc79d3 Re-added forge to 1.20.4 build properties 2023-12-24 19:39:22 +10:30
coolGi ffc9771b17 Renamed forge stuff in neoforged to neoforged 2023-12-24 19:25:57 +10:30
coolGi 091b115aad Added neoforged to the ci 2023-12-23 15:31:41 +10:30
coolGi c3bdc22e28 NeoForged for 1.20.4! 2023-12-23 05:31:02 +10:30
coolGi 28d4cc86a9 Fixed 1.20.4 forge remap 2023-12-23 04:07:44 +10:30
James Seibel bb6e29f254 Merge Builderb0y API world gen extension 2023-12-22 07:19:26 -06:00
James Seibel ea0d4ba7d8 Potential fix for world gen lockup if files system fails 2023-12-20 22:06:08 -06:00
James Seibel 0504882afd Fix ocean lighting grid issue 2023-12-20 07:45:21 -06:00
James Seibel 0156f03e91 Update coreSubProjects 2023-12-19 07:38:55 -06:00
James Seibel d2acaba5c7 Add IDhApiWrapperFactory 2023-12-16 15:49:53 -06:00
James Seibel 60e4128316 Add DhApi.isDhThread() 2023-12-16 09:50:41 -06:00
James Seibel dc8aa7624b Update coreSubProjects 2023-12-16 09:09:46 -06:00
James Seibel 941aeedee0 Merge branch 'chisels' 2023-12-15 07:12:07 -06:00
James Seibel 4d8ce3b5ea Optimize DH lighting for chunks with populated sections
Specifically improves lighting speed for BigGlobe worlds
2023-12-14 07:50:57 -06:00
James Seibel 6044d24a48 Fix ThreadPool null pointer before world startup 2023-12-13 22:08:45 -06:00
James Seibel d597634ac6 Fix forge 1.20.4 crashing due to incorrect version number order 2023-12-12 07:47:22 -06:00
coolGi cf8b0329bb Hopefully fixed compolation with the new preprocessor 2023-12-12 22:17:21 +10:30
coolGi 24520824e9 Updated to new git url 2023-12-12 19:28:45 +10:30
coolGi 0d7b0f9fe4 Updated to new git url 2023-12-12 19:28:41 +10:30
coolGi 61460f9ac0 Updated java files to use version numbers 2023-12-12 19:23:28 +10:30
coolGi 14d64d535a Changed preprocessor to use version numbers 2023-12-12 18:11:26 +10:30
coolGi b00c252f17 Updated gradle (and its wrapper), and fabric and architectury loom 2023-12-12 17:56:44 +10:30
Pierre Remacle 0fe017df74 Chisels & Bits blocks transparent 2023-12-11 16:05:03 +01:00
James Seibel 4ae7083dcf Update coreSubProjects 2023-12-11 07:46:53 -06:00
James Seibel 7d5357dec8 downgrade 1.17.1 bclib 0.5.6 -> 0.5.5
For some reason my machine refused to download 0.5.6, anyone can re-update to the latest if the issue becomes resolved
2023-12-11 07:46:50 -06:00
James Seibel 2bb2f5a233 Remove PRE and POST preprocessor MC versions 2023-12-11 07:45:54 -06:00
James Seibel fee1c98a34 Fix 595 (crash on save and exit) 2023-12-09 16:19:05 -06:00
James Seibel e787d7d317 Attempt to fix a rare concurrent lighting engine issue with ChunkWrapper.getBlockLightPosList() 2023-12-09 16:18:17 -06:00
James Seibel ed52efa72b Fix SSAO and Fog not applying when Optifine shaders are enabled 2023-12-09 10:05:17 -06:00
James Seibel 963d22b2f5 Add a potential fix to unconfigured C2ME crashing/log spam 2023-12-09 09:42:18 -06:00
coolGi 8714be1dc7 Updated ci to include 1.20.4 2023-12-09 14:47:19 +10:30
coolGi 04ddd83519 Fixed up screens looking incorrect on 1.20.4 mc versions 2023-12-09 14:41:15 +10:30
coolGi 5b81ca2716 Updated to 1.20.3/4 2023-12-09 14:09:24 +10:30
James Seibel 6f8c7e8249 Add Config API methods getApiValue() and clearValue() 2023-12-07 07:13:43 -06:00
James Seibel fabad7158e Up the api version number 1.0.0 -> 1.1.0 2023-12-06 07:50:23 -06:00
James Seibel bae7e44dd8 Add overdraw number input 2023-12-06 07:50:10 -06:00
James Seibel 926c7924df Merge in Builderb0y's render merging improvements 2023-12-05 07:15:39 -06:00
James Seibel 704a2ff217 Only display empty biomeWrapper string logs once 2023-12-04 07:17:51 -06:00
coolGi 871c6031b8 Added version number to updated jar 2023-12-04 23:34:40 +10:30
James Seibel afb0a57920 up the version number 2.0.1-a -> 2.0.2-a 2023-12-03 19:13:19 -06:00
152 changed files with 6225 additions and 3259 deletions
+6
View File
@@ -34,3 +34,9 @@ build.properties
# Sqlite databases
*.sqlite
*.sqlite-journal
*.sqlite-shm
*.sqlite-wal
# Don't add access transformers to git as they're dynamically generated
accesstransformer.cfg
+12 -5
View File
@@ -1,6 +1,6 @@
# use Eclipse's JDK
# The ci should always use a unix(-like) OS to work
image: eclipse-temurin:17
image: eclipse-temurin:21
# all stages need to be defined here
# TODO: Make stages depend on what is in versionProperties
@@ -30,7 +30,7 @@ build:
stage: build
parallel:
matrix:
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2"]
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21"]
script:
# this both runs the unit tests and assembles the code
- ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
@@ -40,17 +40,24 @@ build:
name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
- Merged/*.jar
- quilt/build/libs/*.jar
- fabric/build/libs/*.jar
- forge/build/libs/*.jar
- quilt/build/libs/*.jar
- neoforge/build/libs/*.jar
exclude:
# TODO: There is a lot of duplicate stuff here, try to maybe make it smaller
- fabric/build/libs/*-all.jar
- fabric/build/libs/*-dev.jar
- fabric/build/libs/*-sources.jar
- forge/build/libs/*-all.jar
- forge/build/libs/*-sources.jar
- quilt/build/libs/*-all.jar
- quilt/build/libs/*-dev.jar
- quilt/build/libs/*-sources.jar
- forge/build/libs/*-all.jar
- forge/build/libs/*-dev.jar
- forge/build/libs/*-sources.jar
- neoforge/build/libs/*-all.jar
- neoforge/build/libs/*-dev.jar
- neoforge/build/libs/*-sources.jar
expire_in: 14 days
when: always
extends: .build_java
+3 -3
View File
@@ -6,13 +6,13 @@ Or click the checkbox once the issue has been created.
-->
1. [ ] Check the FAQ to see if your issue has already been reported and has a solution:
[Problems-and-solutions](https://gitlab.com/jeseibel/minecraft-lod-mod/-/wikis/2-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
[Problems-and-solutions](https://gitlab.com/jeseibel/distant-horizons/-/wikis/2-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
2. [ ] Make sure you are not using any mods on the incompatible list:
[Mod support FAQ](https://gitlab.com/jeseibel/minecraft-lod-mod/-/wikis/2-frequently-asked-questions/4-mod-support/Mod-Support)
[Mod support FAQ](https://gitlab.com/jeseibel/distant-horizons/-/wikis/2-frequently-asked-questions/4-mod-support/Mod-Support)
3. [ ] Check the existing issues to verify that your bug hasn't already been submitted:
[Issues](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/)
[Issues](https://gitlab.com/jeseibel/distant-horizons/-/issues/)
4. [ ] Upload Minecraft's crash report and/or log. \
Minecraft crash reports are located in: `.minecraft/crash-reports` \
+1 -1
View File
@@ -1,4 +1,4 @@
- [ ] Check the existing [feature requests](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/?sort=updated_desc&state=opened&label_name%5B%5D=Feature) to verify that your feature hasn't already been suggested.
- [ ] Check the existing [feature requests](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=updated_desc&state=opened&label_name%5B%5D=Feature) to verify that your feature hasn't already been suggested.
1. **Describe the feature**:
@@ -1,3 +1,3 @@
1. Check the existing [improvement requests](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/?sort=updated_desc&state=all&label_name%5B%5D=Improvement) to verify that your improvement hasn't already been suggested.
1. Check the existing [improvement requests](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=updated_desc&state=all&label_name%5B%5D=Improvement) to verify that your improvement hasn't already been suggested.
2. **Describe the improvement**:
+38 -35
View File
@@ -1,18 +1,16 @@
# <img src="https://gitlab.com/jeseibel/distant-horizons-core/-/raw/main/_Misc%20Files/logo%20files/LOD%20logo%20flat%20-%20with%20boarder.png" width="32"> Distant Horizons
_See farther without turning your game into a slide show._
> A mod that adds a Level of Detail System to Minecraft
<br>
# What is Distant Horizons?
Distant Horizons is a Minecraft mod that adds a Level Of Detail (LOD) system to\
render simplified chunks outside the normal render distance\
allowing for an increased view distance without harming performance.
Distant Horizons is a mod which implements a [Level of Detail](https://en.wikipedia.org/wiki/Level_of_detail_(computer_graphics)) system to Minecraft.\
This allows for far greater render distances without harming performance by gradually lowering the quality of distant terrain.
In other words: this mod lets you see farther without turning your game into a slide show.\
If you want to see a quick demo, check out a video covering the mod here:
Below is a video demonstrating the system:
<a href="https://youtu.be/_04BZ8W2bDM" target="_blank">![Minecraft Level Of Detail (LOD) mod - Alpha 1.6.3](https://cdn.ko-fi.com/cdn/useruploads/png_ef4d209d-50d9-462f-b31f-92e42ec3e260cover.jpg?v=c1097a5b-029c-4484-bec3-80ff58c5d239)</a>
<a href="https://youtu.be/SxQdbtjGEsc" target="_blank">![Distant Horizons - Alpha 2.0](https://i.ytimg.com/vi/SxQdbtjGEsc/hqdefault.jpg)</a>
<br>
@@ -20,6 +18,14 @@ If you want to see a quick demo, check out a video covering the mod here:
### 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\
@@ -27,7 +33,7 @@ Forge: 48.0.13\
Parchment: 1.20.1:2023.09.03\
Modmenu: 8.0.0
#### 1.20.1, 1.20 (Default)
#### 1.20.1, 1.20
Fabric: 0.14.24\
Fabric API: 0.90.4+1.20.1\
Forge: 47.2.1\
@@ -73,14 +79,15 @@ Modmenu: 1.16.22
- 1.18.1, 1.18
- 1.19.1, 1.19
- 1.19.3
<br><br>
<br>
### Plugin and Library versions
Fabric loom: 1.1.+\
Forge gradle (Using Architectury): 3.4-SNAPSHOT\
Gradle: 8.5\
Fabric loom: 1.4-SNAPSHOT\
Architectury loom (Forge gradle replacement): 1.4-SNAPSHOT\
Sponge vanilla gradle: 0.2.1-SNAPSHOT\
Sponge mixin: 0.8.5\
Java Preprocessor plugin: Manifold Preprocessor
<br>
@@ -93,7 +100,7 @@ Java Preprocessor plugin: Manifold Preprocessor
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.
* (Not required) Any Java IDE with plugins that support Manifold, for example Intellij IDEA.
* (Not required) Any Java IDE with plugins that support Manifold, for example IntelliJ IDEA.
**If using IntelliJ:**
1. Install the Manifold plugin
@@ -106,17 +113,14 @@ Java Preprocessor plugin: Manifold Preprocessor
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
To switch between different Minecraft versions, change `mcVer=1.?` in the `gradle.properties` file.
If running in an IDE, to ensure the IDE noticed the version change, run any gradle command to refresh gradle. (In IntellJ you will also need to do a gradle sync if it didn't happen automatically.)
>Note: There may be a `java.nio.file.FileSystemException` thrown when running the command after switching versions. To fix it, either restart your IDE (as your IDE is probably locking a file) or use a tool like LockHunter to unlock the linked file(s). (Generally it is a lib file under `common\build\lib`, `forge\build\lib`, or `fabric\build\lib`). \
> If anyone knows how to solve this issue please let us know here: \
> https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/233
If running in an IDE, to ensure the IDE noticed the version change, run any gradle command to refresh gradle.\
In IntelliJ, you will also need to do a gradle sync if it didn't happen automatically.
<br>
@@ -130,21 +134,20 @@ From the File Explorer:
2. Download the core from https://gitlab.com/jeseibel/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 wih `./gradlew mergeJars`
5. Merge the jars with `./gradlew mergeJars`
6. The compiled jar file will be in the folder `Merged`
From the command line:
1. `git clone --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git`
2. `cd minecraft-lod-mod`
1. `git clone --recurse-submodules https://gitlab.com/jeseibel/distant-horizons.git`
2. `cd distant-horizons`
3. `./gradlew assemble`
4. `./gradlew mergeJars`
5. The compiled jar file will be in the folder `Merged`
Run tests with: `./gradlew test`
>Note: You can add the arg: `-PmcVer=?` to tell gradle to build a selected MC version instead of having to modify the `gradle.properties` file. \
> Example: `./gradlew assemble -PmcVer=1.18.2`
>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>
@@ -154,7 +157,6 @@ Run tests with: `./gradlew test`
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
@@ -163,6 +165,7 @@ You can also locally compile the DH jars without a Java environment by using Doc
`./gradlew clean` to delete any compiled code.
<br>
## Note to self
@@ -170,7 +173,7 @@ The Minecraft source code is NOT added to your workspace in an editable way. Min
Source code uses Mojang mappings & [Parchment](https://parchmentmc.org/) mappings.
To generate the source code run `./gradlew genSources`\
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)
@@ -178,12 +181,12 @@ If your IDE fails to auto-detect the source jars when browsing Minecraft classes
## Other Useful commands
Run the standalone jar: `./gradlew run`\
Build the standalone jar: `./gradlew core:build`\
Only build Fabric: `./gradlew fabric:assemble` or `./gradlew fabric:build`\
Only build Forge: `./gradlew fabric:assemble` or `./gradlew forge:build`\
Run the Fabric client (for debugging): `./gradlew fabric:runClient`\
Run the Forge client (for debugging): `./gradlew forge:runClient`
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>
To build all versions: `./buildAll` (all builds will end up in the `Merged` folder)
@@ -197,7 +200,7 @@ https://github.com/PacifistMC/Forgix
LZ4 for Java (data compression)\
https://github.com/lz4/lz4-java
NightConfig for Json & Toml (config handling)\
NightConfig for JSON & TOML (config handling)\
https://github.com/TheElectronWill/night-config
SVG Salamander for SVG support (not being used atm)\
+136 -159
View File
@@ -1,77 +1,54 @@
plugins {
id "java"
// Plugin to handle dependencies
id "com.github.johnrengelman.shadow" version '7.1.2' apply false
// Plugin to put dependencies inside our final jar
id "com.github.johnrengelman.shadow" version '8.1.1' apply false
// Plugin to create merged jars
id "io.github.pacifistmc.forgix" version "1.2.6"
id "io.github.pacifistmc.forgix" version "1.2.9"
// Manifold preprocessor
id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha"
// Provides mc libraries to core
// id "org.spongepowered.gradle.vanilla" version '0.2.1-SNAPSHOT' apply false
// Architectury is used here only as a replacement for forge's own loom
id "dev.architectury.loom" version "1.1.+" apply false
id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false
id 'xyz.wagyourtail.jvmdowngrader' version '0.9.1' apply true
}
/**
* Creates the list of preprocessors to use.
*
* @param mcVers array of all MC versions
* @param mcIndex array index of the currently active MC version
*/
def writeBuildGradlePredefine(List<String> mcVers, int mcIndex) {
ArrayList<String> redefineList = new ArrayList<String>()
def writeBuildGradlePredefine(List<String> mcVers, int mcIndex)
{
// Build the list of preprocessors to use
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mcVers.size(); i++) {
String mcStr = mcVers[i].replace(".", "_")
if (mcIndex < i) {
// exclusive before
// FIXME doesn't function correctly for 1.16.5 (IE the first item in the list)
redefineList.add("PRE_MC_" + mcStr)
}
if (mcIndex <= i) {
// inclusive before
redefineList.add("PRE_AND_MC_" + mcStr)
}
if (mcIndex == i) {
// exact
redefineList.add("MC_" + mcStr)
}
if (mcIndex > i) {
// inclusive after
redefineList.add("POST_AND_MC_" + mcStr)
}
if (mcIndex >= i) {
// exclusive after
redefineList.add("POST_MC_" + mcStr)
}
sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n");
for (int i = 0; i < mcVers.size(); i++)
{
String verStr = mcVers[i].replace(".", "_");
sb.append("MC_" + verStr + "=" + i.toString() + "\n");
if (mcIndex == i)
sb.append("MC_VER=" + i.toString() + "\n");
}
// Build the list of preprocessors to use
StringBuilder sb = new StringBuilder()
sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n")
// Check if this is a development build
if (mod_version.toLowerCase().contains("dev")) {
if (mod_version.toLowerCase().contains("dev"))
{
// WARNING: only use this for logging, we don't want to have confusion
// when a method doesn't work correctly in the release build.
sb.append("DEV_BUILD")
sb.append("=\n")
sb.append("DEV_BUILD=\n");
}
// Build the MC version preprocessors
for (String redefinedVersion : redefineList) {
sb.append(redefinedVersion)
sb.append("=\n")
}
new File(projectDir, "build.properties").text = sb.toString()
}
@@ -79,7 +56,7 @@ def writeBuildGradlePredefine(List<String> mcVers, int mcIndex) {
// Transfers the values set in settings.gradle to the rest of the project
project.gradle.ext.getProperties().each { prop ->
rootProject.ext.set(prop.key, prop.value)
// println "Added prop [key:" + prop.key + ", value:" + prop.value + "]"
//println "Added prop [key:" + prop.key + ", value:" + prop.value + "]"
}
// Sets up manifold stuff
writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
@@ -98,6 +75,12 @@ forgix {
forge {
jarLocation = "build/libs/DistantHorizons-forge-${rootProject.versionStr}.jar"
}
if (findProject(":neoforge"))
custom {
projectName = "neoforge"
jarLocation = "build/libs/DistantHorizons-neoforge-${rootProject.versionStr}.jar"
}
if (findProject(":fabric"))
fabric {
@@ -121,14 +104,12 @@ subprojects { p ->
// Apply plugins
apply plugin: "java"
apply plugin: "com.github.johnrengelman.shadow"
apply plugin: "xyz.wagyourtail.jvmdowngrader"
if (isMinecraftSubProject)
apply plugin: "systems.manifold.manifold-gradle-plugin"
if (p == project(":core"))
apply plugin: "application"
// apply plugin: "org.spongepowered.gradle.vanilla" // Provides minecraft libraries
// Apply forge's loom
if (findProject(":forge") && p == project(":forge"))
if ( (findProject(":forge") && p == project(":forge")) || (findProject(":neoforge") && p == project(":neoforge")) )
apply plugin: "dev.architectury.loom"
@@ -148,8 +129,8 @@ subprojects { p ->
// Configuration fpr core & api
coreProjects
shadowMe.extendsFrom(coreProjects)
// FIXME this additional configuration is necessary because forge
// needs forgeRuntimeLibrary, although adding it to shadowMe
// causes runtime issues where the libraries aren't properly added
@@ -158,18 +139,24 @@ subprojects { p ->
implementation.extendsFrom(forgeShadowMe)
shadowMe.extendsFrom(forgeShadowMe)
forgeRuntimeLibrary.extendsFrom(forgeShadowMe)
if (isMinecraftSubProject && p != project(":common")) {
// Shadow common
common
shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this.
compileClasspath.extendsFrom common
runtimeClasspath.extendsFrom common
developmentForge.extendsFrom common
if (findProject(":forge"))
developmentForge.extendsFrom common
if (findProject(":neoforge"))
developmentNeoForge.extendsFrom common
compileClasspath.extendsFrom coreProjects
runtimeClasspath.extendsFrom coreProjects
developmentForge.extendsFrom coreProjects
if (findProject(":forge"))
developmentForge.extendsFrom coreProjects
if (findProject(":neoforge"))
developmentNeoForge.extendsFrom coreProjects
if (findProject(":fabricLike") && p != project(":fabricLike")) {
// Shadow fabricLike
@@ -177,31 +164,21 @@ subprojects { p ->
shadowFabricLike
compileClasspath.extendsFrom fabricLike
runtimeClasspath.extendsFrom fabricLike
developmentForge.extendsFrom fabricLike
}
}
}
// Let the application plugin know where the main class is
// (This will point to a non-existent class in all sub-projects except "Core")
if (p == project(":core")) {
application {
mainClass.set("com.seibel.distanthorizons.core.jar.JarMain")
}
}
dependencies {
//=====================//
// shared dependencies //
//=====================//
// Manifold
if (isMinecraftSubProject) {
annotationProcessor("systems.manifold:manifold-preprocessor:${rootProject.manifold_version}")
}
// Log4j
// TODO: Change to shadowMe later to work in the standalone jar
// We cannot do this now as it would break Quilt
@@ -209,26 +186,36 @@ subprojects { p ->
implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
// JOML
implementation("org.joml:joml:${rootProject.joml_version}")
if (project.hasProperty("embed_joml") && embed_joml == "true")
forgeShadowMe("org.joml:joml:${rootProject.joml_version}")
else
implementation("org.joml:joml:${rootProject.joml_version}")
// JUnit tests
implementation("org.junit.jupiter:junit-jupiter:5.8.2")
implementation("org.junit.jupiter:junit-jupiter-engine:5.8.2")
implementation("junit:junit:4.13")
// FastUtil
// Note: MC 1.16 uses 8.2.1, and versions after use 8.5.12
// We cannot relocate this library since we call some MC classes that reference it
implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
// Compression
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}")
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
forgeShadowMe("org.tukaani:xz:${rootProject.xz_version}") // LZMA
// Sqlite Database
forgeShadowMe("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}")
// NightConfig (includes Toml & Json)
forgeShadowMe("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
forgeShadowMe("com.electronwill.night-config:json:${rootProject.nightconfig_version}")
// SVG (not needed atm)
// forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}")
// Netty
// Breaks 1.16.5
//forgeShadowMe("io.netty:netty-all:${rootProject.netty_version}")
@@ -237,9 +224,9 @@ subprojects { p ->
forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") {
exclude group: "org.lwjgl", module: "lwjgl" // This module is imported by Minecraft so exclude it
}
//==========================//
// conditional dependencies //
//==========================//
@@ -295,7 +282,7 @@ subprojects { p ->
relocate "com.seibel.distanthorizons.fabriclike", "loaderCommon.${p.name}.com.seibel.distanthorizons.fabriclike" // Move the loader files to a different location
}
}
def librariesLocation = "distanthorizons.libraries"
def librariesLocation = "DistantHorizons.libraries"
// LWJGL
// Only ever shadow the dependencies we use otherwise some stuff would break when running on an external client
@@ -303,45 +290,63 @@ subprojects { p ->
// Compression (LZ4)
relocate "net.jpountz", "${librariesLocation}.jpountz"
// Sqlite Database
//At the moment, there is a bug in this library which doesnt allow it to be relocated
// relocate "org.sqlite", "${librariesLocation}.sqlite"
// JOML
if (project.hasProperty("embed_joml") && embed_joml == "true")
relocate "org.joml", "${librariesLocation}.joml"
// NightConfig (includes Toml & Json)
relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig"
// SVG (not needed atm)
// relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg"
// Netty
relocate "io.netty", "${librariesLocation}.netty"
mergeServiceFiles()
}
// Using jar.finalizedBy(shadowJar) causes issues so we do this scuffed bypass
jar.dependsOn(shadowJar)
// For downgrading the Java version of our project to match the minimum Java version required by the selected Minecraft version.
task jarDowngrade(type: xyz.wagyourtail.jvmdg.gradle.task.DowngradeJar) {
inputFile = tasks.shadowJar.archiveFile
downgradeTo = JavaVersion.toVersion(rootProject.java_version as Integer)
archiveClassifier = "downgraded-${rootProject.java_version}"
}
task apiDowngrade(type: xyz.wagyourtail.jvmdg.gradle.task.ShadeJar) {
inputFile = jarDowngrade.archiveFile
downgradeTo = JavaVersion.toVersion(rootProject.java_version as Integer)
archiveClassifier = "downgraded-${rootProject.java_version}-shaded-java-api"
}
// We're using a custom downgrade task so we disable the original downgrade tasks
downgradeJar.enabled = false
shadeDowngradedApi.enabled = false
// Put stuff from gradle.properties into the mod info
processResources {
def resourceTargets = [ // Location of where to inject the properties
// Holds info like git commit
// TODO: For some reason this script doesnt work with the core project
"build_info.json",
// Holds info like git commit
// TODO: For some reason this script doesnt work with the core project
"build_info.json",
// Properties for each of the loaders
"fabric.mod.json",
"quilt.mod.json",
"META-INF/mods.toml",
// Properties for each of the loaders
"fabric.mod.json",
"quilt.mod.json",
"META-INF/mods.toml",
"META-INF/neoforge.mods.toml",
// The mixins for each of the loaders
"DistantHorizons."+ p.name +".fabricLike.mixins.json"
// The mixins for each of the loaders
"DistantHorizons."+ p.name +".fabricLike.mixins.json"
]
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
// Fix forge version numbering system as it is weird
// For whatever reason forge uses [1.18, 1.18.1, 1.18.2) instead of the standard ["1.18", "1.18.1", "1.18.2"] which make more sense
// For whatever reason forge uses [1.18, 1.18.1, 1.18.2) instead of the standard ["1.18", "1.18.1", "1.18.2"]
def compatible_forgemc_versions = "${compatible_minecraft_versions}".replaceAll("\"", "").replaceAll("]", ",)")
// println compatible_forgemc_versions
@@ -354,12 +359,12 @@ subprojects { p ->
quilt_contributors.push("\"${dev.strip()}\": \"Developer\"")
}
quilt_contributors.reverse()
// println quilt_contributors.join(", ")
//println quilt_contributors.join(", ")
// TODOI: Find something we can use so we can basically re-map only when the jar is shadowed and relocated
// TODO: Find something we can use so we can basically re-map only when the jar is shadowed and relocated
// println p.tasks.findByName('shadowJar')
// These "hasProperty"'s are so that they can be passed through the cli (ie in the CI)
try {
if (infoGitCommit == "null")
@@ -371,6 +376,7 @@ subprojects { p ->
println "Git or Git project not found"
}
// The left side is what gets replaced in the mod info and the right side is where to get it from in the gradle.properties
def replaceProperties = [
version : mod_version,
mod_name : mod_readable_name,
@@ -394,8 +400,8 @@ subprojects { p ->
fabric_incompatibility_list : fabric_incompatibility_list,
fabric_recommend_list : fabric_recommend_list,
]
// The left side is what gets replaced in the mod info and the right side is where to get it from in the gradle.properties
// replace any properties in the sub-projects with the values defined here
inputs.properties replaceProperties
replaceProperties.put "project", project
filesMatching(resourceTargets) {
@@ -423,7 +429,7 @@ subprojects { p ->
//// include "${accessWidenerVersion}.distanthorizons.accesswidener"
// Jank solution to remove all unused accesswideners
// The line above would work..., except forge requires the original accesswidener file, meaning we require this jank solution to keep it
// The line above would work..., except that (neo)forge (well, mainly architectury) requires the original accesswidener file, meaning we require this jank solution to keep it
exclude { file ->
if (file.name.contains(".distanthorizons.accesswidener") && file.name != "${accessWidenerVersion}.distanthorizons.accesswidener") {
return true
@@ -459,33 +465,39 @@ subprojects { p ->
}
}
*/
// Run mergeJars when running build
// TODO: Fix later
// if (isMinecraftSubProject) {
// build.finalizedBy(mergeJars)
// assemble.finalizedBy(mergeJars)
// }
}
allprojects { p ->
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge")"
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")"
// Useful later on so we dont have duplicated code
def isMinecraftSubProject = p != project(":core") && p != project(":api")
apply plugin: "java"
apply plugin: "maven-publish"
archivesBaseName = rootProject.mod_name
version = project.name + "-" + rootProject.versionStr
group = rootProject.maven_group
// this is the text that appears at the top of the overview (home) page
// and is used when bookmarking a page
javadoc.title = rootProject.mod_name + "-" + project.name
// Some annotations arent "technically" part of the official java standard,
// so we define it ourself here
javadoc {
configure( options ) {
tags(
'todo:X"',
'apiNote:a:API Note:',
'implSpec:a:Implementation Requirements:',
'implNote:a:Implementation Note:'
)
}
}
repositories {
// The central repo
mavenCentral()
@@ -522,15 +534,17 @@ allprojects { p ->
}
}
// Required for ModMenu
maven { url "https://maven.terraformersmc.com/" }
// Required for Mixins & VanillaGradle
// VanillaGradle and Mixins in common
maven { url "https://repo.spongepowered.org/maven/" }
// Required for Canvas (mod)
// Canvas mod
maven { url "https://maven.vram.io/" }
// ModMenu mod
maven { url "https://maven.terraformersmc.com/" }
// neoforge
maven { url "https://maven.neoforged.net/releases/" }
// These 3 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
flatDir {
dirs "${rootDir}/mods/fabric"
@@ -550,47 +564,9 @@ allprojects { p ->
includeGroup "forge-mod"
}
}
// TODO: If neoforged is ever needed, should we use that, or call it a forge mod?
}
// Adds some dependencies that are in vanilla but not in core
if (p == project(":core")) {
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
// Set the OS lwjgl is using to the current os
project.ext.lwjglNatives = "natives-" + os.toFamilyName()
dependencies { // All of these dependencies are in Vanilla Minecraft, but we need to depend on it as we arent importing Minecraft in the core
// Imports most of lwjgl's libraries (well, only the ones that we need)
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}") // TODO: Use Minecraft's version for lwjgl_version (which changes in nearly every version) instead of a hard defined version for all versions
// REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use
implementation "org.lwjgl:lwjgl"
implementation "org.lwjgl:lwjgl-assimp"
implementation "org.lwjgl:lwjgl-glfw"
implementation "org.lwjgl:lwjgl-openal"
implementation "org.lwjgl:lwjgl-opengl"
implementation "org.lwjgl:lwjgl-stb"
implementation "org.lwjgl:lwjgl-tinyfd"
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
implementation "org.joml:joml:${rootProject.joml_version}"
// Some other dependencies
implementation("org.jetbrains:annotations:16.0.2")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("com.google.common:google-collect:0.5")
implementation("com.google.guava:guava:31.1-jre")
implementation("it.unimi.dsi:fastutil:8.5.11")
}
}
task copyCommonLoaderResources(type: Copy) {
from project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
into(file(p.file("build/resources/main")))
@@ -612,16 +588,17 @@ allprojects { p ->
tasks.withType(JavaCompile) {
if (isMinecraftSubProject) {
options.release = rootProject.java_version as Integer
options.release = rootProject.java_version as Integer // Neoforge complains without this
options.compilerArgs += ["-Xplugin:Manifold"]
} else {
options.release = 8; // Core & Api should use Java 8 no matter what
//options.release = rootProject.java_version as Integer // But if you want to test some stuff, then this can be enabled
}
options.encoding = "UTF-8"
}
// Sets the project's actual Java version (it's recommended to use this over the `options.release` method above)
java {
sourceCompatibility = rootProject.java_version
targetCompatibility = rootProject.java_version
withSourcesJar()
}
}
+239
View File
@@ -0,0 +1,239 @@
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AWToAT {
private static final Map<String, String> ACCESS_POINT_MAP = new HashMap<>();
static {
ACCESS_POINT_MAP.put("accessible", "public");
ACCESS_POINT_MAP.put("extendable", "public-f");
ACCESS_POINT_MAP.put("mutable", "public-f");
}
public String minecraftVersion;
public File remap(File file, String minecraftVersion) {
this.minecraftVersion = minecraftVersion.replace("_", ".");
File atFile = createATFile(file);
processFile(file, atFile);
return atFile;
}
private File createATFile(File file) {
File metaInf = new File(file.getParentFile(), "META-INF");
if (!metaInf.exists() && !metaInf.mkdir()) throw new RuntimeException("Error creating META-INF folder");
File atFile = new File(metaInf, "accesstransformer.cfg");
try {
atFile.createNewFile();
} catch (IOException e) {
throw new RuntimeException("Error creating new file", e);
}
return atFile;
}
private void processFile(File file, File atFile) {
/* Validates if we need to recreate the Access Transformer file if it's out of date */
// Get the hash of the file
String fileHash = getFileHash(file);
try (Scanner atScanner = new Scanner(atFile)) {
// Check if the AT file is up-to-date by comparing the hash of the file with the hash stored in the AT file
boolean hashFound = false;
while (atScanner.hasNextLine()) {
String line = atScanner.nextLine();
if (hashCheck(line, fileHash)) {
hashFound = true;
}
}
// If the AT file is up-to-date, print a message and return
if (hashFound) {
System.out.println("Access Transformer file is already up to date.");
return;
}
} catch (FileNotFoundException ignored) {
// If the AT file does not exist, continue
}
/* Creates the Access Transformer file */
// Opens a scanner for reading the Access Widener file and a writer for writing to the Access Transformer file
try (Scanner scanner = new Scanner(file); FileWriter writer = new FileWriter(atFile)) {
// Create an ExecutorService with a fixed thread pool size equal to the number of available processors
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// List to hold Future objects representing results of computation
List<Future<String>> futures = new ArrayList<>();
// Write the hash of the file to the AT file
writer.write("#DH_MAPPING_HASH:" + fileHash + "\n");
// Read each line from the file
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
// Skip lines starting with "accessWidener", "#" or blank lines
if (line.startsWith("accessWidener") || line.startsWith("#") || line.isBlank()) continue;
// Submit the line to the executor service for processing
// The processing is done by the processLine method
futures.add(executor.submit(() -> processLine(line.split(" "))));
}
// Write the results to the output file
// The results are obtained by calling the get method on each Future
for (Future<String> future : futures) {
writer.write(future.get());
}
// Shutdown the executor service to free up resources
executor.shutdown();
} catch (Exception e) {
throw new RuntimeException("Error reading or writing to file", e);
}
}
private String processLine(String[] fields) {
// fields[0] = access point like "accessible", "extendable", "mutable"
// fields[1] = type like "field", "method", "class"
// fields[2] = class name
// fields[3] = field/method name
// fields[4] = field/method descriptor
try {
// Store the original field/method name
String originalName = "";
// If there is a class name, replace the slashes with dots in the package name
if (fields.length > 2) fields[2] = fields[2].replace("/", ".");
// If there is a field/method name, store the original name and remap it to SRG
if (fields.length > 3) {
originalName = fields[3];
fields[3] = remapToSRG(fields[2], fields[3]);
}
StringBuilder line = new StringBuilder(ACCESS_POINT_MAP.getOrDefault(fields[0], "public")).append(" ");
switch (fields[1]) {
case "field":
line.append(fields[2]).append(" ").append(fields[3]).append(" #").append(originalName);
// It'll be like: access-point class-name field-name-SRG # field-name-Mojmap
// Eg: public net.minecraft.client.Minecraft f_90981_ # instance
break;
case "method":
line.append(fields[2]).append(" ").append(fields[3]).append(fields[4]).append(" #").append(originalName);
// It'll be like: access-point class-name method-name-SRG method-descriptor # method-name-Mojmap
// Eg: public net.minecraft.client.Minecraft m_172797_()Lnet/minecraft/client/Minecraft; # getInstance
break;
default:
line.append(fields[2]);
// It'll be like: access-point class-name
// Eg: public net.minecraft.client.Minecraft
break;
}
line.append("\n");
return line.toString();
} catch (Exception e) {
throw new RuntimeException("Error processing line", e);
}
}
private boolean hashCheck(String line, String fileHash) {
if (line.startsWith("#DH_MAPPING_HASH:")) {
String hash = line.substring(17);
return hash.equals(fileHash);
}
return false;
}
public String getFileHash(File file) {
try {
MessageDigest shaDigest = MessageDigest.getInstance("SHA-256");
try (InputStream fis = new FileInputStream(file)) {
byte[] byteArray = new byte[1024];
int bytesCount;
// Read file data and update in message digest
while ((bytesCount = fis.read(byteArray)) != -1) {
shaDigest.update(byteArray, 0, bytesCount);
}
}
byte[] bytes = shaDigest.digest();
// Convert byte array into signum representation
StringBuilder sb = new StringBuilder();
for (byte aByte : bytes) {
sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
}
// Return complete hash
return sb.toString();
} catch (NoSuchAlgorithmException | IOException e) {
throw new RuntimeException(e);
}
}
// WARNING: BELOW LIES HIGHLY CURSED CODE AND MIGHT EVEN BE ILLEGAL
// Flag to track if there was an error in the GET request
boolean error = false;
/**
* This method returns a field or method name from Mojang mappings as SRG mappings.
* It makes a GET request to the Linkie API to fetch the SRG name.
*
* @param clazz The class name
* @param name The field or method name
* @return The SRG name
* @throws Exception If there is an error in the GET request or the SRG name is not found in the response
*/
private String remapToSRG(String clazz, String name) throws Exception {
// Encode the class and field/method name to be used in the URL
String query = URLEncoder.encode(clazz + "." + name, StandardCharsets.UTF_8);
// Construct the URL for the GET request
String urlString = "https://linkieapi.shedaniel.me/api/search?namespace=mojang&query=" + query + "&version=" + this.minecraftVersion + "&limit=1&allowClasses=false&allowFields=true&allowMethods=true&translate=mojang_srg";
URL url = new URI(urlString).toURL();
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuilder content = new StringBuilder();
// Read the response line by line
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
conn.disconnect();
// Regex to find the SRG name in the response
Pattern pattern = Pattern.compile("\"l\"\\s*:\\s*\\{[^}]*\"i\"\\s*:\\s*\"([^\"]*)\"");
Matcher matcher = pattern.matcher(content.toString());
if (matcher.find()) return matcher.group(1);
else throw new Exception("Couldn't find the SRG mapping for name: " + name + "\nCould not find 'i' in 'l' object in the response"); // `i` is the SRG name which is stored in the `l` JSON object
} else {
if (error) {
// If there was an error in the GET request, and we already tried again, throw an exception
throw new Exception("The GET request failed");
}
// If there was an error in the GET request, wait 2.5 seconds and try again as we probably got rate limited
error = true;
Thread.sleep(2500);
return remapToSRG(clazz, name);
}
}
}
+11 -4
View File
@@ -1,3 +1,13 @@
// temporary fix for broken spongepowered version
buildscript {
configurations.configureEach {
resolutionStrategy {
force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82'
}
}
}
plugins {
id "org.spongepowered.gradle.vanilla" version "0.2.1-SNAPSHOT"
}
@@ -8,10 +18,6 @@ minecraft {
}
dependencies {
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
// Do NOT use other classes from fabric loader
// modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
// So mixins can be written in common
compileOnly group:'org.spongepowered', name:'mixin', version:'0.8.5'
}
@@ -30,3 +36,4 @@ publishing {
// Add repositories to publish to here.
}
}
@@ -0,0 +1,182 @@
package com.seibel.distanthorizons.common;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftDedicatedServerWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.ModJarInfo;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccessor;
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 org.apache.logging.log4j.Logger;
import java.lang.invoke.MethodHandles;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* Base for all mod loader initializers
* and handles most setup.
*/
public abstract class AbstractModInitializer
{
protected static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private CommandDispatcher<CommandSourceStack> commandDispatcher;
//==================//
// abstract methods //
//==================//
protected abstract void createInitialBindings();
protected abstract IEventProxy createClientProxy();
protected abstract IEventProxy createServerProxy(boolean isDedicated);
protected abstract void initializeModCompat();
protected abstract void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler);
protected abstract void subscribeClientStartedEvent(Runnable eventHandler);
protected abstract void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler);
protected abstract void runDelayedSetup();
//===================//
// initialize events //
//===================//
public void onInitializeClient()
{
DependencySetup.createClientBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup();
this.printModInfo(true);
this.createClientProxy().registerEvents();
this.createServerProxy(false).registerEvents();
this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
// Client uses config for auto-updater, so it's initialized here instead of post-init stage
this.initConfig();
this.subscribeClientStartedEvent(this::postInit);
}
public void onInitializeServer()
{
DependencySetup.createServerBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup();
this.printModInfo(false);
// This prevents returning uninitialized Config values,
// resulting from a circular reference mid-initialization in a static class
// noinspection ResultOfMethodCallIgnored
ThreadPresetConfigEventHandler.INSTANCE.toString();
this.createServerProxy(true).registerEvents();
LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
this.subscribeRegisterCommandsEvent(dispatcher -> this.commandDispatcher = dispatcher);
this.subscribeServerStartingEvent(server ->
{
MinecraftDedicatedServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server;
this.initConfig();
this.postInit();
this.initCommands();
LOGGER.info("Dedicated server initialized at " + server.getServerDirectory());
});
}
//===========================//
// inner initializer methods //
//===========================//
private void startup()
{
DependencySetup.createSharedBindings();
SharedApi.init();
this.createInitialBindings();
}
private void printModInfo(boolean printGitInfo)
{
LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
if (printGitInfo)
{
// Useful for dev builds
LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch);
LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit);
LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source);
}
}
protected <T extends IModAccessor> void tryCreateModCompatAccessor(String modId, Class<? super T> accessorClass, Supplier<T> accessorConstructor)
{
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
if (modChecker.isModLoaded(modId))
{
//noinspection unchecked
ModAccessorInjector.INSTANCE.bind((Class<? extends IModAccessor>) accessorClass, accessorConstructor.get());
}
}
private void initConfig()
{
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, 2);
Config.completeDelayedSetup();
}
private void postInit()
{
LOGGER.info("Post-Initializing Mod");
this.runDelayedSetup();
LOGGER.info("Mod Post-Initialized");
}
private void initCommands() { /* currently unimplemented */ }
//================//
// helper classes //
//================//
public interface IEventProxy
{
void registerEvents();
}
}
@@ -1,64 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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;
import com.seibel.distanthorizons.common.forge.LodForgeMethodCaller;
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
/**
* This is the common main class
*
* @author Ran
*/
public class LodCommonMain
{
public static boolean forge = false;
public static LodForgeMethodCaller forgeMethodCaller;
public static void startup(LodForgeMethodCaller forgeMethodCaller)
{
if (forgeMethodCaller != null)
{
LodCommonMain.forge = true;
LodCommonMain.forgeMethodCaller = forgeMethodCaller;
}
DependencySetup.createSharedBindings();
SharedApi.init();
// if (!serverSided) {
// new NetworkReceiver().register_Client();
// } else {
// new NetworkReceiver().register_Server();
// }
}
public static void initConfig()
{
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, 2);
Config.completeDelayedSetup();
}
}
@@ -1,52 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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.forge;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.core.Direction;
#if POST_MC_1_19_2
import net.minecraft.util.RandomSource;
#endif
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import java.util.List;
import java.util.Random;
/**
* used for calling methods that forge modified
* (forge modifies vanilla methods for some reason)
*
* @author Ran
*/
public interface LodForgeMethodCaller
{
#if PRE_MC_1_19_2
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, Random random); // FIXME: For 1.19
#else
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, RandomSource random); // FIXME: For 1.19
#endif
int colorResolverGetColor(ColorResolver resolver, Biome biome, double x, double z);
}
@@ -1,96 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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.rendering;
#if PRE_MC_1_19_4
import com.mojang.math.Matrix4f;
#else
import org.joml.Matrix4f;
#endif
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import org.lwjgl.opengl.GL15;
import java.nio.FloatBuffer;
public class SeamlessOverdraw
{
/**
* Proof-of-concept experimental option, not intended for normal use. <br>
* (Poorly) replaces Minecraft's far clip plane so it lines up with DH's near clip plane.
*/
public static float[] overwriteMinecraftNearFarClipPlanes(Matrix4f minecraftProjectionMatrix, float previousPartialTicks)
{
float[] matrixFloatArray;
#if PRE_MC_1_19_4
FloatBuffer matrixFloatBuffer = FloatBuffer.allocate(16);
minecraftProjectionMatrix.store(matrixFloatBuffer);
matrixFloatArray = matrixFloatBuffer.array();
#else
// Passing float buffers in caused native code crashes, so we are passing in a float array instead
matrixFloatArray = new float[16];
minecraftProjectionMatrix.get(matrixFloatArray);
#endif
return overwriteMinecraftNearFarClipPlanes(matrixFloatArray, previousPartialTicks);
}
public static float[] overwriteMinecraftNearFarClipPlanes(Mat4f minecraftProjectionMatrix, float previousPartialTicks)
{
return overwriteMinecraftNearFarClipPlanes(minecraftProjectionMatrix.getValuesAsArray(), previousPartialTicks);
}
private static float[] overwriteMinecraftNearFarClipPlanes(float[] projectionMatrixFloatArray, float previousPartialTicks)
{
float dhFarClipPlane = RenderUtil.getNearClipPlaneDistanceInBlocks(previousPartialTicks);
// works for fabric, bad not for forge for some reason :/
float farClip = dhFarClipPlane * 5.1f; // magic number found via trial and error, James has no idea what it represents, except that it makes the seam between DH and vanilla rendering pretty close
float nearClip = 0.5f; // this causes issues with some vanilla rendering, specifically the wireframe around selected blocks is slightly off. Unfortunately the ratio between the near and far clip plane can't be easily modified without completely screwing up the rendering.
// these may be the wrong index locations in any version of MC other than 1.18.2
projectionMatrixFloatArray[10] = -((farClip + nearClip) / (farClip - nearClip)); // near clip plane
projectionMatrixFloatArray[11] = -((2 * farClip * nearClip) / (farClip - nearClip)); // far clip plane
return projectionMatrixFloatArray;
}
//================//
// helper methods //
//================//
public static void applyLegacyProjectionMatrix(float[] projectionMatrixFloatArray)
{
int glMatrixMode = GL15.glGetInteger(GL15.GL_MATRIX_MODE);
GL15.glMatrixMode(GL15.GL_PROJECTION);
GL15.glLoadMatrixf(projectionMatrixFloatArray);
GL15.glMatrixMode(glMatrixMode);
}
}
@@ -26,6 +26,6 @@ public class DependencySetupDoneCheck
// TODO move to DependencySetup
public static boolean isDone = false;
// TODO why is this here and what is its purpose?
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; });
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> false);
}
@@ -23,16 +23,10 @@ import java.nio.FloatBuffer;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
#if PRE_MC_1_19_4
import com.mojang.math.Matrix4f;
#else
import org.joml.Matrix4f;
#endif
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@@ -51,10 +45,31 @@ public class McObjectConverter
{
return y * 4 + x;
}
/** Taken from Minecraft's com.mojang.math.Matrix4f class from 1.18.2 */
private static void storeMatrix(Matrix4f matrix, FloatBuffer buffer)
/** 4x4 float matrix converter */
@Deprecated
public static Mat4f Convert(
#if MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
#else org.joml.Matrix4f #endif
mcMatrix)
{
#if PRE_MC_1_19_4
FloatBuffer buffer = FloatBuffer.allocate(16);
storeMatrix(mcMatrix, buffer);
Mat4f matrix = new Mat4f(buffer);
#if 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
#else org.joml.Matrix4f #endif
matrix,
FloatBuffer buffer)
{
#if 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
@@ -77,18 +92,6 @@ public class McObjectConverter
#endif
}
/** 4x4 float matrix converter */
public static Mat4f Convert(Matrix4f mcMatrix)
{
FloatBuffer buffer = FloatBuffer.allocate(16);
storeMatrix(mcMatrix, buffer);
Mat4f matrix = new Mat4f(buffer);
#if PRE_MC_1_19_4
matrix.transpose(); // In 1.19.3 and later, we no longer need to transpose it
#endif
return matrix;
}
static final Direction[] directions;
static final EDhDirection[] lodDirections;
@@ -99,33 +102,17 @@ public class McObjectConverter
lodDirections = new EDhDirection[lodDirs.length];
for (EDhDirection lodDir : lodDirs)
{
Direction dir;
switch (lodDir.name().toUpperCase())
{
case "DOWN":
dir = Direction.DOWN;
break;
case "UP":
dir = Direction.UP;
break;
case "NORTH":
dir = Direction.NORTH;
break;
case "SOUTH":
dir = Direction.SOUTH;
break;
case "WEST":
dir = Direction.WEST;
break;
case "EAST":
dir = Direction.EAST;
break;
default:
dir = null;
break;
}
if (dir == null)
Direction dir = switch (lodDir.name().toUpperCase()) {
case "DOWN" -> Direction.DOWN;
case "UP" -> Direction.UP;
case "NORTH" -> Direction.NORTH;
case "SOUTH" -> Direction.SOUTH;
case "WEST" -> Direction.WEST;
case "EAST" -> Direction.EAST;
default -> null;
};
if (dir == null)
{
throw new IllegalArgumentException("Invalid direction on init mapping: " + lodDir);
}
@@ -59,7 +59,7 @@ public class VersionConstants implements IVersionConstants
@Override
public String getMinecraftVersion()
{
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
return Minecraft.getInstance().getGame().getVersion().getId();
#else
return SharedConstants.getCurrentVersion().getId();
@@ -19,7 +19,11 @@
package com.seibel.distanthorizons.common.wrappers;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.interfaces.factories.IDhApiWrapperFactory;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -28,6 +32,7 @@ import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
@@ -35,9 +40,14 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
import net.minecraft.client.multiplayer.ClientLevel;
#if MC_VER > MC_1_17_1
import net.minecraft.core.Holder;
#endif
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;
import java.io.IOException;
@@ -45,9 +55,6 @@ import java.util.HashSet;
/**
* This handles creating abstract wrapper objects.
*
* @author James Seibel
* @version 2022-12-5
*/
public class WrapperFactory implements IWrapperFactory
{
@@ -55,6 +62,9 @@ public class WrapperFactory implements IWrapperFactory
//==============//
// core methods //
//==============//
@Override
public AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel)
@@ -69,17 +79,56 @@ public class WrapperFactory implements IWrapperFactory
}
}
@Override
public IDhApiBiomeWrapper getBiomeWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException
{
if (!(levelWrapper instanceof ILevelWrapper))
{
throw new ClassCastException("levelWrapper must be returned by DH and of type ["+ILevelWrapper.class.getName()+"].");
}
return BiomeWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper);
}
@Override
public IDhApiBlockStateWrapper getDefaultBlockStateWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException
{
if (!(levelWrapper instanceof ILevelWrapper))
{
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
}
return BlockStateWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper);
}
@Override
public IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BiomeWrapper.deserialize(str, levelWrapper); }
@Override
public IBiomeWrapper getPlainsBiomeWrapper(ILevelWrapper levelWrapper) // TODO is there a way we could get this without the levelWrapper? it isn't necessary but would clean up the code a bit
{
try
{
return BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, levelWrapper);
}
catch (IOException e)
{
throw new LodUtil.AssertFailureException("Unable to parse plains resource string ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"], error:\n " + e.getMessage());
}
}
@Override
public IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BlockStateWrapper.deserialize(str, levelWrapper); }
@Override
public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; }
@Override
public HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); }
@Override
public HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); }
@Override
public void resetRendererIgnoredCaveBlocks() { BlockStateWrapper.clearRendererIgnoredCaveBlocks(); }
@Override
public void resetRendererIgnoredBlocksSet() { BlockStateWrapper.clearRendererIgnoredBlocks(); }
/**
@@ -106,26 +155,23 @@ public class WrapperFactory implements IWrapperFactory
}
}
// MC 1.16, 1.18, 1.19, 1.20
#if POST_MC_1_17_1 || MC_1_16_5
#if MC_VER <= MC_1_21
else if (objectArray.length == 2)
{
// correct number of parameters from the API
// chunk
if (!(objectArray[0] instanceof ChunkAccess))
if (!(objectArray[0] instanceof ChunkAccess chunk))
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
ChunkAccess chunk = (ChunkAccess) objectArray[0];
// level / light source
if (!(objectArray[1] instanceof Level))
if (!(objectArray[1] instanceof Level level))
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
// the level is needed for the DH level wrapper...
Level level = (Level) objectArray[1];
// ...the LevelReader is needed for chunk lighting
LevelReader lightSource = level;
@@ -156,7 +202,7 @@ public class WrapperFactory implements IWrapperFactory
#else
// Intentional compiler error to bring attention to the missing wrapper function.
// If you need to work on an unimplemented version but don't have the ability to implement this yet
// you can comment it out, but please don't commit it. Someone will have to implement it .
// you can comment it out, but please don't commit it. Someone will have to implement it.
// After implementing the new version please read this method's javadocs for instructions
// on what other locations also need to be updated, the DhAPI specifically needs to
@@ -170,25 +216,164 @@ public class WrapperFactory implements IWrapperFactory
*/
private static String createChunkWrapperErrorMessage(Object[] objectArray)
{
StringBuilder message = new StringBuilder(
"Chunk wrapper creation failed. \n" +
"Expected parameters: \n");
String[] expectedClassNames;
// MC 1.16, 1.18, 1.19, 1.20
#if POST_MC_1_17_1 || MC_1_16_5
message.append("[" + ChunkAccess.class.getName() + "], \n");
message.append("[" + ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName() + "]. \n");
#if MC_VER <= MC_1_21
expectedClassNames = new String[]
{
ChunkAccess.class.getName(),
ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName()
};
#else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif
return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray);
}
//=============//
// api methods //
//=============//
// documentation should be in the API interface
public IDhApiBiomeWrapper getBiomeWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
{
// confirm the API level wrapper is also a Core wrapper
if (!(levelWrapper instanceof ILevelWrapper coreLevelWrapper))
{
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
}
#if MC_VER < MC_1_20_4
if (objectArray.length != 1)
{
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
}
#endif
#if MC_VER < MC_1_18_2
if (!(objectArray[0] instanceof Biome))
{
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
}
Biome biome = (Biome) objectArray[0];
return BiomeWrapper.getBiomeWrapper(biome, coreLevelWrapper);
#elif MC_VER <= MC_1_21
if (!(objectArray[0] instanceof Holder) || !(((Holder<?>) objectArray[0]).value() instanceof Biome))
{
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
}
Holder<Biome> biomeHolder = (Holder<Biome>) objectArray[0];
return BiomeWrapper.getBiomeWrapper(biomeHolder, coreLevelWrapper);
#else
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
not implemented for this version of Minecraft!
#endif
}
/**
* Note: when this is updated for different MC versions,
* make sure you also update the documentation in {@link IDhApiWrapperFactory#getBiomeWrapper}.
*/
private static String createBiomeWrapperErrorMessage(Object[] objectArray)
{
String[] expectedClassNames;
#if MC_VER < MC_1_18_2
expectedClassNames = new String[] { Biome.class.getName() };
#elif MC_VER <= MC_1_21
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
#else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif
return createWrapperErrorMessage("Biome wrapper", expectedClassNames, objectArray);
}
public IDhApiBlockStateWrapper getBlockStateWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
{
// confirm the API level wrapper is also a Core wrapper
if (!(levelWrapper instanceof ILevelWrapper coreLevelWrapper))
{
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
}
#if MC_VER <= MC_1_21
if (objectArray.length != 1)
{
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
}
if (!(objectArray[0] instanceof BlockState blockState))
{
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
}
return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper);
#else
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
not implemented for this version of Minecraft!
#endif
}
/**
* Note: when this is updated for different MC versions,
* make sure you also update the documentation in {@link IDhApiWrapperFactory#getBlockStateWrapper}.
*/
private static String createBlockStateWrapperErrorMessage(Object[] objectArray)
{
String[] expectedClassNames;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
expectedClassNames = new String[] { Biome.class.getName() };
#elif MC_VER <= MC_1_21
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
#else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif
return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray);
}
//================//
// helper methods //
//================//
private static String createWrapperErrorMessage(String wrapperName, String[] expectedClassNames, Object[] objectArray)
{
// error header
StringBuilder message = new StringBuilder(
wrapperName + " creation failed. \n" +
"Expected object array parameters: \n");
// expected parameters
for (String expectedClassName : expectedClassNames)
{
message.append("[").append(expectedClassName).append("], \n");
}
// given parameters
if (objectArray.length != 0)
{
message.append("Given parameters: ");
for (Object obj : objectArray)
{
message.append("[").append(obj.getClass().getName()).append("], ");
String objClassName = (obj != null) ? obj.getClass().getName() : "NULL";
message.append("[").append(objClassName).append("], ");
}
}
else
@@ -196,8 +381,8 @@ public class WrapperFactory implements IWrapperFactory
message.append(" No parameters given.");
}
return message.toString();
}
}
@@ -32,19 +32,9 @@ import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import net.minecraft.client.Minecraft;
#if POST_MC_1_17
import net.minecraft.core.Holder;
import net.minecraft.resources.RegistryOps;
#endif
#if POST_MC_1_19_2
#endif
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
import net.minecraft.core.Registry;
#elif MC_1_18_2 || MC_1_19_2
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
@@ -56,7 +46,7 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
#if !PRE_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.biome.Biomes;
#endif
@@ -64,32 +54,46 @@ import net.minecraft.world.level.biome.Biomes;
/** This class wraps the minecraft BlockPos.Mutable (and BlockPos) class */
public class BiomeWrapper implements IBiomeWrapper
{
// must be defined before AIR, otherwise a null pointer will be thrown
private static final Logger LOGGER = LogManager.getLogger();
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
public static final ConcurrentMap<Biome, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>();
#else
public static final ConcurrentMap<Holder<Biome>, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>();
#endif
public static final String EMPTY_STRING = "EMPTY";
public static final ConcurrentHashMap<String, BiomeWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
public static final String EMPTY_BIOME_STRING = "EMPTY";
public static final BiomeWrapper EMPTY_WRAPPER = new BiomeWrapper(null, null);
public static final String PLAINS_RESOURCE_LOCATION_STRING = "minecraft:plains";
/** keep track of broken biomes so we don't log every time */
private static final HashSet<String> BrokenResourceLocationStrings = new HashSet<>();
private static final HashSet<String> brokenResourceLocationStrings = new HashSet<>();
/**
* Only display this warning once, otherwise the log may be spammed <br>
* This is a known issue when joining Hypixel.
*/
private static boolean emptyStringWarningLogged = false;
private static boolean emptyLevelSerializeFailLogged = false;
// properties //
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
public final Biome biome;
#else
public final Holder<Biome> biome;
#endif
/** technically final, but since it requires a method call to generate it can't be marked as such */
private String serialString = null;
private String serialString;
private final int hashCode;
@@ -97,7 +101,7 @@ public class BiomeWrapper implements IBiomeWrapper
// constructors //
//==============//
static public IBiomeWrapper getBiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
static public IBiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
{
if (biome == null)
{
@@ -116,12 +120,21 @@ public class BiomeWrapper implements IBiomeWrapper
return newWrapper;
}
}
private BiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
private BiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
{
this.biome = biome;
this.serialString = this.serialize(levelWrapper);
LOGGER.trace("Created BiomeWrapper ["+this.serialString+"] for ["+biome+"]");
this.hashCode = Objects.hash(this.serialString);
//LOGGER.trace("Created BiomeWrapper ["+this.serialString+"] for ["+biome+"]");
}
/** should only be used to create {@link BiomeWrapper#EMPTY_WRAPPER} */
private BiomeWrapper()
{
this.biome = null;
this.serialString = EMPTY_BIOME_STRING;
this.hashCode = Objects.hash(this.serialString);
}
@@ -135,10 +148,10 @@ public class BiomeWrapper implements IBiomeWrapper
{
if (this == EMPTY_WRAPPER)
{
return EMPTY_STRING;
return EMPTY_BIOME_STRING;
}
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
return biome.toString();
#else
return this.biome.unwrapKey().orElse(Biomes.THE_VOID).registry().toString();
@@ -163,7 +176,7 @@ public class BiomeWrapper implements IBiomeWrapper
}
@Override
public int hashCode() { return Objects.hash(this.getSerialString()); }
public int hashCode() { return this.hashCode; }
@Override
public String getSerialString() { return this.serialString; }
@@ -182,107 +195,159 @@ public class BiomeWrapper implements IBiomeWrapper
public String serialize(ILevelWrapper levelWrapper)
{
if (levelWrapper == null)
if (this.biome == null)
{
return EMPTY_STRING;
return EMPTY_BIOME_STRING;
}
if (this.serialString == null)
// we can't generate a serial string if the level is null
if (levelWrapper == null)
{
net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess();
if (!emptyLevelSerializeFailLogged)
{
emptyLevelSerializeFailLogged = true;
LOGGER.warn("Unable to serialize biome: [" + this.biome + "] because the passed in level wrapper is null. Future errors of this type won't be logged.");
}
ResourceLocation resourceLocation;
#if MC_1_16_5 || MC_1_17_1
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
#elif MC_1_18_2 || MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
return EMPTY_BIOME_STRING;
}
// generate the serial string //
Level level = (Level)levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
ResourceLocation resourceLocation;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
#else
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
#endif
if (resourceLocation == null)
{
String biomeName;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
biomeName = this.biome.toString();
#else
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
biomeName = this.biome.value().toString();
#endif
if (resourceLocation == null)
{
String biomeName;
#if MC_1_16_5 || MC_1_17_1
biomeName = this.biome.toString();
#else
biomeName = this.biome.value().toString();
#endif
LOGGER.warn("unable to serialize: " + biomeName);
// shouldn't normally happen, but just in case
this.serialString = "";
}
else
{
this.serialString = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
}
LOGGER.warn("unable to serialize: " + biomeName);
// shouldn't normally happen, but just in case
this.serialString = "";
}
else
{
this.serialString = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
}
return this.serialString;
}
// TODO would it be worth while to cache these objects in a ConcurrentHashMap<string, IBiomeWrapper>?
public static IBiomeWrapper deserialize(String resourceLocationString, ILevelWrapper levelWrapper) throws IOException
{
if (resourceLocationString.equals(EMPTY_STRING))
// we need the final string for the concurrent hash map later
final String finalResourceStateString = resourceLocationString;
if (resourceLocationString.equals(EMPTY_BIOME_STRING))
{
LOGGER.warn("["+EMPTY_STRING+"] biome string deserialized. This may mean there was a file saving error or a biome saving error.");
if (!emptyStringWarningLogged)
{
emptyStringWarningLogged = true;
LOGGER.warn("[" + EMPTY_BIOME_STRING + "] biome string deserialized. This may mean the level was null when a save was attempted, a file saving error, or a biome saving error. Future errors will not be logged.");
}
return EMPTY_WRAPPER;
}
else if (resourceLocationString.trim().isEmpty() || resourceLocationString.equals(""))
else if (resourceLocationString.trim().isEmpty() || resourceLocationString.isEmpty())
{
LOGGER.warn("Null biome string deserialized.");
return EMPTY_WRAPPER;
}
// parse the resource location
int separatorIndex = resourceLocationString.indexOf(":");
if (separatorIndex == -1)
if (WRAPPER_BY_RESOURCE_LOCATION.containsKey(finalResourceStateString))
{
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
return WRAPPER_BY_RESOURCE_LOCATION.get(finalResourceStateString);
}
ResourceLocation resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
// if no wrapper is found, default to the empty wrapper
BiomeWrapper foundWrapper = EMPTY_WRAPPER;
try
{
Level level = (Level)levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
boolean success;
#if MC_1_16_5 || MC_1_17_1
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (biome != null);
#elif MC_1_18_2 || MC_1_19_2
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#else
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#endif
if (!success)
// parse the resource location
int separatorIndex = resourceLocationString.indexOf(":");
if (separatorIndex == -1)
{
if (!BrokenResourceLocationStrings.contains(resourceLocationString))
{
BrokenResourceLocationStrings.add(resourceLocationString);
LOGGER.warn("Unable to deserialize biome from string: [" + resourceLocationString + "]");
}
return EMPTY_WRAPPER;
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
}
return getBiomeWrapper(biome, levelWrapper);
ResourceLocation resourceLocation;
try
{
#if MC_VER < MC_1_21
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
#else
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
#endif
}
catch (Exception e)
{
throw new IOException("No Resource Location found for the string: [" + resourceLocationString + "] Error: [" + e.getMessage() + "].");
}
try
{
Level level = (Level) levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
boolean success;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (biome != null);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#else
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#endif
if (!success)
{
if (!brokenResourceLocationStrings.contains(resourceLocationString))
{
brokenResourceLocationStrings.add(resourceLocationString);
LOGGER.warn("Unable to deserialize biome from string: [" + resourceLocationString + "]");
}
return EMPTY_WRAPPER;
}
foundWrapper = (BiomeWrapper) getBiomeWrapper(biome, levelWrapper);
return foundWrapper;
}
catch (Exception e)
{
throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BiomeWrapper: " + e.getMessage(), e);
}
}
catch (Exception e)
finally
{
throw new IOException("Failed to deserialize the string [" + resourceLocationString + "] into a BiomeWrapper: " + e.getMessage(), e);
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
}
}
@@ -19,24 +19,35 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags;
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;
import net.minecraft.world.level.block.state.properties.Property;
import org.apache.logging.log4j.Logger;
import java.awt.*;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
import net.minecraft.core.Registry;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.EmptyBlockGetter;
#elif MC_1_18_2 || MC_1_19_2
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
import net.minecraft.client.Minecraft;
import net.minecraft.world.level.Level;
import net.minecraft.core.BlockPos;
@@ -62,13 +73,15 @@ public class BlockStateWrapper implements IBlockStateWrapper
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
public static final String AIR_STRING = "AIR";
public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null);
// TODO: Make this changeable through the config
public static final String[] RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS = { AIR_STRING, "minecraft:barrier", "minecraft:structure_void", "minecraft:light", "minecraft:tripwire" };
public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt";
public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
/** keep track of broken blocks so we don't log every time */
private static final HashSet<ResourceLocation> BrokenResourceLocations = new HashSet<>();
@@ -80,11 +93,19 @@ public class BlockStateWrapper implements IBlockStateWrapper
public final BlockState blockState;
/** 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;
/**
* Cached opacity value, -1 if not populated. <br>
* Should be between {@link IBlockStateWrapper#FULLY_OPAQUE} and {@link IBlockStateWrapper#FULLY_OPAQUE}
* Should be between {@link LodUtil#BLOCK_FULLY_OPAQUE} and {@link LodUtil#BLOCK_FULLY_OPAQUE}
*/
private int opacity = -1;
/** used by the Iris shader mod to determine how each LOD should be rendered */
private byte blockMaterialId = 0;
private final boolean isBeaconBlock;
private final boolean isBeaconBaseBlock;
private final boolean isGlassBlock;
private final Color mapColor;
@@ -116,14 +137,47 @@ public class BlockStateWrapper implements IBlockStateWrapper
{
this.blockState = blockState;
this.serialString = this.serialize(levelWrapper);
LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"]");
this.hashCode = Objects.hash(this.serialString);
this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index;
String lowercaseSerial = this.serialString.toLowerCase();
boolean isBeaconBaseBlock = false;
for (int i = 0; i < LodUtil.BEACON_BASE_BLOCK_NAME_LIST.size(); i++)
{
String baseBlockName = LodUtil.BEACON_BASE_BLOCK_NAME_LIST.get(i);
if (lowercaseSerial.contains(baseBlockName))
{
isBeaconBaseBlock = true;
break;
}
}
this.isBeaconBaseBlock = isBeaconBaseBlock;
this.isBeaconBlock = lowercaseSerial.contains("minecraft:beacon");
this.isGlassBlock = lowercaseSerial.contains("glass");
int mcColor = 0;
if (this.blockState != null)
{
#if MC_VER < MC_1_20_1
mcColor = this.blockState.getMaterial().getColor().col;
#else
mcColor = this.blockState.getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).col;
#endif
this.mapColor = ColorUtil.toColorObjRGB(mcColor);
}
else
{
this.mapColor = new Color(0,0,0,0);
}
//LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"] with material ID ["+this.EDhApiBlockMaterialId+"]");
}
//================//
// helper methods //
//================//
//====================//
// LodBuilder methods //
//====================//
/**
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
@@ -137,37 +191,104 @@ public class BlockStateWrapper implements IBlockStateWrapper
return rendererIgnoredBlocks;
}
HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredBlocks = getBlockWrappers(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredBlocks;
}
/**
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
* This way the method won't accidentally be called before the deserialization can be completed.
*/
public static HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper)
{
// use the cached version if possible
if (rendererIgnoredCaveBlocks != null)
{
return rendererIgnoredCaveBlocks;
}
HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredCaveBlocks = getBlockWrappers(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredCaveBlocks;
}
public static void clearRendererIgnoredBlocks() { rendererIgnoredBlocks = null; }
public static void clearRendererIgnoredCaveBlocks() { rendererIgnoredCaveBlocks = null; }
// lod builder helpers //
private static HashSet<IBlockStateWrapper> getBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
{
// get the base blocks
HashSet<String> blockStringList = new HashSet<>();
if (baseResourceLocations != null)
{
blockStringList.addAll(baseResourceLocations);
}
// get the config blocks
String ignoreBlockCsv = config.get();
if (ignoreBlockCsv != null)
{
blockStringList.addAll(List.of(ignoreBlockCsv.split(",")));
}
return getBlockWrappers(blockStringList, levelWrapper);
}
private static HashSet<IBlockStateWrapper> getBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
{
// deserialize each of the given resource locations
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
for (String blockResourceLocation : RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS)
for (String blockResourceLocation : blockResourceLocationSet)
{
try
{
BlockStateWrapper DefaultBlockStateToIgnore = (BlockStateWrapper) deserialize(blockResourceLocation, levelWrapper);
blockStateWrappers.add(DefaultBlockStateToIgnore);
if (DefaultBlockStateToIgnore == AIR)
if (blockResourceLocation == null)
{
// shouldn't happen, but just in case
continue;
}
String cleanedResourceLocation = blockResourceLocation.trim();
if (cleanedResourceLocation.length() == 0)
{
continue;
}
// add all possible blockstates (to account for light blocks with different light values and such)
List<BlockState> blockStatesToIgnore = DefaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
for (BlockState blockState : blockStatesToIgnore)
BlockStateWrapper defaultBlockStateToIgnore = (BlockStateWrapper) deserialize(cleanedResourceLocation, levelWrapper);
blockStateWrappers.add(defaultBlockStateToIgnore);
if (defaultBlockStateToIgnore != AIR)
{
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper);
blockStateWrappers.add(newBlockToIgnore);
// add all possible blockstates (to account for light blocks with different light values and such)
List<BlockState> blockStatesToIgnore = defaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
for (BlockState blockState : blockStatesToIgnore)
{
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper);
blockStateWrappers.add(newBlockToIgnore);
}
}
else
{
// air is a special case so it must be handled separately
blockStateWrappers.add(AIR);
}
}
catch (IOException e)
{
LOGGER.warn("Unable to deserialize rendererIgnoredBlock with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
LOGGER.warn("Unable to deserialize block with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
}
catch (Exception e)
{
LOGGER.warn("Unexpected error deserializing block with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
}
}
rendererIgnoredBlocks = blockStateWrappers;
return rendererIgnoredBlocks;
return blockStateWrappers;
}
@@ -190,23 +311,23 @@ public class BlockStateWrapper implements IBlockStateWrapper
int opacity;
if (this.isAir())
{
opacity = FULLY_TRANSPARENT;
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
}
else if (this.isLiquid() && !this.blockState.canOcclude())
{
// probably not a waterlogged block (which should block light entirely)
// +1 to indicate that the block is translucent (in between transparent and opaque)
opacity = FULLY_TRANSPARENT + 1;
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1;
}
else if (this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO))
{
opacity = FULLY_TRANSPARENT;
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
}
else
{
// default for all other blocks
opacity = FULLY_OPAQUE;
opacity = LodUtil.BLOCK_FULLY_OPAQUE;
}
@@ -239,7 +360,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
@Override
public int hashCode() { return Objects.hash(this.getSerialString()); }
public int hashCode() { return this.hashCode; }
@Override
@@ -252,7 +373,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
@Override
public boolean isSolid()
{
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
return this.blockState.getMaterial().isSolid();
#else
return !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty();
@@ -267,13 +388,26 @@ public class BlockStateWrapper implements IBlockStateWrapper
return false;
}
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
return this.blockState.getMaterial().isLiquid() || !this.blockState.getFluidState().isEmpty();
#else
return !this.blockState.getFluidState().isEmpty();
#endif
}
@Override
public boolean isBeaconBlock() { return this.isBeaconBlock; }
@Override
public boolean isBeaconBaseBlock() { return this.isBeaconBaseBlock; }
@Override
public boolean isGlassBlock() { return this.isGlassBlock; }
@Override
public Color getMapColor() { return this.mapColor; }
@Override
public byte getMaterialId() { return this.blockMaterialId; }
@Override
public String toString() { return this.getSerialString(); }
@@ -293,15 +427,15 @@ public class BlockStateWrapper implements IBlockStateWrapper
// older versions of MC have a static registry
#if !(MC_1_16_5 || MC_1_17_1)
#if MC_VER > MC_1_17_1
Level level = (Level)levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
#endif
ResourceLocation resourceLocation;
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
#elif MC_1_18_2 || MC_1_19_2
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
#else
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
@@ -325,103 +459,139 @@ public class BlockStateWrapper implements IBlockStateWrapper
/** will only work if a level is currently loaded */
public static IBlockStateWrapper deserialize(String resourceStateString, ILevelWrapper levelWrapper) throws IOException
{
if (resourceStateString.equals(AIR_STRING) || resourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case
// we need the final string for the concurrent hash map later
final String finalResourceStateString = resourceStateString;
if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.isEmpty()) // the empty string shouldn't normally happen, but just in case
{
return AIR;
}
// try to parse out the BlockState
String blockStatePropertiesString = null; // will be null if no properties were included
int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR);
if (stateSeparatorIndex != -1)
// attempt to use the existing wrapper
if (WRAPPER_BY_RESOURCE_LOCATION.containsKey(finalResourceStateString))
{
// blockstate properties found
blockStatePropertiesString = resourceStateString.substring(stateSeparatorIndex + STATE_STRING_SEPARATOR.length());
resourceStateString = resourceStateString.substring(0, stateSeparatorIndex);
return WRAPPER_BY_RESOURCE_LOCATION.get(finalResourceStateString);
}
// parse the resource location
int resourceSeparatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR);
if (resourceSeparatorIndex == -1)
{
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
}
ResourceLocation resourceLocation = new ResourceLocation(resourceStateString.substring(0, resourceSeparatorIndex), resourceStateString.substring(resourceSeparatorIndex + 1));
// attempt to get the BlockState from all possible BlockStates
// if no wrapper is found, default to air
BlockStateWrapper foundWrapper = AIR;
try
{
#if !(MC_1_16_5 || MC_1_17_1)
// use the given level if possible, otherwise try using the currently loaded one
Level level = (levelWrapper != null ? (Level)levelWrapper.getWrappedMcObject() : null);
level = (level == null ? Minecraft.getInstance().level : level);
#endif
Block block;
#if MC_1_16_5 || MC_1_17_1
block = Registry.BLOCK.get(resourceLocation);
#elif MC_1_18_2 || MC_1_19_2
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
#else
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
#endif
if (block == null)
// try to parse out the BlockState
String blockStatePropertiesString = null; // will be null if no properties were included
int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR);
if (stateSeparatorIndex != -1)
{
// shouldn't normally happen, but here to make the compiler happy
if (!BrokenResourceLocations.contains(resourceLocation))
{
BrokenResourceLocations.add(resourceLocation);
LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
}
return AIR;
// blockstate properties found
blockStatePropertiesString = resourceStateString.substring(stateSeparatorIndex + STATE_STRING_SEPARATOR.length());
resourceStateString = resourceStateString.substring(0, stateSeparatorIndex);
}
// parse the resource location
int separatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR);
if (separatorIndex == -1)
{
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
}
ResourceLocation resourceLocation;
try
{
#if MC_VER < MC_1_21
resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
#else
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
#endif
}
catch (Exception e)
{
throw new IOException("No Resource Location found for the string: [" + resourceStateString + "] Error: [" + e.getMessage() + "].");
}
// attempt to find the blockstate from all possibilities
BlockState foundState = null;
if (blockStatePropertiesString != null)
{
List<BlockState> possibleStateList = block.getStateDefinition().getPossibleStates();
for (BlockState possibleState : possibleStateList)
{
String possibleStatePropertiesString = serializeBlockStateProperties(possibleState);
if (possibleStatePropertiesString.equals(blockStatePropertiesString))
{
foundState = possibleState;
break;
}
}
}
// use the default if no state was found or given
if (foundState == null)
// attempt to get the BlockState from all possible BlockStates
try
{
if (blockStatePropertiesString != null)
#if MC_VER > MC_1_17_1
// use the given level if possible, otherwise try using the currently loaded one
Level level = (levelWrapper != null ? (Level) levelWrapper.getWrappedMcObject() : null);
level = (level == null ? Minecraft.getInstance().level : level);
#endif
Block block;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
block = Registry.BLOCK.get(resourceLocation);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
#else
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
#endif
if (block == null)
{
// we should have found a blockstate, but didn't
// shouldn't normally happen, but here to make the compiler happy
if (!BrokenResourceLocations.contains(resourceLocation))
{
BrokenResourceLocations.add(resourceLocation);
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
}
return AIR;
}
// attempt to find the blockstate from all possibilities
BlockState foundState = null;
if (blockStatePropertiesString != null)
{
List<BlockState> possibleStateList = block.getStateDefinition().getPossibleStates();
for (BlockState possibleState : possibleStateList)
{
String possibleStatePropertiesString = serializeBlockStateProperties(possibleState);
if (possibleStatePropertiesString.equals(blockStatePropertiesString))
{
foundState = possibleState;
break;
}
}
}
foundState = block.defaultBlockState();
// use the default if no state was found or given
if (foundState == null)
{
if (blockStatePropertiesString != null)
{
// we should have found a blockstate, but didn't
if (!BrokenResourceLocations.contains(resourceLocation))
{
BrokenResourceLocations.add(resourceLocation);
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
}
}
foundState = block.defaultBlockState();
}
foundWrapper = new BlockStateWrapper(foundState, levelWrapper);
return foundWrapper;
}
catch (Exception e)
{
throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BlockStateWrapper: " + e.getMessage(), e);
}
return new BlockStateWrapper(foundState, levelWrapper);
}
catch (Exception e)
finally
{
throw new IOException("Failed to deserialize the string [" + resourceStateString + "] into a BlockStateWrapper: " + e.getMessage(), e);
// 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);
}
}
@@ -433,7 +603,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// 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()));
sortedBlockPropteryList.sort(Comparator.comparing(Property::getName));
StringBuilder stringBuilder = new StringBuilder();
@@ -457,4 +627,111 @@ public class BlockStateWrapper implements IBlockStateWrapper
//==============//
// Iris methods //
//==============//
private EDhApiBlockMaterial calculateEDhApiBlockMaterialId()
{
if (this.blockState == null)
{
return EDhApiBlockMaterial.AIR;
}
String serialString = this.getSerialString().toLowerCase();
if (this.blockState.is(BlockTags.LEAVES)
|| serialString.contains("bamboo")
|| serialString.contains("cactus")
|| serialString.contains("chorus_flower")
|| serialString.contains("mushroom")
)
{
return EDhApiBlockMaterial.LEAVES;
}
else if (this.blockState.is(Blocks.LAVA))
{
return EDhApiBlockMaterial.LAVA;
}
else if (this.isLiquid() || this.blockState.is(Blocks.WATER))
{
return EDhApiBlockMaterial.WATER;
}
else if (this.blockState.getSoundType() == SoundType.WOOD
|| serialString.contains("root")
#if MC_VER >= MC_1_19_4
|| this.blockState.getSoundType() == SoundType.CHERRY_WOOD
#endif
)
{
return EDhApiBlockMaterial.WOOD;
}
else if (this.blockState.getSoundType() == SoundType.METAL
#if MC_VER >= MC_1_19_2
|| this.blockState.getSoundType() == SoundType.COPPER
#endif
#if MC_VER >= MC_1_20_4
|| this.blockState.getSoundType() == SoundType.COPPER_BULB
|| this.blockState.getSoundType() == SoundType.COPPER_GRATE
#endif
)
{
return EDhApiBlockMaterial.METAL;
}
else if (serialString.contains("grass_block"))
{
return EDhApiBlockMaterial.GRASS;
}
else if (
serialString.contains("dirt")
|| serialString.contains("gravel")
|| serialString.contains("mud")
|| serialString.contains("podzol")
|| serialString.contains("mycelium")
)
{
return EDhApiBlockMaterial.DIRT;
}
#if MC_VER >= MC_1_17_1
else if (this.blockState.getSoundType() == SoundType.DEEPSLATE
|| this.blockState.getSoundType() == SoundType.DEEPSLATE_BRICKS
|| this.blockState.getSoundType() == SoundType.DEEPSLATE_TILES
|| this.blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE
|| serialString.contains("deepslate") )
{
return EDhApiBlockMaterial.DEEPSLATE;
}
#endif
else if (this.serialString.contains("snow"))
{
return EDhApiBlockMaterial.SNOW;
}
else if (serialString.contains("sand"))
{
return EDhApiBlockMaterial.SAND;
}
else if (serialString.contains("terracotta"))
{
return EDhApiBlockMaterial.TERRACOTTA;
}
else if (this.blockState.is(BlockTags.BASE_STONE_NETHER))
{
return EDhApiBlockMaterial.NETHER_STONE;
}
else if (serialString.contains("stone")
|| serialString.contains("ore"))
{
return EDhApiBlockMaterial.STONE;
}
else if (this.blockState.getLightEmission() > 0)
{
return EDhApiBlockMaterial.ILLUMINATED;
}
else
{
return EDhApiBlockMaterial.UNKNOWN;
}
}
}
@@ -40,11 +40,11 @@ public class TextureAtlasSpriteWrapper
*/
public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y)
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
return sprite.mainImage[0].getPixelRGBA(
x + sprite.framesX[frameIndex] * sprite.getWidth(),
y + sprite.framesY[frameIndex] * sprite.getHeight());
#elif PRE_MC_1_19_4
#elif MC_VER < MC_1_19_4
if (sprite.animatedTexture != null)
{
x += sprite.animatedTexture.getFrameX(frameIndex) * sprite.width;
@@ -19,7 +19,6 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.common.LodCommonMain;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.*;
@@ -50,7 +49,7 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
private Biome _getBiome(BlockPos pos)
{
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
return parent.getBiome(pos).value();
#else
return parent.getBiome(pos);
@@ -58,18 +57,7 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
if (LodCommonMain.forgeMethodCaller != null)
{
return LodCommonMain.forgeMethodCaller.colorResolverGetColor(colorResolver, _getBiome(blockPos),
blockPos.getX(), blockPos.getZ());
}
else
{
return colorResolver.getColor(_getBiome(blockPos), blockPos.getX(), blockPos.getZ());
}
}
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) { return colorResolver.getColor(this._getBiome(blockPos), blockPos.getX(), blockPos.getZ()); }
@Override
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
@@ -167,7 +155,7 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
return parent.getMaxBuildHeight();
}
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
@Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType)
{
@@ -19,7 +19,6 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.common.LodCommonMain;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Cursor3D;
import net.minecraft.core.Direction;
@@ -53,7 +52,7 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
private Biome _getBiome(BlockPos pos)
{
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
return parent.getBiome(pos).value();
#else
return parent.getBiome(pos);
@@ -74,16 +73,7 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
while (cursor3D.advance())
{
mutableBlockPos.set(cursor3D.nextX(), cursor3D.nextY(), cursor3D.nextZ());
int n;
if (LodCommonMain.forgeMethodCaller != null)
{
n = LodCommonMain.forgeMethodCaller.colorResolverGetColor(colorResolver, _getBiome(mutableBlockPos),
mutableBlockPos.getX(), mutableBlockPos.getZ());
}
else
{
n = colorResolver.getColor(_getBiome(mutableBlockPos), mutableBlockPos.getX(), mutableBlockPos.getZ());
}
int n = colorResolver.getColor(this._getBiome(mutableBlockPos), mutableBlockPos.getX(), mutableBlockPos.getZ());
k += (n & 0xFF0000) >> 16;
l += (n & 0xFF00) >> 8;
@@ -93,177 +83,96 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
return calculateBlockTint(blockPos, colorResolver);
}
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) { return this.calculateBlockTint(blockPos, colorResolver); }
@Override
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
@Override
public LevelLightEngine getLightEngine()
{
return parent.getLightEngine();
}
public LevelLightEngine getLightEngine() { return this.parent.getLightEngine(); }
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos)
{
return parent.getBrightness(lightLayer, blockPos);
}
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return this.parent.getBrightness(lightLayer, blockPos); }
@Override
public int getRawBrightness(BlockPos blockPos, int i)
{
return parent.getRawBrightness(blockPos, i);
}
public int getRawBrightness(BlockPos blockPos, int i) { return this.parent.getRawBrightness(blockPos, i); }
@Override
public boolean canSeeSky(BlockPos blockPos)
{
return parent.canSeeSky(blockPos);
}
public boolean canSeeSky(BlockPos blockPos) { return this.parent.canSeeSky(blockPos); }
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos blockPos)
{
return parent.getBlockEntity(blockPos);
}
public BlockEntity getBlockEntity(BlockPos blockPos) { return this.parent.getBlockEntity(blockPos); }
@Override
public BlockState getBlockState(BlockPos blockPos)
{
return parent.getBlockState(blockPos);
}
public BlockState getBlockState(BlockPos blockPos) { return this.parent.getBlockState(blockPos); }
@Override
public FluidState getFluidState(BlockPos blockPos)
{
return parent.getFluidState(blockPos);
}
public FluidState getFluidState(BlockPos blockPos) { return this.parent.getFluidState(blockPos); }
@Override
public int getLightEmission(BlockPos blockPos)
{
return parent.getLightEmission(blockPos);
}
public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); }
@Override
public int getMaxLightLevel()
{
return parent.getMaxLightLevel();
}
public int getMaxLightLevel() { return this.parent.getMaxLightLevel(); }
@Override
public Stream<BlockState> getBlockStates(AABB aABB)
{
return parent.getBlockStates(aABB);
}
public Stream<BlockState> getBlockStates(AABB aABB) { return this.parent.getBlockStates(aABB); }
@Override
public BlockHitResult clip(ClipContext clipContext)
{
return parent.clip(clipContext);
}
public BlockHitResult clip(ClipContext clipContext) { return this.parent.clip(clipContext); }
@Override
@Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
{
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
return this.parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier)
{
return parent.getBlockFloorHeight(voxelShape, supplier);
}
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) { return this.parent.getBlockFloorHeight(voxelShape, supplier); }
@Override
public double getBlockFloorHeight(BlockPos blockPos)
{
return parent.getBlockFloorHeight(blockPos);
}
public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); }
@Override
public int getMaxBuildHeight()
{
return parent.getMaxBuildHeight();
}
public int getMaxBuildHeight() { return this.parent.getMaxBuildHeight(); }
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
@Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType)
{
return parent.getBlockEntity(blockPos, blockEntityType);
}
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType) { return this.parent.getBlockEntity(blockPos, blockEntityType); }
@Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext)
{
return parent.isBlockInLine(clipBlockStateContext);
}
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext) { return this.parent.isBlockInLine(clipBlockStateContext); }
@Override
public int getHeight()
{
return parent.getHeight();
}
public int getHeight() { return this.parent.getHeight(); }
@Override
public int getMinBuildHeight()
{
return parent.getMinBuildHeight();
}
public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); }
@Override
public int getSectionsCount()
{
return parent.getSectionsCount();
}
public int getSectionsCount() { return this.parent.getSectionsCount(); }
@Override
public int getMinSection()
{
return parent.getMinSection();
}
public int getMinSection() { return this.parent.getMinSection(); }
@Override
public int getMaxSection()
{
return parent.getMaxSection();
}
public int getMaxSection() { return this.parent.getMaxSection(); }
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos)
{
return parent.isOutsideBuildHeight(blockPos);
}
public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); }
@Override
public boolean isOutsideBuildHeight(int i)
{
return parent.isOutsideBuildHeight(i);
}
public boolean isOutsideBuildHeight(int i) { return this.parent.isOutsideBuildHeight(i); }
@Override
public int getSectionIndex(int i)
{
return parent.getSectionIndex(i);
}
public int getSectionIndex(int i) { return this.parent.getSectionIndex(i); }
@Override
public int getSectionIndexFromSectionY(int i)
{
return parent.getSectionIndexFromSectionY(i);
}
public int getSectionIndexFromSectionY(int i) { return this.parent.getSectionIndexFromSectionY(i); }
@Override
public int getSectionYFromSectionIndex(int i)
{
return parent.getSectionYFromSectionIndex(i);
}
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
#endif
}
@@ -19,72 +19,120 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#endif
public class TintWithoutLevelOverrider implements BlockAndTintGetter
{
final BiomeWrapper biome;
/**
* This will only ever be null if there was an issue with {@link IClientLevelWrapper#getPlainsBiomeWrapper()}
* but {@link Nullable} is there just in case.
*/
@Nullable
private final Biome biome;
public TintWithoutLevelOverrider(BiomeWrapper biome)
public TintWithoutLevelOverrider(BiomeWrapper biomeWrapper, IClientLevelWrapper clientLevelWrapper)
{
this.biome = biome;
// try to get the wrapped biome
Biome unwrappedBiome = null;
if (biomeWrapper.biome != null)
{
unwrappedBiome = unwrap(biomeWrapper.biome);
}
if(unwrappedBiome == null)
{
// we are looking at the empty biome wrapper, try using plains as a backup
BiomeWrapper plainsBiomeWrapper = ((BiomeWrapper) clientLevelWrapper.getPlainsBiomeWrapper());
if (plainsBiomeWrapper != null)
{
unwrappedBiome = unwrap(plainsBiomeWrapper.biome);
}
}
this.biome = unwrappedBiome;
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ());
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
{
if (this.biome != null)
{
return colorResolver.getColor(this.biome, blockPos.getX(), blockPos.getZ());
}
else
{
// hopefully unneeded debug color
return ColorUtil.CYAN;
}
}
private Biome _unwrap(#if POST_MC_1_18_2 Holder<Biome> #else Biome #endif biome)
private static Biome unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
return biome.value();
#else
return biome;
#endif
}
//================//
// unused methods //
//================//
@Override
public float getShade(Direction direction, boolean shade)
public float getShade(@NotNull Direction direction, boolean shade)
{
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public LevelLightEngine getLightEngine()
public @NotNull LevelLightEngine getLightEngine()
{
throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos)
public BlockEntity getBlockEntity(@NotNull BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public BlockState getBlockState(BlockPos pos)
public @NotNull BlockState getBlockState(@NotNull BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public FluidState getFluidState(BlockPos pos)
public @NotNull FluidState getFluidState(@NotNull BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
#if MC_1_17_1 || POST_MC_1_18_2
#if MC_VER >= MC_1_17_1
@Override
public int getHeight()
{
@@ -30,7 +30,7 @@ import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import org.jetbrains.annotations.Nullable;
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#endif
@@ -49,9 +49,9 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
{
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ());
}
private Biome _unwrap(#if POST_MC_1_18_2 Holder<Biome> #else Biome #endif biome)
private Biome _unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
return biome.value();
#else
return biome;
@@ -116,7 +116,7 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
}
#if MC_1_17_1 || POST_MC_1_18_2
#if MC_VER >= MC_1_17_1
@Override
public int getHeight()
{
@@ -29,7 +29,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class ClientBlockDetailMap
{
private final ConcurrentHashMap<BlockState, ClientBlockStateCache> blockCache = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ClientLevelWrapper level;
public ClientBlockDetailMap(ClientLevelWrapper level) { this.level = level; }
@@ -38,7 +38,7 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.RotatedPillarBlock;
#if POST_MC_1_19_2
#if MC_VER >= MC_1_19_2
import net.minecraft.util.RandomSource;
#else
import java.util.Random;
@@ -54,27 +54,31 @@ import java.util.List;
*/
public class ClientBlockStateCache
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
public static final Random random = new Random(0);
#else
public static final RandomSource random = RandomSource.create();
#endif
public final IClientLevelWrapper levelWrapper;
public final BlockState blockState;
public final LevelReader level;
public final BlockPos pos;
public ClientBlockStateCache(BlockState blockState, IClientLevelWrapper samplingLevel, DhBlockPos samplingPos)
{
this.blockState = blockState;
level = (LevelReader) samplingLevel.getWrappedMcObject();
pos = McObjectConverter.Convert(samplingPos);
resolveColors();
this.levelWrapper = samplingLevel;
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
this.pos = McObjectConverter.Convert(samplingPos);
this.resolveColors();
//LOGGER.info("ClientBlocKCache created for {}", blockState);
}
@@ -91,18 +95,98 @@ public class ClientBlockStateCache
{
Default,
Flower,
Leaves;
Leaves,
Chisel,
Glass;
static ColorMode getColorMode(Block b)
{
if (b instanceof LeavesBlock) return Leaves;
if (b instanceof FlowerBlock) return Flower;
if (b.toString().contains("glass")) return Glass;
if (b.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
return Default;
}
}
//Way to efficiently do this was suggested by IMS from sodium. This is where those numbers and support code was lifted from.
private static final int MIN_BITS = 0x39000000; // 2^(-13)
private static final int MAX_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
private static final float MIN_BOUND = Float.intBitsToFloat(MIN_BITS);
private static final float MAX_BOUND = Float.intBitsToFloat(MAX_BITS);
private static final int[] linearToSrgbTable = new int[] {
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
};
private static final float[] srgbToLinearTable = new float[] {
0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f,
0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f,
0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
};
private static int linearToSrgb(float c) {
if (!(c > MIN_BOUND)) {
c = MIN_BOUND;
}
if (c > MAX_BOUND) {
c = MAX_BOUND;
}
int inputBits = Float.floatToRawIntBits(c);
int entry = linearToSrgbTable[((inputBits - MIN_BITS) >> 20)];
int bias = (entry >>> 16) << 9;
int scale = entry & 0xffff;
int t = (inputBits >>> 12) & 0xff;
return (bias + (scale * t)) >>> 16;
}
//////////////
private static int getWidth(TextureAtlasSprite texture)
{
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
return texture.getWidth();
#else
return texture.contents().width();
@@ -111,27 +195,30 @@ public class ClientBlockStateCache
private static int getHeight(TextureAtlasSprite texture)
{
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
return texture.getHeight();
#else
return texture.contents().height();
#endif
}
//TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
{
int count = 0;
double alpha = 0;
int alpha = 0;
double red = 0;
double green = 0;
double blue = 0;
int tempColor;
//make Chiseled block not render. Since ColorMode is set per block, you only need to check it once
if (colorMode != ColorMode.Chisel)
{
// textures normally use u and v instead of x and y
for (int u = 0; u < getWidth(texture); u++)
for (int v = 0; v < getHeight(texture); v++)
{
for (int v = 0; v < getHeight(texture); v++)
for (int u = 0; u < getWidth(texture); u++)
{
//note: Minecraft color format is: 0xAA BB GG RR
//________ DH mod color format is: 0xAA RR GG BB
@@ -139,33 +226,45 @@ public class ClientBlockStateCache
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
double r = ((tempColor & 0x000000FF)) / 255.;
double g = ((tempColor & 0x0000FF00) >>> 8) / 255.;
double b = ((tempColor & 0x00FF0000) >>> 16) / 255.;
double a = ((tempColor & 0xFF000000) >>> 24) / 255.;
int r = (tempColor & 0x000000FF);
int g = (tempColor & 0x0000FF00) >>> 8;
int b = (tempColor & 0x00FF0000) >>> 16;
int a = (tempColor & 0xFF000000) >>> 24;
int scale = 1;
if (colorMode == ColorMode.Leaves)
{
r *= a;
g *= a;
b *= a;
a = 1.;
//switch (//FIXME add config option)
// case BLACK:
// a = 255; //simulate black background of fast leaves
// break;
// case IGNORE:
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.)
else if (a == 0 && colorMode != ColorMode.Glass)
{
continue;
}
else if (colorMode == ColorMode.Flower && (g + 0.1 < b || g + 0.1 < r))
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r))
{
scale = FLOWER_COLOR_SCALE;
}
count += scale;
alpha += a * a * scale;
red += r * r * scale;
green += g * g * scale;
blue += b * b * scale;
//apparently alpha is linear
alpha += a * scale;
//gamma correction is complicated
red += srgbToLinearTable[r] * a * scale;
green += srgbToLinearTable[g] * a * scale;
blue += srgbToLinearTable[b] * a * scale;
}
}
}
@@ -177,10 +276,16 @@ public class ClientBlockStateCache
{
// determine the average color
tempColor = ColorUtil.rgbToInt(
(int) (Math.sqrt(alpha / count) * 255.),
(int) (Math.sqrt(red / count) * 255.),
(int) (Math.sqrt(green / count) * 255.),
(int) (Math.sqrt(blue / count) * 255.));
alpha / count,
linearToSrgb((float) (red / (double) alpha)),
linearToSrgb((float) (green / (double) alpha)),
linearToSrgb((float) (blue / (double) alpha)));
}
//check if not missing texture
if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182))
{
//make it not render at all
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
}
return tempColor;
}
@@ -195,7 +300,7 @@ public class ClientBlockStateCache
for (Direction direction : DIRECTION_ORDER)
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(blockState).getQuads(blockState, direction, random);
getBlockModel(blockState).getQuads(blockState, direction, random); // TODO getQuads sometimes throws a "makeThreadingException", is there anything we can do about that? Note: this isn't a critical issue, it just prints an ugly error and the render data will need to be regenered.
if (quads != null && !quads.isEmpty() &&
!(blockState.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
break;
@@ -207,12 +312,12 @@ public class ClientBlockStateCache
}
if (quads != null && !quads.isEmpty())
{
needPostTinting = quads.get(0).isTinted();
needShade = quads.get(0).isShade();
tintIndex = quads.get(0).getTintIndex();
needPostTinting = quads.getFirst().isTinted();
needShade = quads.getFirst().isShade();
tintIndex = quads.getFirst().getTintIndex();
baseColor = calculateColorFromTexture(
#if PRE_MC_1_17_1 quads.get(0).sprite,
#else quads.get(0).getSprite(), #endif
#if MC_VER < MC_1_17_1 quads.get(0).sprite,
#else quads.getFirst().getSprite(), #endif
ColorMode.getColorMode(blockState.getBlock()));
}
else
@@ -262,7 +367,7 @@ public class ClientBlockStateCache
try
{
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState, new TintWithoutLevelOverrider(biome), McObjectConverter.Convert(pos), this.tintIndex);
.getColor(this.blockState, new TintWithoutLevelOverrider(biome, this.levelWrapper), McObjectConverter.Convert(pos), this.tintIndex);
}
catch (UnsupportedOperationException e)
{
@@ -29,7 +29,7 @@ import net.minecraft.world.level.block.state.BlockState;
public class ServerBlockDetailMap
{
private final ConcurrentHashMap<BlockState, ServerBlockStateCache> blockCache = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ServerLevelWrapper level;
public ServerBlockDetailMap(ServerLevelWrapper level) { this.level = level; }
@@ -1,216 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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.chunk;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Compact, efficient storage for light levels.
* all blocks only take up 4 bits in total,
* and if a 16x16x16 area is detected to have the same light level in all positions,
* then we store a single byte for that light level, instead of 2 kilobytes.
*
* @author Builderb0y
*/
public class ChunkLightStorage
{
/** the minimum Y level in the chunk which this storage is storing light levels for (inclusive). */
public int minY;
/** the maximum Y level in the chunk which this storage is storing light levels for (exclusive). */
public int maxY;
/** the data stored in this storage, split up into 16x16x16 areas. */
public LightSection[] lightSections;
public ChunkLightStorage(int minY, int maxY)
{
this.minY = minY;
this.maxY = maxY;
}
public int get(int x, int y, int z)
{
if (y < this.minY)
{
return 0;
}
if (y >= this.maxY)
{
return 15;
}
if (this.lightSections != null)
{
LightSection lightSection = this.lightSections[BitShiftUtil.divideByPowerOfTwo(y - this.minY, 4)];
if (lightSection != null)
{
return lightSection.get(x, y, z);
}
}
return 0;
}
public void set(int x, int y, int z, int lightLevel)
{
if (y < this.minY || y >= this.maxY)
{
return;
}
//populate array if it doesn't exist.
if (this.lightSections == null)
{
this.lightSections = new LightSection[BitShiftUtil.divideByPowerOfTwo(this.maxY - this.minY, 4)];
}
int index = (y - this.minY) >> 4;
LightSection lightSection = this.lightSections[index];
//populate lightSection in array if it doesn't exist.
if (lightSection == null)
{
lightSection = new LightSection(0);
this.lightSections[index] = lightSection;
}
lightSection.set(x, y, z, lightLevel);
}
//================//
// helper classes //
//================//
public static class LightSection
{
public byte constantValue;
public long[] data;
public short[] counts;
public LightSection(int initialValue)
{
this.constantValue = (byte) (initialValue);
this.counts = new short[16];
this.counts[initialValue] = 16 * 16 * 16;
}
public int get(int x, int y, int z)
{
if (this.constantValue >= 0)
{
return this.constantValue;
}
x &= 15;
y &= 15;
z &= 15;
long bits = this.data[(z << 4) | x];
return ((int) (bits >>> (y << 2))) & 15;
}
public void set(int x, int y, int z, int lightLevel)
{
int oldLightLevel = -1;
if (this.constantValue >= 0)
{
oldLightLevel = this.constantValue;
//if the light level didn't change, then there's nothing to do.
if (oldLightLevel == lightLevel) return;
//if we are a constant value and need to change something,
//then that means we need to convert to a non-constant value.
this.data = DataRecycler.get();
//repeat oldLightLevel 16 times as a bit pattern.
long payload = oldLightLevel;
payload |= payload << 4;
payload |= payload << 8;
payload |= payload << 16;
payload |= payload << 32;
//fill our data with our constant value.
Arrays.fill(this.data, payload);
//we are no longer a constant value.
this.constantValue = -1;
}
x &= 15;
y &= 15;
z &= 15;
int index = (z << 4) | x;
long bits = this.data[index];
//if we weren't a constant value before, now's the time to initialize oldLightLevel.
if (oldLightLevel < 0)
{
oldLightLevel = ((int) (bits >>> (y << 2))) & 15;
}
//clear the 4 bits that correspond to the light level at x, y, z...
bits &= ~(15L << (y << 2));
//...and then re-populate those bits with the new light level.
bits |= ((long) (lightLevel)) << (y << 2);
//store the updated bits in our data.
this.data[index] = bits;
//we have one less of the old light level...
this.counts[oldLightLevel]--;
//...and one more of the new level.
//if the number associated with the new level is now 4096 (AKA 16 ^ 3),
//then this implies every position in this section has the same light level,
//and therefore we can convert back to a constant value.
if (++this.counts[lightLevel] == 4096)
{
this.constantValue = (byte) (lightLevel);
DataRecycler.reclaim(this.data);
this.data = null;
}
}
}
static class DataRecycler
{
private static final ArrayList<long[]> recycled = new ArrayList<>(256);
static synchronized long[] get()
{
if (recycled.isEmpty())
{
return new long[256];
}
else
{
return recycled.remove(recycled.size() - 1);
}
}
static synchronized void reclaim(long[] data) { if (recycled.size() < 256) recycled.add(data); }
}
}
@@ -25,20 +25,18 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.Dh
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.client.multiplayer.ClientChunkCache;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.Heightmap;
@@ -47,41 +45,45 @@ import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
import net.minecraft.core.QuartPos;
#endif
#if MC_1_16_5
#if MC_VER == MC_1_16_5
import net.minecraft.world.level.chunk.LevelChunkSection;
#endif
#if MC_1_17_1
#if MC_VER == MC_1_17_1
import net.minecraft.world.level.chunk.LevelChunkSection;
#endif
#if MC_1_18_2
#if MC_VER == MC_1_18_2
import net.minecraft.world.level.chunk.LevelChunkSection;
#endif
#if MC_1_19_2 || MC_1_19_4
#if MC_VER == MC_1_19_2 || MC_VER == MC_1_19_4
import net.minecraft.world.level.chunk.LevelChunkSection;
#endif
#if POST_MC_1_20_1
#if MC_VER >= MC_1_20_1
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.core.SectionPos;
#endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public class ChunkWrapper implements IChunkWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/** useful for debugging, but can slow down chunk operations quite a bit due to being called every time. */
private static final boolean RUN_RELATIVE_POS_INDEX_VALIDATION = ModInfo.IS_DEV_BUILD;
/** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos());
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(BlockPos.MutableBlockPos::new);
private final ChunkAccess chunk;
@@ -100,6 +102,11 @@ public class ChunkWrapper implements IChunkWrapper
private boolean useDhLighting;
private int minNonEmptyHeight = Integer.MIN_VALUE;
private int maxNonEmptyHeight = Integer.MAX_VALUE;
private int blockBiomeHashCode = 0;
/**
* Due to vanilla `isClientLightReady()` not being designed for use by a non-render thread, it may return 'true'
* before the light engine has ticked, (right after all light changes is marked by the engine to be processed).
@@ -139,34 +146,48 @@ public class ChunkWrapper implements IChunkWrapper
//=========//
// methods //
// getters //
//=========//
@Override
public int getHeight()
public int getHeight() { return getHeight(this.chunk); }
public static int getHeight(ChunkAccess chunk)
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
return 255;
#else
return this.chunk.getHeight();
return chunk.getHeight();
#endif
}
@Override
public int getMinBuildHeight()
public int getMinBuildHeight() { return getMinBuildHeight(this.chunk); }
public static int getMinBuildHeight(ChunkAccess chunk)
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
return 0;
#else
return this.chunk.getMinBuildHeight();
return chunk.getMinBuildHeight();
#endif
}
@Override
public int getMaxBuildHeight() { return this.chunk.getMaxBuildHeight(); }
@Override
public int getMinFilledHeight()
public int getMaxBuildHeight() { return getMaxBuildHeight(this.chunk); }
public static int getMaxBuildHeight(ChunkAccess chunk) { return chunk.getMaxBuildHeight(); }
@Override
public int getMinNonEmptyHeight()
{
if (this.minNonEmptyHeight != Integer.MIN_VALUE)
{
return this.minNonEmptyHeight;
}
// default if every section is empty or missing
this.minNonEmptyHeight = this.getMinBuildHeight();
// determine the lowest empty section (bottom up)
LevelChunkSection[] sections = this.chunk.getSections();
for (int index = 0; index < sections.length; index++)
{
@@ -175,30 +196,63 @@ public class ChunkWrapper implements IChunkWrapper
continue;
}
#if MC_1_16_5
if (!sections[index].isEmpty())
if (!isChunkSectionEmpty(sections[index]))
{
// convert from an index to a block coordinate
return this.chunk.getSections()[index].bottomBlockY() * 16;
this.minNonEmptyHeight = this.getChunkSectionMinHeight(index);
break;
}
#elif MC_1_17_1
if (!sections[index].isEmpty())
{
// convert from an index to a block coordinate
return this.chunk.getSections()[index].bottomBlockY() * 16;
}
#else
if (!sections[index].hasOnlyAir())
{
// convert from an index to a block coordinate
return this.chunk.getSectionYFromSectionIndex(index) * 16;
}
#endif
}
return Integer.MAX_VALUE;
return this.minNonEmptyHeight;
}
@Override
public int getMaxNonEmptyHeight()
{
if (this.maxNonEmptyHeight != Integer.MAX_VALUE)
{
return this.maxNonEmptyHeight;
}
// default if every section is empty or missing
this.maxNonEmptyHeight = this.getMaxBuildHeight();
// determine the highest empty section (top down)
LevelChunkSection[] sections = this.chunk.getSections();
for (int index = sections.length-1; index >= 0; index--)
{
// update at each position to fix using the max height if the chunk is empty
this.maxNonEmptyHeight = this.getChunkSectionMinHeight(index) + 16;
if (sections[index] == null)
{
continue;
}
if (!isChunkSectionEmpty(sections[index]))
{
// non-empty section found
break;
}
}
return this.maxNonEmptyHeight;
}
private static boolean isChunkSectionEmpty(LevelChunkSection section)
{
#if MC_VER == MC_1_16_5
return section.isEmpty();
#elif MC_VER == MC_1_17_1
return section.isEmpty();
#else
return section.hasOnlyAir();
#endif
}
private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getMinBuildHeight(); }
@Override
public int getSolidHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel); }
@@ -206,19 +260,18 @@ public class ChunkWrapper implements IChunkWrapper
public int getLightBlockingHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel); }
@Override
public IBiomeWrapper getBiome(int relX, int relY, int relZ)
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome(
relX >> 2, relY >> 2, relZ >> 2),
this.wrappedLevel);
#elif PRE_MC_1_18_2
#elif MC_VER < MC_1_18_2
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome(
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
this.wrappedLevel);
#elif PRE_MC_1_18_2
#elif MC_VER < MC_1_18_2
return BiomeWrapper.getBiomeWrapper(this.chunk.getNoiseBiome(
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
this.wrappedLevel);
@@ -230,11 +283,35 @@ public class ChunkWrapper implements IChunkWrapper
#endif
}
@Override
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get();
blockPos.setX(relX);
blockPos.setY(relY);
blockPos.setZ(relZ);
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
}
@Override
public DhChunkPos getChunkPos() { return this.chunkPos; }
public ChunkAccess getChunk() { return this.chunk; }
public ChunkStatus getStatus() { return getStatus(this.getChunk()); }
public static ChunkStatus getStatus(ChunkAccess chunk)
{
#if MC_VER < MC_1_21
return chunk.getStatus();
#else
return chunk.getPersistedStatus();
#endif
}
@Override
public int getMaxBlockX() { return this.chunk.getPos().getMaxBlockX(); }
@Override
@@ -244,8 +321,11 @@ public class ChunkWrapper implements IChunkWrapper
@Override
public int getMinBlockZ() { return this.chunk.getPos().getMinBlockZ(); }
@Override
public long getLongChunkPos() { return this.chunk.getPos().toLong(); }
//==========//
// lighting //
//==========//
@Override
public void setIsDhLightCorrect(boolean isDhLightCorrect) { this.isDhLightCorrect = isDhLightCorrect; }
@@ -253,8 +333,6 @@ public class ChunkWrapper implements IChunkWrapper
@Override
public void setUseDhLighting(boolean useDhLighting) { this.useDhLighting = useDhLighting; }
@Override
public boolean isLightCorrect()
{
@@ -264,12 +342,11 @@ public class ChunkWrapper implements IChunkWrapper
}
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
return false; // MC's lighting engine doesn't work consistently enough to trust for 1.16 or 1.17
#else
if (this.chunk instanceof LevelChunk)
if (this.chunk instanceof LevelChunk levelChunk)
{
LevelChunk levelChunk = (LevelChunk) this.chunk;
if (levelChunk.getLevel() instanceof ClientLevel)
{
// connected to a server
@@ -307,10 +384,11 @@ public class ChunkWrapper implements IChunkWrapper
{
if (this.blockLightStorage == null)
{
this.blockLightStorage = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight());
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(this);
}
return this.blockLightStorage;
}
public void setBlockLightStorage(ChunkLightStorage lightStorage) { this.blockLightStorage = lightStorage; }
@Override
@@ -330,10 +408,11 @@ public class ChunkWrapper implements IChunkWrapper
{
if (this.skyLightStorage == null)
{
this.skyLightStorage = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight());
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(this);
}
return this.skyLightStorage;
}
public void setSkyLightStorage(ChunkLightStorage lightStorage) { this.skyLightStorage = lightStorage; }
@Override
@@ -374,8 +453,12 @@ public class ChunkWrapper implements IChunkWrapper
}
}
/**
* FIXME synchronized is necessary for a rare issue where this method is called from two separate threads at the same time
* before the list has finished populating.
*/
@Override
public ArrayList<DhBlockPos> getBlockLightPosList()
public synchronized ArrayList<DhBlockPos> getBlockLightPosList()
{
// only populate the list once
if (this.blockLightPosList == null)
@@ -383,22 +466,82 @@ public class ChunkWrapper implements IChunkWrapper
this.blockLightPosList = new ArrayList<>();
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
this.chunk.getLights().forEach((blockPos) ->
{
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
});
#else
this.chunk.findBlockLightSources((blockPos, blockState) ->
{
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
});
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ())));
#endif
}
return this.blockLightPosList;
}
public static void syncedUpdateClientLightStatus()
{
#if MC_VER < MC_1_18_2
// TODO: Check what to do in 1.18.1 and older
// since we don't currently handle this list,
// clear it to prevent memory leaks
chunksNeedingClientLightUpdating.clear();
#else
// update the chunks client lighting
ChunkWrapper chunkWrapper = chunksNeedingClientLightUpdating.poll();
while (chunkWrapper != null)
{
chunkWrapper.updateIsClientLightingCorrect();
chunkWrapper = chunksNeedingClientLightUpdating.poll();
}
#endif
}
/** Should be called after client light updates are triggered. */
private void updateIsClientLightingCorrect()
{
if (this.chunk instanceof LevelChunk levelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel)
{
ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource();
this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null &&
#if MC_VER <= MC_1_17_1
levelChunk.isLightCorrect();
#elif MC_VER < MC_1_20_1
levelChunk.isClientLightReady();
#else
checkLightSectionsOnChunk(levelChunk, levelChunk.getLevel().getLightEngine());
#endif
}
}
#if MC_VER >= MC_1_20_1
private static boolean checkLightSectionsOnChunk(LevelChunk chunk, LevelLightEngine engine)
{
LevelChunkSection[] sections = chunk.getSections();
int minY = chunk.getMinSection();
int maxY = chunk.getMaxSection();
for (int y = minY; y < maxY; ++y)
{
LevelChunkSection section = sections[chunk.getSectionIndexFromSectionY(y)];
if (section.hasOnlyAir()) continue;
if (!engine.lightOnInSection(SectionPos.of(chunk.getPos(), y)))
{
return false;
}
}
return true;
}
#endif
//===============//
// other methods //
//===============//
@Override
public boolean doNearbyChunksExist()
{
@@ -425,146 +568,27 @@ public class ChunkWrapper implements IChunkWrapper
return true;
}
public LevelReader getColorResolver() { return this.lightSource; }
@Override
public String toString() { return this.chunk.getClass().getSimpleName() + this.chunk.getPos(); }
@Override
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get();
blockPos.setX(relX);
blockPos.setY(relY);
blockPos.setZ(relZ);
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
}
@Override
public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; }
public static void syncedUpdateClientLightStatus()
{
#if PRE_MC_1_18_2
// TODO: Check what to do in 1.18.1 and older
// since we don't currently handle this list,
// clear it to prevent memory leaks
chunksNeedingClientLightUpdating.clear();
#else
// update the chunks client lighting
ChunkWrapper chunkWrapper = chunksNeedingClientLightUpdating.poll();
while (chunkWrapper != null)
{
chunkWrapper.updateIsClientLightingCorrect();
chunkWrapper = chunksNeedingClientLightUpdating.poll();
}
#endif
}
/** Should be called after client light updates are triggered. */
private void updateIsClientLightingCorrect()
{
if (this.chunk instanceof LevelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel)
{
LevelChunk levelChunk = (LevelChunk) this.chunk;
ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource();
this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null &&
#if MC_1_16_5 || MC_1_17_1
levelChunk.isLightCorrect();
#elif PRE_MC_1_20_1
levelChunk.isClientLightReady();
#else
checkLightSectionsOnChunk(levelChunk, levelChunk.getLevel().getLightEngine());
#endif
}
}
#if POST_MC_1_20_1
private static boolean checkLightSectionsOnChunk(LevelChunk chunk, LevelLightEngine engine)
{
LevelChunkSection[] sections = chunk.getSections();
int minY = chunk.getMinSection();
int maxY = chunk.getMaxSection();
for (int y = minY; y < maxY; ++y)
{
LevelChunkSection section = sections[chunk.getSectionIndexFromSectionY(y)];
if (section.hasOnlyAir()) continue;
if (!engine.lightOnInSection(SectionPos.of(chunk.getPos(), y)))
{
return false;
}
}
return true;
}
#endif
//================//
// helper methods //
// base overrides //
//================//
/** used to prevent accidentally attempting to get/set values outside this chunk's boundaries */
private void throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(int x, int y, int z) throws IndexOutOfBoundsException
{
if (!RUN_RELATIVE_POS_INDEX_VALIDATION)
{
return;
}
// FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the constructor
int minHeight = this.getMinBuildHeight();
int maxHeight = this.getMaxBuildHeight() + 1;
if (x < 0 || x >= LodUtil.CHUNK_WIDTH
|| z < 0 || z >= LodUtil.CHUNK_WIDTH
|| y < minHeight || y > maxHeight)
{
String errorMessage = "Relative position [" + x + "," + y + "," + z + "] out of bounds. \n" +
"X/Z must be between 0 and 15 (inclusive) \n" +
"Y must be between [" + minHeight + "] and [" + maxHeight + "] (inclusive).";
throw new IndexOutOfBoundsException(errorMessage);
}
}
/**
* Converts a 3D position into a 1D array index. <br><br>
*
* Source: <br>
* <a href="https://stackoverflow.com/questions/7367770/how-to-flatten-or-index-3d-array-in-1d-array">stackoverflow</a>
*/
public int relativeBlockPosToIndex(int xRel, int y, int zRel)
{
int yRel = y - this.getMinBuildHeight();
return (zRel * LodUtil.CHUNK_WIDTH * this.getHeight()) + (yRel * LodUtil.CHUNK_WIDTH) + xRel;
}
/**
* Converts a 3D position into a 1D array index. <br><br>
*
* Source: <br>
* <a href="https://stackoverflow.com/questions/7367770/how-to-flatten-or-index-3d-array-in-1d-array">stackoverflow</a>
*/
public DhBlockPos indexToRelativeBlockPos(int index)
{
final int zRel = index / (LodUtil.CHUNK_WIDTH * this.getHeight());
index -= (zRel * LodUtil.CHUNK_WIDTH * this.getHeight());
final int y = index / LodUtil.CHUNK_WIDTH;
final int yRel = y + this.getMinBuildHeight();
final int xRel = index % LodUtil.CHUNK_WIDTH;
return new DhBlockPos(xRel, yRel, zRel);
}
@Override
public String toString() { return this.chunk.getClass().getSimpleName() + this.chunk.getPos(); }
//@Override
//public int hashCode()
//{
// if (this.blockBiomeHashCode == 0)
// {
// this.blockBiomeHashCode = this.getBlockBiomeHashCode();
// }
//
// return this.blockBiomeHashCode;
//}
}
@@ -16,7 +16,7 @@ import java.util.regex.Pattern;
// Logger (for debug stuff)
import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui;
import com.seibel.distanthorizons.api.enums.config.EUpdateBranch;
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.types.*;
@@ -35,7 +35,7 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.GuiComponent;
#else
@@ -49,7 +49,7 @@ import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.client.resources.language.I18n; // translation
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
import net.minecraft.client.gui.narration.NarratableEntry;
#endif
import net.minecraft.resources.ResourceLocation;
@@ -98,6 +98,7 @@ public class ClassicConfigGUI
public static final int SpaceFromRightScreen = 10;
public static final int ButtonWidthSpacing = 5;
public static final int ResetButtonWidth = 40;
public static final int ResetButtonHeight = 20;
}
@@ -144,9 +145,9 @@ public class ClassicConfigGUI
case 0:
((EntryInfo) info.guiValue).error = null; break;
case -1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry) info).getMin())); break;
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry<?>) info).getMin())); break;
case 1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry) info).getMax())); break;
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry<?>) info).getMax())); break;
case 2:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid")); break;
}
@@ -157,19 +158,22 @@ public class ClassicConfigGUI
// button.active = entries.stream().allMatch(e -> e.inLimits);
if (((ConfigEntry) info).isValid(value) == 0 && info.getType() != List.class)
if (info.getType() == String.class
|| info.getType() == List.class)
{
((ConfigEntry) info).uiSetWithoutSaving(stringValue);
}
else if (((ConfigEntry) info).isValid(value) == 0)
{
if (!cast)
{
((ConfigEntry) info).uiSetWithoutSaving(value);
}
else
{
((ConfigEntry) info).uiSetWithoutSaving(value.intValue());
}
}
// else if (((ConfigEntry) info).isValidMemoryAddress() == 0)
// {
// if (((List<String>) info.get()).size() == ((EntryInfo) info.guiValue).index)
// info.uiSet(((List<String>) info.get()).add(""));
// info.uiSet(((List<String>) info.get()).set(((EntryInfo) info.guiValue).index, Arrays.stream(((EntryInfo) info.guiValue).tempValue.replace("[", "").replace("]", "").split(", ")).collect(Collectors.toList()).get(0)));
// }
return true;
};
@@ -231,7 +235,7 @@ public class ClassicConfigGUI
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(this.minecraft).setScreen(this.parent);
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach(Runnable::run);
}
@Override
@@ -244,17 +248,23 @@ public class ClassicConfigGUI
}
// Changelog button
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() && Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE)
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() && !ModInfo.IS_DEV_BUILD) // we only have changelogs for stable builds
{
this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen
this.width - 28, this.height - 28,
// Width and height of the button
20, 20,
// Offset
// texture UV Offset
0, 0,
// Some textuary stuff
0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20,
0,
#if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
#endif
20, 20,
// Create the button and tell it where to go
(buttonWidget) -> {
ChangelogScreen changelogScreen = new ChangelogScreen(this);
@@ -269,18 +279,25 @@ public class ClassicConfigGUI
}
addBtn(MakeBtn(Translatable("distanthorizons.general.cancel"), this.width / 2 - 154, this.height - 28, 150, 20, button -> {
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile();
Objects.requireNonNull(minecraft).setScreen(parent);
}));
addBtn(MakeBtn(Translatable("distanthorizons.general.cancel"),
this.width / 2 - 154, this.height - 28,
150, 20,
button ->
{
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile();
Objects.requireNonNull(minecraft).setScreen(parent);
}));
doneButton = addBtn(MakeBtn(Translatable("distanthorizons.general.done"), this.width / 2 + 4, this.height - 28, 150, 20, (button) -> {
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(minecraft).setScreen(parent);
}));
this.list = new ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, this.height - 32, 25);
this.list = new ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, 32, 25);
#if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
if (this.minecraft != null && this.minecraft.level != null)
this.list.setRenderBackground(false);
#endif
this.addWidget(this.list);
@@ -302,7 +319,7 @@ public class ClassicConfigGUI
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach(Runnable::run);
}
@@ -311,20 +328,21 @@ public class ClassicConfigGUI
initEntry(info, this.translationPrefix);
Component name = Translatable(translationPrefix + info.getNameWCategory());
if (ConfigEntry.class.isAssignableFrom(info.getClass()))
{
Button.OnPress btnAction = button -> {
((ConfigEntry) info).uiSetWithoutSaving(((ConfigEntry) info).getDefaultValue());
((ConfigEntry) info).uiSetWithoutSaving(((ConfigEntry<?>) info).getDefaultValue());
((EntryInfo) info.guiValue).index = 0;
this.reload = true;
Objects.requireNonNull(minecraft).setScreen(this);
};
int a = this.width - ConfigScreenConfigs.SpaceFromRightScreen - 150 - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth;
int b = 0;
int c = ConfigScreenConfigs.ResetButtonWidth;
int d = 20;
int posX = this.width - ConfigScreenConfigs.SpaceFromRightScreen - 150 - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth;
int posZ = 0;
Button resetButton = MakeBtn(Translatable("distanthorizons.general.reset").withStyle(ChatFormatting.RED), a, b, c, d, btnAction);
Button resetButton = MakeBtn(Translatable("distanthorizons.general.reset").withStyle(ChatFormatting.RED),
posX, posZ, ConfigScreenConfigs.ResetButtonWidth, ConfigScreenConfigs.ResetButtonHeight,
btnAction);
if (((EntryInfo) info.guiValue).widget instanceof Map.Entry)
{
@@ -358,9 +376,7 @@ public class ClassicConfigGUI
}
if (ConfigUIButton.class.isAssignableFrom(info.getClass()))
{
Button widget = MakeBtn(name, this.width / 2 - 100, this.height - 28, 100 * 2, 20, (button -> {
((ConfigUIButton) info).runAction();
}));
Button widget = MakeBtn(name, this.width / 2 - 100, this.height - 28, 100 * 2, 20, (button -> ((ConfigUIButton) info).runAction()));
this.list.addButton(widget, null, null, null);
return;
}
@@ -379,13 +395,13 @@ public class ClassicConfigGUI
}
@Override
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
#else
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#endif
{
#if PRE_MC_1_20_2 // 1.20.2 now enables this by default in the `this.list.render` function
#if MC_VER < MC_1_20_2 // 1.20.2 now enables this by default in the `this.list.render` function
this.renderBackground(matrices); // Renders background
#else
super.render(matrices, mouseX, mouseY, delta);
@@ -428,7 +444,9 @@ public class ClassicConfigGUI
String key = translationPrefix + (newInfo.category.isEmpty() ? "" : newInfo.category + ".") + newInfo.getName() + ".@tooltip";
if (((EntryInfo) newInfo.guiValue).error != null && text.equals(name))
{
DhRenderTooltip(matrices, font, ((EntryInfo) newInfo.guiValue).error.getValue(), mouseX, mouseY);
}
else if (I18n.exists(key) && (text != null && text.equals(name)))
{
List<Component> list = new ArrayList<>();
@@ -441,7 +459,7 @@ public class ClassicConfigGUI
}
}
}
#if PRE_MC_1_20_2
#if MC_VER < MC_1_20_2
super.render(matrices, mouseX, mouseY, delta);
#endif
}
@@ -537,9 +555,13 @@ public class ClassicConfigGUI
{
Font textRenderer;
public ConfigListWidget(Minecraft minecraftClient, int i, int j, int k, int l, int m)
public ConfigListWidget(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing)
{
super(minecraftClient, i, j, k, l, m);
#if MC_VER < MC_1_20_4
super(minecraftClient, canvasWidth, canvasHeight, topMargin, canvasHeight - botMargin, itemSpacing);
#else
super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing);
#endif
this.centerListVertically = false;
textRenderer = minecraftClient.font;
}
@@ -601,7 +623,7 @@ public class ClassicConfigGUI
}
@Override
#if PRE_MC_1_20_1
#if 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)
#else
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
@@ -623,7 +645,7 @@ public class ClassicConfigGUI
indexButton.render(matrices, mouseX, mouseY, tickDelta);
}
if (text != null && (!text.getString().contains("spacer") || button != null))
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF);
#else
matrices.drawString(textRenderer, text, 12, y + 5, 0xFFFFFF);
@@ -638,7 +660,7 @@ public class ClassicConfigGUI
// Only for 1.17 and over
// Remove in 1.16 and below
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
@Override
public List<? extends NarratableEntry> narratables()
{
@@ -1,7 +1,7 @@
package com.seibel.distanthorizons.common.wrappers.gui;
import net.minecraft.client.gui.Font;
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
#else
import net.minecraft.client.gui.GuiGraphics;
@@ -24,14 +24,14 @@ public class DhScreen extends Screen
// addButton in 1.16 and below
protected Button addBtn(Button button)
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
return this.addButton(button);
#else
return this.addRenderableWidget(button);
#endif
}
#if PRE_MC_1_20_1
#if 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);
@@ -25,19 +25,18 @@ public class GetConfigScreen
// This shouldn't be here, but I need a way to test it after Minecraft inits its assets
//System.out.println(ConfigBase.INSTANCE.generateLang(false, true));
switch (useScreen)
return switch (useScreen)
{
case Classic:
return ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client");
case OpenGL:
case Classic -> ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client");
case OpenGL ->
{
MinecraftScreen.getScreen(parent, new OpenGLConfigScreen(), ModInfo.ID + ".title");
return null;
yield null;
}
// case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title");
case JavaFX:
return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
default:
return null;
}
case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
default -> throw new IllegalArgumentException("No config screen implementation defined for [" + useScreen + "].");
};
}
}
@@ -5,7 +5,7 @@ import net.minecraft.client.gui.components.Button;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
#endif
@@ -15,18 +15,18 @@ public class GuiHelper
/**
* Helper static methods for versional compat
*/
public static Button MakeBtn(Component base, int a, int b, int c, int d, Button.OnPress action)
public static Button MakeBtn(Component base, int posX, int posZ, int width, int height, Button.OnPress action)
{
#if PRE_MC_1_19_4
return new Button(a, b, c, d, base, action);
#if MC_VER < MC_1_19_4
return new Button(posX, posZ, width, height, base, action);
#else
return Button.builder(base, action).bounds(a, b, c, d).build();
return Button.builder(base, action).bounds(posX, posZ, width, height).build();
#endif
}
public static MutableComponent TextOrLiteral(String text)
{
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
return new TextComponent(text);
#else
return Component.literal(text);
@@ -35,7 +35,7 @@ public class GuiHelper
public static MutableComponent TextOrTranslatable(String text)
{
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
return new TextComponent(text);
#else
return Component.translatable(text);
@@ -44,7 +44,7 @@ public class GuiHelper
public static MutableComponent Translatable(String text, Object... args)
{
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
return new TranslatableComponent(text, args);
#else
return Component.translatable(text, args);
@@ -53,7 +53,7 @@ public class GuiHelper
public static void SetX(AbstractWidget w, int x)
{
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
w.x = x;
#else
w.setX(x);
@@ -62,7 +62,7 @@ public class GuiHelper
public static void SetY(AbstractWidget w, int y)
{
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
w.y = y;
#else
w.setY(y);
@@ -4,7 +4,7 @@ import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.distanthorizons.core.config.gui.AbstractScreen;
import net.minecraft.client.Minecraft;
#if POST_MC_1_20_1
#if MC_VER >= MC_1_20_1
import net.minecraft.client.gui.GuiGraphics;
#endif
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
@@ -28,7 +28,7 @@ public class MinecraftScreen
private AbstractScreen screen;
#if PRE_MC_1_19_2
#if 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);
@@ -59,23 +59,27 @@ public class MinecraftScreen
screen.scaledHeight = this.height;
screen.init(); // Init our own config screen
this.list = new ConfigListWidget(this.minecraft, this.width, this.height, 0, this.height, 25); // Select the area to tint
this.list = new ConfigListWidget(this.minecraft, this.width, this.height, 0, 0, 25); // Select the area to tint
#if 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.list.setRenderBackground(false); // Disable from rendering
#endif
this.addWidget(this.list); // Add the tint to the things to be rendered
}
@Override
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
#else
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#endif
{
#if MC_1_20_2
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#else
#if MC_VER < MC_1_20_2
this.renderBackground(matrices); // Render background
#else
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#endif
this.list.render(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
@@ -131,9 +135,13 @@ public class MinecraftScreen
public static class ConfigListWidget extends ContainerObjectSelectionList
{
public ConfigListWidget(Minecraft minecraftClient, int i, int j, int k, int l, int m)
public ConfigListWidget(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing)
{
super(minecraftClient, i, j, k, l, m);
#if MC_VER < MC_1_20_4
super(minecraftClient, canvasWidth, canvasHeight, topMargin, canvasHeight - botMargin, itemSpacing);
#else
super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing);
#endif
this.centerListVertically = false;
}
@@ -34,17 +34,17 @@ import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
import net.minecraft.client.renderer.GameRenderer;
#endif
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
#else
import net.minecraft.client.gui.GuiGraphics;
#endif
#if PRE_MC_1_20_2
#if MC_VER < MC_1_20_2
public class TexturedButtonWidget extends ImageButton
#else
public class TexturedButtonWidget extends Button
@@ -52,7 +52,7 @@ public class TexturedButtonWidget extends Button
{
public final boolean renderBackground;
#if POST_MC_1_20_2
#if MC_VER >= MC_1_20_2
private final int u;
private final int v;
private final int hoveredVOffset;
@@ -69,7 +69,7 @@ public class TexturedButtonWidget extends Button
}
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground)
{
#if PRE_MC_1_20_2
#if MC_VER < MC_1_20_2
super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text);
#else
// We don't pass on the text option as otherwise it will render (we normally pass it for narration)
@@ -89,13 +89,13 @@ public class TexturedButtonWidget extends Button
this.renderBackground = renderBackground;
}
#if PRE_MC_1_20_2
#if PRE_MC_1_19_4
#if 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) {
if (this.renderBackground) // Renders the background of the button
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
Minecraft.getInstance().getTextureManager().bind(WIDGETS_LOCATION);
RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
#else
@@ -108,7 +108,7 @@ public class TexturedButtonWidget extends Button
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableDepthTest();
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
this.blit(matrices, this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height);
this.blit(matrices, this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
#else
@@ -120,7 +120,7 @@ public class TexturedButtonWidget extends Button
super.renderButton(matrices, mouseX, mouseY, delta);
}
#else
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
@Override
public void renderWidget(PoseStack matrices, int mouseX, int mouseY, float delta)
{
@@ -138,7 +138,7 @@ public class TexturedButtonWidget extends Button
if (!this.active) i = 0;
else if (this.isHovered) i = 2;
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableDepthTest();
@@ -15,11 +15,11 @@ import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
import net.minecraft.client.gui.narration.NarratableEntry;
#endif
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
import net.minecraft.client.gui.GuiComponent;
#else
import net.minecraft.client.gui.GuiGraphics;
@@ -101,11 +101,17 @@ public class ChangelogScreen extends DhScreen
this.changelog.add("");
this.changelog.add("");
String changelog = ModrinthGetter.changeLogs.get(versionID);
if (changelog == null)
{
// in case something goes wrong this will prevent null pointers
changelog = "";
}
// Get the release changelog and split it by the new lines
String[] unwrappedChangelog = // Arrays.asList could be used if a list object is desired here vs List.of which is only available for Java 9+
new MarkdownFormatter.MinecraftFormat().convertTo( // This formats markdown to minecraft's "§" characters
ModrinthGetter.changeLogs.get(versionID)
).split("\\n");
// This formats markdown to minecraft's "§" charactersnew MarkdownFormatter.MinecraftFormat().convertTo(
new MarkdownFormatter.MinecraftFormat().convertTo(changelog).split("\\n");
// Makes the words wrap around to not go off the screen
for (String str : unwrappedChangelog)
{
@@ -128,13 +134,11 @@ public class ChangelogScreen extends DhScreen
this.addBtn( // Close
MakeBtn(Translatable(ModInfo.ID + ".general.back"), 5, this.height - 25, 100, 20, (btn) -> {
this.onClose();
})
MakeBtn(Translatable(ModInfo.ID + ".general.back"), 5, this.height - 25, 100, 20, (btn) -> this.onClose())
);
this.changelogArea = new TextArea(this.minecraft, this.width * 2, this.height, 32, this.height - 32, 10);
this.changelogArea = new TextArea(this.minecraft, this.width * 2, this.height, 32, 32, 10);
for (int i = 0; i < changelog.size(); i++)
{
this.changelogArea.addButton(TextOrLiteral(changelog.get(i)));
@@ -144,16 +148,16 @@ public class ChangelogScreen extends DhScreen
}
@Override
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
#else
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#endif
{
#if MC_1_20_2
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#else
#if MC_VER < MC_1_20_2
this.renderBackground(matrices); // Render background
#else
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#endif
if (!usable)
return;
@@ -161,17 +165,16 @@ public class ChangelogScreen extends DhScreen
// Set the scroll position to the mouse height relative to the screen
// This is a bit of a hack as we cannot scroll on this area
double scrollAmount = ((double) mouseY) / ((double) this.height) * 1.1 * this.changelogArea.getMaxScroll();
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
this.changelogArea.setScrollAmount(scrollAmount);
#else
this.changelogArea.scrollAmount = scrollAmount;
#endif
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
// render order matters, otherwise on 1.20.6+ the blurred background will render on top of the text
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
DhDrawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title
}
@@ -185,9 +188,13 @@ public class ChangelogScreen extends DhScreen
{
Font textRenderer;
public TextArea(Minecraft minecraftClient, int i, int j, int k, int l, int m)
public TextArea(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing)
{
super(minecraftClient, i, j, k, l, m);
#if MC_VER < MC_1_20_4
super(minecraftClient, canvasWidth, canvasHeight, topMargin, canvasHeight - botMargin, itemSpacing);
#else
super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing);
#endif
this.centerListVertically = false;
textRenderer = minecraftClient.font;
}
@@ -221,7 +228,7 @@ public class ChangelogScreen extends DhScreen
return new ButtonEntry(text);
}
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
@Override
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
{
@@ -240,7 +247,7 @@ public class ChangelogScreen extends DhScreen
{
return children;
}
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
@Override
public List<? extends NarratableEntry> narratables()
{
@@ -1,23 +1,19 @@
package com.seibel.distanthorizons.common.wrappers.gui.updater;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.api.enums.config.EUpdateBranch;
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
import com.seibel.distanthorizons.core.jar.ModJarInfo;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.jar.JarUtils;
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import net.minecraft.client.Minecraft;
#if POST_MC_1_20_1
#if MC_VER >= MC_1_20_1
import net.minecraft.client.gui.GuiGraphics;
#else
import com.mojang.blaze3d.vertex.PoseStack;
#endif
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.resources.ResourceLocation;
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
@@ -45,16 +41,18 @@ public class UpdateModScreen extends DhScreen
super(Translatable(ModInfo.ID + ".updater.title"));
this.parent = parent;
this.newVersionID = newVersionID;
switch (Config.Client.Advanced.AutoUpdater.updateBranch.get()) {
case STABLE:
currentVer = ModInfo.VERSION;
nextVer = ModrinthGetter.releaseNames.get(this.newVersionID);
break;
case NIGHTLY:
currentVer = ModJarInfo.Git_Commit.substring(0,7);
nextVer = this.newVersionID.substring(0,7);
break;
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
if (updateBranch == EDhApiUpdateBranch.STABLE)
{
this.currentVer = ModInfo.VERSION;
this.nextVer = ModrinthGetter.releaseNames.get(this.newVersionID);
}
else
{
this.currentVer = ModJarInfo.Git_Commit.substring(0,7);
this.nextVer = this.newVersionID.substring(0,7);
}
}
@@ -66,13 +64,6 @@ public class UpdateModScreen extends DhScreen
try
{
// We cannot get assets from the root of the mod so we use this hack
// TODO: Load the icon.png and logo.png in the mod initialise rather than here
ResourceLocation logoLocation = new ResourceLocation(ModInfo.ID, "logo.png");
Minecraft.getInstance().getTextureManager().register(
logoLocation,
new DynamicTexture(NativeImage.read(JarUtils.accessFile("logo.png")))
);
// Logo image
@@ -84,7 +75,13 @@ public class UpdateModScreen extends DhScreen
// Offset
0, 0,
// Some textuary stuff
0, logoLocation, 130, 65,
0,
#if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "logo.png"),
#else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
#endif
130, 65,
// Create the button and tell it where to go
// For now it goes to the client option by default
(buttonWidget) -> System.out.println("Nice, you found an easter egg :)"), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
@@ -99,7 +96,7 @@ public class UpdateModScreen extends DhScreen
e.printStackTrace();
}
if (Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE)
if (!ModInfo.IS_DEV_BUILD)
{
this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen
@@ -109,7 +106,13 @@ public class UpdateModScreen extends DhScreen
// Offset
0, 0,
// Some textuary stuff
0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20,
0,
#if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
#endif
20, 20,
// Create the button and tell it where to go
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
// Add a title to the button
@@ -132,9 +135,7 @@ public class UpdateModScreen extends DhScreen
})
);
this.addBtn( // Later (not now)
MakeBtn(Translatable(ModInfo.ID + ".updater.later"), this.width / 2 + 2, this.height / 2 + 70, 100, 20, (btn) -> {
this.onClose();
})
MakeBtn(Translatable(ModInfo.ID + ".updater.later"), this.width / 2 + 2, this.height / 2 + 70, 100, 20, (btn) -> this.onClose())
);
this.addBtn( // Never
MakeBtn(Translatable(ModInfo.ID + ".updater.never"), this.width / 2 - 102, this.height / 2 + 70, 100, 20, (btn) -> {
@@ -146,29 +147,27 @@ public class UpdateModScreen extends DhScreen
}
@Override
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
#else
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#endif
{
#if MC_1_20_2
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#else
#if MC_VER < MC_1_20_2
this.renderBackground(matrices); // Render background
#else
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#endif
// TODO: add the tooltips for the buttons
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
// TODO: Add tooltips
// Render the text's
DhDrawCenteredString(matrices, this.font, Translatable(ModInfo.ID + ".updater.text1"), this.width / 2, this.height / 2 - 35, 0xFFFFFF);
DhDrawCenteredString(matrices, this.font,
Translatable(ModInfo.ID + ".updater.text2", currentVer, nextVer),
this.width / 2, this.height / 2 - 20, 0x52FD52);
// TODO: add the tooltips for the buttons
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
// TODO: Add tooltips
}
@Override
@@ -25,12 +25,13 @@ import java.util.ArrayList;
import java.util.UUID;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.api.enums.config.ELodShading;
import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -43,11 +44,12 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import net.minecraft.CrashReport;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
import net.minecraft.network.chat.TextComponent;
#endif
import net.minecraft.server.level.ServerLevel;
@@ -59,16 +61,15 @@ import org.jetbrains.annotations.Nullable;
* A singleton that wraps the Minecraft object.
*
* @author James Seibel
* @version 3-5-2022
*/
//@Environment(EnvType.CLIENT)
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private static final Minecraft MINECRAFT = Minecraft.getInstance();
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
public final Minecraft mc = Minecraft.getInstance();
/**
* The lightmap for the current:
@@ -99,10 +100,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
* This doesn't affect OpenGL objects in any way.
*/
@Override
public void clearFrameObjectCache()
{
lightMap = null;
}
public void clearFrameObjectCache() { this.lightMap = null; }
@@ -113,140 +111,140 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public float getShade(EDhDirection lodDirection)
{
ELodShading lodShading = Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading.get();
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading.get();
switch (lodShading)
{
default:
case MINECRAFT:
if (this.mc.level != null)
case AUTO:
if (MINECRAFT.level != null)
{
Direction mcDir = McObjectConverter.Convert(lodDirection);
return this.mc.level.getShade(mcDir, true);
return MINECRAFT.level.getShade(mcDir, true);
}
else
{
return 0.0f;
}
case OLD_LIGHTING:
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 ENABLED:
return switch (lodDirection) {
case DOWN -> 0.5F;
default -> 1.0F;
case NORTH, SOUTH -> 0.8F;
case WEST, EAST -> 0.6F;
};
case NONE:
case DISABLED:
return 1.0F;
}
}
@Override
public boolean hasSinglePlayerServer() { return mc.hasSingleplayerServer(); }
public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); }
@Override
public boolean clientConnectedToDedicatedServer() { return mc.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
public boolean clientConnectedToDedicatedServer() { return MINECRAFT.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
@Override
public boolean connectedToReplay() { return !MINECRAFT.hasSingleplayerServer() && MINECRAFT.getCurrentServer() == null; }
@Override
public String getCurrentServerName() { return mc.getCurrentServer().name; }
public String getCurrentServerName()
{
if (this.connectedToReplay())
{
return ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME;
}
else
{
ServerData server = MINECRAFT.getCurrentServer();
return (server != null) ? server.name : "NULL";
}
}
@Override
public String getCurrentServerIp() { return mc.getCurrentServer().ip; }
public String getCurrentServerIp()
{
if (this.connectedToReplay())
{
return "";
}
else
{
ServerData server = MINECRAFT.getCurrentServer();
return (server != null) ? server.ip : "NA";
}
}
@Override
public String getCurrentServerVersion()
{
return mc.getCurrentServer().version.getString();
ServerData server = MINECRAFT.getCurrentServer();
return (server != null) ? server.version.getString() : "UNKOWN";
}
//=============//
// Simple gets //
//=============//
public LocalPlayer getPlayer()
{
return mc.player;
}
public LocalPlayer getPlayer() { return MINECRAFT.player; }
@Override
public boolean playerExists()
{
return mc.player != null;
}
public boolean playerExists() { return MINECRAFT.player != null; }
@Override
public UUID getPlayerUUID()
{
return getPlayer().getUUID();
}
public UUID getPlayerUUID() { return this.getPlayer().getUUID(); }
@Override
public DhBlockPos getPlayerBlockPos()
{
BlockPos playerPos = getPlayer().blockPosition();
BlockPos playerPos = this.getPlayer().blockPosition();
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
}
@Override
public DhChunkPos getPlayerChunkPos()
{
#if PRE_MC_1_17_1
ChunkPos playerPos = new ChunkPos(getPlayer().blockPosition());
#if MC_VER < MC_1_17_1
ChunkPos playerPos = new ChunkPos(this.getPlayer().blockPosition());
#else
ChunkPos playerPos = getPlayer().chunkPosition();
ChunkPos playerPos = this.getPlayer().chunkPosition();
#endif
return new DhChunkPos(playerPos.x, playerPos.z);
}
public ModelManager getModelManager()
{
return mc.getModelManager();
}
@Nullable
@Override
public IClientLevelWrapper getWrappedClientLevel()
{
if (this.mc.level == null)
if (MINECRAFT.level == null)
{
return null;
}
return ClientLevelWrapper.getWrapperIgnoringOverride(this.mc.level);
}
/** Please move over to getInstallationDirectory() */
@Deprecated
@Override
public File getGameDirectory()
{
return getInstallationDirectory();
return ClientLevelWrapper.getWrapperIgnoringOverride(MINECRAFT.level);
}
@Override
public IProfilerWrapper getProfiler()
{
if (profilerWrapper == null)
profilerWrapper = new ProfilerWrapper(mc.getProfiler());
else if (mc.getProfiler() != profilerWrapper.profiler)
profilerWrapper.profiler = mc.getProfiler();
return profilerWrapper;
if (this.profilerWrapper == null)
{
this.profilerWrapper = new ProfilerWrapper(MINECRAFT.getProfiler());
}
else if (MINECRAFT.getProfiler() != this.profilerWrapper.profiler)
{
this.profilerWrapper.profiler = MINECRAFT.getProfiler();
}
return this.profilerWrapper;
}
/** Returns all worlds available to the server */
@Override
public ArrayList<ILevelWrapper> getAllServerWorlds()
{
ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>();
ArrayList<ILevelWrapper> worlds = new ArrayList<>();
Iterable<ServerLevel> serverWorlds = mc.getSingleplayerServer().getAllLevels();
Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels();
for (ServerLevel world : serverWorlds)
{
worlds.add(ServerLevelWrapper.getWrapper(world));
@@ -260,12 +258,12 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public void sendChatMessage(String string)
{
LocalPlayer p = getPlayer();
if (p == null) return;
#if PRE_MC_1_19_2
p.sendMessage(new TextComponent(string), getPlayer().getUUID());
LocalPlayer player = this.getPlayer();
if (player == null) return;
#if MC_VER < MC_1_19_2
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
#else
p.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
player.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
#endif
}
@@ -282,28 +280,23 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
{
LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception);
CrashReport report = new CrashReport(errorMessage, exception);
#if MC_VER < MC_1_20_4
Minecraft.crash(report);
#else
Minecraft.getInstance().delayCrash(report);
#endif
}
@Override
public Object getOptionsObject()
{
return mc.options;
}
public Object getOptionsObject() { return MINECRAFT.options; }
@Override
public boolean isDedicatedServer()
{
return false;
}
public boolean isDedicatedServer() { return false; }
@Override
public File getInstallationDirectory()
{
return mc.gameDirectory;
}
public File getInstallationDirectory() { return MINECRAFT.gameDirectory; }
@Override
public void executeOnRenderThread(Runnable runnable) { this.mc.execute(runnable); }
public void executeOnRenderThread(Runnable runnable) { MINECRAFT.execute(runnable); }
}
@@ -12,16 +12,20 @@ public class MinecraftDedicatedServerWrapper implements IMinecraftSharedWrapper
private MinecraftDedicatedServerWrapper() { }
public DedicatedServer dedicatedServer = null;
@Override
public boolean isDedicatedServer()
{
return true;
}
public boolean isDedicatedServer() { return true; }
@Override
public File getInstallationDirectory()
{
if (dedicatedServer == null)
if (this.dedicatedServer == null)
{
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server complete initialization!");
return dedicatedServer.getServerDirectory();
}
#if MC_VER < MC_1_21
return this.dedicatedServer.getServerDirectory();
#else
return this.dedicatedServer.getServerDirectory().toFile();
#endif
}
}
@@ -22,8 +22,8 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
import java.awt.Color;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import com.mojang.blaze3d.pipeline.RenderTarget;
@@ -36,15 +36,16 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
#if PRE_MC_1_19_4
import com.mojang.math.Vector3f;
#if MC_VER < MC_1_19_4
import org.joml.Matrix4f;
import org.joml.Vector3f;
#else
import org.joml.Matrix4f;
import org.joml.Vector3f;
#endif
#if MC_1_20_2
#if MC_VER >= MC_1_20_2
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
#endif
@@ -52,9 +53,9 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOpt
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.coreapi.util.math.Mat4f;
import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
@@ -67,7 +68,7 @@ import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.world.effect.MobEffects;
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.material.FluidState;
@@ -102,7 +103,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
* In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps. <br>
* Requiring the use of multiple {@link LightMapWrapper}.
*/
public HashMap<IDimensionTypeWrapper, LightMapWrapper> lightmapByDimensionType = new HashMap<>();
public ConcurrentHashMap<IDimensionTypeWrapper, LightMapWrapper> lightmapByDimensionType = new ConcurrentHashMap<>();
/**
* Holds the render buffer that should be used when displaying levels to the screen.
@@ -116,8 +117,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
public Vec3f getLookAtVector()
{
Camera camera = MC.gameRenderer.getMainCamera();
Vector3f cameraDir = camera.getLookVector();
return new Vec3f(cameraDir.x(), cameraDir.y(), cameraDir.z());
return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z());
}
@Override
@@ -133,7 +133,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
public boolean playerHasBlindingEffect()
{
return MC.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null
#if POST_AND_MC_1_19_2
#if MC_VER >= MC_1_19_2
|| MC.player.getActiveEffectsMap().get(MobEffects.DARKNESS) != null // Deep dark effect
#endif
;
@@ -148,10 +148,27 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
}
@Override
public Mat4f getWorldViewMatrix()
{
Camera camera = MC.gameRenderer.getMainCamera();
Vector3f cameraVec3 = new Vector3f(
(float)camera.getPosition().x,
(float)camera.getPosition().y,
(float)camera.getPosition().z);
cameraVec3 = cameraVec3.negate();
Matrix4f matWorldView = new Matrix4f()
.rotateX((float)Math.toRadians(camera.getXRot()))
.rotateY((float)Math.toRadians(camera.getYRot() + 180f))
.translate(cameraVec3);
return new Mat4f(matWorldView);
}
@Override
public Mat4f getDefaultProjectionMatrix(float partialTicks)
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
return McObjectConverter.Convert(Minecraft.getInstance().gameRenderer.getProjectionMatrix(Minecraft.getInstance().gameRenderer.getMainCamera(), partialTicks, true));
#else
return McObjectConverter.Convert(MC.gameRenderer.getProjectionMatrix(MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true)));
@@ -161,7 +178,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public double getGamma()
{
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
return MC.options.gamma;
#else
return MC.options.gamma().get();
@@ -171,7 +188,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public Color getFogColor(float partialTicks)
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
float[] colorValues = new float[4];
GL15.glGetFloatv(GL15.GL_FOG_COLOR, colorValues);
#else
@@ -192,15 +209,24 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
{
if (MC.level.dimensionType().hasSkyLight())
{
#if PRE_MC_1_17_1
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), MC.getFrameTime());
float frameTime;
#if MC_VER < MC_1_21
frameTime = MC.getFrameTime();
#else
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), MC.getFrameTime());
frameTime = MC.getTimer().getRealtimeDeltaTicks();
#endif
#if MC_VER < MC_1_17_1
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), frameTime);
#else
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
#endif
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
}
else
{
return new Color(0, 0, 0);
}
}
@Override
@@ -213,7 +239,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public int getRenderDistance()
{
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
//FIXME: How to resolve this?
return MC.options.renderDistance;
#else
@@ -261,12 +287,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public int getTargetFrameBuffer()
{
int frameBufferOverrideId = DhApiRenderProxy.INSTANCE.targetFrameBufferOverride;
if (frameBufferOverrideId != -1)
{
return frameBufferOverrideId;
}
// used so we can access the framebuffer shaders end up rendering to
if (AbstractOptifineAccessor.optifinePresent())
{
@@ -321,25 +341,25 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
{
try
{
#if MC_1_20_2
#if MC_VER < MC_1_20_2
LevelRenderer levelRenderer = MC.levelRenderer;
Collection<SectionRenderDispatcher.RenderSection> chunks = levelRenderer.visibleSections;
Collection<LevelRenderer.RenderChunkInfo> chunks =
#if MC_VER < MC_1_18_2 levelRenderer.renderChunks;
#else levelRenderer.renderChunkStorage.get().renderChunks; #endif
return (chunks.stream().map((chunk) -> {
AABB chunkBoundingBox = chunk.getBoundingBox();
AABB chunkBoundingBox =
#if MC_VER < MC_1_18_2 chunk.chunk.bb;
#else chunk.chunk.getBoundingBox(); #endif
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
}).collect(Collectors.toCollection(HashSet::new)));
#else
LevelRenderer levelRenderer = MC.levelRenderer;
Collection<LevelRenderer.RenderChunkInfo> chunks =
#if PRE_MC_1_18_2 levelRenderer.renderChunks;
#else levelRenderer.renderChunkStorage.get().renderChunks; #endif
Collection<SectionRenderDispatcher.RenderSection> chunks = levelRenderer.visibleSections;
return (chunks.stream().map((chunk) -> {
AABB chunkBoundingBox =
#if PRE_MC_1_18_2 chunk.chunk.bb;
#else chunk.chunk.getBoundingBox(); #endif
AABB chunkBoundingBox = chunk.getBoundingBox();
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
}).collect(Collectors.toCollection(HashSet::new)));
@@ -371,7 +391,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public boolean isFogStateSpecial()
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
FluidState fluidState = camera.getFluidInCamera();
Entity entity = camera.getEntity();
@@ -392,11 +412,8 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
// so this will have to do for now
IDimensionTypeWrapper dimensionType = level.getDimensionType();
if (!this.lightmapByDimensionType.containsKey(dimensionType))
{
this.lightmapByDimensionType.put(dimensionType, new LightMapWrapper());
}
this.lightmapByDimensionType.get(dimensionType).uploadLightmap(lightPixels);
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
wrapper.uploadLightmap(lightPixels);
}
}
@@ -25,38 +25,45 @@ import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer;
/**
* @author James Seibel
* @version 11-21-2021
*/
public class LightMapWrapper implements ILightMapWrapper
{
private int textureId = 0;
public LightMapWrapper()
{
}
private void createLightmap(NativeImage image)
{
textureId = GL32.glGenTextures();
GL32.glBindTexture(GL32.GL_TEXTURE_2D, textureId);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
}
//==============//
// constructors //
//==============//
public LightMapWrapper() { }
//=========//
// methods //
//=========//
public void uploadLightmap(NativeImage image)
{
int currentBind = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, textureId);
if (textureId == 0)
if (this.textureId == 0)
{
createLightmap(image);
this.createLightmap(image);
}
else
{
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
}
// NativeImage::upload(int levelOfDetail, int xOffset, int yOffset, bool shouldCleanup?)
image.upload(0, 0, 0, false);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, currentBind);
}
private void createLightmap(NativeImage image)
{
this.textureId = GL32.glGenTextures();
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
}
@Override
public void bind()
@@ -66,9 +73,6 @@ public class LightMapWrapper implements ILightMapWrapper
}
@Override
public void unbind()
{
GL32.glBindTexture(GL32.GL_TEXTURE_2D, 0);
}
public void unbind() { GL32.glBindTexture(GL32.GL_TEXTURE_2D, 0); }
}
@@ -33,7 +33,7 @@ public class ServerPlayerWrapper implements IServerPlayerWrapper
public IServerLevelWrapper getLevel()
{
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
return ServerLevelWrapper.getWrapper(this.serverPlayer.getLevel());
#else
return ServerLevelWrapper.getWrapper(this.serverPlayer.serverLevel());
@@ -1,7 +1,7 @@
package com.seibel.distanthorizons.common.wrappers.world;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper;
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;
@@ -9,7 +9,7 @@ import com.seibel.distanthorizons.common.wrappers.block.cache.ClientBlockDetailM
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.level.*;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
@@ -19,28 +19,40 @@ 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;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public class ClientLevelWrapper implements IClientLevelWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName());
private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>(); // TODO can leak
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
private static final Minecraft MINECRAFT = Minecraft.getInstance();
private final ClientLevel level;
private final ClientBlockDetailMap blockMap = new ClientBlockDetailMap(this);
private BlockStateWrapper dirtBlockWrapper;
private BiomeWrapper plainsBiomeWrapper;
@Deprecated // TODO circular references are bad
private IDhLevel parentDhLevel;
//=============//
@@ -79,7 +91,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
{
try
{
Iterable<ServerLevel> serverLevels = MinecraftClientWrapper.INSTANCE.mc.getSingleplayerServer().getAllLevels();
Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels();
// attempt to find the server level with the same dimension type
// TODO this assumes only one level per dimension type, the SubDimensionLevelMatcher will need to be added for supporting multiple levels per dimension
@@ -116,6 +128,46 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return this.blockMap.getColor(((BlockStateWrapper) blockState).blockState, (BiomeWrapper) biome, pos);
}
@Override
public int getDirtBlockColor()
{
if (this.dirtBlockWrapper == null)
{
try
{
this.dirtBlockWrapper = (BlockStateWrapper) BlockStateWrapper.deserialize(BlockStateWrapper.DIRT_RESOURCE_LOCATION_STRING, this);
}
catch (IOException e)
{
// shouldn't happen, but just in case
LOGGER.warn("Unable to get dirt color with resource location ["+BlockStateWrapper.DIRT_RESOURCE_LOCATION_STRING+"] with level ["+this+"].", e);
return -1;
}
}
return this.blockMap.getColor(this.dirtBlockWrapper.blockState, BiomeWrapper.EMPTY_WRAPPER, DhBlockPos.ZERO);
}
@Override
public IBiomeWrapper getPlainsBiomeWrapper()
{
if (this.plainsBiomeWrapper == null)
{
try
{
this.plainsBiomeWrapper = (BiomeWrapper) BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, this);
}
catch (IOException e)
{
// shouldn't happen, but just in case
LOGGER.warn("Unable to get planes biome with resource location ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"] with level ["+this+"].", e);
return null;
}
}
return this.plainsBiomeWrapper;
}
@Override
public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
@@ -131,12 +183,12 @@ public class ClientLevelWrapper implements IClientLevelWrapper
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
@Override
public int getHeight() { return this.level.getHeight(); }
public int getMaxHeight() { return this.level.getHeight(); }
@Override
public int getMinHeight()
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
return 0;
#else
return this.level.getMinBuildHeight();
@@ -180,7 +232,37 @@ public class ClientLevelWrapper implements IClientLevelWrapper
public ClientLevel getWrappedMcObject() { return this.level; }
@Override
public void onUnload() { LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level); }
public void onUnload()
{
LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level);
this.parentDhLevel = null;
}
//===================//
// generic rendering //
//===================//
@Override
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; }
@Override
public IDhApiCustomRenderRegister getRenderRegister()
{
if (this.parentDhLevel == null)
{
return null;
}
return this.parentDhLevel.getGenericRenderer();
}
//================//
// base overrides //
//================//
@Override
public String toString()
@@ -23,12 +23,14 @@ import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
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.cache.ServerBlockDetailMap;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
@@ -41,7 +43,12 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
#if 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.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
@@ -54,8 +61,9 @@ public class ServerLevelWrapper implements IServerLevelWrapper
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final ConcurrentHashMap<ServerLevel, ServerLevelWrapper> LEVEL_WRAPPER_BY_SERVER_LEVEL = new ConcurrentHashMap<>();
final ServerLevel level;
ServerBlockDetailMap blockMap = new ServerBlockDetailMap(this);
private final ServerLevel level;
@Deprecated // TODO circular references are bad
private IDhLevel parentDhLevel;
@@ -76,19 +84,6 @@ public class ServerLevelWrapper implements IServerLevelWrapper
// methods //
//=========//
@Nullable
@Override
public IClientLevelWrapper tryGetClientLevelWrapper()
{
MinecraftClientWrapper client = MinecraftClientWrapper.INSTANCE;
if (client.mc.level == null)
{
return null;
}
return ClientLevelWrapper.getWrapper(client.mc.level);
}
@Override
public File getSaveFolder()
{
@@ -122,7 +117,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper
}
@Override
public int getHeight()
public int getMaxHeight()
{
return level.getHeight();
}
@@ -130,12 +125,13 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@Override
public int getMinHeight()
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
return 0;
#else
return level.getMinBuildHeight();
#endif
}
@Override
public IChunkWrapper tryGetChunk(DhChunkPos pos)
{
@@ -166,18 +162,33 @@ public class ServerLevelWrapper implements IServerLevelWrapper
}
@Override
public ServerLevel getWrappedMcObject()
{
return level;
}
public ServerLevel getWrappedMcObject() { return this.level; }
@Override
public void onUnload() { LEVEL_WRAPPER_BY_SERVER_LEVEL.remove(this.level); }
@Override
public String toString()
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; }
@Override
public IDhApiCustomRenderRegister getRenderRegister()
{
return "Wrapped{" + level.toString() + "@" + getDimensionType().getDimensionName() + "}";
if (this.parentDhLevel == null)
{
return null;
}
return this.parentDhLevel.getGenericRenderer();
}
//================//
// base overrides //
//================//
@Override
public String toString() { return "Wrapped{" + this.level.toString() + "@" + this.getDimensionType().getDimensionName() + "}"; }
}
@@ -35,6 +35,7 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.objects.EventTimer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -56,7 +57,7 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepStruc
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepStructureStart;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepSurface;
#if POST_MC_1_19_4
#if MC_VER >= MC_1_19_4
import net.minecraft.core.registries.Registries;
#else
import net.minecraft.core.Registry;
@@ -66,10 +67,10 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.chunk.storage.IOWorker;
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
import net.minecraft.world.level.levelgen.DebugLevelSource;
import net.minecraft.world.level.levelgen.FlatLevelSource;
@@ -78,6 +79,12 @@ import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.nbt.CompoundTag;
import org.apache.logging.log4j.LogManager;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
/*
Total: 3.135214124s
=====================================
@@ -103,8 +110,6 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
new ConfigBasedLogger(LogManager.getLogger("LodWorldGen"),
() -> Config.Client.Advanced.Logging.logWorldGenLoadEvent.get());
//TODO: Make actual proper support for StarLight
public static class PerfCalculator
{
private static final String[] TIME_NAMES = {
@@ -142,7 +147,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (index == -1) continue;
times.get(index).add(e.timeNs);
}
times.get(0).add(event.getTotalTimeNs());
times.getFirst().add(event.getTotalTimeNs());
}
public String toString()
@@ -288,6 +293,9 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
//=================//
// synchronization //
//=================//
public <T> T joinSync(CompletableFuture<T> future)
{
@@ -338,8 +346,11 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
}
else if (event.hasTimeout(Config.Client.Advanced.WorldGenerator.worldGenerationTimeoutLengthInSeconds.get(), TimeUnit.SECONDS))
{
EVENT_LOGGER.error("Batching World Generator: " + event + " timed out and terminated!");
EVENT_LOGGER.info("Dump PrefEvent: " + event.timer);
EVENT_LOGGER.warn(
"Batching World Generator: [" + event + "] timed out and terminated after ["+Config.Client.Advanced.WorldGenerator.worldGenerationTimeoutLengthInSeconds.get()+"] seconds. " +
"\nYour computer might be overloaded or your world gen mods might be causing world gen to take longer than expected. " +
"\nEither increase DH's world gen timeout or reduce your computer's CPU load.");
EVENT_LOGGER.debug("Dump PrefEvent: " + event.timer);
try
{
if (!event.terminate())
@@ -362,69 +373,11 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
}
}
private static ProtoChunk EmptyChunk(ServerLevel level, ChunkPos chunkPos)
{
return new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if POST_MC_1_17_1 , level #endif
#if POST_MC_1_18_2 , level.registryAccess().registryOrThrow(
#if PRE_MC_1_19_4
Registry.BIOME_REGISTRY
#else
Registries.BIOME
#endif
), null #endif
);
}
public ChunkAccess loadOrMakeChunk(ChunkPos chunkPos)
{
ServerLevel level = this.params.level;
CompoundTag chunkData = null;
try
{
// Warning: if multiple threads attempt to access this method at the same time,
// it can throw EOFExceptions that are caught and logged by Minecraft
//chunkData = level.getChunkSource().chunkMap.readChunk(chunkPos);
RegionFileStorage storage = this.params.level.getChunkSource().chunkMap.worker.storage;
RegionFileStorageExternalCache cache = this.getOrCreateRegionFileCache(storage);
chunkData = cache.read(chunkPos);
}
catch (Exception e)
{
LOAD_LOGGER.error("DistantHorizons: Couldn't load or make chunk " + chunkPos + ". Error: " + e.getMessage(), e);
}
if (chunkData == null)
{
return EmptyChunk(level, chunkPos);
}
else
{
try
{
LOAD_LOGGER.info("DistantHorizons: Loading chunk " + chunkPos + " from disk.");
return ChunkLoader.read(level, chunkPos, chunkData);
}
catch (Exception e)
{
LOAD_LOGGER.error("DistantHorizons: Couldn't load or make chunk " + chunkPos + ". Returning an empty chunk. Error: " + e.getMessage(), e);
return EmptyChunk(level, chunkPos);
}
}
}
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, MaxBorderNeeded - BorderNeeded.get(step));
}
//==================//
// world generation //
//==================//
public void generateLodFromList(GenerationEvent genEvent) throws InterruptedException
{
@@ -432,7 +385,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
ArrayGridList<ChunkWrapper> chunkWrapperList;
DhLitWorldGenRegion region;
DummyLightEngine lightEngine;
DummyLightEngine dummyLightEngine;
LightGetterAdaptor adaptor;
int borderSize = MaxBorderNeeded;
@@ -445,50 +398,102 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
ArrayGridList<ChunkAccess> totalChunks;
adaptor = new LightGetterAdaptor(this.params.level);
lightEngine = new DummyLightEngine(adaptor);
dummyLightEngine = new DummyLightEngine(adaptor);
EmptyChunkGenerator generator = (int x, int z) ->
//=============================//
// try getting existing chunks //
//=============================//
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos = new HashMap<>();
IEmptyChunkGeneratorFunc emptyChunkGeneratorFunc = (int x, int z) ->
{
ChunkPos chunkPos = new ChunkPos(x, z);
ChunkAccess target = null;
DhChunkPos dhChunkPos = new DhChunkPos(x, z);
ChunkAccess newChunk = null;
try
{
target = this.loadOrMakeChunk(chunkPos);
// get the chunk
CompoundTag chunkData = this.getChunkNbtData(chunkPos);
newChunk = this.loadOrMakeChunk(chunkPos, chunkData);
if (Config.Client.Advanced.LodBuilding.pullLightingForPregeneratedChunks.get())
{
// attempt to get chunk lighting
ChunkLoader.CombinedChunkLightStorage combinedLights = ChunkLoader.readLight(newChunk, chunkData);
if (combinedLights != null)
{
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
}
}
}
catch (RuntimeException e2)
catch (RuntimeException loadChunkError)
{
// Continue...
}
if (target == null)
if (newChunk == null)
{
target = new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if POST_MC_1_17_1 , params.level #endif
#if POST_MC_1_18_2 , params.biomes, null #endif
newChunk = new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if MC_VER >= MC_1_17_1 , this.params.level #endif
#if MC_VER >= MC_1_18_2 , this.params.biomes, null #endif
);
}
return target;
return newChunk;
};
totalChunks = new ArrayGridList<>(refSize, (x, z) -> emptyChunkGeneratorFunc.generate(x + refPosX, z + refPosZ));
totalChunks = new ArrayGridList<>(refSize, (x, z) -> generator.generate(x + refPosX, z + refPosZ));
int radius = refSize / 2;
int centerX = refPosX + radius;
int centerZ = refPosZ + radius;
ChunkAccess centerChunk = totalChunks.stream().filter(chunk -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ).findFirst().get();
genEvent.refreshTimeout();
region = new DhLitWorldGenRegion(params.level, lightEngine, totalChunks,
ChunkStatus.STRUCTURE_STARTS, refSize / 2, generator);
region = new DhLitWorldGenRegion(
centerX, centerZ,
centerChunk,
this.params.level, dummyLightEngine, totalChunks,
ChunkStatus.STRUCTURE_STARTS, radius, emptyChunkGeneratorFunc);
adaptor.setRegion(region);
genEvent.threadedParam.makeStructFeat(region, params);
genEvent.threadedParam.makeStructFeat(region, this.params);
//=======================//
// create chunk wrappers //
//=======================//
chunkWrapperList = new ArrayGridList<>(totalChunks.gridSize);
totalChunks.forEachPos((x, z) ->
{
ChunkAccess chunk = totalChunks.get(x, z);
if (chunk != null)
{
chunkWrapperList.set(x, z, new ChunkWrapper(chunk, region, serverlevel.getLevelWrapper()));
// wrap the chunk
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, region, this.serverlevel.getLevelWrapper());
chunkWrapperList.set(x, z, chunkWrapper);
// try setting the wrapper's lighting
if (chunkBlockLightingByDhPos.containsKey(chunkWrapper.getChunkPos()))
{
chunkWrapper.setBlockLightStorage(chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos()));
chunkWrapper.setSkyLightStorage(chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos()));
chunkWrapper.setUseDhLighting(true);
chunkWrapper.setIsDhLightCorrect(true);
}
}
});
//=================//
// generate chunks //
//=================//
this.generateDirect(genEvent, chunkWrapperList, borderSize, genEvent.targetGenerationStep, region);
genEvent.timer.nextEvent("cleanup");
}
@@ -507,7 +512,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
ChunkAccess target = wrappedChunk.getChunk();
if (target instanceof LevelChunk)
{
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
((LevelChunk) target).setLoaded(true);
#else
((LevelChunk) target).loaded = true;
@@ -519,8 +524,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
throw new RuntimeException("The generated chunk somehow has isLightCorrect() returning false");
}
boolean isFull = target.getStatus() == ChunkStatus.FULL || target instanceof LevelChunk;
#if POST_MC_1_18_2
boolean isFull = ChunkWrapper.getStatus(target) == ChunkStatus.FULL || target instanceof LevelChunk;
#if MC_VER >= MC_1_18_2
boolean isPartial = target.isOldNoiseGeneration();
#endif
if (isFull)
@@ -528,14 +533,14 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
LOAD_LOGGER.info("Detected full existing chunk at {}", target.getPos());
genEvent.resultConsumer.accept(wrappedChunk);
}
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
else if (isPartial)
{
LOAD_LOGGER.info("Detected old existing chunk at {}", target.getPos());
genEvent.resultConsumer.accept(wrappedChunk);
}
#endif
else if (target.getStatus() == ChunkStatus.EMPTY)
else if (ChunkWrapper.getStatus(target) == ChunkStatus.EMPTY)
{
genEvent.resultConsumer.accept(wrappedChunk);
}
@@ -554,6 +559,85 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
PREF_LOGGER.infoInc("{}", genEvent.timer);
}
}
private CompoundTag getChunkNbtData(ChunkPos chunkPos)
{
ServerLevel level = this.params.level;
CompoundTag chunkData = null;
try
{
IOWorker ioWorker = level.getChunkSource().chunkMap.worker;
#if MC_VER <= MC_1_18_2
chunkData = ioWorker.load(chunkPos);
#else
// timeout should prevent locking up the thread if the ioWorker dies or has issues
int maxGetTimeInSec = Config.Client.Advanced.WorldGenerator.worldGenerationTimeoutLengthInSeconds.get();
CompletableFuture<Optional<CompoundTag>> future = ioWorker.loadAsync(chunkPos);
try
{
Optional<CompoundTag> data = future.get(maxGetTimeInSec, TimeUnit.SECONDS);
if (data.isPresent())
{
chunkData = data.get();
}
}
catch (Exception e)
{
LOAD_LOGGER.warn("Unable to get chunk at pos ["+chunkPos+"] after ["+maxGetTimeInSec+"] milliseconds.", e);
future.cancel(true);
}
#endif
}
catch (Exception e)
{
LOAD_LOGGER.error("DistantHorizons: Couldn't load or make chunk " + chunkPos + ". Error: " + e.getMessage(), e);
}
return chunkData;
}
private ChunkAccess loadOrMakeChunk(ChunkPos chunkPos, CompoundTag chunkData)
{
ServerLevel level = this.params.level;
if (chunkData == null)
{
return CreateEmptyChunk(level, chunkPos);
}
else
{
try
{
LOAD_LOGGER.info("DistantHorizons: Loading chunk [" + chunkPos + "] from disk.");
return ChunkLoader.read(level, chunkPos, chunkData);
}
catch (Exception e)
{
LOAD_LOGGER.error(
"DistantHorizons: couldn't load or make chunk at ["+chunkPos+"]." +
"Please try optimizing your world to fix this issue. \n" +
"World optimization can be done from the singleplayer world selection screen.\n" +
"Error: ["+e.getMessage()+"]."
, e);
return CreateEmptyChunk(level, chunkPos);
}
}
}
private static ProtoChunk CreateEmptyChunk(ServerLevel level, ChunkPos chunkPos)
{
return new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if MC_VER >= MC_1_17_1 , level #endif
#if MC_VER >= MC_1_18_2 , level.registryAccess().registryOrThrow(
#if MC_VER < MC_1_19_4
Registry.BIOME_REGISTRY
#else
Registries.BIOME
#endif
), null #endif
);
}
public void generateDirect(
GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunksToGenerate, int border,
@@ -569,9 +653,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
chunksToGenerate.forEach((chunkWrapper) ->
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk instanceof ProtoChunk)
if (chunk instanceof ProtoChunk protoChunk)
{
ProtoChunk protoChunk = ((ProtoChunk) chunk);
protoChunk.setLightEngine(region.getLightEngine());
}
@@ -648,7 +731,19 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
int maxSkyLight = this.serverlevel.getServerLevelWrapper().hasSkyLight() ? 15 : 0;
ArrayList<IChunkWrapper> iChunkWrapperList = new ArrayList<>(chunksToGenerate);
// only light generated chunks,
// attempting to light un-generated chunks will cause lighting issues on bordering generated chunks
ArrayList<IChunkWrapper> iChunkWrapperList = new ArrayList<>();
for (int i = 0; i < chunksToGenerate.size(); i++) // regular for loop since enhanced for loops increase GC pressure slightly
{
ChunkWrapper chunkWrapper = chunksToGenerate.get(i);
if (chunkWrapper.getStatus() != ChunkStatus.EMPTY)
{
iChunkWrapperList.add(chunkWrapper);
}
}
// light each chunk in the list
for (int i = 0; i < iChunkWrapperList.size(); i++)
{
IChunkWrapper centerChunk = iChunkWrapperList.get(i);
@@ -663,19 +758,19 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
// if this isn't done everything else afterward may fail
Heightmap.primeHeightmaps(((ChunkWrapper)centerChunk).getChunk(), ChunkStatus.FEATURES.heightmapsAfter());
// populate the lighting
DhLightingEngine.INSTANCE.lightChunk(centerChunk, iChunkWrapperList, maxSkyLight);
// pre-generated chunks should have lighting but new ones won't
if (!centerChunk.isLightCorrect())
{
DhLightingEngine.INSTANCE.lightChunk(centerChunk, iChunkWrapperList, maxSkyLight);
}
}
genEvent.refreshTimeout();
}
}
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, MaxBorderNeeded - BorderNeeded.get(step)); }
public interface EmptyChunkGenerator
{
ChunkAccess generate(int x, int z);
}
@Override
public int getEventCount() { return this.generationEventList.size(); }
@@ -724,6 +819,12 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
return genEvent.future;
}
//================//
// helper methods //
//================//
/**
* Called before code that may run for an extended period of time. <br>
* This is necessary to allow canceling world gen since waiting
@@ -737,4 +838,16 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
}
}
//================//
// helper classes //
//================//
@FunctionalInterface
public interface IEmptyChunkGeneratorFunc
{
ChunkAccess generate(int x, int z);
}
}
@@ -26,13 +26,11 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.distanthorizons.core.generation.WorldGenerationQueue;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.objects.EventTimer;
import com.seibel.distanthorizons.core.util.threading.ThreadPools;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import org.apache.logging.log4j.Logger;
@@ -123,7 +121,7 @@ public final class GenerationEvent
public boolean terminate()
{
LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
ThreadPools.WORLD_GEN_THREAD_FACTORY.dumpAllThreadStacks();
ThreadPoolUtil.WORLD_GEN_THREAD_FACTORY.dumpAllThreadStacks();
this.future.cancel(true);
return this.future.isCancelled();
}
@@ -31,16 +31,16 @@ import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.chunk.ChunkGenerator;
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.chunk.storage.ChunkScanAccess;
#endif
import net.minecraft.world.level.levelgen.WorldGenSettings;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
#else
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.levelgen.RandomState;
#if POST_MC_1_19_4
#if MC_VER >= MC_1_19_4
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.core.registries.Registries;
#endif
@@ -50,13 +50,13 @@ import net.minecraft.world.level.storage.WorldData;
public final class GlobalParameters
{
public final ChunkGenerator generator;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
public final StructureManager structures;
#else
public final StructureTemplateManager structures;
public final RandomState randomState;
#endif
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
public final WorldGenSettings worldGenSettings;
#else
public final WorldOptions worldOptions;
@@ -67,7 +67,7 @@ public final class GlobalParameters
public final RegistryAccess registry;
public final long worldSeed;
public final DataFixer fixerUpper;
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
public final BiomeManager biomeManager;
public final ChunkScanAccess chunkScanner; // FIXME: Figure out if this is actually needed
#endif
@@ -81,7 +81,7 @@ public final class GlobalParameters
WorldData worldData = server.getWorldData();
registry = server.registryAccess();
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
worldGenSettings = worldData.worldGenSettings();
biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY);
worldSeed = worldGenSettings.seed();
@@ -90,14 +90,14 @@ public final class GlobalParameters
biomes = registry.registryOrThrow(Registries.BIOME);
worldSeed = worldOptions.seed();
#endif
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
biomeManager = new BiomeManager(level, BiomeManager.obfuscateSeed(worldSeed));
chunkScanner = level.getChunkSource().chunkScanner();
#endif
structures = server.getStructureManager();
generator = level.getChunkSource().getGenerator();
fixerUpper = server.getFixerUpper();
#if POST_MC_1_19_2
#if MC_VER >= MC_1_19_2
randomState = level.getChunkSource().randomState();
#endif
}
@@ -25,7 +25,7 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.Wo
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.WorldGenLevel;
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.structure.StructureCheck;
#endif
@@ -35,7 +35,7 @@ public final class ThreadedParameters
final ServerLevel level;
public WorldGenStructFeatManager structFeat = null;
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
public StructureCheck structCheck;
#endif
boolean isValid = true;
@@ -63,9 +63,9 @@ public final class ThreadedParameters
previousGlobalParameters = param;
this.level = param.level;
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
this.structFeat = new WorldGenStructFeatManager(param.worldGenSettings, level);
#elif PRE_MC_1_19_2
#elif MC_VER < MC_1_19_2
this.structCheck = this.createStructureCheck(param);
#else
this.structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
@@ -80,15 +80,15 @@ public final class ThreadedParameters
public void makeStructFeat(WorldGenLevel genLevel, GlobalParameters param)
{
#if PRE_MC_1_19_4
structFeat = new WorldGenStructFeatManager(param.worldGenSettings, genLevel #if POST_MC_1_18_2 , structCheck #endif );
#if MC_VER < MC_1_19_4
structFeat = new WorldGenStructFeatManager(param.worldGenSettings, genLevel #if MC_VER >= MC_1_18_2 , structCheck #endif );
#else
structFeat = new WorldGenStructFeatManager(param.worldOptions, genLevel, structCheck);
#endif
}
#if POST_MC_1_18_2 && PRE_MC_1_19_2
#if MC_VER >= MC_1_18_2 && MC_VER < MC_1_19_2
public void recreateStructureCheck()
{
if (previousGlobalParameters != null)
@@ -22,9 +22,14 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.google.common.collect.Maps;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Dynamic;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
@@ -37,13 +42,14 @@ import java.util.Objects;
import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
#if POST_MC_1_19_4
#if MC_VER >= MC_1_19_4
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
#endif
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.*;
@@ -55,35 +61,45 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.*;
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
import net.minecraft.world.level.levelgen.Heightmap;
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.BlendingData;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext;
import net.minecraft.world.ticks.LevelChunkTicks;
#endif
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
#endif
#endif
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
import net.minecraft.world.level.material.Fluids;
#endif
#if MC_VER == MC_1_20_6
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
#elif MC_VER == MC_1_21
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
#endif
import net.minecraft.world.level.material.Fluid;
public class ChunkLoader
{
#if POST_MC_1_19_2
private static boolean zeroChunkPosErrorLogged = false;
#if MC_VER >= MC_1_19_2
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#elif POST_MC_1_18_2
#elif MC_VER >= MC_1_18_2
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#endif
private static final String TAG_UPGRADE_DATA = "UpgradeData";
@@ -93,137 +109,17 @@ public class ChunkLoader
private static final String FLUID_TICKS_TAG_PRE18 = "LiquidTicks";
private static final ConfigBasedLogger LOGGER = BatchGenerationEnvironment.LOAD_LOGGER;
#if POST_MC_1_18_2
private static BlendingData readBlendingData(CompoundTag chunkData)
{
BlendingData blendingData = null;
if (chunkData.contains("blending_data", 10))
{
@SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null);
}
return blendingData;
}
#endif
private static boolean lightingSectionErrorLogged = false;
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if POST_MC_1_18_2
#if PRE_MC_1_19_4
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#else
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#endif
#if PRE_MC_1_18_2
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#elif PRE_MC_1_19_2
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#endif
#endif
int i = #if PRE_MC_1_17_1 16; #else level.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[i];
boolean isLightOn = chunkData.getBoolean("isLightOn");
boolean hasSkyLight = level.dimensionType().hasSkyLight();
ListTag tagSections = chunkData.getList("Sections", 10);
if (tagSections.isEmpty()) tagSections = chunkData.getList("sections", 10);
for (int j = 0; j < tagSections.size(); ++j)
{
CompoundTag tagSection = tagSections.getCompound(j);
int sectionYPos = tagSection.getByte("Y");
#if PRE_MC_1_18_2
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12))
{
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
levelChunkSection.getStates().read(tagSection.getList("Palette", 10),
tagSection.getLongArray("BlockStates"));
levelChunkSection.recalcBlockCounts();
if (!levelChunkSection.isEmpty())
chunkSections[#if PRE_MC_1_17_1 sectionYPos #else level.getSectionIndexFromSectionY(sectionYPos) #endif ]
= levelChunkSection;
}
#else
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
if (sectionId >= 0 && sectionId < chunkSections.length)
{
PalettedContainer<BlockState> blockStateContainer;
#if PRE_MC_1_18_2
PalettedContainer<Biome> biomeContainer;
#else
PalettedContainer<Holder<Biome>> biomeContainer;
#endif
blockStateContainer = tagSection.contains("block_states", 10)
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#if PRE_MC_1_18_2
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, i, (String) string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#endif
#if PRE_MC_1_20_1
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
#else
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
#endif
}
#endif
}
return chunkSections;
}
private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
{
CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps");
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
{
String heightmap = type.getSerializationKey();
if (tagHeightmaps.contains(heightmap, 12))
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
}
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
}
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
{
ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
for (int n = 0; n < tagPostProcessings.size(); ++n)
{
ListTag listTag3 = tagPostProcessings.getList(n);
for (int o = 0; o < listTag3.size(); ++o)
{
chunk.addPackedPostProcess(listTag3.getShort(o), n);
}
}
}
public static ChunkStatus.ChunkType readChunkType(CompoundTag tagLevel)
{
ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status"));
if (chunkStatus != null)
{
return chunkStatus.getChunkType();
}
return ChunkStatus.ChunkType.PROTOCHUNK;
}
//============//
// read chunk //
//============//
public static LevelChunk read(WorldGenLevel level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
CompoundTag tagLevel = chunkData.getCompound("Level");
#else
CompoundTag tagLevel = chunkData;
@@ -232,50 +128,82 @@ public class ChunkLoader
ChunkPos actualPos = new ChunkPos(tagLevel.getInt("xPos"), tagLevel.getInt("zPos"));
if (!Objects.equals(chunkPos, actualPos))
{
LOGGER.error("Chunk file at {} is in the wrong location; Ignoring. (Expected {}, got {})", chunkPos, chunkPos, actualPos);
return null;
#if MC_VER > MC_1_17_1
if (actualPos.equals(ChunkPos.ZERO))
#else
if (actualPos.equals(ChunkPos.INVALID_CHUNK_POS))
#endif
{
if (!zeroChunkPosErrorLogged)
{
zeroChunkPosErrorLogged = true;
// explicit chunkPos toString is necessary otherwise the JDK 17 compiler breaks
LOGGER.warn("Chunk file at ["+chunkPos.toString()+"] doesn't have a chunk pos. \n" +
"This might happen if the world was created using an external program. \n" +
"DH will attempt to parse the chunk anyway and won't log this message again.\n" +
"If issues arise please try optimizing your world to fix this issue. \n" +
"World optimization can be done from the singleplayer world selection screen."+
"");
}
}
else
{
// everything is on one line to fix a JDK 17 compiler issue
// if the issue is ever resolved, feel free to make this multi-line for readability
LOGGER.error("Chunk file at ["+chunkPos.toString()+"] is in the wrong location. \nPlease try optimizing your world to fix this issue. \nWorld optimization can be done from the singleplayer world selection screen. \n(Expected pos: ["+chunkPos.toString()+"], actual ["+actualPos.toString()+"])");
return null;
}
}
ChunkStatus.ChunkType chunkType = readChunkType(tagLevel);
#if PRE_MC_1_18_2
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
return null;
#if MC_VER < MC_1_20_6
ChunkStatus.ChunkType chunkType;
#else
BlendingData blendingData = readBlendingData(tagLevel);
#if PRE_MC_1_19_2
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
return null;
#else
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && blendingData == null)
return null;
ChunkType chunkType;
#endif
chunkType = readChunkType(tagLevel);
#if MC_VER < MC_1_18_2
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
return null;
#else
BlendingData blendingData = readBlendingData(tagLevel);
#if MC_VER < MC_1_19_2
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
return null;
#else
if (chunkType == #if MC_VER < MC_1_20_6 ChunkStatus.ChunkType.PROTOCHUNK #else ChunkType.PROTOCHUNK #endif && blendingData == null)
return null;
#endif
#endif
long inhabitedTime = tagLevel.getLong("InhabitedTime");
//================== Read params for making the LevelChunk ==================
UpgradeData upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
? new UpgradeData(tagLevel.getCompound(TAG_UPGRADE_DATA)#if POST_MC_1_17_1 , level #endif )
? new UpgradeData(tagLevel.getCompound(TAG_UPGRADE_DATA)#if MC_VER >= MC_1_17_1 , level #endif )
: UpgradeData.EMPTY;
boolean isLightOn = tagLevel.getBoolean("isLightOn");
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)#if POST_MC_1_17_1 , level #endif ,
level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)#if MC_VER >= MC_1_17_1 , level #endif ,
chunkPos, level.getLevel().getChunkSource().getGenerator().getBiomeSource(),
tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null);
TickList<Block> blockTicks = tagLevel.contains(BLOCK_TICKS_TAG_PRE18, 9)
? ChunkTickList.create(tagLevel.getList(BLOCK_TICKS_TAG_PRE18, 10), Registry.BLOCK::getKey, Registry.BLOCK::get)
: new ProtoTickList<Block>(block -> (block == null || block.defaultBlockState().isAir()), chunkPos,
tagLevel.getList("ToBeTicked", 9)#if POST_MC_1_17_1 , level #endif );
tagLevel.getList("ToBeTicked", 9)#if MC_VER >= MC_1_17_1 , level #endif );
TickList<Fluid> fluidTicks = tagLevel.contains(FLUID_TICKS_TAG_PRE18, 9)
? ChunkTickList.create(tagLevel.getList(FLUID_TICKS_TAG_PRE18, 10), Registry.FLUID::getKey, Registry.FLUID::get)
: new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos,
tagLevel.getList("LiquidsToBeTicked", 9)#if POST_MC_1_17_1 , level #endif );
tagLevel.getList("LiquidsToBeTicked", 9)#if MC_VER >= MC_1_17_1 , level #endif );
#else
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
string -> Registry.BLOCK.getOptional(ResourceLocation.tryParse(string)), chunkPos);
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
@@ -291,7 +219,7 @@ public class ChunkLoader
LevelChunkSection[] levelChunkSections = readSections(level, chunkPos, tagLevel);
// ====================== Make the chunk =========================
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
LevelChunk chunk = new LevelChunk((Level) level.getLevel(), chunkPos, chunkBiomeContainer, upgradeData, blockTicks,
fluidTicks, inhabitedTime, levelChunkSections, null);
#else
@@ -305,10 +233,292 @@ public class ChunkLoader
readPostPocessings(chunk, chunkData);
return chunk;
}
private static void logErrors(ChunkPos chunkPos, int i, String string)
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
{
LOGGER.error("Distant Horizons: Recoverable errors when loading section [" + chunkPos.x + ", " + i + ", " + chunkPos.z + "]: " + string);
#if MC_VER >= MC_1_18_2
#if MC_VER < MC_1_19_4
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#else
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#endif
#if MC_VER < MC_1_18_2
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_19_2
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#endif
#endif
int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex];
boolean isLightOn = chunkData.getBoolean("isLightOn");
boolean hasSkyLight = level.dimensionType().hasSkyLight();
ListTag tagSections = chunkData.getList("Sections", 10);
if (tagSections.isEmpty()) tagSections = chunkData.getList("sections", 10);
for (int j = 0; j < tagSections.size(); ++j)
{
CompoundTag tagSection = tagSections.getCompound(j);
int sectionYPos = tagSection.getByte("Y");
#if MC_VER < MC_1_18_2
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12))
{
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
levelChunkSection.getStates().read(tagSection.getList("Palette", 10),
tagSection.getLongArray("BlockStates"));
levelChunkSection.recalcBlockCounts();
if (!levelChunkSection.isEmpty())
chunkSections[#if MC_VER < MC_1_17_1 sectionYPos #else level.getSectionIndexFromSectionY(sectionYPos) #endif ]
= levelChunkSection;
}
#else
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
if (sectionId >= 0 && sectionId < chunkSections.length)
{
PalettedContainer<BlockState> blockStateContainer;
#if MC_VER < MC_1_18_2
PalettedContainer<Biome> biomeContainer;
#else
PalettedContainer<Holder<Biome>> biomeContainer;
#endif
blockStateContainer = tagSection.contains("block_states", 10)
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
#if MC_VER < MC_1_20_6
.getOrThrow(false, LOGGER::error)
#else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
#endif
: new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#if MC_VER < MC_1_18_2
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
#if MC_VER < MC_1_20_6
.getOrThrow(false, LOGGER::error)
#else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
#endif
: new PalettedContainer<>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#endif
#if MC_VER < MC_1_20_1
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
#else
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
#endif
}
#endif
}
return chunkSections;
}
private static
#if MC_VER < MC_1_20_6 ChunkStatus.ChunkType
#elif MC_VER < MC_1_21 ChunkType
#else ChunkType #endif
readChunkType(CompoundTag tagLevel)
{
ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status"));
if (chunkStatus != null)
{
return chunkStatus.getChunkType();
}
return
#if MC_VER <= MC_1_20_4 ChunkStatus.ChunkType.PROTOCHUNK;
#else ChunkType.PROTOCHUNK; #endif
}
private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
{
CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps");
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
{
String heightmap = type.getSerializationKey();
if (tagHeightmaps.contains(heightmap, 12))
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
}
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
}
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
{
ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
for (int n = 0; n < tagPostProcessings.size(); ++n)
{
ListTag listTag3 = tagPostProcessings.getList(n);
for (int o = 0; o < listTag3.size(); ++o)
{
chunk.addPackedPostProcess(listTag3.getShort(o), n);
}
}
}
#if MC_VER >= MC_1_18_2
private static BlendingData readBlendingData(CompoundTag chunkData)
{
BlendingData blendingData = null;
if (chunkData.contains("blending_data", 10))
{
@SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null);
}
return blendingData;
}
#endif
//=====================//
// read chunk lighting //
//=====================//
/**
* https://minecraft.wiki/w/Chunk_format
*/
public static CombinedChunkLightStorage readLight(ChunkAccess chunk, CompoundTag chunkData)
{
#if MC_VER <= MC_1_17_1
// MC 1.16 and 1.17 doesn't have the necessary NBT info
return null;
#else
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getMinBuildHeight(chunk), ChunkWrapper.getMaxBuildHeight(chunk));
ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage;
ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage;
boolean foundSkyLight = false;
//===================//
// get NBT tags info //
//===================//
Tag chunkSectionTags = chunkData.get("sections");
if (chunkSectionTags == null)
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("No sections found for chunk at pos ["+chunk.getPos()+"] chunk data may be out of date.");
}
return null;
}
else if (!(chunkSectionTags instanceof ListTag))
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("Chunk section tag list have unexpected type ["+chunkSectionTags.getClass().getName()+"], expected ["+ListTag.class.getName()+"].");
}
return null;
}
ListTag chunkSectionListTag = (ListTag) chunkSectionTags;
//===================//
// get lighting info //
//===================//
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
{
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
if (!(chunkSectionTag instanceof CompoundTag chunkSectionCompoundTag))
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("Chunk section tag has an unexpected type ["+chunkSectionTag.getClass().getName()+"], expected ["+CompoundTag.class.getName()+"].");
}
return null;
}
// if null all lights = 0
byte[] blockLightNibbleArray = chunkSectionCompoundTag.getByteArray("BlockLight");
byte[] skyLightNibbleArray = chunkSectionCompoundTag.getByteArray("SkyLight");
// if any sky light was found then all lights above will be max brightness
if (skyLightNibbleArray.length != 0)
{
foundSkyLight = true;
}
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
{
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
{
// chunk sections are also 16 blocks tall
for (int relY = 0; relY < LodUtil.CHUNK_WIDTH; relY++)
{
int blockPosIndex = relY*16*16 + relZ*16 + relX;
byte blockLight = (blockLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(blockLightNibbleArray, blockPosIndex);
byte skyLight = (skyLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(skyLightNibbleArray, blockPosIndex);
if (skyLightNibbleArray.length == 0 && foundSkyLight)
{
skyLight = LodUtil.MAX_MC_LIGHT;
}
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getMinBuildHeight(chunk);
blockLightStorage.set(relX, y, relZ, blockLight);
skyLightStorage.set(relX, y, relZ, skyLight);
}
}
}
}
return combinedStorage;
#endif
}
/** source: https://minecraft.wiki/w/Chunk_format#Block_Format */
private static byte getNibbleAtIndex(byte[] arr, int index)
{
if (index % 2 == 0)
{
return (byte)(arr[index/2] & 0x0F);
}
else
{
return (byte)((arr[index/2]>>4) & 0x0F);
}
}
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+message+"]. This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
}
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+message+"]. This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
}
//================//
// helper classes //
//================//
public static class CombinedChunkLightStorage
{
public ChunkLightStorage blockLightStorage;
public ChunkLightStorage skyLightStorage;
public CombinedChunkLightStorage(int minY, int maxY)
{
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(minY, maxY);
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(minY, maxY);
}
}
}
@@ -0,0 +1,23 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
#if MC_VER >= MC_1_21
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.world.level.ChunkPos;
public class DhGenerationChunkHolder extends GenerationChunkHolder
{
public DhGenerationChunkHolder(ChunkPos pos)
{
super(pos);
}
@Override
public int getTicketLevel() { return 0; }
@Override
public int getQueueLevel() { return 0; }
}
#endif
@@ -20,14 +20,17 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import java.lang.invoke.MethodHandles;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.SpawnerBlock;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
@@ -41,7 +44,7 @@ import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.ColorResolver;
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
import net.minecraft.world.level.LevelHeightAccessor;
#endif
import net.minecraft.world.level.LightLayer;
@@ -50,34 +53,50 @@ import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.lighting.LevelLightEngine;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.*;
#endif
#if MC_VER == MC_1_21
import net.minecraft.util.StaticCache2D;
import com.google.common.collect.ImmutableList;
import net.minecraft.server.level.GenerationChunkHolder;
#endif
public class DhLitWorldGenRegion extends WorldGenRegion
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private static ChunkStatus debugTriggeredForStatus = null;
public final DummyLightEngine lightEngine;
public final BatchGenerationEnvironment.EmptyChunkGenerator generator;
public final BatchGenerationEnvironment.IEmptyChunkGeneratorFunc generator;
public final int writeRadius;
public final int size;
private final ChunkPos firstPos;
private final List<ChunkAccess> cache;
Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>();
private final Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<>();
/**
* Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe,
* specifically: "it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap.getAndMoveToFirst()"
*/
ReentrantLock getChunkLock = new ReentrantLock();
private final ReentrantLock getChunkLock = new ReentrantLock();
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
private ChunkPos overrideCenterPos = null;
public void setOverrideCenter(ChunkPos pos) { overrideCenterPos = pos; }
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
@Override
public int getCenterX()
{
@@ -100,12 +119,30 @@ public class DhLitWorldGenRegion extends WorldGenRegion
public DhLitWorldGenRegion(
int centerChunkX, int centerChunkZ,
ChunkAccess centerChunk,
ServerLevel serverLevel, DummyLightEngine lightEngine,
List<ChunkAccess> chunkList, ChunkStatus chunkStatus, int writeRadius,
BatchGenerationEnvironment.EmptyChunkGenerator generator)
BatchGenerationEnvironment.IEmptyChunkGeneratorFunc generator)
{
super(serverLevel, chunkList #if POST_MC_1_17_1 , chunkStatus, writeRadius #endif );
this.firstPos = chunkList.get(0).getPos();
#if MC_VER == MC_1_16_5
super(serverLevel, chunkList);
#elif MC_VER < MC_1_21
super(serverLevel, chunkList, chunkStatus, writeRadius);
#else
super(serverLevel,
StaticCache2D.create(
centerChunkX, centerChunkZ,
writeRadius * 2, (x,z) -> new DhGenerationChunkHolder(new ChunkPos(x, z))),
new ChunkStep(chunkStatus,
// reverse is needed because MC uses the index of the chunkStatus to determine how many items are in the list instead of the actual list count
new ChunkDependencies(ImmutableList.copyOf(ChunkStatus.getStatusList()).reverse()),
new ChunkDependencies(ImmutableList.copyOf(ChunkStatus.getStatusList()).reverse()),
writeRadius, (WorldGenContext var1, ChunkStep var2, StaticCache2D<GenerationChunkHolder> var3, ChunkAccess var4) -> null),
centerChunk);
#endif
this.firstPos = chunkList.getFirst().getPos();
this.generator = generator;
this.lightEngine = lightEngine;
this.writeRadius = writeRadius;
@@ -115,7 +152,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
// Bypass BCLib mixin overrides.
@Override
public boolean ensureCanWrite(BlockPos blockPos)
@@ -130,7 +167,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
{
return false;
}
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
if (center.isUpgrading())
{
LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration();
@@ -185,7 +222,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
BlockState blockState = this.getBlockState(blockPos);
// This is a bypass for the spawner block since MC complains about not having it
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
if (blockState.getBlock() instanceof SpawnerBlock)
{
return ((EntityBlock) blockState.getBlock()).newBlockEntity(blockPos, blockState);
@@ -198,12 +235,9 @@ public class DhLitWorldGenRegion extends WorldGenRegion
#endif
}
// Skip BlockEntity stuff. It aren't really needed
/** Skip BlockEntity stuff. They aren't needed for our use case. */
@Override
public boolean addFreshEntity(Entity entity)
{
return true;
}
public boolean addFreshEntity(@NotNull Entity entity) { return true; }
// Allays have empty chunks even if it's outside the worldGenRegion
// @Override
@@ -214,13 +248,13 @@ public class DhLitWorldGenRegion extends WorldGenRegion
// Override to ensure no other mod mixins cause skipping the overrided
// getChunk(...)
@Override
public ChunkAccess getChunk(int i, int j)
public @NotNull ChunkAccess getChunk(int chunkX, int chunkZ)
{
try
{
// lock is to prevent issues with underlying MC code that doesn't support multithreading
this.getChunkLock.lock();
return this.getChunk(i, j, ChunkStatus.EMPTY);
return this.getChunk(chunkX, chunkZ, ChunkStatus.EMPTY);
}
finally
{
@@ -231,13 +265,19 @@ public class DhLitWorldGenRegion extends WorldGenRegion
// Override to ensure no other mod mixins cause skipping the overrided
// getChunk(...)
@Override
public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus)
public @NotNull ChunkAccess getChunk(int chunkX, int chunkZ, @NotNull ChunkStatus chunkStatus)
{
try
{
// lock is to prevent issues with underlying MC code that doesn't support multithreading
this.getChunkLock.lock();
return this.getChunk(i, j, chunkStatus, true);
ChunkAccess chunk = this.getChunk(chunkX, chunkZ, chunkStatus, true);
if (chunk == null)
{
LodUtil.assertNotReach("getChunk shouldn't return null values");
}
return chunk;
}
finally
{
@@ -245,102 +285,117 @@ public class DhLitWorldGenRegion extends WorldGenRegion
}
}
// Use this instead of super.getChunk() to bypass C2ME concurrency checks
private ChunkAccess superGetChunk(int x, int z, ChunkStatus cs)
{
int k = x - firstPos.x;
int l = z - firstPos.z;
return cache.get(k + l * size);
}
// Use this instead of super.hasChunk() to bypass C2ME concurrency checks
public boolean superHasChunk(int x, int z)
{
int k = x - firstPos.x;
int l = z - firstPos.z;
return l >= 0 && l < size && k >= 0 && k < size;
}
// Allow creating empty chunks even if it's outside the worldGenRegion
/** Allows creating empty chunks even if they're outside the worldGenRegion */
@Override
@Nullable
public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl)
public ChunkAccess getChunk(int chunkX, int chunkZ, @NotNull ChunkStatus chunkStatus, boolean returnNonNull)
{
ChunkAccess chunk = getChunkAccess(i, j, chunkStatus, bl);
ChunkAccess chunk = this.getChunkAccess(chunkX, chunkZ, chunkStatus, returnNonNull);
if (chunk instanceof LevelChunk)
{
chunk = new ImposterProtoChunk((LevelChunk) chunk #if POST_MC_1_18_2 , true #endif );
chunk = new ImposterProtoChunk((LevelChunk) chunk #if MC_VER >= MC_1_18_2 , true #endif );
}
return chunk;
}
private static ChunkStatus debugTriggeredForStatus = null;
private ChunkAccess getChunkAccess(int i, int j, ChunkStatus chunkStatus, boolean bl)
/**
* @param returnNonNull if true this method will always return a non-null chunk,
* if false it will return null if no chunk exists at the given position with the given status
*/
private ChunkAccess getChunkAccess(int chunkX, int chunkZ, ChunkStatus chunkStatus, boolean returnNonNull)
{
ChunkAccess chunk = superHasChunk(i, j) ? superGetChunk(i, j, ChunkStatus.EMPTY) : null;
if (chunk != null && chunk.getStatus().isOrAfter(chunkStatus))
ChunkAccess chunk = this.superHasChunk(chunkX, chunkZ) ? this.superGetChunk(chunkX, chunkZ) : null;
if (chunk != null && ChunkWrapper.getStatus(chunk).isOrAfter(chunkStatus))
{
return chunk;
}
if (!bl)
else if (!returnNonNull)
{
// no chunk found with the necessary status and null return values are allowed
return null;
}
// we need a non-null chunk
if (chunk == null)
{
chunk = chunkMap.get(ChunkPos.asLong(i, j));
// check memory
chunk = this.chunkMap.get(ChunkPos.asLong(chunkX, chunkZ));
if (chunk == null)
{
chunk = generator.generate(i, j);
// chunk isn't in memory, generate a new one
chunk = this.generator.generate(chunkX, chunkZ);
if (chunk == null)
{
throw new NullPointerException("The provided generator should not return null!");
chunkMap.put(ChunkPos.asLong(i, j), chunk);
}
this.chunkMap.put(ChunkPos.asLong(chunkX, chunkZ), chunk);
}
}
if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus)
{
LOGGER.info("WorldGen requiring " + chunkStatus
+ " outside expected range detected. Force passing EMPTY chunk and seeing if it works.");
debugTriggeredForStatus = chunkStatus;
}
return chunk;
}
/** Overriding allows us to use our own lighting engine */
@Override
public LevelLightEngine getLightEngine() { return this.lightEngine; }
/** Overriding allows us to use our own lighting engine */
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return 0; }
/** Overriding allows us to use our own lighting engine */
@Override
public int getRawBrightness(BlockPos blockPos, int i) { return 0; }
/** Overriding allows us to use our own lighting engine */
@Override
public boolean canSeeSky(BlockPos blockPos)
/** Use this instead of super.hasChunk() to bypass C2ME concurrency checks */
public boolean superHasChunk(int x, int z)
{
return (getBrightness(LightLayer.SKY, blockPos) >= getMaxLightLevel());
int k = x - this.firstPos.x;
int l = z - this.firstPos.z;
return l >= 0 && l < this.size && k >= 0 && k < this.size;
}
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
/** Use this instead of super.getChunk() to bypass C2ME concurrency checks */
private ChunkAccess superGetChunk(int x, int z)
{
return calculateBlockTint(blockPos, colorResolver);
int k = x - this.firstPos.x;
int l = z - this.firstPos.z;
return this.cache.get(k + l * this.size);
}
/** Overriding allows us to use our own lighting engine */
@Override
public @NotNull LevelLightEngine getLightEngine() { return this.lightEngine; }
/** Overriding allows us to use our own lighting engine */
@Override
public int getBrightness(@NotNull LightLayer lightLayer, @NotNull BlockPos blockPos) { return 0; }
/** Overriding allows us to use our own lighting engine */
@Override
public int getRawBrightness(@NotNull BlockPos blockPos, int i) { return 0; }
/** Overriding allows us to use our own lighting engine */
@Override
public boolean canSeeSky(@NotNull BlockPos blockPos)
{
return (this.getBrightness(LightLayer.SKY, blockPos) >= this.getMaxLightLevel());
}
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
{
return this.calculateBlockTint(blockPos, colorResolver);
}
private Biome _getBiome(BlockPos pos)
{
#if POST_MC_1_18_2
return getBiome(pos).value();
#if MC_VER >= MC_1_18_2
return this.getBiome(pos).value();
#else
return getBiome(pos);
return this.getBiome(pos);
#endif
}
public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
int i = (Minecraft.getInstance()).options.biomeBlendRadius;
#else
int i = (Minecraft.getInstance()).options.biomeBlendRadius().get();
@@ -39,7 +39,7 @@ public class DummyLightEngine extends LevelLightEngine
}
#if PRE_MC_1_20_1
#if MC_VER < MC_1_20_1
@Override
public void onBlockEmissionIncrease(BlockPos blockPos, int i) { }
@@ -63,7 +63,7 @@ public class DummyLightEngine extends LevelLightEngine
#endif
@Override
public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer #if PRE_MC_1_20_1 , boolean bl #endif ) { }
public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer #if MC_VER < MC_1_20_1 , boolean bl #endif ) { }
@Override
public void checkBlock(BlockPos blockPos) { }
@@ -87,7 +87,7 @@ public class DummyLightEngine extends LevelLightEngine
@Override
public void retainData(ChunkPos chunkPos, boolean bl) { }
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
@Override
public int getLightSectionCount() { throw new UnsupportedOperationException("This should never be used!"); }
@Override
@@ -23,15 +23,24 @@ import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IStarlightAccessor;
import net.minecraft.world.level.BlockGetter;
#if POST_MC_1_17_1
import net.minecraft.world.level.chunk.LightChunkGetter;
#if MC_VER >= MC_1_17_1
import net.minecraft.world.level.LevelHeightAccessor;
#endif
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LightChunkGetter;
#if POST_MC_1_20_1
#if MC_VER >= MC_1_20_1
import net.minecraft.world.level.chunk.LightChunk;
#endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public class LightGetterAdaptor implements LightChunkGetter
{
private final BlockGetter heightGetter;
@@ -50,7 +59,7 @@ public class LightGetterAdaptor implements LightChunkGetter
}
@Override
public #if PRE_MC_1_20_1 BlockGetter #else LightChunk #endif getChunkForLighting(int chunkX, int chunkZ)
public #if MC_VER < MC_1_20_1 BlockGetter #else LightChunk #endif getChunkForLighting(int chunkX, int chunkZ)
{
if (genRegion == null)
throw new IllegalStateException("World Gen region has not been set!");
@@ -64,7 +73,7 @@ public class LightGetterAdaptor implements LightChunkGetter
return shouldReturnNull ? null : (genRegion != null ? genRegion : heightGetter);
}
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
public LevelHeightAccessor getLevelHeightAccessor()
{
return heightGetter;
@@ -1,13 +1,15 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFile;
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Nullable;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.file.Files;
@@ -15,11 +17,27 @@ import java.nio.file.Path;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantLock;
#if MC_VER >= MC_1_20_6
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
#endif
/**
* @deprecated should be replaced with net.minecraft.world.level.chunk.storage.IOWorker to
* prevent potential file corruption and issues with the C2ME mod.
* Generally this would be done via (MC ServerLevel) level.getChunkSource().chunkMap.worker#loadAsync()
*/
@Deprecated
public class RegionFileStorageExternalCache implements AutoCloseable
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/** Can be null due to the C2ME mod */
@Nullable
public final RegionFileStorage storage;
public static final int MAX_CACHE_SIZE = 16;
public static boolean regionCacheNullPointerWarningSent = false;
/**
* Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe,
* specifically: "it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap.getAndMoveToFirst()"
@@ -50,6 +68,19 @@ public class RegionFileStorageExternalCache implements AutoCloseable
@Nullable
public RegionFile getRegionFile(ChunkPos pos) throws IOException
{
if (this.storage == null)
{
if (!regionCacheNullPointerWarningSent)
{
regionCacheNullPointerWarningSent = true;
LOGGER.warn("Unable to access Minecraft's chunk cache. This may be due to another mod changing said cache. DH will be unable to access any Minecraft chunk data until said mod is removed.");
}
return null;
}
long posLong = ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ());
RegionFile rFile = null;
@@ -64,7 +95,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
{
this.getRegionFileLock.lock();
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
rFile = this.storage.getRegionFile(pos);
// keeping the region cache size low helps prevent concurrency issues
@@ -84,7 +115,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
}
catch (ArrayIndexOutOfBoundsException e)
{
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
// the file just wasn't cached
break;
#else
@@ -98,6 +129,19 @@ public class RegionFileStorageExternalCache implements AutoCloseable
}
#endif
}
catch (NullPointerException e)
{
// Can sometimes happen when other mods modify the region cache system (IE C2ME)
// instead of blowing up, just use DH's cache instead
if (!regionCacheNullPointerWarningSent)
{
regionCacheNullPointerWarningSent = true;
LOGGER.warn("Unable to access Minecraft's chunk cache. This may be due to another mod changing said cache. Falling back to DH's internal cache.");
}
break;
}
finally
{
this.getRegionFileLock.unlock();
@@ -126,7 +170,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
// Otherwise, check if file exist, and if so, add it to the cache
Path storageFolderPath;
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
storageFolderPath = this.storage.folder.toPath();
#else
storageFolderPath = this.storage.folder;
@@ -138,10 +182,12 @@ public class RegionFileStorageExternalCache implements AutoCloseable
}
Path regionFilePath = storageFolderPath.resolve("r." + pos.getRegionX() + "." + pos.getRegionZ() + ".mca");
#if MC_1_16_5 || MC_1_17_1
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
rFile = new RegionFile(regionFilePath.toFile(), storageFolderPath.toFile(), false);
#else
#elif MC_VER <= MC_1_20_4
rFile = new RegionFile(regionFilePath, storageFolderPath, false);
#else
rFile = new RegionFile(new RegionStorageInfo("level", null, "level type"), regionFilePath, storageFolderPath, false);
#endif
this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ()), rFile));
@@ -35,51 +35,56 @@ import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.levelgen.WorldGenSettings;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
import net.minecraft.world.level.StructureFeatureManager;
#else
#if POST_MC_1_19_4
#if MC_VER >= MC_1_19_4
import net.minecraft.world.level.levelgen.WorldOptions;
#endif
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.StructureManager;
#endif
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.structure.StructureCheck;
#endif
import net.minecraft.world.level.levelgen.structure.StructureStart;
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatureManager #else StructureManager #endif
public class WorldGenStructFeatManager extends #if MC_VER < MC_1_19_2 StructureFeatureManager #else StructureManager #endif
{
final WorldGenLevel genLevel;
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
WorldGenSettings worldGenSettings;
#else
WorldOptions worldOptions;
#endif
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
StructureCheck structureCheck;
#endif
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
public WorldGenStructFeatManager(
WorldGenSettings worldGenSettings,
WorldGenLevel genLevel #if POST_MC_1_18_2 , StructureCheck structureCheck #endif )
WorldGenLevel genLevel #if MC_VER >= MC_1_18_2 , StructureCheck structureCheck #endif )
{
super(genLevel, worldGenSettings #if POST_MC_1_18_2 , structureCheck #endif );
super(genLevel, worldGenSettings #if MC_VER >= MC_1_18_2 , structureCheck #endif );
this.genLevel = genLevel;
this.worldGenSettings = worldGenSettings;
}
@@ -100,8 +105,8 @@ public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatur
{
if (worldGenRegion == genLevel)
return this;
#if PRE_MC_1_19_4
return new WorldGenStructFeatManager(worldGenSettings, worldGenRegion #if POST_MC_1_18_2 , structureCheck #endif );
#if MC_VER < MC_1_19_4
return new WorldGenStructFeatManager(worldGenSettings, worldGenRegion #if MC_VER >= MC_1_18_2 , structureCheck #endif );
#else
return new WorldGenStructFeatManager(worldOptions, worldGenRegion, structureCheck);
#endif
@@ -113,7 +118,7 @@ public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatur
return genLevel.getChunk(x, z, status, false);
}
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
@Override
public Stream<? extends StructureStart<?>> startsForFeature(
SectionPos sectionPos2,
@@ -140,7 +145,7 @@ public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatur
return chunk.hasAnyStructureReferences();
}
#if MC_1_18_1
#if MC_VER == MC_1_18_1
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos,
@@ -165,7 +170,7 @@ public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatur
return builder.build();
}
#else
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
@Override
public List<StructureStart> startsForFeature(SectionPos sectionPos, Predicate<ConfiguredStructureFeature<?, ?>> predicate)
{
@@ -226,11 +231,9 @@ public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatur
Map<Structure, LongSet> map = chunk.getAllReferences();
ImmutableList.Builder<StructureStart> builder = ImmutableList.builder();
Iterator<Map.Entry<Structure, LongSet>> var5 = map.entrySet().iterator();
while (var5.hasNext())
for (Map.Entry<Structure, LongSet> entry : map.entrySet())
{
Map.Entry<Structure, LongSet> entry = var5.next();
Structure configuredStructureFeature = entry.getKey();
if (predicate.test(configuredStructureFeature))
{
@@ -27,15 +27,19 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGeneratio
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import net.minecraft.server.level.WorldGenRegion;
#if PRE_MC_1_19_2
#endif
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.Blender;
#endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepBiomes
{
public static final ChunkStatus STATUS = ChunkStatus.BIOMES;
@@ -52,29 +56,44 @@ public final class StepBiomes
List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue;
((ProtoChunk) chunk).setStatus(STATUS);
chunksToDo.add(chunk);
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
}
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepBiomes: "+chunk.getPos());
#if PRE_MC_1_18_2
environment.params.generator.createBiomes(environment.params.biomes, chunk);
#elif PRE_MC_1_19_2
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, Blender.of(worldGenRegion),
#if MC_VER < MC_1_18_2
this.environment.params.generator.createBiomes(this.environment.params.biomes, chunk);
#elif MC_VER < MC_1_19_2
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif PRE_MC_1_19_4
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, environment.params.randomState, Blender.of(worldGenRegion),
#elif MC_VER < MC_1_19_4
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_21
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else
chunk = environment.joinSync(environment.params.generator.createBiomes(Runnable::run, environment.params.randomState, Blender.of(worldGenRegion),
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#endif
}
@@ -19,24 +19,29 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.ReportedException;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap;
#if POST_MC_1_18_2
import org.apache.logging.log4j.Logger;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepFeatures
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final ChunkStatus STATUS = ChunkStatus.FEATURES;
private final BatchGenerationEnvironment environment;
@@ -51,36 +56,48 @@ public final class StepFeatures
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue;
((ProtoChunk) chunk).setStatus(STATUS);
chunksToDo.add(chunk);
}
for (ChunkAccess chunk : chunksToDo)
{
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
}
try
{
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
worldGenRegion.setOverrideCenter(chunk.getPos());
environment.params.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeat);
#else
environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk,
tParams.structFeat.forWorldGenRegion(worldGenRegion));
if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().x, chunkWrapper.getChunkPos().z))
{
this.environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeat.forWorldGenRegion(worldGenRegion));
}
else
{
LOGGER.warn("Unable to generate features for chunk at pos ["+chunkWrapper.getChunkPos()+"], world gen region doesn't contain the chunk.");
}
#endif
Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter());
BatchGenerationEnvironment.clearDistantGenerationMixinData();
}
catch (ReportedException e)
catch (Exception e)
{
e.printStackTrace();
LOGGER.warn("Unexpected issue when generating features for chunk at pos ["+chunkWrapper.getChunkPos()+"], error: ["+e.getMessage()+"].", e);
// FIXME: Features concurrent modification issue. Something about cocobeans might just
// error out. For now just retry.
// error out. For now just retry.
}
}
}
@@ -28,17 +28,19 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParame
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import net.minecraft.server.level.WorldGenRegion;
#if POST_MC_1_17_1
#endif
#if PRE_MC_1_19_2
#endif
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.Blender;
#endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepNoise
{
private static final ChunkStatus STATUS = ChunkStatus.NOISE;
@@ -56,29 +58,40 @@ public final class StepNoise
List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue;
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
continue;
}
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepNoise: "+chunk.getPos());
#if PRE_MC_1_17_1
environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk);
#elif PRE_MC_1_18_2
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run,
#if MC_VER < MC_1_17_1
this.environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk);
#elif MC_VER < MC_1_18_2
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif PRE_MC_1_19_2
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion),
#elif MC_VER < MC_1_19_2
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_21
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), environment.params.randomState,
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Blender.of(worldGenRegion), this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#endif
UncheckedInterruptedException.throwIfInterrupted();
@@ -27,12 +27,16 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGeneratio
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import net.minecraft.server.level.WorldGenRegion;
#if PRE_MC_1_19_2
#endif
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepStructureReference
{
private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES;
@@ -50,14 +54,25 @@ public final class StepStructureReference
List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue;
((ProtoChunk) chunk).setStatus(STATUS);
chunksToDo.add(chunk);
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
}
for (ChunkAccess chunk : chunksToDo)
@@ -30,10 +30,16 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParame
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
import org.apache.logging.log4j.Logger;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepStructureStart
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -70,20 +76,29 @@ public final class StepStructureStart
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (!chunk.getStatus().isOrAfter(STATUS))
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
}
#if PRE_MC_1_19_2
if (environment.params.worldGenSettings.generateFeatures())
#if MC_VER < MC_1_19_2
if (this.environment.params.worldGenSettings.generateFeatures())
{
#elif PRE_MC_1_19_4
if (environment.params.worldGenSettings.generateStructures()) {
#elif MC_VER < MC_1_19_4
if (this.environment.params.worldGenSettings.generateStructures()) {
#else
if (environment.params.worldOptions.generateStructures())
if (this.environment.params.worldOptions.generateStructures())
{
#endif
for (ChunkAccess chunk : chunksToDo)
@@ -98,10 +113,10 @@ public final class StepStructureStart
// and should prevent some concurrency issues
STRUCTURE_PLACEMENT_LOCK.lock();
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
environment.params.generator.createStructures(environment.params.registry, tParams.structFeat, chunk, environment.params.structures,
environment.params.worldSeed);
#elif PRE_MC_1_19_4
#elif MC_VER < MC_1_19_4
environment.params.generator.createStructures(environment.params.registry, environment.params.randomState, tParams.structFeat, chunk, environment.params.structures,
environment.params.worldSeed);
#else
@@ -110,7 +125,7 @@ public final class StepStructureStart
tParams.structFeat, chunk, environment.params.structures);
#endif
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
try
{
tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
@@ -28,9 +28,14 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParame
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepSurface
{
@@ -53,17 +58,29 @@ public final class StepSurface
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue;
((ProtoChunk) chunk).setStatus(STATUS);
chunksToDo.add(chunk);
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
}
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepSurface: "+chunk.getPos());
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
environment.params.generator.buildSurfaceAndBedrock(worldGenRegion, chunk);
#elif PRE_MC_1_19_2
#elif MC_VER < MC_1_19_2
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
#else
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), environment.params.randomState, chunk);
@@ -0,0 +1,46 @@
accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
# used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList;
#accessible method net/minecraft/client/renderer/LevelRenderer renderSectionLayer (Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V
# world generation
# accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage;
accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
accessible field net/minecraft/world/level/chunk/storage/IOWorker storage Lnet/minecraft/world/level/chunk/storage/RegionFileStorage;
accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage regionCache Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage folder Ljava/nio/file/Path;
# grabbing textures
accessible class net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameX (I)I
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameY (I)I
accessible field net/minecraft/client/renderer/texture/SpriteContents animatedTexture Lnet/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture;
accessible field net/minecraft/client/renderer/texture/SpriteContents originalImage Lcom/mojang/blaze3d/platform/NativeImage;
# UI stuff
accessible field net/minecraft/client/gui/components/AbstractButton SPRITES Lnet/minecraft/client/gui/components/WidgetSprites;
# Handles inserting the config button
accessible field net/minecraft/client/gui/layouts/HeaderAndFooterLayout headerFrame Lnet/minecraft/client/gui/layouts/FrameLayout;
accessible field net/minecraft/client/gui/layouts/FrameLayout children Ljava/util/List;
accessible class net/minecraft/client/gui/layouts/FrameLayout$ChildContainer
accessible field net/minecraft/client/gui/layouts/LinearLayout wrapped Lnet/minecraft/client/gui/layouts/GridLayout;
# hacky stuff
accessible field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
mutable field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
accessible field net/minecraft/client/gui/components/AbstractSelectionList scrollAmount D # Hack to bypass vanilla's setScrollAmount's clamp
+3 -3
View File
@@ -14,7 +14,7 @@ By sending a merge request, you agree to abide by the Distant Horizons [Contribu
Contributions to this project are under the [lesser GPL v3 license](LICENSE.txt) Copyright James Seibel, so please include the [license header](license_header.txt) at the top of any new code files.
1. Fork, then clone the repo: \
`git clone --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git`
`git clone --recurse-submodules https://gitlab.com/jeseibel/distant-horizons.git`
2. Set up your dev environment: \
`./gradlew build`
@@ -37,13 +37,13 @@ Contributions to this project are under the [lesser GPL v3 license](LICENSE.txt)
`./gradlew fabric:runClient` \
When running the game, load or generate a world to confirm Distant Horizons initializes correctly.
9. Push to your fork, make sure to include the Core submodule, and submit a [new merge request](https://gitlab.com/jeseibel/minecraft-lod-mod/-/merge_requests/new).
9. Push to your fork, make sure to include the Core submodule, and submit a [new merge request](https://gitlab.com/jeseibel/distant-horizons/-/merge_requests/new).
## General Guidelines
* Check the existing issue list to verify that a given [bug](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/?sort=created_date&state=opened&label_name%5B%5D=Bug&first_page_size=100), [feature](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/?sort=created_date&state=opened&label_name%5B%5D=Feature&first_page_size=100), or [improvement](https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/?sort=created_date&state=opened&label_name%5B%5D=Improvement&first_page_size=100) hasn't already been submitted.
* Check the existing issue list to verify that a given [bug](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=created_date&state=opened&label_name%5B%5D=Bug&first_page_size=100), [feature](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=created_date&state=opened&label_name%5B%5D=Feature&first_page_size=100), or [improvement](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=created_date&state=opened&label_name%5B%5D=Improvement&first_page_size=100) hasn't already been submitted.
* Please open an issue if things aren't working as expected.
* Open a merge request to: fix bugs, fix documentations, improve an existing system, or complete a feature.
* When contributing:
+29 -29
View File
@@ -1,10 +1,10 @@
plugins {
id "fabric-loom" version "1.1.+"
id "fabric-loom" version "1.6-SNAPSHOT"
}
loom {
accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
// "runs" isn't required, but when we do need it then it can be useful
runs {
client {
@@ -23,17 +23,19 @@ loom {
}
remapJar {
inputFile = shadowJar.archiveFile
dependsOn shadowJar
classifier null
inputFile = shadeDowngradedApi.archiveFile
dependsOn shadeDowngradedApi
}
configurations {
// The addModJar basically embeds the mod to the built jar
addModJar
include.extendsFrom addModJar
modImplementation.extendsFrom addModJar
}
def addMod(path, enabled) {
if (enabled == "2")
@@ -43,7 +45,7 @@ def addMod(path, enabled) {
}
dependencies {
minecraft "com.mojang:minecraft:${minecraft_version}"
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
mappings loom.layered() {
// Mojmap mappings
officialMojangMappings()
@@ -53,6 +55,13 @@ dependencies {
// Fabric loader
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
// annotationProcessor "javax.annotation:javax.annotation-api:1.3.2"
// implementation("javax.annotation:javax.annotation-api:1.3.2")
// runtimeOnly "javax.annotation:javax.annotation-api:1.3.2"
// compileOnly "javax.annotation:javax.annotation-api:1.3.2"
// modImplementation "javax.annotation:javax.annotation-api:1.3.2"
// Fabric API
addModJar(fabricApi.module("fabric-api-base", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version))
@@ -60,12 +69,15 @@ dependencies {
addModJar(fabricApi.module("fabric-events-interaction-v0", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-rendering-v1", rootProject.fabric_api_version)) // TODO: Remove this as it is only needed in 1 line (FabricClientProxy)
addModJar(fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version))
// used by mod menu in MC 1.20.6+
addModJar(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-key-binding-api-v1", rootProject.fabric_api_version))
// Mod Menu
modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}")
// Starlight
addMod("curse.maven:starlight-521783:${rootProject.starlight_version_fabric}", rootProject.enable_starlight)
@@ -79,22 +91,23 @@ dependencies {
modImplementation(fabricApi.module("fabric-rendering-data-attachment-v1", rootProject.fabric_api_version))
modImplementation(fabricApi.module("fabric-rendering-fluids-v1", rootProject.fabric_api_version))
}
// Lithium
addMod("maven.modrinth:lithium:${rootProject.lithium_version}", rootProject.enable_lithium)
// Iris
addMod("maven.modrinth:iris:${rootProject.iris_version}", rootProject.enable_iris)
// BCLib
addMod("com.github.quiqueck:BCLib:${rootProject.bclib_version}", rootProject.enable_bclib)
// Canvas
addMod("io.vram:canvas-fabric-${project.canvas_version}", rootProject.enable_canvas)
// Immersive Portals
if (rootProject.enable_immersive_portals == "1")
modCompileOnly ("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${project.immersive_portals_version}")
if (rootProject.enable_immersive_portals == "1") {
modCompileOnly("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${project.immersive_portals_version}")
}
else if (rootProject.enable_immersive_portals == "2") {
modImplementation ("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${project.immersive_portals_version}") {
exclude(group: "net.fabricmc.fabric-api")
@@ -127,26 +140,13 @@ processResources {
runClient {
dependsOn(copyCoreResources)
dependsOn(copyCommonLoaderResources)
jvmArgs([ "-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg ])
// jvmArgs([ "-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg ])
finalizedBy(deleteResources)
}
//jar {
// classifier "dev"
//}
sourcesJar {
def commonSources = project(":common").sourcesJar
dependsOn commonSources
from commonSources.archiveFile.map { zipTree(it) }
// def fabricLikeSources = project(":fabricLike").sourcesJar
// dependsOn fabricLikeSources
// from fabricLikeSources.archiveFile.map { zipTree(it) }
}
//components.java {
// withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
// skip()
// }
//}
@@ -1,35 +0,0 @@
package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.common.LodCommonMain;
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
@Environment(EnvType.CLIENT)
public class FabricClientMain implements ClientModInitializer
{
public static FabricClientProxy client_proxy;
public static FabricServerProxy server_proxy;
// Do if implements ClientModInitializer
// This loads the mod before minecraft loads which causes a lot of issues
@Override
public void onInitializeClient()
{
DependencySetup.createClientBindings();
FabricMain.init();
LodCommonMain.initConfig();
server_proxy = new FabricServerProxy(false);
server_proxy.registerEvents();
client_proxy = new FabricClientProxy();
client_proxy.registerEvents();
ClientLifecycleEvents.CLIENT_STARTED.register((mc) -> FabricMain.postInit());
}
}
@@ -19,7 +19,9 @@
package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.common.rendering.SeamlessOverdraw;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
@@ -27,41 +29,39 @@ import com.mojang.blaze3d.platform.InputConstants;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
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.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.fabric.wrappers.modAccessor.SodiumAccessor;
//import io.netty.buffer.ByteBuf;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen;
#if MC_VER < MC_1_19_4
import java.nio.FloatBuffer;
#endif
import java.util.HashSet;
import java.util.concurrent.ThreadPoolExecutor;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.HitResult;
import org.apache.logging.log4j.Logger;
import org.joml.Matrix4f;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL15;
/**
* This handles all events sent to the client,
@@ -72,7 +72,7 @@ import org.lwjgl.opengl.GL15;
* @version 2023-7-27
*/
@Environment(EnvType.CLIENT)
public class FabricClientProxy
public class FabricClientProxy implements AbstractModInitializer.IEventProxy
{
private final ClientApi clientApi = ClientApi.INSTANCE;
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
@@ -106,7 +106,7 @@ public class FabricClientProxy
// tick events //
//=============//
ClientTickEvents.START_CLIENT_TICK.register((client) -> { ClientApi.INSTANCE.clientTickEvent(); });
ClientTickEvents.START_CLIENT_TICK.register((client) -> ClientApi.INSTANCE.clientTickEvent());
@@ -122,22 +122,35 @@ public class FabricClientProxy
});
// (kinda) block break event
// Since fabric doesn't have a client-side break-block API event, this is the next best thing
AttackBlockCallback.EVENT.register((player, level, interactionHand, blockPos, direction) ->
{
// if we have access to the server, use the chunk save event instead
if (MC.clientConnectedToDedicatedServer())
{
// Since fabric doesn't have a client-side break-block API event, this is the next best thing
ChunkAccess chunk = level.getChunk(blockPos);
if (chunk != null)
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(blockPos.getX(), blockPos.getZ()))
{
LOGGER.trace("attack block at blockPos: " + blockPos);
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
SharedApi.INSTANCE.chunkBlockChangedEvent(
new ChunkWrapper(chunk, level, wrappedLevel),
wrappedLevel
);
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(blockPos);
if (chunk != null)
{
//LOGGER.trace("attack block at blockPos: " + blockPos);
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
SharedApi.INSTANCE.chunkBlockChangedEvent(
new ChunkWrapper(chunk, level, wrappedLevel),
wrappedLevel
);
}
});
}
}
}
@@ -146,25 +159,38 @@ public class FabricClientProxy
});
// (kinda) block place event
// Since fabric doesn't have a client-side place-block API event, this is the next best thing
UseBlockCallback.EVENT.register((player, level, hand, hitResult) ->
{
// if we have access to the server, use the chunk save event instead
if (MC.clientConnectedToDedicatedServer())
{
// Since fabric doesn't have a client-side place-block API event, this is the next best thing
if (hitResult.getType() == HitResult.Type.BLOCK
&& !hitResult.isInside())
{
ChunkAccess chunk = level.getChunk(hitResult.getBlockPos());
if (chunk != null)
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(hitResult.getBlockPos().getX(), hitResult.getBlockPos().getZ()))
{
LOGGER.trace("use block at blockPos: " + hitResult.getBlockPos());
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
SharedApi.INSTANCE.chunkBlockChangedEvent(
new ChunkWrapper(chunk, level, wrappedLevel),
wrappedLevel
);
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(hitResult.getBlockPos());
if (chunk != null)
{
//LOGGER.trace("use block at blockPos: " + hitResult.getBlockPos());
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
SharedApi.INSTANCE.chunkBlockChangedEvent(
new ChunkWrapper(chunk, level, wrappedLevel),
wrappedLevel
);
}
});
}
}
}
}
@@ -187,30 +213,26 @@ public class FabricClientProxy
// render event //
//==============//
//Define this in the MixinLevelRenderer so that it works with sodium without any changes to the code
// TODO: If all else is fine, can we remove these commented code
// Client Render Level
WorldRenderEvents.AFTER_SETUP.register((renderContext) ->
{
Mat4f projectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f modelViewMatrix;
#if MC_VER < MC_1_20_6
modelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
#else
modelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
#endif
this.clientApi.renderLods(ClientLevelWrapper.getWrapper(renderContext.world()),
McObjectConverter.Convert(renderContext.matrixStack().last().pose()),
McObjectConverter.Convert(renderContext.projectionMatrix()),
renderContext.tickDelta());
// experimental proof-of-concept option
if (Config.Client.Advanced.Graphics.AdvancedGraphics.seamlessOverdraw.get())
{
float[] matrixFloatArray = SeamlessOverdraw.overwriteMinecraftNearFarClipPlanes(renderContext.projectionMatrix(), renderContext.tickDelta());
#if MC_1_16_5
SeamlessOverdraw.applyLegacyProjectionMatrix(matrixFloatArray);
#elif PRE_MC_1_19_4
renderContext.projectionMatrix().load(FloatBuffer.wrap(matrixFloatArray));
#else
renderContext.projectionMatrix().set(matrixFloatArray);
#endif
}
modelViewMatrix,
projectionMatrix,
#if MC_VER < MC_1_21
renderContext.tickDelta()
#else
renderContext.tickCounter().getGameTimeDeltaTicks()
#endif
);
});
// Debug keyboard event
@@ -1,56 +0,0 @@
package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.common.LodCommonMain;
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftDedicatedServerWrapper;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
import com.seibel.distanthorizons.core.util.LodUtil;
import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.server.dedicated.DedicatedServer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Environment(EnvType.SERVER)
public class FabricDedicatedServerMain implements DedicatedServerModInitializer
{
private static final Logger LOGGER = LogManager.getLogger(FabricDedicatedServerMain.class.getSimpleName());
public static FabricServerProxy server_proxy;
public boolean hasPostSetupDone = false;
@Override
public void onInitializeServer()
{
DependencySetup.createServerBindings();
FabricMain.init();
// FIXME this prevents returning uninitialized Config values
// resulting from a circular reference mid-initialization in a static class
// ThreadPresetConfigEventHandler <-> Config
ThreadPresetConfigEventHandler.INSTANCE.toString();
server_proxy = new FabricServerProxy(true);
server_proxy.registerEvents();
ServerLifecycleEvents.SERVER_STARTING.register((server) ->
{
if (this.hasPostSetupDone)
{
return;
}
this.hasPostSetupDone = true;
LodUtil.assertTrue(server instanceof DedicatedServer);
MinecraftDedicatedServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer) server;
LodCommonMain.initConfig();
FabricMain.postInit();
LOGGER.info("Dedicated server initialized at " + server.getServerDirectory());
});
}
}
@@ -19,73 +19,56 @@
package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.jar.ModJarInfo;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.*;
import com.seibel.distanthorizons.common.LodCommonMain;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.fabric.wrappers.FabricDependencySetup;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.*;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.fabric.wrappers.modAccessor.*;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import org.apache.logging.log4j.Logger;
import javax.swing.*;
import java.awt.*;
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.
*
* @author coolGi
* @author Ran
* @version 9-2-2022
*/
public class FabricMain
public class FabricMain extends AbstractModInitializer implements ClientModInitializer, DedicatedServerModInitializer
{
private static final ResourceLocation INITIAL_PHASE = ResourceLocation.tryParse("distanthorizons:dedicated_server_initial");
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static void postInit()
{
LOGGER.info("Post-Initializing Mod");
FabricDependencySetup.runDelayedSetup();
if (Config.Client.Advanced.Graphics.Fog.disableVanillaFog.get() && SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("bclib"))
ModAccessorInjector.INSTANCE.get(IBCLibAccessor.class).setRenderCustomFog(false); // Remove BCLib's fog
#if POST_MC_1_20_1
if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium"))
ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false); // FIXME: This is a tmp fix for sodium 0.5.0, and 0.5.1. This is fixed in sodium 0.5.2
#endif
if (ConfigBase.INSTANCE == null)
throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method.");
LOGGER.info("Mod Post-Initialized");
}
// This loads the mod after minecraft loads which doesn't causes a lot of issues
public static void init()
@Override
protected void createInitialBindings() { SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE); }
@Override
protected IEventProxy createClientProxy() { return new FabricClientProxy(); }
@Override
protected IEventProxy createServerProxy(boolean isDedicated) { return new FabricServerProxy(isDedicated); }
@Override
protected void initializeModCompat()
{
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
LOGGER.info("Initializing Mod");
LodCommonMain.startup(null);
FabricDependencySetup.createInitialBindings();
LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
// Print git info (Useful for dev builds)
LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch);
LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit);
LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source);
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
if (modChecker.isModLoaded("sodium"))
{
@@ -94,10 +77,13 @@ public class FabricMain
// If sodium is installed Indium is also necessary in order to use the Fabric rendering API
if (!modChecker.isModLoaded("indium"))
{
// People don't read the crash logs!!!
// So, just put a notification, so they hopefully realise what's the problem (and dont just open issues)
System.setProperty("java.awt.headless", "false"); // Required to make it work
JOptionPane.showMessageDialog(null, ModInfo.READABLE_NAME + " now relies on Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium", ModInfo.READABLE_NAME, JOptionPane.INFORMATION_MESSAGE);
String indiumMissingMessage = ModInfo.READABLE_NAME + " needs Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium";
LOGGER.fatal(indiumMissingMessage);
if (!GraphicsEnvironment.isHeadless())
{
JOptionPane.showMessageDialog(null, indiumMissingMessage, ModInfo.READABLE_NAME, JOptionPane.INFORMATION_MESSAGE);
}
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
String errorMessage = "loading Distant Horizons. Distant Horizons requires Indium in order to run with Sodium.";
@@ -105,30 +91,44 @@ public class FabricMain
mc.crashMinecraft(errorMessage, new Exception(exceptionError));
}
}
if (modChecker.isModLoaded("starlight"))
{
ModAccessorInjector.INSTANCE.bind(IStarlightAccessor.class, new StarlightAccessor());
}
if (modChecker.isModLoaded("optifine"))
{
ModAccessorInjector.INSTANCE.bind(IOptifineAccessor.class, new OptifineAccessor());
}
if (modChecker.isModLoaded("bclib"))
{
ModAccessorInjector.INSTANCE.bind(IBCLibAccessor.class, new BCLibAccessor());
}
#if MC_1_16_5 || MC_1_18_2 || MC_1_19_2 || MC_1_19_4 || MC_1_20_1
// 1.17.1 won't support this since there isn't a matching Iris version
if (modChecker.isModLoaded("iris"))
{
ModAccessorInjector.INSTANCE.bind(IIrisAccessor.class, new IrisAccessor());
}
this.tryCreateModCompatAccessor("starlight", IStarlightAccessor.class, StarlightAccessor::new);
this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new);
this.tryCreateModCompatAccessor("bclib", IBCLibAccessor.class, BCLibAccessor::new);
#if MC_VER >= MC_1_19_4
// 1.19.4 is the lowest version Iris supports DH
this.tryCreateModCompatAccessor("iris", IIrisAccessor.class, IrisAccessor::new);
#endif
}
@Override
protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler) { }
@Override
protected void subscribeClientStartedEvent(Runnable eventHandler) { ClientLifecycleEvents.CLIENT_STARTED.register((mc) -> eventHandler.run()); }
@Override
protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler)
{
ServerLifecycleEvents.SERVER_STARTING.addPhaseOrdering(INITIAL_PHASE, Event.DEFAULT_PHASE);
ServerLifecycleEvents.SERVER_STARTING.register(INITIAL_PHASE, eventHandler::accept);
}
@Override
protected void runDelayedSetup()
{
SingletonInjector.INSTANCE.runDelayedSetup();
if (Config.Client.Advanced.Graphics.Fog.disableVanillaFog.get() && SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("bclib"))
ModAccessorInjector.INSTANCE.get(IBCLibAccessor.class).setRenderCustomFog(false); // Remove BCLib's fog
#if MC_VER >= MC_1_20_1
if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium"))
ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false); // FIXME: This is a tmp fix for sodium 0.5.0, and 0.5.1. This is fixed in sodium 0.5.2
#endif
LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
if (ConfigBase.INSTANCE == null)
throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method.");
}
}
@@ -1,11 +1,11 @@
package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
@@ -32,7 +32,7 @@ import java.util.function.Supplier;
* @author Tomlee
* @version 5-11-2022
*/
public class FabricServerProxy
public class FabricServerProxy implements AbstractModInitializer.IEventProxy
{
private static final ServerApi SERVER_API = ServerApi.INSTANCE;
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import net.minecraft.client.multiplayer.ClientLevel;
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
#endif
import net.minecraft.world.level.chunk.LevelChunk;
import org.spongepowered.asm.mixin.Mixin;
@@ -44,14 +44,14 @@ public class MixinClientLevel
// //Moved to MixinClientPacketListener
// @Inject(method = "<init>", at = @At("TAIL"))
// private void loadWorldEvent(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey resourceKey,
// #if POST_MC_1_18_2 Holder holder, #else DimensionType dimensionType, #endif int i,
// #if POST_MC_1_18_2 int j, #endif Supplier supplier, LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci)
// #if MC_VER >= MC_1_18_2 Holder holder, #else DimensionType dimensionType, #endif int i,
// #if MC_VER >= MC_1_18_2 int j, #endif Supplier supplier, LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci)
// {
// ClientApi.INSTANCE.clientLevelLoadEvent(WorldWrapper.getWorldWrapper((ClientLevel)(Object)this));
// }
// Moved to overriding the enableChunkLight(...) method over at ClientPacketListener for 1.20+
#if POST_MC_1_18_2 && PRE_MC_1_20_1 // Only the setLightReady is only available after 1.18. This ensures the light data is ready.
#if MC_VER >= MC_1_18_2 && MC_VER < MC_1_20_1 // Only the setLightReady is only available after 1.18. This ensures the light data is ready.
@Inject(method = "setLightReady", at = @At("HEAD"))
private void onChunkLightReady(int x, int z, CallbackInfo ci)
{
@@ -7,11 +7,12 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
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;
#if POST_MC_1_20_1
#if MC_VER >= MC_1_20_1
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.world.level.chunk.LevelChunk;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -23,15 +24,31 @@ public class MixinClientPacketListener
@Shadow
private ClientLevel level;
@Unique
private ClientLevel previousLevel;
@Inject(method = "handleLogin", at = @At("RETURN"))
void onHandleLoginEnd(CallbackInfo ci) { ClientApi.INSTANCE.onClientOnlyConnected(); }
@Inject(method = "handleRespawn", at = @At("HEAD"))
void onHandleRespawnStart(CallbackInfo ci) { ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper(this.level)); }
void onHandleRespawnStart(CallbackInfo ci) { this.previousLevel = this.level; }
@Inject(method = "handleRespawn", at = @At("RETURN"))
void onHandleRespawnEnd(CallbackInfo ci) { ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper(this.level)); }
void onHandleRespawnEnd(CallbackInfo ci)
{
// If the player changes dimensions the "this.level" will be changed halfway through the respawn method.
// By checking if the object references are the same, we can see if the previous level should be unloaded
// or if the player just respawned in the same level.
if (this.previousLevel != this.level)
{
ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper(this.previousLevel));
ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper(this.level));
}
this.previousLevel = null;
}
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
@Inject(method = "cleanup", at = @At("HEAD"))
#else
@Inject(method = "close", at = @At("HEAD"))
@@ -45,7 +62,7 @@ public class MixinClientPacketListener
ClientApi.INSTANCE.onClientOnlyDisconnected();
}
#if POST_MC_1_20_1
#if MC_VER >= MC_1_20_1
@Inject(method = "enableChunkLight", at = @At("TAIL"))
void onEnableChunkLight(LevelChunk chunk, int x, int z, CallbackInfo ci)
{
@@ -1,69 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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.fabric.mixins.client;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.common.util.ILightTextureMarker;
import net.minecraft.client.renderer.texture.DynamicTexture;
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;
@Mixin(DynamicTexture.class)
public class MixinDynamicTexture implements ILightTextureMarker
{
/** Used to prevent accidentally using other dynamic textures as a lightmap */
@Unique
private boolean isLightTexture = false;
@Shadow
@Final
private NativeImage pixels;
@Inject(method = "upload()V", at = @At("HEAD"))
public void updateLightTexture(CallbackInfo ci)
{
// since the light map is always updated on the client render thread we should be able to access the client level at the same time
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (!this.isLightTexture
|| mc == null
|| mc.getWrappedClientLevel() == null
)
{
return;
}
//ApiShared.LOGGER.info("Lightmap update");
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.pixels, clientLevel);
}
public void markLightTexture() { this.isLightTexture = true; }
}
@@ -35,7 +35,7 @@ import net.minecraft.client.renderer.FogRenderer.FogMode;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
import net.minecraft.world.level.material.FluidState;
#else
import net.minecraft.world.level.material.FogType;
@@ -50,14 +50,14 @@ public class MixinFogRenderer
private static final float A_EVEN_LARGER_VALUE = 42069420694206942069.F;
@Inject(at = @At("RETURN"), method = "setupFog")
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, CallbackInfo callback)
{
#else
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float g, CallbackInfo callback)
{
#endif
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
FluidState fluidState = camera.getFluidInCamera();
boolean cameraNotInFluid = fluidState.isEmpty();
#else
@@ -71,7 +71,7 @@ public class MixinFogRenderer
&& !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial()
&& Config.Client.Advanced.Graphics.Fog.disableVanillaFog.get())
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE);
RenderSystem.fogEnd(A_EVEN_LARGER_VALUE);
#else
@@ -16,7 +16,7 @@ public class MixinGameRenderer
private static final Logger LOGGER = LogManager.getLogger(MixinGameRenderer.class.getSimpleName());
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
// FIXME: This I think will dup multiple renderStartupEvent calls...
@Inject(method = {"reloadShaders", "preloadUiShader"}, at = @At("TAIL"))
public void onStartupShaders(CallbackInfo ci)
@@ -20,26 +20,27 @@
package com.seibel.distanthorizons.fabric.mixins.client;
import com.mojang.blaze3d.vertex.PoseStack;
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
import com.mojang.math.Matrix4f;
import org.lwjgl.opengl.GL32;
#else
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LightTexture;
import org.joml.Matrix4f;
#endif
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
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.Camera;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.LightTexture;
import com.seibel.distanthorizons.core.config.Config;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.lighting.LevelLightEngine;
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;
@@ -62,50 +63,67 @@ public class MixinLevelRenderer
{
@Shadow
private ClientLevel level;
@Unique
private static float previousPartialTicks = 0;
// Inject rendering at first call to renderChunkLayer
// HEAD or RETURN
#if PRE_MC_1_17_1
@Inject(at = @At("RETURN"), method = "renderSky(Lcom/mojang/blaze3d/vertex/PoseStack;F)V")
private void renderSky(PoseStack matrixStackIn, float partialTicks, CallbackInfo callback)
{
// get the partial ticks since renderBlockLayer doesn't
// have access to them
previousPartialTicks = partialTicks;
}
#else
@Inject(method = "renderClouds", at = @At("HEAD"), cancellable = true)
public void renderClouds(PoseStack poseStack, Matrix4f projectionMatrix, float tickDelta, double cameraX, double cameraY, double cameraZ, CallbackInfo ci) {
// get the partial ticks since renderChunkLayer doesn't
// have access to them
previousPartialTicks = tickDelta;
}
#endif
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
@Inject(at = @At("HEAD"),
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDD)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack matrixStackIn, double xIn, double yIn, double zIn, CallbackInfo callback)
#elif PRE_MC_1_19_4
#elif MC_VER < MC_1_19_4
@Inject(at = @At("HEAD"),
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLcom/mojang/math/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
#elif PRE_MC_1_20_2
#elif MC_VER < MC_1_20_2
@Inject(at = @At("HEAD"),
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
#else
#elif MC_VER < MC_1_20_6
@Inject(at = @At("HEAD"),
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback)
#else
@Inject(at = @At("HEAD"),
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;DDDLorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f projectionMatrix, Matrix4f frustumMatrix, CallbackInfo callback)
#endif
{
#if MC_VER == MC_1_16_5
// get the matrices from the OpenGL fixed pipeline
float[] mcProjMatrixRaw = new float[16];
GL32.glGetFloatv(GL32.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
mcProjectionMatrix.transpose();
Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
#elif MC_VER <= MC_1_20_4
// get the matrices directly from MC
Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#else
// MC combined the model view and projection matricies
Mat4f mcModelViewMatrix = McObjectConverter.Convert(projectionMatrix);
Mat4f mcProjectionMatrix = new Mat4f();
mcProjectionMatrix.setIdentity();
#endif
if (renderType.equals(RenderType.translucent()))
{
ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(this.level),
mcModelViewMatrix,
mcProjectionMatrix,
#if MC_VER < MC_1_21
Minecraft.getInstance().getFrameTime()
#else
Minecraft.getInstance().getTimer().getRealtimeDeltaTicks()
#endif
);
}
// FIXME completely disables rendering when sodium is installed
if (Config.Client.Advanced.Debugging.lodOnlyMode.get())
{
@@ -113,15 +131,18 @@ public class MixinLevelRenderer
}
}
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runUpdates(IZZ)I"), method = "renderLevel")
public void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
#elif PRE_MC_1_20_1
#elif MC_VER < MC_1_20_1
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runUpdates(IZZ)I"), method = "renderLevel")
public void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
#elif MC_VER < MC_1_20_6
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I"), method = "renderLevel")
private void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
#else
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I"), method = "renderLevel")
private void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
private void callAfterRunUpdates(CallbackInfo ci)
#endif
{
ChunkWrapper.syncedUpdateClientLightStatus();
@@ -20,9 +20,14 @@
package com.seibel.distanthorizons.fabric.mixins.client;
import com.seibel.distanthorizons.common.util.ILightTextureMarker;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -35,13 +40,20 @@ public class MixinLightTexture
{
@Shadow
@Final
private DynamicTexture lightTexture;
private NativeImage lightPixels;
@Inject(method = "<init>", at = @At("RETURN"))
public void markLightTexture(CallbackInfo ci)
@Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci)
{
//
((ILightTextureMarker) this.lightTexture).markLightTexture();
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (mc == null || mc.getWrappedClientLevel() == null)
{
return;
}
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel);
}
}
@@ -1,6 +1,6 @@
package com.seibel.distanthorizons.fabric.mixins.client;
import com.seibel.distanthorizons.api.enums.config.EUpdateBranch;
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
import com.seibel.distanthorizons.common.wrappers.gui.updater.UpdateModScreen;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
@@ -8,10 +8,12 @@ import com.seibel.distanthorizons.core.jar.installer.GitlabGetter;
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import net.minecraft.client.Minecraft;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen;
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.Redirect;
@@ -25,8 +27,17 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Minecraft.class)
public class MixinMinecraft
{
#if PRE_MC_1_20_2
#if MC_1_20_1
/**
* Can be enabled for testing the auto updater UI. <br/>
* will always show the auto updater if set to true.
*/
@Unique
private static final boolean DEBUG_ALWAYS_SHOW_UPDATER = false;
#if MC_VER < MC_1_20_2
#if MC_VER == MC_1_20_1
@Redirect(
method = "Lnet/minecraft/client/Minecraft;setInitialScreen(Lcom/mojang/realmsclient/client/RealmsClient;Lnet/minecraft/server/packs/resources/ReloadInstance;Lnet/minecraft/client/main/GameConfig$QuickPlayData;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setScreen(Lnet/minecraft/client/gui/screens/Screen;)V")
@@ -41,17 +52,17 @@ public class MixinMinecraft
public void onOpenScreen(Minecraft instance, Screen guiScreen)
{
#endif
if (!Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get()) // Don't do anything if the user doesn't want it
if (!Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() && !DEBUG_ALWAYS_SHOW_UPDATER) // Don't do anything if the user doesn't want it
{
instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
return;
}
if (SelfUpdater.onStart())
if (SelfUpdater.onStart() || DEBUG_ALWAYS_SHOW_UPDATER)
{
instance.setScreen(new UpdateModScreen(
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
));
}
else
@@ -61,7 +72,7 @@ public class MixinMinecraft
}
#endif
#if POST_MC_1_20_2
#if MC_VER >= MC_1_20_2
@Redirect(
method = "Lnet/minecraft/client/Minecraft;onGameLoadFinished(Lnet/minecraft/client/Minecraft$GameLoadCookie;)V",
at = @At(value = "INVOKE", target = "Ljava/lang/Runnable;run()V")
@@ -69,15 +80,31 @@ public class MixinMinecraft
private void buildInitialScreens(Runnable runnable)
{
if (
Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() // Don't do anything if the user doesn't want it
&& SelfUpdater.onStart()
)
DEBUG_ALWAYS_SHOW_UPDATER ||
(
// Don't do anything if the user doesn't want it
Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get()
&& SelfUpdater.onStart()
)
)
{
runnable = () -> {
runnable = () ->
{
String versionId;
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
if (updateBranch == EDhApiUpdateBranch.STABLE)
{
versionId = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
}
else
{
versionId = GitlabGetter.INSTANCE.projectPipelines.getFirst().get("sha");
}
Minecraft.getInstance().setScreen(new UpdateModScreen(
// TODO: Change to runnable, instead of tittle screen
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()) : GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
versionId
));
};
}
@@ -23,59 +23,134 @@ import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.config.Config;
import net.minecraft.client.gui.screens.OptionsScreen;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
import net.minecraft.network.chat.TranslatableComponent;
#endif
import net.minecraft.resources.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;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
#if MC_VER >= MC_1_20_6
import net.minecraft.client.gui.layouts.LinearLayout;
import net.minecraft.client.gui.layouts.HeaderAndFooterLayout;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Shadow;
#endif
#if MC_VER < MC_1_21
import net.minecraft.client.gui.screens.OptionsScreen;
#else
import net.minecraft.client.gui.screens.options.OptionsScreen;
#endif
/**
* Adds a button to the menu to goto the config
*
* @author coolGi
* @version 12-02-2021
* @version 2024-5-20
*/
@Mixin(OptionsScreen.class)
public class MixinOptionsScreen extends Screen
{
// Get the texture for the button
private static final ResourceLocation ICON_TEXTURE = new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
protected MixinOptionsScreen(Component title)
{
super(title);
}
/** Texture used for the config opening button */
@Unique
private static final ResourceLocation ICON_TEXTURE =
#if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
#else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png");
#endif
@Inject(at = @At("HEAD"), method = "init")
@Unique
private TexturedButtonWidget optionsButton = null;
#if MC_VER >= MC_1_20_6
@Shadow
@Final
protected HeaderAndFooterLayout layout;
#endif
//==============//
// constructors //
//==============//
protected MixinOptionsScreen(Component title) { super(title); }
@Inject(at = @At("RETURN"), method = "init")
private void lodconfig$init(CallbackInfo ci)
{
if (Config.Client.optionsButton.get())
this. #if PRE_MC_1_17_1 addButton #else addRenderableWidget #endif
(new TexturedButtonWidget(
// 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
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(GetConfigScreen.getScreen(this)),
// Add a title to the utton
#if PRE_MC_1_19_2
new TranslatableComponent(ModInfo.ID + ".title")));
#else
Component.translatable(ModInfo.ID + ".title")));
#endif
{
#if MC_VER < MC_1_17_1
this.addButton(this.getOptionsButton());
#elif MC_VER < MC_1_20_6
this.addRenderableWidget(this.getOptionsButton());
#else
// add the button so it's rendered
this.addRenderableWidget(this.getOptionsButton());
// add the button to the correct location in the UI
// TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements?
LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.getFirst().child;
// determine how wide the other option buttons are so we can put our botton to the left of them all
AtomicInteger width = new AtomicInteger(0);
layout.visitChildren(x -> width.addAndGet(x.getWidth()));
width.addAndGet(-10); // padding between the DH button and the FOV slider
layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> settings.paddingLeft(width.get() * -1));
layout.arrangeElements();
#endif
}
}
//================//
// helper methods //
//================//
@Unique
public TexturedButtonWidget getOptionsButton()
{
if (this.optionsButton == null)
{
this.optionsButton
= new TexturedButtonWidget(
// Where the button is on the screen
this.width / 2 - 180, this.height / 6 - 12,
// Width and height of the button
20, 20,
// texture UV 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
(buttonWidget) -> Objects.requireNonNull(this.minecraft).setScreen(GetConfigScreen.getScreen(this)),
// Add a title to the button
#if MC_VER < MC_1_19_2
new TranslatableComponent(ModInfo.ID + ".title"));
#else
Component.translatable(ModInfo.ID + ".title"));
#endif
}
return this.optionsButton;
}
}
@@ -16,7 +16,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
public class MixinTextureUtil
{
@Redirect(method = "Lcom/mojang/blaze3d/platform/TextureUtil;prepareImage(Lcom/mojang/blaze3d/platform/NativeImage$InternalGlFormat;IIII)V",
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;_texParameter(IIF)V", #if MC_1_16_5 remap = true #else remap = false #endif))
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;_texParameter(IIF)V", #if MC_VER == MC_1_16_5 remap = true #else remap = false #endif))
private static void setLodBias(int target, int pname, float param)
{
float biasValue = Config.Client.Advanced.Graphics.AdvancedGraphics.lodBias.get().floatValue();
@@ -31,7 +31,7 @@ import org.spongepowered.asm.mixin.Mixin;
@Deprecated // TODO: Not sure if this is needed anymore
public class MixinServerLevel
{
// #if PRE_MC_1_17_1
// #if MC_VER < MC_1_17_1
// @Inject(method = "save", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;save(Z)V", shift = At.Shift.AFTER))
// private void saveWorldEvent(ProgressListener progressListener, boolean bl, boolean bl2, CallbackInfo ci) {
// Main.client_proxy.worldSaveEvent();
@@ -2,7 +2,7 @@ package com.seibel.distanthorizons.fabric.mixins.mods.sodium;
/* Removed since DH now uses Indium so we can use the Fabric rendering API instead
#if POST_MC_1_20_1
#if MC_VER >= MC_1_20_1
// Sodium 0.5
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
@@ -55,7 +55,7 @@ public class MixinSodiumRenderer
}
#elif POST_MC_1_17_1
#elif MC_VER >= MC_1_17_1
// Sodium 0.3 to 0.4
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
@@ -22,7 +22,7 @@ package com.seibel.distanthorizons.fabric.mixins.server;
import org.spongepowered.asm.mixin.Mixin;
import net.minecraft.world.level.chunk.ChunkGenerator;
#if PRE_MC_1_18_2
#if MC_VER < MC_1_18_2
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@@ -28,56 +28,66 @@ public class MixinChunkMap
@Final
ServerLevel level;
@Inject(method = "save", at = @At(value = "INVOKE", target = CHUNK_SERIALIZER_WRITE))
// firing at INVOKE causes issues with C2ME and is probably unnecessary since we
// don't need the chunk(s) before MC has finished saving them
@Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE))
private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{
//=====================================//
// corrupt/incomplete chunk validation //
//=====================================//
// 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_1_16_5 || MC_1_17_1
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
// true means a chunk was saved to disk
if (ci.getReturnValue())
{
return;
// TODO is this validation necessary since we are checking above if
// the callback return value should state if the chunk was actually saved or not?
// Do we trust it to always be correct?
//=====================================//
// corrupt/incomplete chunk validation //
//=====================================//
// 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_16_5 || MC_VER == MC_1_17_1
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
{
return;
}
#else
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
{
return;
}
#endif
//==================//
// biome validation //
//==================//
// some chunks may be missing their biomes, which cause issues when attempting to save them
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.getBiomes() == null)
{
return;
}
#else
try
{
// this will throw an exception if the biomes aren't set up
chunk.getNoiseBiome(0,0,0);
}
catch (Exception e)
{
return;
}
#endif
ServerApi.INSTANCE.serverChunkSaveEvent(
new ChunkWrapper(chunk, this.level, ServerLevelWrapper.getWrapper(this.level)),
ServerLevelWrapper.getWrapper(this.level)
);
}
#else
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
{
return;
}
#endif
//==================//
// biome validation //
//==================//
// some chunks may be missing their biomes, which cause issues when attempting to save them
#if MC_1_16_5 || MC_1_17_1
if (chunk.getBiomes() == null)
{
return;
}
#else
try
{
// this will throw an exception if the biomes aren't set up
chunk.getNoiseBiome(0,0,0);
}
catch (Exception e)
{
return;
}
#endif
ServerApi.INSTANCE.serverChunkSaveEvent(
new ChunkWrapper(chunk, this.level, ServerLevelWrapper.getWrapper(this.level)),
ServerLevelWrapper.getWrapper(this.level)
);
}
}
@@ -50,7 +50,7 @@ public class MixinUtilBackgroundThread
}
}
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
@Inject(method = "wrapThreadWithTaskName(Ljava/lang/String;Ljava/lang/Runnable;)Ljava/lang/Runnable;",
at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci)
@@ -62,7 +62,7 @@ public class MixinUtilBackgroundThread
}
}
#endif
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
@Inject(method = "wrapThreadWithTaskName(Ljava/lang/String;Ljava/util/function/Supplier;)Ljava/util/function/Supplier;",
at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci)
@@ -1,60 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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.fabric.mixins.server.unsafe;
import org.spongepowered.asm.mixin.Mixin;
//FIXME: Is this still needed?
#if POST_MC_1_18_2
import net.minecraft.util.ThreadingDetector;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.concurrent.Semaphore;
/**
* Why does this exist? But okay! (Will be probably removed when the experimental generator is done)
* FIXME: Recheck this
*/
@Mixin(ThreadingDetector.class)
public class MixinThreadingDetector
{
@Mutable
@Shadow
private Semaphore lock;
@Inject(method = "<init>", at = @At("RETURN"))
private void setSemaphore(CallbackInfo ci)
{
this.lock = new Semaphore(2);
}
}
#else
import net.minecraft.server.level.ServerLevel;
@Mixin(ServerLevel.class)
public class MixinThreadingDetector { } //FIXME: Is there some way to make this file just not be added?
#endif
@@ -32,7 +32,7 @@ public class ModMenuIntegration implements ModMenuApi
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory()
{
return parent -> GetConfigScreen.getScreen(parent);
return GetConfigScreen::getScreen;
}
}
@@ -1,11 +1,11 @@
package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IBCLibAccessor;
#if MC_1_16_5 || MC_1_17_1
#elif MC_1_18_2
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 || MC_VER == MC_1_20_4 || MC_VER == MC_1_20_6 // These versions either don't have BCLib, or the implementation is different
#elif MC_VER == MC_1_18_2
import ru.bclib.config.ClientConfig;
import ru.bclib.config.Configs;
#else
#elif MC_VER < MC_1_21
import org.betterx.bclib.config.ClientConfig;
import org.betterx.bclib.config.Configs;
#endif
@@ -17,7 +17,8 @@ public class BCLibAccessor implements IBCLibAccessor
public void setRenderCustomFog(boolean newValue)
{
#if !(MC_1_16_5 || MC_1_17_1) // 1.16 and 1.17 don't have "ClientConfig.CUSTOM_FOG_RENDERING"
// only some MC versions have BCLib and require this fix
#if (MC_VER > MC_1_17_1 && MC_VER < MC_1_20_4)
// Change the value of CUSTOM_FOG_RENDERING in the bclib client config
// This disabled fog from rendering within bclib
@@ -19,10 +19,14 @@
package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
#if MC_1_16_5 || MC_1_18_2 || MC_1_19_2 || MC_1_19_4 || MC_1_20_1
#if MC_VER >= MC_1_19_4
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
#if MC_VER <= MC_1_20_4
import net.coderbot.iris.Iris;
#else
import net.irisshaders.iris.Iris;
#endif
import net.irisshaders.iris.api.v0.IrisApi;
public class IrisAccessor implements IIrisAccessor
@@ -22,6 +22,8 @@ package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import net.fabricmc.loader.api.FabricLoader;
import java.io.File;
public class ModChecker implements IModChecker
{
public static final ModChecker INSTANCE = new ModChecker();
@@ -32,4 +34,10 @@ public class ModChecker implements IModChecker
return FabricLoader.getInstance().isModLoaded(modid);
}
@Override
public File modLocation(String modid)
{
return new File(FabricLoader.getInstance().getModContainer(modid).get().getOrigin().getPaths().getFirst().toUri());
}
}
@@ -24,7 +24,7 @@ import java.util.stream.Collectors;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
@@ -33,7 +33,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAcce
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
import net.minecraft.client.Minecraft;
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.Packet;
import net.minecraft.world.entity.Entity;
@@ -60,21 +60,19 @@ public class SodiumAccessor implements ISodiumAccessor
return "Sodium-Fabric";
}
#if POST_MC_1_17_1
#if MC_VER >= MC_1_17_1
@Override
public HashSet<DhChunkPos> getNormalRenderedChunks()
{
SodiumWorldRenderer renderer = SodiumWorldRenderer.instance();
LevelHeightAccessor height = Minecraft.getInstance().level;
#if POST_MC_1_20_1
#if MC_VER >= MC_1_20_1
// TODO: This is just a tmp solution, use a proper solution later
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
return (renderer.isBoxVisible(
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1,
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15));
}).collect(Collectors.toCollection(HashSet::new));
#elif POST_MC_1_18_2
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> (renderer.isBoxVisible(
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1,
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15))).collect(Collectors.toCollection(HashSet::new));
#elif MC_VER >= MC_1_18_2
// 0b11 = Lighted chunk & loaded chunk
return renderer.getChunkTracker().getChunks(0b00).filter(
(long l) -> {
@@ -134,7 +132,7 @@ public class SodiumAccessor implements ISodiumAccessor
@Override
public void setFogOcclusion(boolean b)
{
#if POST_MC_1_20_1
#if MC_VER >= MC_1_20_1
me.jellysquid.mods.sodium.client.SodiumClientMod.options().performance.useFogOcclusion = b;
#endif
}
@@ -3,7 +3,6 @@
"minVersion": "0.8",
"package": "com.seibel.distanthorizons.fabric.mixins",
"mixins": [
"server.unsafe.MixinThreadingDetector",
"server.MixinChunkGenerator",
"server.MixinChunkMap",
"server.MixinUtilBackgroundThread"
@@ -15,7 +14,6 @@
"client.MixinFogRenderer",
"client.MixinGameRenderer",
"client.MixinLevelRenderer",
"client.MixinDynamicTexture",
"client.MixinLightTexture",
"client.MixinOptionsScreen",
"client.MixinMinecraft",
+3 -4
View File
@@ -17,17 +17,17 @@
},
"license": "LGPL-3",
"icon": "icon.png",
"icon": "assets/distanthorizons/icon.png",
"accessWidener": "distanthorizons.accesswidener",
"environment": "*",
"entrypoints": {
"client": [
"com.seibel.distanthorizons.fabric.FabricClientMain"
"com.seibel.distanthorizons.fabric.FabricMain"
],
"server": [
"com.seibel.distanthorizons.fabric.FabricDedicatedServerMain"
"com.seibel.distanthorizons.fabric.FabricMain"
],
"modmenu": [
"com.seibel.distanthorizons.fabric.wrappers.config.ModMenuIntegration"
@@ -56,7 +56,6 @@
},
"suggests": {
"blendium": "*"
},
"breaks": $fabric_incompatibility_list,
+21 -33
View File
@@ -1,8 +1,10 @@
plugins {
// Note: This is only needed for multi-loader projects
// The main architectury loom version is set at the start of the root build.gradle
id "architectury-plugin" version "3.4-SNAPSHOT"
}
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_21
architectury {
platformSetupLoomIde()
@@ -18,6 +20,7 @@ architectury {
//}
loom {
silentMojangMappingsLicense() // Shut the licencing warning
accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
forge {
@@ -25,7 +28,7 @@ loom {
extraAccessWideners.add loom.accessWidenerPath.get().asFile.name
mixinConfigs = [
"DistantHorizons.mixins.json"
"DistantHorizons.forge.mixins.json"
]
}
@@ -36,7 +39,7 @@ loom {
setConfigName("Forge Client")
ideConfigGenerated(true)
runDir("../run")
vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg)
// vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg)
}
server {
server()
@@ -48,18 +51,17 @@ loom {
}
remapJar {
inputFile = shadowJar.archiveFile
dependsOn shadowJar
classifier null
inputFile = shadeDowngradedApi.archiveFile
dependsOn shadeDowngradedApi
}
def addMod(path, enabled) {
if (enabled == "2")
dependencies { implementation(path) }
else if (enabled == "1")
dependencies { modCompileOnly(path) }
}
dependencies {
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
mappings loom.layered() {
@@ -72,24 +74,22 @@ dependencies {
// Forge
forge "net.minecraftforge:forge:${rootProject.minecraft_version}-${rootProject.forge_version}"
// Architectury API
// if (minecraft_version == "1.16.5") {
// implementation("me.shedaniel:architectury-forge:${rootProject.architectury_version}")
// } else {
// implementation("dev.architectury:architectury-forge:${rootProject.architectury_version}")
// }
// Starlight
addMod("curse.maven:starlight-forge-526854:${rootProject.starlight_version_forge}", rootProject.enable_starlight_forge)
// annotationProcessor "org.spongepowered:mixin:0.8.4:processor"
addMod("curse.maven:TerraForged-363820:${rootProject.terraforged_version}", rootProject.enable_terraforged)
addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft)
// if (System.getProperty("idea.sync.active") != "true") {
// annotationProcessor "org.spongepowered:mixin:0.8.4:processor"
// }
if ( // Only run on MC 1.20.6 or later
// FIXME: Add an environment variable for the Major, Minor, and Patch version number of Minecraft
minecraft_version.split("\\.")[1].toInteger() >= 20 &&
(
minecraft_version.split("\\.").length > 1 && // Incase there isn't a minor version
minecraft_version.split("\\.")[2].toInteger() >= 6
)
) {
// (potential) hack fix, force jopt-simple to be exactly 5.0.4 because Mojang ships that version, but some transitive dependencies request 6.0+
implementation('net.sf.jopt-simple:jopt-simple:5.0.4') //{ version { strictly '5.0.4' } }
}
}
task deleteResources(type: Delete) {
@@ -110,15 +110,3 @@ tasks.named('runClient') {
finalizedBy(deleteResources)
}
sourcesJar {
def commonSources = project(":common").sourcesJar
dependsOn commonSources
from commonSources.archiveFile.map { zipTree(it) }
}
//components.java {
// withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
// skip()
// }
//}
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.forge;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.util.ProxyUtil;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
@@ -26,6 +27,7 @@ import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
@@ -36,7 +38,7 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.client.multiplayer.ClientLevel;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
#else
@@ -44,13 +46,13 @@ import net.minecraftforge.event.level.ChunkEvent;
import net.minecraftforge.event.level.LevelEvent;
#endif
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
import net.minecraftforge.client.event.RenderLevelStageEvent;
#endif
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.common.MinecraftForge;
//import net.minecraftforge.network.NetworkRegistry;
//import net.minecraftforge.network.simple.SimpleChannel;
import org.apache.logging.log4j.Logger;
@@ -64,6 +66,8 @@ import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import org.lwjgl.opengl.GL32;
import java.util.concurrent.ThreadPoolExecutor;
/**
* This handles all events sent to the client,
* and is the starting point for most of the mod.
@@ -71,7 +75,7 @@ import org.lwjgl.opengl.GL32;
* @author James_Seibel
* @version 2023-7-27
*/
public class ForgeClientProxy
public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
{
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -79,7 +83,7 @@ public class ForgeClientProxy
// private static SimpleChannel multiversePluginChannel;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
private static LevelAccessor GetEventLevel(WorldEvent e) { return e.getWorld(); }
#else
private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); }
@@ -87,6 +91,15 @@ public class ForgeClientProxy
@Override
public void registerEvents()
{
MinecraftForge.EVENT_BUS.register(this);
this.setupNetworkingListeners();
}
//=============//
// tick events //
//=============//
@@ -107,7 +120,7 @@ public class ForgeClientProxy
//==============//
@SubscribeEvent
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
public void clientLevelLoadEvent(WorldEvent.Load event)
#else
public void clientLevelLoadEvent(LevelEvent.Load event)
@@ -115,7 +128,7 @@ public class ForgeClientProxy
{
LOGGER.info("level load");
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
@@ -131,15 +144,15 @@ public class ForgeClientProxy
ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper);
}
@SubscribeEvent
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
public void clientLevelUnloadEvent(WorldEvent.Unload event)
#else
public void clientLevelUnloadEvent(LevelEvent.Load event)
public void clientLevelUnloadEvent(LevelEvent.Unload event)
#endif
{
LOGGER.info("level unload");
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
@@ -163,30 +176,54 @@ public class ForgeClientProxy
@SubscribeEvent
public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
{
LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{
return;
}
#if PRE_MC_1_19_2
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
#endif
ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk);
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk);
});
}
}
@SubscribeEvent
public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
{
LOGGER.trace("break or block attack at blockPos: " + event.getPos());
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{
return;
}
#if PRE_MC_1_19_2
//LOGGER.trace("break or block attack at blockPos: " + event.getPos());
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
#endif
ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk);
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk);
});
}
}
private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk)
{
@@ -217,7 +254,7 @@ public class ForgeClientProxy
//==============//
@SubscribeEvent
public void registerKeyBindings(#if PRE_MC_1_19_2 InputEvent.KeyInputEvent #else InputEvent.Key #endif event)
public void registerKeyBindings(#if MC_VER < MC_1_19_2 InputEvent.KeyInputEvent #else InputEvent.Key #endif event)
{
if (Minecraft.getInstance().player == null)
{
@@ -237,8 +274,7 @@ public class ForgeClientProxy
// networking //
//============//
/** @param event this is just to ensure the event is called at the right time, if it is called outside the {@link FMLClientSetupEvent} event, the binding may fail */
public static void setupNetworkingListeners(FMLClientSetupEvent event)
public void setupNetworkingListeners()
{
// multiversePluginChannel = NetworkRegistry.newSimpleChannel(
// new ResourceLocation(ModInfo.NETWORKING_RESOURCE_NAMESPACE, ModInfo.MULTIVERSE_PLUGIN_NAMESPACE),
@@ -298,20 +334,18 @@ public class ForgeClientProxy
//===========//
@SubscribeEvent
#if POST_MC_1_18_2
#if MC_VER >= MC_1_18_2
public void afterLevelRenderEvent(RenderLevelStageEvent event)
#else
public void afterLevelRenderEvent(TickEvent.RenderTickEvent event)
#endif
{
#if POST_MC_1_20_1
#if MC_VER >= MC_1_20_1
if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_LEVEL)
#elif POST_MC_1_18_2
#elif MC_VER >= MC_1_18_2
if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_SOLID_BLOCKS)
#else
// FIXME: Is this the correct location for 1.16 & 1.17???
// I couldnt find anything for rendering after the level, so is rendering after overlays ok?
if (event.type.equals(TickEvent.RenderTickEvent.Type.WORLD))
if (event.type.equals(TickEvent.RenderTickEvent.Type.RENDER))
#endif
{
try
@@ -19,184 +19,115 @@
package com.seibel.distanthorizons.forge;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
import com.seibel.distanthorizons.common.LodCommonMain;
import com.seibel.distanthorizons.common.forge.LodForgeMethodCaller;
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.jar.ModJarInfo;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.forge.wrappers.ForgeDependencySetup;
import com.seibel.distanthorizons.forge.wrappers.modAccessor.ModChecker;
import com.seibel.distanthorizons.forge.wrappers.modAccessor.OptifineAccessor;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.core.Direction;
#if POST_MC_1_19_2
import net.minecraft.util.RandomSource;
#endif
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.*;
#if MC_VER == MC_1_16_5
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
#elif MC_VER == MC_1_17_1
import net.minecraftforge.fmlserverevents.FMLServerStartingEvent;
#else
import net.minecraftforge.event.server.ServerStartingEvent;
#endif
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
import net.minecraftforge.fml.ExtensionPoint;
#elif MC_1_17_1
#elif MC_VER == MC_1_17_1
import net.minecraftforge.fmlclient.ConfigGuiHandler;
#elif POST_MC_1_18_2 && PRE_MC_1_19_2
#elif MC_VER >= MC_1_18_2 && MC_VER < MC_1_19_2
import net.minecraftforge.client.ConfigGuiHandler;
#else
import net.minecraftforge.client.ConfigScreenHandler;
#endif
import org.apache.logging.log4j.Logger;
// these imports change due to forge refactoring classes in 1.19
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
import net.minecraftforge.client.model.data.ModelDataMap;
import java.util.Random;
#else
import net.minecraft.client.renderer.RenderType;
import net.minecraftforge.client.model.data.ModelData;
#endif
import java.lang.invoke.MethodHandles;
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.
*
* @author coolGi
* @author Ran
* @author James Seibel
* @version 8-15-2022
*/
@Mod(ModInfo.ID)
public class ForgeMain implements LodForgeMethodCaller
public class ForgeMain extends AbstractModInitializer
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
public static ForgeClientProxy client_proxy = null;
public static ForgeServerProxy server_proxy = null;
public ForgeMain()
{
DependencySetup.createClientBindings();
// initDedicated(null);
// initDedicated(null);
// Register the mod initializer (Actual event registration is done in the different proxies)
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::initClient);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::initDedicated);
FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLClientSetupEvent e) -> this.onInitializeClient());
FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLDedicatedServerSetupEvent e) -> this.onInitializeServer());
}
private void initClient(final FMLClientSetupEvent event)
@Override
protected void createInitialBindings() { SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE); }
@Override
protected IEventProxy createClientProxy() { return new ForgeClientProxy(); }
@Override
protected IEventProxy createServerProxy(boolean isDedicated) { return new ForgeServerProxy(isDedicated); }
@Override
protected void initializeModCompat()
{
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new);
LOGGER.info("Initializing Mod");
LodCommonMain.startup(this);
ForgeDependencySetup.createInitialBindings();
LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
// Print git info (Useful for dev builds)
LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch);
LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit);
LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source);
client_proxy = new ForgeClientProxy();
MinecraftForge.EVENT_BUS.register(client_proxy);
server_proxy = new ForgeServerProxy(false);
MinecraftForge.EVENT_BUS.register(server_proxy);
if (AbstractOptifineAccessor.optifinePresent())
{
ModAccessorInjector.INSTANCE.bind(IOptifineAccessor.class, new OptifineAccessor());
}
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.CONFIGGUIFACTORY,
() -> (client, parent) -> GetConfigScreen.getScreen(parent));
#elif MC_1_17_1 || MC_1_18_2 || PRE_MC_1_19_2
#elif MC_VER >= MC_1_17_1 && MC_VER < MC_1_19_2
ModLoadingContext.get().registerExtensionPoint(ConfigGuiHandler.ConfigGuiFactory.class,
() -> new ConfigGuiHandler.ConfigGuiFactory((client, parent) -> GetConfigScreen.getScreen(parent)));
#else
ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class,
() -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> GetConfigScreen.getScreen(parent)));
#endif
ForgeClientProxy.setupNetworkingListeners(event);
LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
// Init config
// The reason im initialising in this rather than the post init process is cus im using this for the auto updater
LodCommonMain.initConfig();
}
private void initDedicated(final FMLDedicatedServerSetupEvent event)
{
// DependencySetup.createServerBindings();
// initCommon();
// server_proxy = new ForgeServerProxy(true);
// MinecraftForge.EVENT_BUS.register(server_proxy);
//
postInitCommon();
}
private void postInitCommon()
{
LOGGER.info("Post-Initializing Mod");
ForgeDependencySetup.runDelayedSetup();
LOGGER.info("Mod Post-Initialized");
}
#if PRE_MC_1_19_2
private final ModelDataMap modelData = new ModelDataMap.Builder().build();
#else
private final ModelData modelData = ModelData.EMPTY;
#endif
@Override
#if PRE_MC_1_19_2
public List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, Random random)
protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler)
{
return mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, modelData);
MinecraftForge.EVENT_BUS.addListener((RegisterCommandsEvent e) -> { eventHandler.accept(e.getDispatcher()); });
}
#else
public List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, RandomSource random)
{
return mc.getModelManager().getBlockModelShaper().getBlockModel(block.defaultBlockState()).getQuads(blockState, direction, random, modelData #if POST_MC_1_19_2 , RenderType.solid() #endif );
}
#endif
@Override //TODO: Check this if its still needed
public int colorResolverGetColor(ColorResolver resolver, Biome biome, double x, double z)
@Override
protected void subscribeClientStartedEvent(Runnable eventHandler)
{
#if MC_1_17_1______Still_needed
return resolver.m_130045_(biome, x, z);
#else
return resolver.getColor(biome, x, z);
#endif
// FIXME What event is this?
}
@Override
protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler)
{
MinecraftForge.EVENT_BUS.addListener((#if MC_VER >= MC_1_18_2 ServerStartingEvent #else FMLServerStartingEvent #endif e) ->
{
eventHandler.accept(e.getServer());
});
}
@Override
protected void runDelayedSetup() { SingletonInjector.INSTANCE.runDelayedSetup(); }
}
@@ -1,20 +1,19 @@
package com.seibel.distanthorizons.forge;
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.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TickEvent;
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
#else
@@ -23,10 +22,10 @@ import net.minecraftforge.event.level.LevelEvent;
#endif
import net.minecraftforge.eventbus.api.SubscribeEvent;
#if MC_1_16_5
#if MC_VER == MC_1_16_5
import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent;
import net.minecraftforge.fml.event.server.FMLServerStoppingEvent;
#elif MC_1_17_1
#elif MC_VER == MC_1_17_1
import net.minecraftforge.fmlserverevents.FMLServerAboutToStartEvent;
import net.minecraftforge.fmlserverevents.FMLServerStoppingEvent;
#else
@@ -39,9 +38,9 @@ import org.apache.logging.log4j.Logger;
import java.util.function.Supplier;
public class ForgeServerProxy
public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
{
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
private static LevelAccessor GetEventLevel(WorldEvent e) { return e.getWorld(); }
#else
private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); }
@@ -53,6 +52,15 @@ public class ForgeServerProxy
public static Supplier<Boolean> isGenerationThreadChecker = null;
@Override
public void registerEvents()
{
MinecraftForge.EVENT_BUS.register(this);
}
//=============//
// constructor //
//=============//
@@ -81,21 +89,21 @@ public class ForgeServerProxy
// ServerWorldLoadEvent
@SubscribeEvent
public void dedicatedWorldLoadEvent(#if MC_1_16_5 || MC_1_17_1 FMLServerAboutToStartEvent #else ServerAboutToStartEvent #endif event)
public void dedicatedWorldLoadEvent(#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 FMLServerAboutToStartEvent #else ServerAboutToStartEvent #endif event)
{
this.serverApi.serverLoadEvent(this.isDedicated);
}
// ServerWorldUnloadEvent
@SubscribeEvent
public void serverWorldUnloadEvent(#if MC_1_16_5 || MC_1_17_1 FMLServerStoppingEvent #else ServerStoppingEvent #endif event)
public void serverWorldUnloadEvent(#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 FMLServerStoppingEvent #else ServerStoppingEvent #endif event)
{
this.serverApi.serverUnloadEvent();
}
// ServerLevelLoadEvent
@SubscribeEvent
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
public void serverLevelLoadEvent(WorldEvent.Load event)
#else
public void serverLevelLoadEvent(LevelEvent.Load event)
@@ -109,7 +117,7 @@ public class ForgeServerProxy
// ServerLevelUnloadEvent
@SubscribeEvent
#if PRE_MC_1_19_2
#if MC_VER < MC_1_19_2
public void serverLevelUnloadEvent(WorldEvent.Unload event)
#else
public void serverLevelUnloadEvent(LevelEvent.Unload event)
@@ -14,10 +14,24 @@ import java.util.Set;
*/
public class ForgeMixinPlugin implements IMixinConfigPlugin
{
private boolean firstRun = false;
private boolean isForgeMixinFile;
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName)
{
if (!this.firstRun) {
try {
Class<?> cls = Class.forName("net.neoforged.fml.common.Mod"); // Check if a NeoForge exclusive class exists
this.isForgeMixinFile = false;
} catch (ClassNotFoundException e) {
this.isForgeMixinFile = true;
}
}
if (!this.isForgeMixinFile)
return false;
if (mixinClassName.contains(".mods."))
{ // If the mixin wants to go into a mod then we check if that mod is loaded or not
return ModList.get().isLoaded(
@@ -28,44 +42,27 @@ public class ForgeMixinPlugin implements IMixinConfigPlugin
.replaceAll("\\..*$", "") // Replaces everything after the mod name
);
}
return true;
}
@Override
public void onLoad(String mixinPackage)
{
}
public void onLoad(String mixinPackage) { }
@Override
public String getRefMapperConfig()
{
return null;
}
public String getRefMapperConfig() { return null; }
@Override
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets)
{
}
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { }
@Override
public List<String> getMixins()
{
return null;
}
public List<String> getMixins() { return null; }
@Override
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo)
{
}
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { }
@Override
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo)
{
}
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { }
}
@@ -16,7 +16,7 @@ public class MixinClientPacketListener
@Inject(method = "handleLogin", at = @At("RETURN"))
void onHandleLoginEnd(CallbackInfo ci) { ClientApi.INSTANCE.onClientOnlyConnected(); }
#if PRE_MC_1_19_4
#if MC_VER < MC_1_19_4
@Inject(method = "cleanup", at = @At("HEAD"))
#else
@Inject(method = "close", at = @At("HEAD"))
@@ -35,7 +35,7 @@ import net.minecraft.client.renderer.FogRenderer.FogMode;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
import net.minecraft.world.level.material.FluidState;
#else
import net.minecraft.world.level.material.FogType;
@@ -53,10 +53,10 @@ public class MixinFogRenderer
@Inject(at = @At("RETURN"),
method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;FZF)V",
remap = #if MC_1_17_1 || MC_1_18_2 false #else true #endif ) // Remap messiness due to this being weird in forge
remap = #if MC_VER == MC_1_17_1 || MC_VER == MC_1_18_2 false #else true #endif ) // Remap messiness due to this being weird in forge
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float partTick, CallbackInfo callback)
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
FluidState fluidState = camera.getFluidInCamera();
boolean cameraNotInFluid = fluidState.isEmpty();
#else
@@ -71,7 +71,7 @@ public class MixinFogRenderer
&& !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial()
&& Config.Client.Advanced.Graphics.Fog.disableVanillaFog.get())
{
#if PRE_MC_1_17_1
#if MC_VER < MC_1_17_1
RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE);
RenderSystem.fogEnd(A_EVEN_LARGER_VALUE);
#else

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