Compare commits

..

230 Commits

Author SHA1 Message Date
James Seibel 2d2e7524ae up version number 2.4.4 -> 2.4.5 2025-12-24 22:06:59 -06:00
James Seibel e8ff7abaea Replace MC color code strings with an enum 2025-12-24 22:05:59 -06:00
James Seibel 008ad52bbc remove dev from version number 2025-12-23 22:57:10 -06:00
James Seibel d0b44a1ffc Fix toggling world gen not recreating queue 2025-12-23 22:57:05 -06:00
James Seibel 4ffe430686 up DH api version 5.0.0 -> 5.1.0 2025-12-23 20:01:19 -06:00
James Seibel 19ca97c6c1 add experimental option to ignore rendering dimensions by name 2025-12-23 12:22:05 -06:00
James Seibel 3334394bfd Fix earth curvature shader compiling 2025-12-23 08:47:50 -06:00
James Seibel 180e7cd814 change forge fog config to match neo/fabric 2025-12-22 20:32:41 -06:00
James Seibel 84cf4505f2 merge world gen refactor 2025-12-20 10:54:07 -06:00
James Seibel 1d74eea3ef reduce stuttering at the cost of lighting quality 2025-12-20 10:53:14 -06:00
James Seibel 6ee2e6be25 up manifold version 25.1.27 -> 25.1.28 2025-12-19 16:55:32 -06:00
James Seibel b5c47d67cb up version number 2.4.3 -> 2.4.4-dev 2025-12-18 10:04:57 -06:00
James Seibel ead59d0817 remove dev from version number 2025-12-18 09:35:57 -06:00
James Seibel 1c9229c8f1 Fix 1.21.11 stuttering when flying into new chunks in singleplayer 2025-12-18 09:35:54 -06:00
James Seibel 968a14c6a5 remove chunkWrapper.isStillValid() 2025-12-18 09:35:21 -06:00
James Seibel 851c7439d5 Fix GLProxy error in multiplayer 2025-12-17 09:02:15 -06:00
s809 c902357a8f Update core 2025-12-17 00:17:27 +05:00
s809 63170078f5 Fix returning wrong dimension name 2025-12-17 00:17:21 +05:00
James Seibel d0dd1f125b ignore chunk update events during all world gen pos 2025-12-15 15:07:01 -06:00
James Seibel 32950d793e slight light engine optimization 2025-12-15 14:37:22 -06:00
James Seibel 54e9bad907 up version number 2.4.2 -> 2.4.3-dev 2025-12-15 10:17:34 -06:00
James Seibel bb4ac770bd remove dev from version number 2025-12-15 09:49:23 -06:00
James Seibel 16afada6e9 Fix inconsistency with server/client wrapper dim names 2025-12-15 09:49:23 -06:00
James Seibel 7d0785a5fa Fix dimension names missing namespace for multiplayer folders 2025-12-15 08:56:08 -06:00
James Seibel 6a67df462b Move GC warning into the log 2025-12-15 08:44:12 -06:00
s809 0c45c76ff8 Use a different path for zstd natives 2025-12-15 11:21:28 +05:00
James Seibel bcb442e38d Improve initial library check error handling 2025-12-14 22:29:22 -06:00
James Seibel 977ae471ea Fix auto update success dialog 2025-12-14 21:51:00 -06:00
James Seibel b1701ab0d0 remove iris unsupported error for neo 1.21.11 2025-12-14 21:20:22 -06:00
James Seibel c048d5cb56 hide LODs when underwater 2025-12-14 17:22:40 -06:00
James Seibel 2702f742d6 up version number 2.4.1 -> 2.4.2-dev 2025-12-14 17:00:44 -06:00
James Seibel 7add025c8a remove dev from version number 2025-12-14 13:46:24 -06:00
James Seibel 9dc220feb2 re-generate ZStd relocated cache
The previous versions appeared to be out of date, causing linker errors
2025-12-13 21:32:15 -06:00
s809 79955252c9 Add zstd to relocated natives & update sqlite 2025-12-14 04:03:19 +05:00
James Seibel bf13cc48a3 Print a warning if G1GC is used 2025-12-13 16:47:03 -06:00
James Seibel cf454c80d7 add Zstd decompress lib check in initalizer 2025-12-13 15:48:09 -06:00
James Seibel e6404cd882 remove double ";" in ForgeMain 2025-12-13 15:44:40 -06:00
James Seibel 52e64dc403 log if a mod accessor isn't added 2025-12-13 15:44:27 -06:00
James Seibel 7c858afc5d add partial oculus support 2025-12-13 13:17:43 -06:00
James Seibel a44a5d7465 replace client ticks with a timer
Prevents DH loading issues when MC ticks are paused
2025-12-13 11:19:39 -06:00
James Seibel f2d373b779 up version number 2.4.0 -> 2.4.1-dev 2025-12-13 10:20:53 -06:00
James Seibel 5138af0b78 up version number 2.3.7 -> 2.4.0 2025-12-13 10:20:17 -06:00
James Seibel 37e99cd0a5 Fix infinite loop in DhSectionPos 2025-12-13 09:10:17 -06:00
James Seibel 6cb8cd2bd9 fix missing localization 2025-12-12 07:45:54 -06:00
James Seibel c5ae7da96c add mod menu and neo iris support 2025-12-11 21:37:08 -06:00
James Seibel 59b886c5e3 API cleanup 2025-12-11 07:35:47 -06:00
James Seibel 01e78f249f auto disable clouds/chunk fading and fix sodium crash 2025-12-10 18:51:13 -06:00
James Seibel e1c3da59db Fix pre MC 1.21.11 compiling 2025-12-10 07:28:39 -06:00
James Seibel cdfb46b041 Allow world gen limits on singleplayer 2025-12-10 07:09:44 -06:00
James Seibel f944fe4409 change the default issue template 2025-12-09 21:15:20 -06:00
James Seibel c2e45f3d65 add support for MC 1.21.11 2025-12-09 21:15:09 -06:00
James Seibel a9c4f3ea46 revert long windows filepath char 2025-12-09 07:21:45 -06:00
James Seibel 204d48517b Merge branch 'batchGenRefactor' 2025-12-09 07:16:32 -06:00
James Seibel 759ec3676c Add logging/messaging for corrupted DB files 2025-12-09 07:12:38 -06:00
James Seibel 8d2b5fa3ce fix unit tests and enable long config file paths 2025-12-06 12:28:35 -06:00
James Seibel 2851b2c2db Merge branch 'batchGenRefactor' 2025-12-06 12:19:26 -06:00
James Seibel e5ea86bf8f Fix handling bad heightmaps 2025-12-06 12:14:03 -06:00
James Seibel 533c6a7f93 Fix compiling MC 1.21.5 2025-12-06 12:04:35 -06:00
James Seibel ff41e070fd Fix some iris issues on Neoforge 2025-12-06 11:59:39 -06:00
James Seibel b3f607e132 clean up version property files 2025-12-06 11:07:41 -06:00
James Seibel 9ecbb9cc9f Fix old version compiling 2025-12-06 10:05:32 -06:00
James Seibel 7888de8200 Move world gen files into different namespaces 2025-12-06 09:44:32 -06:00
James Seibel d2327ae836 log incomplete world gen warnings 2025-12-06 09:35:39 -06:00
James Seibel ea92a8f922 revert internal server to FEATURES to improve speed 2025-12-06 09:18:55 -06:00
James Seibel 47e97630b4 Improve pre-existing chunk handling 2025-12-04 07:52:54 -06:00
James Seibel c518345bcd enable long file paths on windows for the DB 2025-12-02 07:08:27 -06:00
James Seibel c250a7408e enable long file paths on windows for the DB 2025-12-02 07:07:31 -06:00
James Seibel 30da01f580 TEST 2025-11-29 09:59:38 -06:00
James Seibel 2c71c2bf76 maybe fix concurrency error during world gen shutdown 2025-11-28 16:31:23 -06:00
James Seibel 13a4505d7d Fix biome blending using underground biomes 2025-11-28 15:54:06 -06:00
James Seibel 7f0eeb9f15 ignore chunk updates during internal server generation 2025-11-28 10:48:55 -06:00
James Seibel d7eabcf3a6 don't render thick snow layers 2025-11-28 09:39:22 -06:00
James Seibel 7047d0afdf fix 1.21.10 compiling 2025-11-27 22:16:48 -06:00
James Seibel f318b52280 Fix compiling for older MC versions 2025-11-27 22:12:46 -06:00
James Seibel f50613e20c world gen var renaming 2025-11-27 18:56:58 -06:00
James Seibel af3a993042 massively speed up pre-existing world importing 2025-11-27 18:51:30 -06:00
James Seibel ace1aab42e refactor internal server world gen 2025-11-27 10:44:47 -06:00
James Seibel 350d72b6ec Update .editorconfig 2025-11-26 13:55:47 -06:00
James Seibel 986a6cdc19 Update coreSubProjects 2025-11-26 13:53:10 -06:00
James Seibel 2aa8d9f489 update neoforge reference 1.21.10 6 -> 55 2025-11-26 13:52:51 -06:00
James Seibel 5652a9328c add/replace ZStd stream with block compression 2025-11-24 14:43:56 -06:00
James Seibel b7253b6549 refactor chunk file handling 2025-11-22 12:23:54 -06:00
James Seibel e026cf104c remove unneeded lambda 2025-11-22 11:59:49 -06:00
James Seibel ab4a9cbb55 Remove world gen step duplicate code 2025-11-22 11:27:01 -06:00
James Seibel a709ab6071 move internal server into its own file 2025-11-22 11:02:13 -06:00
James Seibel 3c11a2dc33 Update .editorconfig 2025-11-22 10:29:00 -06:00
James Seibel ce2aa6602a remove unused items from world gen event 2025-11-22 09:48:35 -06:00
James Seibel 738aff8ec6 remove performance recording in batch gen 2025-11-22 09:30:13 -06:00
James Seibel 91da0bf252 replace and simplify WorldGenThreadCheck 2025-11-22 09:25:55 -06:00
James Seibel af8dea9d9f Start world gen refactoring 2025-11-22 08:17:01 -06:00
James Seibel 384933d351 re-add biome blending to API config options 2025-11-18 07:42:48 -06:00
James Seibel a5344d50c2 hopefully fix some downsampling holes 2025-11-18 07:33:33 -06:00
James Seibel 2b4c5b91a4 Fix biome fading after removing FullData MinY 2025-11-16 16:48:13 -06:00
James Seibel 3090544b85 Fix rendering when Iris isn't installed 2025-11-16 16:11:56 -06:00
James Seibel 75e1bbbe17 Fix biome blending gridlines 2025-11-15 19:09:42 -06:00
James Seibel 31b604b5c8 Fix shaders when far clip fading is active 2025-11-15 18:20:52 -06:00
James Seibel 70ba93abec Fix batch generator compiling 2025-11-15 15:36:18 -06:00
James Seibel f628a7f21e Fix WorldGen after restarting generation 2025-11-15 12:08:03 -06:00
James Seibel 254f51ea5e replace server tick/world gen tick with a timer 2025-11-15 09:50:05 -06:00
James Seibel d44152dc46 fix compiling 2025-11-14 07:48:33 -06:00
James Seibel 0ef979ee6c Merge branch 'adjData' 2025-11-14 07:46:46 -06:00
James Seibel f67fb1758d Speed up shutdown and reduce logging 2025-11-14 07:46:10 -06:00
James Seibel cf643372b6 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-11-13 07:22:30 -06:00
James Seibel dc1a117f6b revert adjData core changes 34412305 2025-11-13 07:22:25 -06:00
James Seibel 2545c7e76d Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-11-13 07:21:10 -06:00
James Seibel 506ba05b34 Don't duplicate adjacent data 2025-11-13 07:18:13 -06:00
James Seibel 70be3f9364 Add varint encoding for full data 2025-11-12 07:22:00 -06:00
s809 8d9b5f66fa Prevent auto-pause while pregen is running 2025-11-11 23:48:17 +05:00
James Seibel 3a01151137 re-add GPU upload config including "none" 2025-11-10 07:33:11 -06:00
James Seibel 197051747a put zstd version in main gradle file 2025-11-10 07:31:14 -06:00
James Seibel 45efeb96fa Optimize ColumnBox building 2025-11-08 18:08:10 -06:00
James Seibel e05dff3fb9 Optimize DhTintGetter 2025-11-08 17:51:50 -06:00
s809 aec28854a3 Clean up code a bit 2025-11-08 16:10:15 +05:00
James Seibel 106d97e0a1 Revert "minor AbstractDhTintGetter optimization"
This reverts commit 6a418de153.
2025-11-07 07:02:21 -06:00
Acuadragon100 63acd94fd4 Fix /dh command being deleted after reloading. 2025-11-06 16:19:53 +01:00
James Seibel 34412305d0 adjData core changes 2025-11-04 07:48:35 -06:00
James Seibel 6a418de153 minor AbstractDhTintGetter optimization 2025-10-29 21:38:57 -05:00
James Seibel 41c6b2936b Add experimental fast loading option 2025-10-28 07:47:14 -05:00
James Seibel 97130d1535 minor optimization to tint getting 2025-10-28 07:35:26 -05:00
James Seibel d61dcfaab6 optimize BiomeWrapper getting slightly 2025-10-28 07:26:44 -05:00
James Seibel 3c3f1ef41b Add far clip fading 2025-10-25 11:54:44 -05:00
James Seibel ed1d6396fd Fix iris not setting face culling in the MC state manager 2025-10-25 08:38:34 -05:00
James Seibel 518ec18362 framebuffer name consistency fix 2025-10-25 08:37:16 -05:00
James Seibel 5715cd9266 Fixes !82 (replay mod crash during shutdown) 2025-10-23 07:18:04 -05:00
James Seibel 56953efabc disallow writing to ImposterProtoChunks during world gen 2025-10-22 07:31:59 -05:00
James Seibel 351802de4c run occlusion culling whenever saving a LOD
Also run culling for every column in an LOD, which improves compression by about 20%
- Thanks Scaevolus
2025-10-22 07:25:08 -05:00
James Seibel f60e74c838 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-10-19 16:08:02 -05:00
James Seibel 2007a6af24 Clean up LodRendering logic 2025-10-19 16:06:19 -05:00
s809 4bc199fe14 Merge branch 'feature/server-keys' 2025-10-19 22:58:48 +05:00
James Seibel bcd9a0da2c Make LodRenderer a singleton 2025-10-18 11:43:50 -05:00
James Seibel 50c97e3ca3 Fix Batch generator registries import 2025-10-18 11:43:00 -05:00
James Seibel 7539cb94d4 Fix internal server generation not loading chunks 2025-10-17 07:27:04 -05:00
James Seibel 3a20329096 Dh and level wrapper refactoring and commenting 2025-10-17 07:21:48 -05:00
James Seibel 702002c540 up api version 4.1.0 -> 5.0.0
due to logger enum changes
2025-10-15 17:39:03 -05:00
James Seibel 7f790e2c9c merge loggers and add logger builder 2025-10-15 17:38:43 -05:00
James Seibel b3f8b03fdf up version number 2.3.6 -> 2.3.7 2025-10-13 18:03:26 -05:00
James Seibel 3597d69fa4 remove dev from version number 2025-10-13 17:45:45 -05:00
James Seibel ab10265150 Fix 1.21.9 missing fabric resource loader v1 2025-10-13 17:45:45 -05:00
s809 671ee84136 Handle server keyed level wrappers correctly 2025-10-13 18:07:06 +05:00
James Seibel c489cebae3 Fix world gen progress ui button 2025-10-13 07:43:02 -05:00
James Seibel e55eeda1ac Improve config gui object casting 2025-10-13 07:35:53 -05:00
James Seibel 0086f40053 bug fixing for major config refactoring 2025-10-12 21:11:16 -05:00
James Seibel 71bb151e61 Fix 1.21.10 compile typo 2025-10-12 19:41:42 -05:00
James Seibel a71ceac15d Fix forge compiling 2025-10-12 19:41:03 -05:00
James Seibel ada36c34c7 minor client wrapper refactor for clarity 2025-10-12 15:30:20 -05:00
James Seibel 3cfb4386d9 set default dev version to 1.21.10 2025-10-12 15:07:23 -05:00
James Seibel 352d0f4759 re-add MC 1.21.9 support 2025-10-12 15:04:31 -05:00
James Seibel d158a89592 Fix neoforge rendering 2025-10-12 15:02:09 -05:00
James Seibel 9cc826f8a9 Fix client tinted biomes 2025-10-12 14:49:32 -05:00
James Seibel 22a4c6bc79 Fix neoforge server startup crash 2025-10-12 14:05:27 -05:00
James Seibel 20b9f4f1cb Fix stuttering when flying over un-generated chunks 2025-10-12 13:58:03 -05:00
James Seibel 5c0c1c5e20 up version number 2.3.5 -> 2.3.6 2025-10-11 20:55:45 -05:00
James Seibel 69645994c9 remove dev from version number 2025-10-11 20:54:47 -05:00
James Seibel fdcbb0594d fix version constant 1.21.9 -> 1.21.10 2025-10-11 20:40:06 -05:00
James Seibel e5711b4ddb Fix newer fabric versions needing two resource loaders 2025-10-11 18:42:08 -05:00
James Seibel cb1a617b92 Fix a few old MC versions compiling 2025-10-11 12:04:41 -05:00
James Seibel 4187eaf112 Fix fading when Sodium is installed 2025-10-11 11:14:12 -05:00
James Seibel 084de2b3f1 Fix double pass fading (sodium still breaks it) 2025-10-10 07:44:47 -05:00
James Seibel 7b0a9d4843 Replace 1.21.9 with 1.21.10 2025-10-10 07:35:37 -05:00
James Seibel c42f800db5 add fabric 1.21.9 support 2025-10-10 07:15:27 -05:00
James Seibel 300ef3745f Fix VANILLA_CHUNKS API world gen 2025-10-08 17:27:11 -05:00
James Seibel 5a9fdb3314 Fix neforge texture validation support 2025-10-07 07:43:46 -05:00
James Seibel 8708ca3048 Update to latest neoforge 1.21.9 version 2025-10-07 07:18:32 -05:00
James Seibel 9cfdbdc0ca Fix compiling for MC 1.21.8 and older 2025-10-05 16:27:06 -05:00
James Seibel 48f82b0966 Re-add biome blending 2025-10-05 16:23:27 -05:00
James Seibel 75f7ef0085 Separate DH pool threads and new executor "Render Loader" 2025-10-04 20:11:19 -05:00
James Seibel 270776a782 fix world gen protochunks in 1.21.9 2025-10-04 10:48:50 -05:00
James Seibel 21fff72521 Fix f3 menu for 1.21.9 2025-10-04 10:28:52 -05:00
James Seibel a860a9740d Fix neoforge not rendering 2025-10-02 20:30:18 -05:00
James Seibel a2105699fa Write custom timeout logic for DelayedDataSourceCache 2025-10-02 20:30:16 -05:00
James Seibel ea4838c791 add DhApiChunkProcessingEvent 2025-10-02 18:03:43 -05:00
James Seibel 9168543945 Fix a few compiler errors for old MC versions 2025-10-02 17:34:29 -05:00
James Seibel 460996d8cd add 1.21.9 to the CI list 2025-10-02 07:44:08 -05:00
James Seibel c3948abc40 fix older MC version compiling 2025-10-02 07:42:49 -05:00
James Seibel 8ed902f6f2 rename ChunkLoader -> ChunkFileReader 2025-10-02 07:39:03 -05:00
James Seibel 15dda30050 add neo 1.21.9 support 2025-10-02 07:37:54 -05:00
James Seibel 26428ff905 Fix config UI crashing on older MC versions 2025-09-29 19:06:20 -05:00
James Seibel 4419ab4b8c Manually close compression streams to try reducing GC reliance 2025-09-29 17:21:08 -05:00
James Seibel 5d50994beb cull LOD rendering on the quad tree 2025-09-29 07:28:12 -05:00
James Seibel ce9f843048 config gui refactoring 2025-09-28 16:32:20 -05:00
James Seibel 8c934b4982 config gui refactoring 2025-09-28 16:30:33 -05:00
James Seibel 9ae41865f3 Fix tooltips rendering incorrectly 2025-09-28 16:29:08 -05:00
James Seibel ade7a8d8dc minor config handler refactoring 2025-09-28 16:14:38 -05:00
James Seibel 461dad9fe8 Fix compiling on MC 1.19.4 and older 2025-09-27 22:09:36 -05:00
James Seibel b832e77ac2 Refactor ClassicConfigGUI, add disabled API buttons 2025-09-27 20:58:55 -05:00
James Seibel 66c41e49db update manifold 2025.1.20 -> 2025.1.27 2025-09-27 20:08:30 -05:00
James Seibel 0f968255e0 Move lang missing test back to GetConfigScreen 2025-09-27 20:08:04 -05:00
James Seibel ed50161cfd Fix compiling for pre MC 1.20.6 2025-09-27 08:35:57 -05:00
s809 7e18acdb8f Make lang initialization client only 2025-09-26 21:45:40 +05:00
s809 5c05fdd9fa Add global bandwidth limit setting 2025-09-26 21:45:19 +05:00
James Seibel afca4d837f Force Mac upload method to DATA
Maybe will help with crashing/memory corruption?
Data is the most basic upload method in GL so Mac should be able to support it a lot better than BUFFER_STORAGE.
2025-09-24 07:23:36 -05:00
James Seibel 6db3795fa5 Start refactoring ClassicConfigGUI 2025-09-21 21:30:01 -05:00
James Seibel 21fe38da8e up LWJGL version 3.3.1 -> 3.3.3
Shouldn't actually be used by anything, just for use with unused the Swing GUI
2025-09-21 21:28:12 -05:00
James Seibel 340c0bc586 Move lang test to abstract mod init 2025-09-21 21:26:36 -05:00
James Seibel 77c7ebc0d0 Fix a few UI screens crashing 2025-09-21 21:25:57 -05:00
James Seibel f55cb4b320 maybe fix freebsd OS crashing 2025-09-20 22:40:58 -05:00
James Seibel 9d3c88f0b2 Update coreSubProjects 2025-09-20 16:29:38 -05:00
James Seibel e397a3e47a fix annotation compiling again 2025-09-20 16:29:32 -05:00
James Seibel 4ff315de91 fix missing annotations for compiling 2025-09-20 15:58:40 -05:00
James Seibel 2fb1c43d7c Allow native relocator to run on freebsd 2025-09-20 15:58:19 -05:00
James Seibel 58b5fac20b Add experimental option to maybe help with Mac crashing 2025-09-20 15:11:09 -05:00
James Seibel ec238d29c6 Fix "CUSTOM" quality preset when Iris is present 2025-09-16 07:44:26 -05:00
James Seibel dac36c9e34 include world gen chunk/sec rate in progress log 2025-09-14 08:18:40 -05:00
James Seibel caca05c0f0 up fabric loader/api version for 1.21.8 2025-09-14 08:12:00 -05:00
James Seibel 5ce7eae7c0 Fix DH world gen missing structures 2025-09-14 08:11:47 -05:00
James Seibel 2c38401637 Improve world gen task queue speed slightly 2025-09-13 17:59:52 -05:00
James Seibel 489fe753f5 remove unexplored terrain rendering 2025-09-11 07:07:26 -05:00
James Seibel 2293afc2d3 remove unexplored terrain rendering 2025-09-11 07:06:20 -05:00
James Seibel b48adeb3e3 Add unexplored ocean for overworld 2025-09-10 07:46:31 -05:00
James Seibel 438186cb70 improve lod load time slightly 2025-09-07 16:16:33 -05:00
James Seibel c86ff4acae fix api jar version 4.0.0 -> 4.1.0 2025-09-06 12:18:00 -05:00
James Seibel e27e1bc2d7 add unexplored fog 2025-09-06 12:00:00 -05:00
James Seibel 336bf2ea26 Don't merge blocks that get colored by blocks above into columns 2025-09-06 08:53:30 -05:00
James Seibel e72c08b0bc Fix LOD-only rendering mode 2025-09-06 08:38:39 -05:00
James Seibel 4ced316304 add (native) ZStd compression as default compressor 2025-09-03 07:40:24 -05:00
s809 c84ee721e3 Bump protocol version 2025-08-16 21:01:49 +05:00
s809 c46c056980 Add a server keys feature 2025-08-16 20:59:34 +05:00
s809 d486878876 Replace pooled buffers with unpooled 2025-08-07 17:55:27 +05:00
s809 b0b0b38bf8 Reduce network logging by default 2025-07-27 23:21:30 +05:00
James Seibel 9e43896864 up version number 2.3.4 -> 2.3.5 2025-07-19 14:59:19 -05:00
James Seibel d09ddf57d3 remove dev from version number 2025-07-19 14:57:19 -05:00
James Seibel 486edac1d8 replace 1.21.7 with 1.21.8 in gitlab CI 2025-07-19 14:56:32 -05:00
James Seibel 3b3731a137 Replace 1.21.7 support with 1.21.8 2025-07-19 13:29:29 -05:00
s809 563ec70154 Load level on player add if missing 2025-07-19 19:05:59 +05:00
James Seibel 175f5ed6d6 Improve logging for initial DH startup 2025-07-16 07:28:15 -05:00
James Seibel 1e63607233 Fix AfterDhInitEvent not firing on Neo
Fixes some Iris shader compile issues
2025-07-16 07:18:23 -05:00
James Seibel 4dd8be23fa Merge !81 (forge AfterInit API Event not firing on clients) 2025-07-16 06:56:38 -05:00
James Seibel 7fac5b4c6e Remove chunky incompatibility
I'm still a bit annoyed that problems can arise, but if people are able to configure DH/Chunky to work well together, I won't stop them from running together.

Note that a warning will be printed to the chat/log, if chunky is detected.
2025-07-15 07:49:44 -05:00
James Seibel f2ec1ecf3f Update all neoforge references to latest
Hopefully will fix some shader issues #1087
2025-07-15 07:41:19 -05:00
James Seibel 2674e6b2e9 up the version number 2.3.3 -> 2.3.4 2025-07-12 09:35:52 -05:00
162 changed files with 6266 additions and 4603 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ insert_final_newline = false
max_line_length = 1000 max_line_length = 1000
tab_width = 4 tab_width = 4
trim_trailing_whitespace = false trim_trailing_whitespace = false
ij_continuation_indent_size = 8 ij_continuation_indent_size = 4
ij_formatter_off_tag = @formatter:off ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = true ij_formatter_tags_enabled = true
+8 -1
View File
@@ -35,7 +35,14 @@ build:
stage: build stage: build
parallel: parallel:
matrix: matrix:
- 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.1", "1.21.3", "1.21.4", "1.21.5", "1.21.6", "1.21.7"] - MC_VER: [
"1.21.11", "1.21.10", "1.21.9", "1.21.8", "1.21.6", "1.21.5", "1.21.4", "1.21.3", "1.21.1",
"1.20.6", "1.20.4", "1.20.2", "1.20.1",
"1.19.4", "1.19.2",
"1.18.2",
"1.17.1",
"1.16.5"
]
script: script:
# this both runs the unit tests and assembles the code # 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/; - ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
+36 -2
View File
@@ -1,3 +1,37 @@
Before creating an issue, please select the appropriate template from the dropdown above. ## Check off each item in this list before submitting:
<!--
To mark a section as complete either put an "x" in between the square brackets, example: "[x]"
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/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-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/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-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/distant-horizons-team/distant-horizons/-/issues)
4. [ ] Upload Minecraft's crash report and/or log. \
Minecraft crash reports are located in: `.minecraft/crash-reports` \
Minecraft logs are located in: `.minecraft/logs`
5. [ ] Upload your Distant Horizons Config. \
The config is found in: `.minecraft/configs/DistantHorizons.toml`
6. [ ] Fill out the information below:
* **minecraft version**:
* **Distant Horizons version**:
* **Mod loader**:
* **Installed mods (list the smallest number of mods that you can use to re-create the bug)**:
* **Describe the bug**:
* **Steps to reproduce the bug**:
The template will walk you through submitting a bug, feature, or improvement request.
+43 -11
View File
@@ -5,6 +5,9 @@ import org.apache.tools.zip.ZipEntry
import javax.annotation.Nonnull import javax.annotation.Nonnull
import org.apache.tools.zip.ZipOutputStream import org.apache.tools.zip.ZipOutputStream
import java.util.function.Function
import java.util.function.Predicate
plugins { plugins {
id "java" id "java"
@@ -19,7 +22,7 @@ plugins {
id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha" id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha"
// Architectury is used here only as a replacement for forge's own loom // Architectury is used here only as a replacement for forge's own loom
id "dev.architectury.loom" version "1.10-SNAPSHOT" apply false id "dev.architectury.loom" version "1.13-SNAPSHOT" apply false
} }
@@ -43,7 +46,9 @@ def writeBuildGradlePredefine(List<String> mcVers, int mcIndex)
sb.append("MC_" + verStr + "=" + i.toString() + "\n"); sb.append("MC_" + verStr + "=" + i.toString() + "\n");
if (mcIndex == i) if (mcIndex == i)
{
sb.append("MC_VER=" + i.toString() + "\n"); sb.append("MC_VER=" + i.toString() + "\n");
}
} }
@@ -71,12 +76,23 @@ writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm") rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm")
class NativeTransformer implements Transformer { class NativeTransformer implements Transformer {
private Predicate<String> fileMatcher
private Function<String, String> filePathMapper
private final HashMap<String, String> replacements = new HashMap() private final HashMap<String, String> replacements = new HashMap()
private final HashMap<String, byte[]> rewrittenFiles = new HashMap() private final HashMap<String, byte[]> rewrittenFiles = new HashMap()
private var nativeRelocator private var nativeRelocator
public File rootDir public File rootDir
void matchFiles(Predicate<String> matcher) {
fileMatcher = matcher
}
void mapPaths(Function<String, String> mapper) {
filePathMapper = mapper
}
void relocateNative(String target, String replacement) { void relocateNative(String target, String replacement) {
if (replacement.length() > target.length()) { if (replacement.length() > target.length()) {
throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}") throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}")
@@ -87,9 +103,7 @@ class NativeTransformer implements Transformer {
@Override @Override
boolean canTransformResource(@Nonnull FileTreeElement element) { boolean canTransformResource(@Nonnull FileTreeElement element) {
return replacements.keySet().stream().anyMatch { return fileMatcher.test(element.name)
element.name.startsWith(it as String)
}
} }
@Override @Override
@@ -101,11 +115,9 @@ class NativeTransformer implements Transformer {
} }
try { try {
Map.Entry<String, String> pathReplacement = replacements.entrySet().stream().filter { String path = filePathMapper != null
context.path.startsWith(it.key as String) ? filePathMapper.apply(context.path)
}.findFirst().orElseThrow() : context.path
String path = context.path.replace(pathReplacement.key as String, pathReplacement.value as String)
content = nativeRelocator.processBinary(path, content, replacements) content = nativeRelocator.processBinary(path, content, replacements)
rewrittenFiles.put(path, content) rewrittenFiles.put(path, content)
@@ -246,8 +258,7 @@ subprojects { p ->
// We cannot relocate this library since we call some MC classes that reference it // We cannot relocate this library since we call some MC classes that reference it
implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}") implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
//forgeShadowMe("com.github.luben:zstd-jni:1.5.7-3") forgeShadowMe("com.github.luben:zstd-jni:${rootProject.zstd_version}")
//forgeShadowMe("org.apache.commons:commons-compress:1.27.1")
// Compression // Compression
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4 forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
@@ -350,10 +361,31 @@ subprojects { p ->
transform(NativeTransformer) { transform(NativeTransformer) {
rootDir = project.rootDir rootDir = project.rootDir
matchFiles { it.startsWith("org/sqlite") }
mapPaths { it.replace("org/sqlite", "dh_sqlite") }
relocateNative "org/sqlite", "dh_sqlite" relocateNative "org/sqlite", "dh_sqlite"
relocateNative "org_sqlite", "dh_1sqlite" relocateNative "org_sqlite", "dh_1sqlite"
} }
// ZStd
// librariesLocation isn't used because it's too long for replacing paths in native libraries
// Allowing strings larger than the original string would require shifting the entire binary's contents
relocate "com.github.luben", "dhcomgithubluben"
relocate "libzstd-jni", "libzstd-jni_dh"
relocate "zstd-jni", "zstd-jni_dh"
transform(NativeTransformer) {
rootDir = project.rootDir
matchFiles { it.contains("libzstd-jni") && !it.contains("aix/ppc64") }
mapPaths { it.replace("libzstd-jni", "libzstd-jni_dh") }
relocateNative "com/github/luben", "dhcomgithubluben"
relocateNative "com_github_luben", "dhcomgithubluben"
}
// JOML // JOML
if (project.hasProperty("embed_joml") && embed_joml == "true") if (project.hasProperty("embed_joml") && embed_joml == "true")
+4 -1
View File
@@ -34,7 +34,10 @@ class NativeRelocator
{ {
processBuilder.command("powershell", "-ExecutionPolicy", "Bypass", "./prepare.ps1"); processBuilder.command("powershell", "-ExecutionPolicy", "Bypass", "./prepare.ps1");
} }
else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) else if (os.contains("nix")
|| os.contains("nux")
|| os.contains("mac")
|| os.contains("freebsd"))
{ {
processBuilder.command("./prepare.sh"); processBuilder.command("./prepare.sh");
} }
@@ -5,14 +5,16 @@ import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDh
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
import com.seibel.distanthorizons.common.commands.CommandInitializer; import com.seibel.distanthorizons.common.commands.CommandInitializer;
import com.seibel.distanthorizons.common.wrappers.DependencySetup; import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import com.seibel.distanthorizons.common.wrappers.gui.DhDebugScreenEntry;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase; import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler; import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
import com.seibel.distanthorizons.core.jar.ModJarInfo; import com.seibel.distanthorizons.core.jar.ModJarInfo;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -23,9 +25,8 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.lang.invoke.MethodHandles;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -35,7 +36,7 @@ import java.util.function.Supplier;
*/ */
public abstract class AbstractModInitializer public abstract class AbstractModInitializer
{ {
protected static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
private CommandInitializer commandInitializer; private CommandInitializer commandInitializer;
@@ -45,7 +46,8 @@ public abstract class AbstractModInitializer
// abstract methods // // abstract methods //
//==================// //==================//
protected abstract void createInitialBindings(); protected abstract void createInitialSharedBindings();
protected abstract void createInitialClientBindings();
protected abstract IEventProxy createClientProxy(); protected abstract IEventProxy createClientProxy();
protected abstract IEventProxy createServerProxy(boolean isDedicated); protected abstract IEventProxy createServerProxy(boolean isDedicated);
protected abstract void initializeModCompat(); protected abstract void initializeModCompat();
@@ -65,8 +67,9 @@ public abstract class AbstractModInitializer
public void onInitializeClient() public void onInitializeClient()
{ {
DependencySetup.createClientBindings(); DependencySetup.createClientBindings();
this.createInitialClientBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " client."); LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " client, firing DhApiBeforeDhInitEvent...");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup(); this.startup();
@@ -83,6 +86,12 @@ public abstract class AbstractModInitializer
LOGGER.info(ModInfo.READABLE_NAME + " client Initialized."); LOGGER.info(ModInfo.READABLE_NAME + " client Initialized.");
#if MC_VER < MC_1_21_9
// debug screen rendering handled via a mixin
#else
DhDebugScreenEntry.register();
#endif
this.subscribeClientStartedEvent(this::postInit); this.subscribeClientStartedEvent(this::postInit);
} }
@@ -90,7 +99,7 @@ public abstract class AbstractModInitializer
{ {
DependencySetup.createServerBindings(); DependencySetup.createServerBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " server."); LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " server, firing DhApiBeforeDhInitEvent event...");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup(); this.startup();
@@ -106,8 +115,8 @@ public abstract class AbstractModInitializer
this.initializeModCompat(); this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized, adding event subscribers..."); LOGGER.info(ModInfo.READABLE_NAME + " server Initialized, adding event subscribers...");
this.commandInitializer = new CommandInitializer();
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer = new CommandInitializer(dispatcher); }); this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer.initCommands(dispatcher); });
this.subscribeServerStartingEvent(server -> this.subscribeServerStartingEvent(server ->
{ {
@@ -115,7 +124,7 @@ public abstract class AbstractModInitializer
this.initConfig(); this.initConfig();
this.postInit(); this.postInit();
this.commandInitializer.initCommands(); this.commandInitializer.onServerReady();
this.checkForUpdates(); this.checkForUpdates();
@@ -133,7 +142,7 @@ public abstract class AbstractModInitializer
{ {
DependencySetup.createSharedBindings(); DependencySetup.createSharedBindings();
SharedApi.init(); SharedApi.init();
this.createInitialBindings(); this.createInitialSharedBindings();
} }
private void logBuildInfo() private void logBuildInfo()
@@ -157,12 +166,17 @@ public abstract class AbstractModInitializer
//noinspection unchecked //noinspection unchecked
ModAccessorInjector.INSTANCE.bind((Class<? extends IModAccessor>) accessorClass, accessorConstructor.get()); ModAccessorInjector.INSTANCE.bind((Class<? extends IModAccessor>) accessorClass, accessorConstructor.get());
} }
else
{
LOGGER.debug("Skipping mod compatibility accessor for: ["+modId+"]");
}
} }
private void initConfig() private void initConfig()
{ {
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, ModInfo.CONFIG_FILE_VERSION); ConfigHandler.tryRunFirstTimeSetup();
Config.completeDelayedSetup(); Config.completeDelayedSetup();
DhLogger.runDelayedConfigSetup();
} }
private void checkForUpdates() private void checkForUpdates()
@@ -181,9 +195,15 @@ public abstract class AbstractModInitializer
private void postInit() private void postInit()
{ {
LOGGER.info("Post-Initializing Mod"); LOGGER.info("Running Delayed setup...");
this.runDelayedSetup(); this.runDelayedSetup();
LOGGER.info("Mod Post-Initialized");
if (ConfigHandler.INSTANCE == null)
{
throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method.");
}
LOGGER.info("Delayed setup complete, firing DhApiAfterDhInitEvent event...");
// should be fired after all delayed setup so singletons and config can be accessed // should be fired after all delayed setup so singletons and config can be accessed
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
@@ -220,8 +240,7 @@ public abstract class AbstractModInitializer
if (showChatWarnings) if (showChatWarnings)
{ {
String message = String message =
// orange text EMinecraftColor.ORANGE + "Distant Horizons: Alex's Cave detected." + EMinecraftColor.CLEAR_FORMATTING +
"\u00A76" + "Distant Horizons: Alex's Cave detected." + "\u00A7r\n" +
"You may have to change Alex's config for DH to render. "; "You may have to change Alex's config for DH to render. ";
ClientApi.INSTANCE.showChatMessageNextFrame(message); ClientApi.INSTANCE.showChatMessageNextFrame(message);
} }
@@ -243,8 +262,7 @@ public abstract class AbstractModInitializer
if (showChatWarnings) if (showChatWarnings)
{ {
String message = String message =
// orange text EMinecraftColor.ORANGE + "Distant Horizons: WWOO detected." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
"\u00A76" + "Distant Horizons: WWOO detected." + "\u00A7r\n" +
wwooWarning; wwooWarning;
ClientApi.INSTANCE.showChatMessageNextFrame(message); ClientApi.INSTANCE.showChatMessageNextFrame(message);
} }
@@ -274,8 +292,7 @@ public abstract class AbstractModInitializer
if (showChatWarnings) if (showChatWarnings)
{ {
String message = String message =
// orange text EMinecraftColor.ORANGE + "Distant Horizons: Chunky detected." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
"\u00A76" + "Distant Horizons: Chunky detected." + "\u00A7r\n" +
chunkyWarning; chunkyWarning;
ClientApi.INSTANCE.showChatMessageNextFrame(message); ClientApi.INSTANCE.showChatMessageNextFrame(message);
} }
@@ -1,8 +1,8 @@
package com.seibel.distanthorizons.common; package com.seibel.distanthorizons.common;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent; import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorInternalEvent; import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorInternalEvent;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry; import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
@@ -13,22 +13,29 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapp
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import org.apache.logging.log4j.LogManager;
#if MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
#endif
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
public abstract class AbstractPluginPacketSender implements IPluginPacketSender public abstract class AbstractPluginPacketSender implements IPluginPacketSender
{ {
private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), private static final DhLogger LOGGER = new DhLoggerBuilder()
() -> Config.Common.Logging.logNetworkEvent.get()); .fileLevelConfig(Config.Common.Logging.logNetworkEventToFile)
.build();
#if MC_VER >= MC_1_21_1 #if MC_VER <= MC_1_20_6
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
#elif MC_VER <= MC_1_21_10
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH); public static final ResourceLocation WRAPPER_PACKET_RESOURCE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
#else #else
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH); public static final Identifier WRAPPER_PACKET_RESOURCE = Identifier.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
#endif #endif
// "Forge byte" is an unused packet ID. We have our own system which works with all mod loaders, // "Forge byte" is an unused packet ID. We have our own system which works with all mod loaders,
@@ -3,36 +3,71 @@ package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import org.jetbrains.annotations.Nullable;
import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE; import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE;
import static net.minecraft.commands.Commands.literal; import static net.minecraft.commands.Commands.literal;
/** #if MC_VER <= MC_1_21_10
* Initializes commands of the mod. #else
*/ import net.minecraft.server.permissions.PermissionCheck;
import net.minecraft.server.permissions.Permissions;
#endif
public class CommandInitializer public class CommandInitializer
{ {
private final CommandDispatcher<CommandSourceStack> commandDispatcher; private boolean serverReady = false;
#if MC_VER <= MC_1_21_10
private static final int REQUIRED_PERMISSION_LEVEL = 4;
#else
private static final PermissionCheck COMMAND_PERMISSION_CHECK = new PermissionCheck.Require(Permissions.COMMANDS_OWNER);
#endif
/** /**
* Constructs a new instance of this class. * A received command dispatcher, which is held until the server is ready to initialize the commands.
*
* @param commandDispatcher The dispatcher to use for registering commands.
*/ */
public CommandInitializer(CommandDispatcher<CommandSourceStack> commandDispatcher) @Nullable
private CommandDispatcher<CommandSourceStack> commandDispatcher;
/**
* Notify the command initializer that the game is ready to accept commands.
* If {@link CommandInitializer#initCommands(CommandDispatcher)} has been fired before it was ready, it will also initialize the commands.
*/
public void onServerReady()
{ {
this.commandDispatcher = commandDispatcher; this.serverReady = true;
if (this.commandDispatcher != null)
{
this.initCommands(this.commandDispatcher);
this.commandDispatcher = null;
}
} }
/** /**
* Initializes all available commands. * Initializes all available commands.
* If the game is not ready yet, it stores the dispatcher to initialize the commands later.
*
* @param commandDispatcher The command dispatcher to register commands to.
*/ */
public void initCommands() public void initCommands(CommandDispatcher<CommandSourceStack> commandDispatcher)
{ {
if (!this.serverReady)
{
this.commandDispatcher = commandDispatcher;
return;
}
LiteralArgumentBuilder<CommandSourceStack> builder = literal("dh") LiteralArgumentBuilder<CommandSourceStack> builder = literal("dh")
.requires(source -> source.hasPermission(4)); .requires((source) ->
{
#if MC_VER <= MC_1_21_10
return source.hasPermission(REQUIRED_PERMISSION_LEVEL);
#else
return COMMAND_PERMISSION_CHECK.check(source.permissions());
#endif
});
builder.then(new ConfigCommand().buildCommand()); builder.then(new ConfigCommand().buildCommand());
builder.then(new DebugCommand().buildCommand()); builder.then(new DebugCommand().buildCommand());
@@ -43,7 +78,7 @@ public class CommandInitializer
builder.then(new CrashCommand().buildCommand()); builder.then(new CrashCommand().buildCommand());
} }
this.commandDispatcher.register(builder); commandDispatcher.register(builder);
} }
} }
@@ -3,8 +3,8 @@ package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.arguments.*; import com.mojang.brigadier.arguments.*;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.seibel.distanthorizons.core.config.ConfigBase; import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.types.AbstractConfigType; import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
@@ -43,7 +43,7 @@ public class ConfigCommand extends AbstractCommand
LiteralArgumentBuilder<CommandSourceStack> builder = literal("config"); LiteralArgumentBuilder<CommandSourceStack> builder = literal("config");
HashSet<String> addedCommands = new HashSet<>(); HashSet<String> addedCommands = new HashSet<>();
for (AbstractConfigType<?, ?> type : ConfigBase.INSTANCE.entries) for (AbstractConfigBase<?> type : ConfigHandler.INSTANCE.configBaseList)
{ {
// Skip non-config entries // Skip non-config entries
if (!(type instanceof ConfigEntry)) if (!(type instanceof ConfigEntry))
@@ -17,9 +17,9 @@ public class CrashCommand extends AbstractCommand
.requires(this::isPlayerSource) .requires(this::isPlayerSource)
.then(literal("encode") .then(literal("encode")
.executes(c -> { .executes(c -> {
assert SharedApi.getIDhServerWorld() != null; assert SharedApi.tryGetDhServerWorld() != null;
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager() ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld().getServerPlayerStateManager()
.getConnectedPlayer(this.getSourcePlayer(c)); .getConnectedPlayer(this.getSourcePlayer(c));
if (serverPlayerState != null) if (serverPlayerState != null)
{ {
@@ -29,9 +29,9 @@ public class CrashCommand extends AbstractCommand
})) }))
.then(literal("decode") .then(literal("decode")
.executes(c -> { .executes(c -> {
assert SharedApi.getIDhServerWorld() != null; assert SharedApi.tryGetDhServerWorld() != null;
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager() ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld().getServerPlayerStateManager()
.getConnectedPlayer(this.getSourcePlayer(c)); .getConnectedPlayer(this.getSourcePlayer(c));
if (serverPlayerState != null) if (serverPlayerState != null)
{ {
@@ -56,16 +56,11 @@ public class DependencySetup
SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE); SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE);
SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE); SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
SingletonInjector.INSTANCE.bind(IKeyedClientLevelManager.class, KeyedClientLevelManager.INSTANCE); SingletonInjector.INSTANCE.bind(IKeyedClientLevelManager.class, KeyedClientLevelManager.INSTANCE);
DependencySetupDoneCheck.isDone = true;
} }
//@Environment(EnvType.SERVER)
public static void createServerBindings() public static void createServerBindings()
{ { SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftServerWrapper.INSTANCE); }
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftServerWrapper.INSTANCE);
}
//@Environment(EnvType.CLIENT)
public static void createClientBindings() public static void createClientBindings()
{ {
SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE); SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE);
@@ -76,8 +76,15 @@ public class VersionConstants implements IVersionConstants
return "1.21.5"; return "1.21.5";
#elif MC_VER == MC_1_21_6 #elif MC_VER == MC_1_21_6
return "1.21.6"; return "1.21.6";
#elif MC_VER == MC_1_21_7 #elif MC_VER == MC_1_21_8
return "1.21.7"; return "1.21.8";
#elif MC_VER == MC_1_21_9
return "1.21.9";
#elif MC_VER == MC_1_21_10
return "1.21.10";
#elif MC_VER == MC_1_21_11
return "1.21.11";
#else #else
ERROR MC version constant missing ERROR MC version constant missing
#endif #endif
@@ -38,7 +38,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
#if MC_VER > MC_1_17_1 #if MC_VER > MC_1_17_1
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
@@ -67,7 +67,7 @@ public class WrapperFactory implements IWrapperFactory
//==============// //==============//
@Override @Override
public AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel) public IBatchGeneratorEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel)
{ {
if (targetLevel instanceof IDhServerLevel) if (targetLevel instanceof IDhServerLevel)
{ {
@@ -0,0 +1,337 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.BlockBiomeWrapperPair;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#endif
public abstract class AbstractDhTintGetter implements BlockAndTintGetter
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER < MC_1_18_2
private static final ConcurrentHashMap<String, Biome> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
#else
private static final ConcurrentHashMap<String, Holder<Biome>> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
#endif
private static final ConcurrentHashMap<BlockBiomeWrapperPair, Integer> COLOR_BY_BLOCK_BIOME_PAIR = new ConcurrentHashMap<>();
/** returned if the color cache is incomplete */
public static final int INVALID_COLOR = Integer.MIN_VALUE;
protected BiomeWrapper biomeWrapper;
protected BlockStateWrapper blockStateWrapper;
protected FullDataSourceV2 fullDataSource;
protected int smoothingRadiusInBlocks;
protected IClientLevelWrapper clientLevelWrapper;
//=============//
// constructor //
//=============//
public AbstractDhTintGetter() { }
/**
* Mutates this getter so we can access the necessary
* variables for tint getting.
*/
public void update(BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
{
this.biomeWrapper = biomeWrapper;
this.blockStateWrapper = blockStateWrapper;
this.fullDataSource = fullDataSource;
this.clientLevelWrapper = clientLevelWrapper;
this.smoothingRadiusInBlocks = Config.Client.Advanced.Graphics.Quality.lodBiomeBlending.get();
}
//================//
// shared methods //
//================//
/** Called by MC's tint getter */
@Override
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
{
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(blockPos.getX(), blockPos.getY(), blockPos.getZ());
return this.tryGetBlockTint(mutableBlockPos, colorResolver);
}
/**
* Can be called by DH directly, skipping some of MC's logic
* to speed up tint getting slightly.
*
* @return {@link AbstractDhTintGetter#INVALID_COLOR} if any of the biomes needed for this position
* were not cached. In that case calling {@link AbstractDhTintGetter#getBlockTint(BlockPos, ColorResolver)}
* will need to be called by MC's ColorResolver so we can
* populate the color cache.
*/
public int tryGetBlockTint(DhBlockPosMutable mutableBlockPos)
{ return this.tryGetBlockTint(mutableBlockPos, null); }
private int tryGetBlockTint(DhBlockPosMutable mutableBlockPos, @Nullable ColorResolver colorResolver)
{
// determine how wide this data source is so we can determine
// if blending should be used
byte dataSourceDetailLevel = DhSectionPos.getDetailLevel(this.fullDataSource.getPos());
// convert from section detail level to absolute detail level
dataSourceDetailLevel = (byte)(dataSourceDetailLevel - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
int dataSourceLodWidthInBlocks = DhSectionPos.getDetailLevelWidthInBlocks(dataSourceDetailLevel);
// don't do any smoothing if smoothing is disabled or if the LOD
// is to large for block-based smoothing to show up
if (this.smoothingRadiusInBlocks == 0
|| dataSourceLodWidthInBlocks > this.smoothingRadiusInBlocks)
{
return this.tryGetClientBiomeColor(colorResolver, this.biomeWrapper);
}
// use a rolling average to calculate the color
int dataPointCount = 0;
int rollingRed = 0;
int rollingGreen = 0;
int rollingBlue = 0;
int xMin = mutableBlockPos.getX() - this.smoothingRadiusInBlocks;
int xMax = mutableBlockPos.getX() + this.smoothingRadiusInBlocks + 1; // +1 to account for the center block
int zMin = mutableBlockPos.getZ() - this.smoothingRadiusInBlocks;
int zMax = mutableBlockPos.getZ() + this.smoothingRadiusInBlocks + 1;
int levelMinY = this.clientLevelWrapper.getMinHeight();
for (int x = xMin; x < xMax; x++)
{
for (int z = zMin; z < zMax; z++)
{
mutableBlockPos.setX(x);
mutableBlockPos.setZ(z);
// this can return the same position/datapoint for larger LODs duplicating work,
// however for small smoothing ranges that isn't a big deal and for large LODs
// we ignore smoothing anyway
long dataPoint = this.fullDataSource.getDataPointAtBlockPos(mutableBlockPos.getX(), mutableBlockPos.getY(), mutableBlockPos.getZ(), levelMinY);
if (dataPoint == FullDataPointUtil.EMPTY_DATA_POINT)
{
continue;
}
// get the color for this nearby position
int id = FullDataPointUtil.getId(dataPoint);
BiomeWrapper biomeWrapper = (BiomeWrapper) this.fullDataSource.mapping.getBiomeWrapper(id);
int color = this.tryGetClientBiomeColor(colorResolver, biomeWrapper);
if (color == INVALID_COLOR)
{
return INVALID_COLOR;
}
// rolling average
rollingRed += ColorUtil.getRed(color);
rollingGreen += ColorUtil.getGreen(color);
rollingBlue += ColorUtil.getBlue(color);
dataPointCount++;
}
}
// if no data was present (rarely possible)
// just use the default center's color
if (dataPointCount == 0)
{
return this.tryGetClientBiomeColor(colorResolver, this.biomeWrapper);
}
int colorInt = ColorUtil.argbToInt(
255, // blending often ignores alpha, having it always 255 prevents multiplication issues later
rollingRed / dataPointCount,
rollingGreen / dataPointCount,
rollingBlue / dataPointCount);
return colorInt;
}
/**
* If given a ColorResolver this will always succeed. <Br>
* If not it will attempt to use the cached color.
*/
private int tryGetClientBiomeColor(@Nullable ColorResolver colorResolver, BiomeWrapper biomeWrapper)
{
BlockBiomeWrapperPair pair = BlockBiomeWrapperPair.get(this.blockStateWrapper, biomeWrapper);
// use the cached color if possible
Integer cachedColor = COLOR_BY_BLOCK_BIOME_PAIR.get(pair); // explicit Integer return here reduces unnecessary allocations
if (cachedColor != null)
{
return cachedColor;
}
if (colorResolver == null)
{
// no color resolver is present,
// the cache needs to be populated before
// we can use the fast path
return INVALID_COLOR;
}
int color = colorResolver.getColor(unwrapClientBiome(biomeWrapper), 0, 0);
COLOR_BY_BLOCK_BIOME_PAIR.put(pair, color);
return color;
}
protected static Biome unwrapClientBiome(BiomeWrapper biomeWrapper)
{
String biomeString = biomeWrapper.getSerialString();
if (biomeString == null
|| biomeString.isEmpty()
|| biomeString.equals(BiomeWrapper.EMPTY_BIOME_STRING))
{
// default to "plains" for empty/invalid biomes
biomeString = "minecraft:plains";
}
return unwrapBiome(getClientBiome(biomeString));
}
protected static Biome unwrapBiome(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{
#if MC_VER >= MC_1_18_2
return biome.value();
#else
return biome;
#endif
}
/**
* <p>Previously, this class might have immediately unwrapped the Holder like this:</p>
* <pre>{@code
* // Inside constructor (OLD WAY - PROBLEMATIC):
* Holder<Biome> biomeHolder = getTheHolderFromSomewhere();
* this.biome = biomeHolder.value(); // <-- PROBLEM HERE
* }</pre>
*
* <p>This approach is problematic because the {@link net.minecraft.core.Holder} system,
* particularly {@code Holder.Reference}, is designed for <strong>late binding</strong>. Here's why storing
* the Holder itself is now necessary:</p>
* <ol>
* <li>A {@code Holder.Reference<Biome>} might be created initially just with a
* {@link net.minecraft.resources.ResourceKey} (like {@code minecraft:plains}), but its actual
* {@link net.minecraft.core.Holder#value() value()} (the {@code Biome} object itself) might be {@code null}
* at construction time.</li>
* <li>Later, during game loading, registry population, or potentially due to modifications by other mods
* (e.g., Polytone), the system calls internal binding methods (like {@code bindValue(Biome)})
* on the {@code Holder} instance. This sets or <strong>updates</strong> the internal reference to the
* actual {@code Biome} object.</li>
* <li>Crucially, the binding process might assign a completely <strong>new</strong> {@code Biome} object
* instance to the {@code Holder} reference, replacing any previous one.</li>
* </ol>
*
* <p>If we unwrapped the {@code Holder} using {@code .value()} within the constructor (the old way),
* our class's internal {@code biome} field would permanently store a reference to whatever {@code Biome}
* object the {@code Holder} pointed to *at that exact moment*. It would have no link back to the
* {@code Holder} and would be unaware if the {@code Holder} was later updated to point to a different
* (or the initially missing) {@code Biome} object. This would lead to using stale or even {@code null} data.</p>
*
* <p>By storing the {@code Holder<Biome>} itself, this class can call {@link net.minecraft.core.Holder#value()}
* whenever the biome information is needed, ensuring it always retrieves the most current {@code Biome}
* instance associated with the holder at that time.</p>
*/
private static #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif getClientBiome(String biomeResourceString)
{
#if MC_VER < MC_1_18_2
Biome biome;
#else
Holder<Biome> biome;
#endif
// calling get instead of compute is slightly faster for already
// computed values
biome = BIOME_BY_RESOURCE_STRING.get(biomeResourceString);
if (biome != null)
{
return biome;
}
// cache the client biomes so we don't have to re-parse the resource location every time
return BIOME_BY_RESOURCE_STRING.compute(biomeResourceString,
(resourceString, existingBiome) ->
{
if (existingBiome != null)
{
return existingBiome;
}
ClientLevel clientLevel = Minecraft.getInstance().level;
if (clientLevel == null)
{
// shouldn't happen, but just in case
throw new IllegalStateException("Attempted to get client biome when no client level was loaded.");
}
BiomeWrapper.BiomeDeserializeResult result;
try
{
result = BiomeWrapper.deserializeBiome(resourceString, clientLevel.registryAccess());
}
catch (Exception e)
{
LOGGER.warn("Unable to deserialize client biome ["+resourceString+"], using fallback...");
try
{
result = BiomeWrapper.deserializeBiome(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, clientLevel.registryAccess());
}
catch (IOException ex)
{
// should never happen, if it does this log will explode, but just in case
LOGGER.error("Unable to deserialize fallback client biome ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"], returning NULL.");
return null;
}
}
if (result.success)
{
existingBiome = result.biome;
}
return existingBiome;
});
}
}
@@ -26,10 +26,10 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import org.apache.logging.log4j.LogManager; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
@@ -45,7 +45,12 @@ import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
#endif #endif
#if MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
#endif
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
@@ -57,7 +62,7 @@ import net.minecraft.world.level.biome.Biomes;
public class BiomeWrapper implements IBiomeWrapper public class BiomeWrapper implements IBiomeWrapper
{ {
// must be defined before AIR, otherwise a null pointer will be thrown // must be defined before AIR, otherwise a null pointer will be thrown
private static final Logger LOGGER = LogManager.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
@@ -103,7 +108,7 @@ public class BiomeWrapper implements IBiomeWrapper
// constructors // // constructors //
//==============// //==============//
static public IBiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper) public static BiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
{ {
if (biome == null) if (biome == null)
{ {
@@ -111,9 +116,10 @@ public class BiomeWrapper implements IBiomeWrapper
} }
if (WRAPPER_BY_BIOME.containsKey(biome)) BiomeWrapper biomeWrapper = WRAPPER_BY_BIOME.get(biome);
if (biomeWrapper != null)
{ {
return WRAPPER_BY_BIOME.get(biome); return biomeWrapper;
} }
else else
{ {
@@ -215,7 +221,12 @@ public class BiomeWrapper implements IBiomeWrapper
Level level = (Level)levelWrapper.getWrappedMcObject(); Level level = (Level)levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
#if MC_VER < MC_1_21_11
ResourceLocation resourceLocation; ResourceLocation resourceLocation;
#else
Identifier resourceLocation;
#endif
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome); resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2 #elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
@@ -299,7 +310,7 @@ public class BiomeWrapper implements IBiomeWrapper
} }
foundWrapper = (BiomeWrapper) getBiomeWrapper(deserializeResult.biome, levelWrapper); foundWrapper = getBiomeWrapper(deserializeResult.biome, levelWrapper);
return foundWrapper; return foundWrapper;
} }
catch (Exception e) catch (Exception e)
@@ -322,13 +333,19 @@ public class BiomeWrapper implements IBiomeWrapper
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "]."); throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
} }
#if MC_VER < MC_1_21_11
ResourceLocation resourceLocation; ResourceLocation resourceLocation;
#else
Identifier resourceLocation;
#endif
try try
{ {
#if MC_VER < MC_1_21_1 #if MC_VER <= MC_1_20_6
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1)); resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
#else #elif MC_VER <= MC_1_21_10
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1)); resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
#else
resourceLocation = Identifier.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
#endif #endif
} }
catch (Exception e) catch (Exception e)
@@ -28,14 +28,13 @@ import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags; import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.BeaconBeamBlock; import net.minecraft.world.level.block.BeaconBeamBlock;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.awt.*; import java.awt.*;
import java.io.IOException; import java.io.IOException;
@@ -63,6 +62,12 @@ import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
#endif #endif
#if MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
#endif
public class BlockStateWrapper implements IBlockStateWrapper public class BlockStateWrapper implements IBlockStateWrapper
{ {
/** example "minecraft:water" */ /** example "minecraft:water" */
@@ -72,7 +77,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// must be defined before AIR, otherwise a null pointer will be thrown // must be defined before AIR, otherwise a null pointer will be thrown
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>(); 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 ConcurrentHashMap<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
@@ -81,12 +86,17 @@ public class BlockStateWrapper implements IBlockStateWrapper
public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null); public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null);
public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt"; public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt";
public static final String WATER_RESOURCE_LOCATION_STRING = "minecraft:water";
public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null; public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null; public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
/** keep track of broken blocks so we don't log every time */ /** keep track of broken blocks so we don't log every time */
#if MC_VER <= MC_1_21_10
private static final HashSet<ResourceLocation> BROKEN_RESOURCE_LOCATIONS = new HashSet<>(); private static final HashSet<ResourceLocation> BROKEN_RESOURCE_LOCATIONS = new HashSet<>();
#else
private static final HashSet<Identifier> BROKEN_RESOURCE_LOCATIONS = new HashSet<>();
#endif
@@ -273,7 +283,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
HashSet<String> baseIgnoredBlock = new HashSet<>(); HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING); baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredBlocks = getBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper); rendererIgnoredBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredBlocks; return rendererIgnoredBlocks;
} }
/** /**
@@ -290,7 +300,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
HashSet<String> baseIgnoredBlock = new HashSet<>(); HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING); baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredCaveBlocks = getBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper); rendererIgnoredCaveBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredCaveBlocks; return rendererIgnoredCaveBlocks;
} }
@@ -301,7 +311,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// lod builder helpers // // lod builder helpers //
private static HashSet<IBlockStateWrapper> getBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper) private static HashSet<IBlockStateWrapper> getAllBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
{ {
// get the base blocks // get the base blocks
HashSet<String> blockStringList = new HashSet<>(); HashSet<String> blockStringList = new HashSet<>();
@@ -317,9 +327,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(","))); blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(",")));
} }
return getBlockWrappers(blockStringList, levelWrapper); return getAllBlockWrappers(blockStringList, levelWrapper);
} }
private static HashSet<IBlockStateWrapper> getBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper) private static HashSet<IBlockStateWrapper> getAllBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
{ {
// deserialize each of the given resource locations // deserialize each of the given resource locations
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>(); HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
@@ -554,7 +564,12 @@ public class BlockStateWrapper implements IBlockStateWrapper
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
#endif #endif
#if MC_VER < MC_1_21_11
ResourceLocation resourceLocation; ResourceLocation resourceLocation;
#else
Identifier resourceLocation;
#endif
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock()); resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2 #elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
@@ -586,7 +601,8 @@ public class BlockStateWrapper implements IBlockStateWrapper
// we need the final string for the concurrent hash map later // we need the final string for the concurrent hash map later
final String finalResourceStateString = resourceStateString; final String finalResourceStateString = resourceStateString;
if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case if (finalResourceStateString.equals(AIR_STRING)
|| finalResourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case
{ {
return AIR; return AIR;
} }
@@ -620,13 +636,20 @@ public class BlockStateWrapper implements IBlockStateWrapper
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "]."); throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
} }
#if MC_VER < MC_1_21_11
ResourceLocation resourceLocation; ResourceLocation resourceLocation;
#else
Identifier resourceLocation;
#endif
try try
{ {
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21_1
resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1)); resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
#else #elif MC_VER <= MC_1_21_10
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1)); resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
#else
resourceLocation = Identifier.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
#endif #endif
} }
catch (Exception e) catch (Exception e)
@@ -20,15 +20,16 @@
package com.seibel.distanthorizons.common.wrappers.block; package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
import com.seibel.distanthorizons.core.util.ColorUtil; import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FlowerBlock; import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.LeavesBlock; import net.minecraft.world.level.block.LeavesBlock;
@@ -39,7 +40,7 @@ import net.minecraft.util.RandomSource;
import java.util.Random; import java.util.Random;
#endif #endif
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
@@ -61,7 +62,7 @@ import net.minecraft.client.renderer.block.model.BlockModelPart;
*/ */
public class ClientBlockStateColorCache public class ClientBlockStateColorCache
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>(); private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>(); private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
@@ -93,11 +94,10 @@ public class ClientBlockStateColorCache
private final IClientLevelWrapper clientLevelWrapper; private final IClientLevelWrapper clientLevelWrapper;
private final BlockState blockState; private final BlockState blockState;
private final LevelReader level; private final BlockStateWrapper blockStateWrapper;
private boolean isColorResolved = false; private boolean isColorResolved = false;
private int baseColor = 0; private int baseColor = 0;
private boolean needShade = true;
private boolean needPostTinting = false; private boolean needPostTinting = false;
private int tintIndex = 0; private int tintIndex = 0;
@@ -165,17 +165,21 @@ public class ClientBlockStateColorCache
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f 0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
}; };
private static final ThreadLocal<TintWithoutLevelOverrider> TintWithoutLevelOverrideGetter = ThreadLocal.withInitial(() -> new TintWithoutLevelOverrider());
private static final ThreadLocal<TintGetterOverride> TintOverrideGetter = ThreadLocal.withInitial(() -> new TintGetterOverride());
//=============// //=============//
// constructor // // constructor //
//=============// //=============//
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel) public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper clientLevelWrapper)
{ {
this.blockState = blockState; this.blockState = blockState;
this.clientLevelWrapper = samplingLevel; this.blockStateWrapper = BlockStateWrapper.fromBlockState(blockState, clientLevelWrapper);
this.level = (LevelReader) samplingLevel.getWrappedMcObject(); this.clientLevelWrapper = clientLevelWrapper;
this.resolveColors(); this.resolveColors();
} }
@@ -228,32 +232,29 @@ public class ClientBlockStateColorCache
this.needPostTinting = firstQuad.isTinted(); this.needPostTinting = firstQuad.isTinted();
#if MC_VER <= MC_1_21_4 #if MC_VER <= MC_1_21_4
this.needShade = firstQuad.isShade();
this.tintIndex = firstQuad.getTintIndex(); this.tintIndex = firstQuad.getTintIndex();
#else #else
this.needShade = firstQuad.shade();
this.tintIndex = firstQuad.tintIndex(); this.tintIndex = firstQuad.tintIndex();
#endif #endif
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
this.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.sprite, firstQuad.sprite,
ColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
#elif MC_VER < MC_1_21_5 #elif MC_VER < MC_1_21_5
this.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.getSprite(), firstQuad.getSprite(),
ColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
#else #else
this.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.sprite(), firstQuad.sprite(),
ColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
#endif #endif
} }
else else
{ {
// Backup method. // Backup method.
this.needPostTinting = false; this.needPostTinting = false;
this.needShade = false;
this.tintIndex = 0; this.tintIndex = 0;
this.baseColor = this.getParticleIconColor(); this.baseColor = this.getParticleIconColor();
} }
@@ -262,11 +263,11 @@ public class ClientBlockStateColorCache
{ {
// Liquid Block // Liquid Block
this.needPostTinting = true; this.needPostTinting = true;
this.needShade = false;
this.tintIndex = 0; this.tintIndex = 0;
this.baseColor = this.getParticleIconColor(); this.baseColor = this.getParticleIconColor();
} }
this.isColorResolved = true; this.isColorResolved = true;
} }
finally finally
@@ -304,7 +305,7 @@ public class ClientBlockStateColorCache
} }
//TODO: Perhaps make this not just use the first frame? //TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode) private static int calculateColorFromTexture(TextureAtlasSprite texture, EColorMode colorMode)
{ {
int count = 0; int count = 0;
int alpha = 0; int alpha = 0;
@@ -314,8 +315,8 @@ public class ClientBlockStateColorCache
int tempColor; int tempColor;
// don't render Chiseled blocks. // don't render Chiseled blocks.
// Since ColorMode is set per block, you only need to check this once. // Since EColorMode is set per block, you only need to check this once.
if (colorMode != ColorMode.Chisel) if (colorMode != EColorMode.Chisel)
{ {
// textures normally use u and v instead of x and y // textures normally use u and v instead of x and y
for (int v = 0; v < getTextureHeight(texture); v++) for (int v = 0; v < getTextureHeight(texture); v++)
@@ -333,7 +334,7 @@ public class ClientBlockStateColorCache
int b = (tempColor & 0x00FF0000) >>> 16; int b = (tempColor & 0x00FF0000) >>> 16;
int a = (tempColor & 0xFF000000) >>> 24; int a = (tempColor & 0xFF000000) >>> 24;
int scale = 1; int scale = 1;
if (colorMode == ColorMode.Leaves) if (colorMode == EColorMode.Leaves)
{ {
//switch (//FIXME add config option) //switch (//FIXME add config option)
// case BLACK: // case BLACK:
@@ -352,11 +353,11 @@ public class ClientBlockStateColorCache
// break; //do nothing, let it count towards transparency // break; //do nothing, let it count towards transparency
} }
else if (a == 0 && colorMode != ColorMode.Glass) else if (a == 0 && colorMode != EColorMode.Glass)
{ {
continue; continue;
} }
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r)) else if (colorMode == EColorMode.Flower && (g + 25 < b || g + 25 < r))
{ {
scale = FLOWER_COLOR_SCALE; scale = FLOWER_COLOR_SCALE;
} }
@@ -414,16 +415,18 @@ public class ClientBlockStateColorCache
* This method was suggested by IMS from the Iris/Sodium team. * This method was suggested by IMS from the Iris/Sodium team.
* That's where the numbers and code are based. * That's where the numbers and code are based.
*/ */
private static int linearToSrgb(float c) private static int linearToSrgb(float color)
{ {
if (!(c > MIN_SRGB_BOUND)) { if (!(color > MIN_SRGB_BOUND))
c = MIN_SRGB_BOUND; {
color = MIN_SRGB_BOUND;
} }
if (c > MAX_SRGB_BOUND) { if (color > MAX_SRGB_BOUND)
c = MAX_SRGB_BOUND; {
color = MAX_SRGB_BOUND;
} }
int inputBits = Float.floatToRawIntBits(c); int inputBits = Float.floatToRawIntBits(color);
int entry = linearToSrgbTable[((inputBits - MIN_SRGB_BITS) >> 20)]; int entry = linearToSrgbTable[((inputBits - MIN_SRGB_BITS) >> 20)];
int bias = (entry >>> 16) << 9; int bias = (entry >>> 16) << 9;
@@ -437,7 +440,7 @@ public class ClientBlockStateColorCache
{ {
return calculateColorFromTexture( return calculateColorFromTexture(
Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState), Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
} }
@@ -446,7 +449,7 @@ public class ClientBlockStateColorCache
// public getter // // public getter //
//===============// //===============//
public int getColor(BiomeWrapper biome, DhBlockPos pos) public int getColor(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, DhBlockPos blockPos)
{ {
// only get the tint if the block needs to be tinted // only get the tint if the block needs to be tinted
if (!this.needPostTinting) if (!this.needPostTinting)
@@ -470,13 +473,27 @@ public class ClientBlockStateColorCache
{ {
try try
{ {
tintColor = Minecraft.getInstance().getBlockColors() TintWithoutLevelOverrider tintOverride = TintWithoutLevelOverrideGetter.get();
.getColor(this.blockState, new TintWithoutLevelOverrider(biome, this.clientLevelWrapper), McObjectConverter.Convert(pos), this.tintIndex); tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
// try using DH's cached tint values first if possible
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
{
// one or more tint values weren't calculated,
// we need MC's color resolver
tintColor = Minecraft.getInstance()
.getBlockColors()
.getColor(this.blockState,
tintOverride,
McObjectConverter.Convert(blockPos),
this.tintIndex);
}
} }
catch (UnsupportedOperationException e) catch (UnsupportedOperationException e)
{ {
// this exception generally occurs if the tint requires other blocks besides itself // this exception generally occurs if the tint requires other blocks besides itself
LOGGER.debug("Unable to use ["+TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e); LOGGER.debug("Unable to use ["+ TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState); BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
} }
} }
@@ -484,10 +501,22 @@ public class ClientBlockStateColorCache
// use the level logic only if requested // use the level logic only if requested
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState)) if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{ {
// this logic can't be used all the time due to it breaking some blocks tinting // the level shouldn't be used all the time due to it breaking some blocks tinting
// specifically oceans don't render correctly // specifically oceans don't render correctly
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState, new TintGetterOverrideFast(this.level), McObjectConverter.Convert(pos), this.tintIndex); TintGetterOverride tintOverride = TintOverrideGetter.get();
tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
{
tintColor = Minecraft.getInstance()
.getBlockColors()
.getColor(this.blockState,
tintOverride,
McObjectConverter.Convert(blockPos),
this.tintIndex);
}
} }
} }
catch (Exception e) catch (Exception e)
@@ -495,7 +524,7 @@ public class ClientBlockStateColorCache
// only display the error once per block/biome type to reduce log spam // only display the error once per block/biome type to reduce log spam
if (!BROKEN_BLOCK_STATES.contains(this.blockState)) if (!BROKEN_BLOCK_STATES.contains(this.blockState))
{ {
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e); LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
BROKEN_BLOCK_STATES.add(this.blockState); BROKEN_BLOCK_STATES.add(this.blockState);
} }
} }
@@ -519,7 +548,7 @@ public class ClientBlockStateColorCache
// helper classes // // helper classes //
//================// //================//
enum ColorMode private enum EColorMode
{ {
Default, Default,
Flower, Flower,
@@ -527,7 +556,7 @@ public class ClientBlockStateColorCache
Chisel, Chisel,
Glass; Glass;
static ColorMode getColorMode(Block block) static EColorMode getColorMode(Block block)
{ {
if (block instanceof LeavesBlock) return Leaves; if (block instanceof LeavesBlock) return Leaves;
if (block instanceof FlowerBlock) return Flower; if (block instanceof FlowerBlock) return Flower;
@@ -19,10 +19,11 @@
package com.seibel.distanthorizons.common.wrappers.block; package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.*; import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
@@ -38,9 +39,9 @@ import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Stream; import java.util.stream.Stream;
public class TintGetterOverrideFast implements BlockAndTintGetter public class TintGetterOverride extends AbstractDhTintGetter
{ {
LevelReader parent; private LevelReader parent;
@@ -48,7 +49,13 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
// constructor // // constructor //
//=============// //=============//
public TintGetterOverrideFast(LevelReader parent) { this.parent = parent; } public TintGetterOverride() { }
public void update(LevelReader parent, BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
{
super.update(biomeWrapper, blockStateWrapper, fullDataSource, clientLevelWrapper);
this.parent = parent;
}
@@ -56,18 +63,6 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
// methods // // methods //
//=========// //=========//
private Biome _getBiome(BlockPos pos)
{
#if MC_VER >= MC_1_18_2
return this.parent.getBiome(pos).value();
#else
return parent.getBiome(pos);
#endif
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) { return colorResolver.getColor(this._getBiome(blockPos), blockPos.getX(), blockPos.getZ()); }
@Override @Override
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); } public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
@@ -87,7 +82,6 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
@Nullable @Nullable
public BlockEntity getBlockEntity(BlockPos blockPos) { return this.parent.getBlockEntity(blockPos); } public BlockEntity getBlockEntity(BlockPos blockPos) { return this.parent.getBlockEntity(blockPos); }
@Override @Override
public BlockState getBlockState(BlockPos blockPos) { return this.parent.getBlockState(blockPos); } public BlockState getBlockState(BlockPos blockPos) { return this.parent.getBlockState(blockPos); }
@@ -99,26 +93,25 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
@Override @Override
public int getMaxLightLevel() { return parent.getMaxLightLevel(); } public int getMaxLightLevel() { return this.parent.getMaxLightLevel(); }
#else #else
#endif #endif
@Override @Override
public Stream<BlockState> getBlockStates(AABB aABB) public Stream<BlockState> getBlockStates(AABB aABB) { return this.parent.getBlockStates(aABB); }
{ return this.parent.getBlockStates(aABB); }
@Override @Override
public BlockHitResult clip(ClipContext clipContext) public BlockHitResult clip(ClipContext clipContext) { return this.parent.clip(clipContext); }
{ return this.parent.clip(clipContext); }
@Override @Override
@Nullable @Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState) public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
{ return this.parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState); } {
return this.parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override @Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) { return this.parent.getBlockFloorHeight(voxelShape, supplier); }
{ return this.parent.getBlockFloorHeight(voxelShape, supplier); }
@Override @Override
public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); } public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); }
@@ -131,20 +124,12 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
public int getMaxY() { return this.parent.getMaxY(); } public int getMaxY() { return this.parent.getMaxY(); }
#endif #endif
//==============//
// post MC 1.17 //
//==============//
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
@Override @Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType) public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType) { return this.parent.getBlockEntity(blockPos, blockEntityType); }
{ return this.parent.getBlockEntity(blockPos, blockEntityType); }
@Override @Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext) public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext) { return this.parent.isBlockInLine(clipBlockStateContext); }
{ return this.parent.isBlockInLine(clipBlockStateContext); }
@Override @Override
public int getHeight() { return this.parent.getHeight(); } public int getHeight() { return this.parent.getHeight(); }
@@ -165,7 +150,7 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
public int getMinSection() { return this.parent.getMinSection(); } public int getMinSection() { return this.parent.getMinSection(); }
#else #else
@Override @Override
public int getMinSectionY() { return BlockAndTintGetter.super.getMinSectionY(); } public int getMinSectionY() { return super.getMinSectionY(); }
#endif #endif
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
@@ -191,4 +176,7 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
@Override @Override
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); } public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
#endif #endif
} }
@@ -1,214 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.util.LodUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Cursor3D;
import net.minecraft.core.Direction;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class TintGetterOverrideSmooth implements BlockAndTintGetter
{
LevelReader parent;
public int smoothingRange;
//=============//
// constructor //
//=============//
public TintGetterOverrideSmooth(LevelReader parent, int smoothingRange)
{
this.parent = parent;
this.smoothingRange = smoothingRange;
}
//=========//
// methods //
//=========//
private Biome _getBiome(BlockPos pos)
{
#if MC_VER >= MC_1_18_2
return this.parent.getBiome(pos).value();
#else
return parent.getBiome(pos);
#endif
}
public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
int i = smoothingRange;
if (i == 0)
return colorResolver.getColor(_getBiome(blockPos), blockPos.getX(), blockPos.getZ());
int j = (i * 2 + 1) * (i * 2 + 1);
int k = 0;
int l = 0;
int m = 0;
Cursor3D cursor3D = new Cursor3D(blockPos.getX() - i, blockPos.getY(), blockPos.getZ() - i, blockPos.getX() + i, blockPos.getY(), blockPos.getZ() + i);
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
while (cursor3D.advance())
{
mutableBlockPos.set(cursor3D.nextX(), cursor3D.nextY(), cursor3D.nextZ());
int n = colorResolver.getColor(this._getBiome(mutableBlockPos), mutableBlockPos.getX(), mutableBlockPos.getZ());
k += (n & 0xFF0000) >> 16;
l += (n & 0xFF00) >> 8;
m += n & 0xFF;
}
return (k / j & 0xFF) << 16 | (l / j & 0xFF) << 8 | m / j & 0xFF;
}
@Override
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 this.parent.getLightEngine(); }
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return this.parent.getBrightness(lightLayer, blockPos); }
@Override
public int getRawBrightness(BlockPos blockPos, int i) { return this.parent.getRawBrightness(blockPos, i); }
@Override
public boolean canSeeSky(BlockPos blockPos) { return this.parent.canSeeSky(blockPos); }
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos blockPos) { return this.parent.getBlockEntity(blockPos); }
@Override
public BlockState getBlockState(BlockPos blockPos) { return this.parent.getBlockState(blockPos); }
@Override
public FluidState getFluidState(BlockPos blockPos) { return this.parent.getFluidState(blockPos); }
@Override
public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); }
#if MC_VER < MC_1_21_3
@Override
public int getMaxLightLevel() { return this.parent.getMaxLightLevel(); }
#else
#endif
@Override
public Stream<BlockState> getBlockStates(AABB aABB) { return this.parent.getBlockStates(aABB); }
@Override
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 this.parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) { return this.parent.getBlockFloorHeight(voxelShape, supplier); }
@Override
public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); }
#if MC_VER < MC_1_21_3
@Override
public int getMaxBuildHeight() { return this.parent.getMaxBuildHeight(); }
#else
@Override
public int getMaxY() { return this.parent.getMaxY(); }
#endif
#if MC_VER >= MC_1_17_1
@Override
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 this.parent.isBlockInLine(clipBlockStateContext); }
@Override
public int getHeight() { return this.parent.getHeight(); }
#if MC_VER < MC_1_21_3
@Override
public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); }
#else
@Override
public int getMinY() { return this.parent.getMinY(); }
#endif
@Override
public int getSectionsCount() { return this.parent.getSectionsCount(); }
#if MC_VER < MC_1_21_3
@Override
public int getMinSection() { return this.parent.getMinSection(); }
#else
@Override
public int getMinSectionY() { return BlockAndTintGetter.super.getMinSectionY(); }
#endif
#if MC_VER < MC_1_21_3
@Override
public int getMaxSection() { return this.parent.getMaxSection(); }
#else
@Override
public int getMaxSectionY() { return this.parent.getMaxSectionY(); }
#endif
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); }
@Override
public boolean isOutsideBuildHeight(int i) { return this.parent.isOutsideBuildHeight(i); }
@Override
public int getSectionIndex(int i) { return this.parent.getSectionIndex(i); }
@Override
public int getSectionIndexFromSectionY(int i) { return this.parent.getSectionIndexFromSectionY(i); }
@Override
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
#endif
}
@@ -19,52 +19,26 @@
package com.seibel.distanthorizons.common.wrappers.block; package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.*; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.FluidState;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
#if MC_VER >= MC_1_18_2 public class TintWithoutLevelOverrider extends AbstractDhTintGetter
import net.minecraft.core.Holder;
#endif
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class TintWithoutLevelOverrider implements BlockAndTintGetter
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
#if MC_VER < MC_1_18_2
public static final ConcurrentMap<String, Biome> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
#else
public static final ConcurrentMap<String, Holder<Biome>> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
#endif
@NotNull
private final BiomeWrapper biomeWrapper;
//=============// //=============//
// constructor // // constructor //
//=============// //=============//
public TintWithoutLevelOverrider(@NotNull BiomeWrapper biomeWrapper, IClientLevelWrapper clientLevelWrapper) public TintWithoutLevelOverrider()
{ this.biomeWrapper = biomeWrapper; } { }
@@ -73,145 +47,22 @@ public class TintWithoutLevelOverrider implements BlockAndTintGetter
//=========// //=========//
@Override @Override
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver) public float getShade(Direction direction, boolean shade)
{ { throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only."); }
String biomeString = this.biomeWrapper.getSerialString();
if (biomeString == null
|| biomeString.isEmpty()
|| biomeString.equals(BiomeWrapper.EMPTY_BIOME_STRING))
{
// default to "plains" for empty/invalid biomes
biomeString = "minecraft:plains";
}
return colorResolver.getColor(unwrap(getClientBiome(biomeString)), blockPos.getX(), blockPos.getZ());
}
private static Biome unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{
#if MC_VER >= MC_1_18_2
return biome.value();
#else
return biome;
#endif
}
/**
* <p>Previously, this class might have immediately unwrapped the Holder like this:</p>
* <pre>{@code
* // Inside constructor (OLD WAY - PROBLEMATIC):
* Holder<Biome> biomeHolder = getTheHolderFromSomewhere();
* this.biome = biomeHolder.value(); // <-- PROBLEM HERE
* }</pre>
*
* <p>This approach is problematic because the {@link net.minecraft.core.Holder} system,
* particularly {@code Holder.Reference}, is designed for <strong>late binding</strong>. Here's why storing
* the Holder itself is now necessary:</p>
* <ol>
* <li>A {@code Holder.Reference<Biome>} might be created initially just with a
* {@link net.minecraft.resources.ResourceKey} (like {@code minecraft:plains}), but its actual
* {@link net.minecraft.core.Holder#value() value()} (the {@code Biome} object itself) might be {@code null}
* at construction time.</li>
* <li>Later, during game loading, registry population, or potentially due to modifications by other mods
* (e.g., Polytone), the system calls internal binding methods (like {@code bindValue(Biome)})
* on the {@code Holder} instance. This sets or <strong>updates</strong> the internal reference to the
* actual {@code Biome} object.</li>
* <li>Crucially, the binding process might assign a completely <strong>new</strong> {@code Biome} object
* instance to the {@code Holder} reference, replacing any previous one.</li>
* </ol>
*
* <p>If we unwrapped the {@code Holder} using {@code .value()} within the constructor (the old way),
* our class's internal {@code biome} field would permanently store a reference to whatever {@code Biome}
* object the {@code Holder} pointed to *at that exact moment*. It would have no link back to the
* {@code Holder} and would be unaware if the {@code Holder} was later updated to point to a different
* (or the initially missing) {@code Biome} object. This would lead to using stale or even {@code null} data.</p>
*
* <p>By storing the {@code Holder<Biome>} itself, this class can call {@link net.minecraft.core.Holder#value()}
* whenever the biome information is needed, ensuring it always retrieves the most current {@code Biome}
* instance associated with the holder at that time.</p>
*/
private static #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif getClientBiome(String biomeResourceString)
{
// cache the client biomes so we don't have to re-parse the resource location every time
return BIOME_BY_RESOURCE_STRING.compute(biomeResourceString,
(resourceString, existingBiome) ->
{
if (existingBiome != null)
{
return existingBiome;
}
ClientLevel clientLevel = Minecraft.getInstance().level;
if (clientLevel == null)
{
// shouldn't happen, but just in case
throw new IllegalStateException("Attempted to get client biome when no client level was loaded.");
}
BiomeWrapper.BiomeDeserializeResult result;
try
{
result = BiomeWrapper.deserializeBiome(resourceString, clientLevel.registryAccess());
}
catch (Exception e)
{
LOGGER.warn("Unable to deserialize client biome ["+resourceString+"], using fallback...");
try
{
result = BiomeWrapper.deserializeBiome("minecraft:plains", clientLevel.registryAccess());
}
catch (IOException ex)
{
// should never happen, if it does this log will explode, but just in case
LOGGER.error("Unable to deserialize fallback client biome [minecraft:plains], returning NULL.");
return null;
}
}
if (result.success)
{
existingBiome = result.biome;
}
return existingBiome;
});
}
//================//
// unused methods //
//================//
@Override @Override
public float getShade(@NotNull Direction direction, boolean shade) public LevelLightEngine getLightEngine()
{ { throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only."); }
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public @NotNull LevelLightEngine getLightEngine()
{
throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Nullable @Nullable
@Override @Override
public BlockEntity getBlockEntity(@NotNull BlockPos pos) public BlockEntity getBlockEntity(BlockPos pos)
{ { throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only."); }
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override @Override
public @NotNull BlockState getBlockState(@NotNull BlockPos pos) public BlockState getBlockState(BlockPos pos)
{ { throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only."); }
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override @Override
public @NotNull FluidState getFluidState(@NotNull BlockPos pos) public FluidState getFluidState(BlockPos pos)
{ { throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only."); }
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
//==============// //==============//
@@ -227,7 +78,7 @@ public class TintWithoutLevelOverrider implements BlockAndTintGetter
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
@Override @Override
public int getMinBuildHeight() public int getMinBuildHeight()
{ throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); } { throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
#else #else
@Override @Override
public int getMinY() public int getMinY()
@@ -1,144 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
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.jetbrains.annotations.Nullable;
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#endif
public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
{
final BiomeWrapper biome;
public int smoothingRange;
//=============//
// constructor //
//=============//
public TintWithoutLevelSmoothOverrider(BiomeWrapper biome, int smoothingRange)
{
this.biome = biome;
this.smoothingRange = smoothingRange;
}
//=========//
// methods //
//=========//
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ());
}
private Biome _unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{
#if MC_VER >= MC_1_18_2
return biome.value();
#else
return biome;
#endif
}
// public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
// {
// int i = smoothingRange;
// if (i == 0)
// return colorResolver.getColor(_getBiome(blockPos), blockPos.getX(), blockPos.getZ());
// int j = (i * 2 + 1) * (i * 2 + 1);
// int k = 0;
// int l = 0;
// int m = 0;
// Cursor3D cursor3D = new Cursor3D(blockPos.getX() - i, blockPos.getY(), blockPos.getZ() - i, blockPos.getX() + i, blockPos.getY(), blockPos.getZ() + i);
// BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
// 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());
// }
//
// k += (n & 0xFF0000) >> 16;
// l += (n & 0xFF00) >> 8;
// m += n & 0xFF;
// }
// return (k / j & 0xFF) << 16 | (l / j & 0xFF) << 8 | m / j & 0xFF;
// }
@Override
public float getShade(Direction direction, boolean shade)
{ throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
@Override
public LevelLightEngine getLightEngine()
{ throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos)
{ throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
@Override
public BlockState getBlockState(BlockPos pos)
{ throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
@Override
public FluidState getFluidState(BlockPos pos)
{ throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
//==============//
// post MC 1.17 //
//==============//
#if MC_VER >= MC_1_17_1
@Override
public int getHeight()
{ throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
#if MC_VER < MC_1_21_3
@Override
public int getMinBuildHeight()
{ throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
#else
@Override
public int getMinY()
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
#endif
#endif
}
@@ -18,10 +18,10 @@
*/ */
package com.seibel.distanthorizons.common.wrappers.chunk; package com.seibel.distanthorizons.common.wrappers.chunk;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.MutableBlockPosWrapper; import com.seibel.distanthorizons.common.wrappers.misc.MutableBlockPosWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
@@ -38,7 +38,7 @@ import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.Heightmap;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.*; import java.util.*;
@@ -75,12 +75,14 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
public class ChunkWrapper implements IChunkWrapper public class ChunkWrapper implements IChunkWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */ /** 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(() -> new BlockPos.MutableBlockPos());
private static final ThreadLocal<MutableBlockPosWrapper> MUTABLE_BLOCK_POS_WRAPPER_REF = ThreadLocal.withInitial(() -> new MutableBlockPosWrapper()); private static final ThreadLocal<MutableBlockPosWrapper> MUTABLE_BLOCK_POS_WRAPPER_REF = ThreadLocal.withInitial(() -> new MutableBlockPosWrapper());
private static boolean heightmapThreadWarningLogged = false;
private final ChunkAccess chunk; private final ChunkAccess chunk;
private final DhChunkPos chunkPos; private final DhChunkPos chunkPos;
@@ -98,9 +100,9 @@ public class ChunkWrapper implements IChunkWrapper
private int maxNonEmptyHeight = Integer.MAX_VALUE; private int maxNonEmptyHeight = Integer.MAX_VALUE;
/** will be null if we are using MC heightmaps */ /** will be null if we are using MC heightmaps */
private final int[][] solidHeightMap; private int[][] solidHeightMap = null;
/** will be null if we are using MC heightmaps */ /** will be null if we are using MC heightmaps */
private final int[][] lightBlockingHeightMap; private int[][] lightBlockingHeightMap = null;
@@ -108,27 +110,21 @@ public class ChunkWrapper implements IChunkWrapper
// constructor // // constructor //
//=============// //=============//
/**
* Note: this constructor should be very
* fast since it will be called frequently on the MC
* server thread and a slow method will cause server lag.
*/
public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel) public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel)
{ {
this.chunk = chunk; this.chunk = chunk;
this.wrappedLevel = wrappedLevel; this.wrappedLevel = wrappedLevel;
this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z); this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z);
// use DH heightmaps if requested
if (Config.Common.LodBuilding.recalculateChunkHeightmaps.get())
{
this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
this.recalculateDhHeightMapsIfNeeded();
}
else
{
this.solidHeightMap = null;
this.lightBlockingHeightMap = null;
}
} }
@Override
public ChunkWrapper copy() { return new ChunkWrapper(this.chunk, this.wrappedLevel); }
//=========// //=========//
@@ -248,56 +244,63 @@ public class ChunkWrapper implements IChunkWrapper
} }
private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getInclusiveMinBuildHeight(); } private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getInclusiveMinBuildHeight(); }
/** Will only run if the config says the MC heightmaps shouldn't be trusted. */ @Override
public void recalculateDhHeightMapsIfNeeded() public void createDhHeightMaps()
{ {
// re-calculate the min/max heights for consistency (during world gen these may be wrong) if (heightmapThreadWarningLogged
this.minNonEmptyHeight = Integer.MIN_VALUE; && !DhApi.isDhThread())
this.maxNonEmptyHeight = Integer.MAX_VALUE;
// recalculate heightmaps if needed
if (this.solidHeightMap != null)
{ {
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) LOGGER.warn("ChunkWrapper Height maps created on non-DH thread ["+Thread.currentThread().getName()+"]. This may cause stuttering.");
}
this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
{
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
{ {
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) int minInclusiveBuildHeight = this.getMinNonEmptyHeight();
// if no blocks are found the height map will be at the bottom of the world
int solidHeight = minInclusiveBuildHeight;
int lightBlockingHeight = minInclusiveBuildHeight;
int y = this.getMaxNonEmptyHeight(); //this.getExclusiveMaxBuildHeight();
IBlockStateWrapper block = this.getBlockState(x, y, z);
while (// go down until we reach the minimum build height
y > minInclusiveBuildHeight
// keep going until we find both height map values
&&
(
solidHeight == minInclusiveBuildHeight
|| lightBlockingHeight == minInclusiveBuildHeight
)
)
{ {
int minInclusiveBuildHeight = this.getMinNonEmptyHeight(); // is this block solid?
// if no blocks are found the height map will be at the bottom of the world if (solidHeight == minInclusiveBuildHeight
int solidHeight = minInclusiveBuildHeight; && block.isSolid())
int lightBlockingHeight = minInclusiveBuildHeight;
int y = this.getMaxNonEmptyHeight(); //this.getExclusiveMaxBuildHeight();
IBlockStateWrapper block = this.getBlockState(x, y, z);
while (// go down until we reach the minimum build height
y > minInclusiveBuildHeight
// keep going until we find both height map values
&& (solidHeight == minInclusiveBuildHeight || lightBlockingHeight == minInclusiveBuildHeight))
{ {
// is this block solid? solidHeight = y;
if (solidHeight == minInclusiveBuildHeight
&& block.isSolid())
{
solidHeight = y;
}
// is this block light blocking?
if (lightBlockingHeight == minInclusiveBuildHeight
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
{
lightBlockingHeight = y;
}
// get the next block down
y--;
block = this.getBlockState(x, y, z);
} }
this.solidHeightMap[x][z] = solidHeight; // is this block light blocking?
this.lightBlockingHeightMap[x][z] = lightBlockingHeight; if (lightBlockingHeight == minInclusiveBuildHeight
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
{
lightBlockingHeight = y;
}
// get the next block down
y--;
block = this.getBlockState(x, y, z);
} }
this.solidHeightMap[x][z] = solidHeight;
this.lightBlockingHeightMap[x][z] = lightBlockingHeight;
} }
} }
} }
@@ -617,15 +620,6 @@ public class ChunkWrapper implements IChunkWrapper
//===============//
// other methods //
//===============//
@Override
public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; }
//================// //================//
// base overrides // // base overrides //
//================// //================//
@@ -0,0 +1,98 @@
package com.seibel.distanthorizons.common.wrappers.gui;
#if MC_VER < MC_1_21_9
// not supported for older MC versions
#else
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.gui.components.debug.DebugScreenDisplayer;
import net.minecraft.client.gui.components.debug.DebugScreenEntries;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.LevelChunk;
#if MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#endif
#endif
#if MC_VER < MC_1_21_9
// not supported for older MC versions
public class DhDebugScreenEntry
{}
#else
public class DhDebugScreenEntry implements net.minecraft.client.gui.components.debug.DebugScreenEntry
{
public static void register()
{
// This method is private, so its access will need to be widened
DebugScreenEntries.register(
// The id, this will be displayed on the options screen
#if MC_VER <= MC_1_21_10
ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, "distant_horizons"),
#else
"distant_horizons",
#endif
// The screen entry
new DhDebugScreenEntry()
);
}
@Override
public void display(@NotNull DebugScreenDisplayer displayer, @Nullable Level level, @Nullable LevelChunk clientChunk, @Nullable LevelChunk serverChunk)
{
List<String> messageList = new ArrayList<>();
F3Screen.addStringToDisplay(messageList);
for (String message : messageList)
{
displayer.addLine(message);
}
//// The following will display like so if it is the only entry on the screen:
//// First left! First Right!
////
//// Hello world! Random text!
//// Lorem ipsum.
//// I am another group!
//// I am one group This will appear after with no line breaks!
//// All in a row
//// Provided in a list.
////
//
//displayer.addLine("Hello world!");
//displayer.addLine("Lorem ipsum.");
//displayer.addLine("Random text!");
//
//// These will be displayed first
//displayer.addPriorityLine("First left!");
//displayer.addPriorityLine("First right!");
//
//// These will be grouped separately based on the key
//displayer.addToGroup(GROUP_ONE, List.of(
// "I am one group",
// "All in a row",
// "Provided in a list."
//));
//
//displayer.addToGroup(GROUP_TWO, "I am another group!");
//displayer.addToGroup(GROUP_TWO, "This will appear after with no line breaks!");
}
@Override
public boolean isAllowed(boolean reducedDebugInfo)
{
// Always show regardless of accessibility option
return true;
}
}
#endif
@@ -1,40 +1,52 @@
package com.seibel.distanthorizons.common.wrappers.gui; package com.seibel.distanthorizons.common.wrappers.gui;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.gui.ConfigScreen;
import com.seibel.distanthorizons.core.config.gui.JavaScreenHandlerScreen; import com.seibel.distanthorizons.core.config.gui.JavaScreenHandlerScreen;
import com.seibel.distanthorizons.core.config.gui.OpenGLConfigScreen;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import com.seibel.distanthorizons.core.logging.DhLogger;
public class GetConfigScreen public class GetConfigScreen
{ {
public static type useScreen = type.Classic; protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
public enum type public static EType useScreen = EType.Classic;
public enum EType
{ {
Classic, Classic,
@Deprecated JavaSwing;
OpenGL, // This was just an attempt, it didn't work out, and we are going to change to javafx soon (as soon as that works)
JavaFX;
} }
public static Screen getScreen(Screen parent) public static Screen getScreen(Screen parent)
{ {
// Generate the language // TODO it'd be nice to have this run automatically on startup
// This shouldn't be here, but I need a way to test it after Minecraft inits its assets // but this will only work once MC has added our lang file,
//System.out.println(ConfigBase.INSTANCE.generateLang(false, true)); // which won't be for sure added until we request a GUI
if (ModInfo.IS_DEV_BUILD)
{
String missingLangEntries = ConfigHandler.INSTANCE.generateLang(true, true);
// trim to remove any newlines/spaces
// that may be present when no lang entries need changing
// then we can check length != 0 if any items are missing and need adding
String trimmedMissingEntries = missingLangEntries.trim();
if (!trimmedMissingEntries.isEmpty())
{
LOGGER.warn("One or more language entries is missing:");
LOGGER.warn(missingLangEntries);
}
}
switch (useScreen) switch (useScreen)
{ {
case Classic: case Classic:
return ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client"); return ClassicConfigGUI.getScreen(parent, "client");
case OpenGL: case JavaSwing:
MinecraftScreen.getScreen(parent, new OpenGLConfigScreen(), ModInfo.ID + ".title"); //return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
return null; return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title");
// 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: default:
throw new IllegalArgumentException("No config screen implementation defined for ["+useScreen+"]."); throw new IllegalArgumentException("No config screen implementation defined for ["+useScreen+"].");
} }
@@ -24,26 +24,26 @@ public class MinecraftScreen
private static class ConfigScreenRenderer extends DhScreen private static class ConfigScreenRenderer extends DhScreen
{ {
private final Screen parent; private final Screen parent;
private ConfigListWidget list; private ConfigListWidget configListWidget;
private AbstractScreen screen; private AbstractScreen screen;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
public static net.minecraft.network.chat.TranslatableComponent translate(String str, Object... args) public static net.minecraft.network.chat.TranslatableComponent translate(String str, Object... args)
{ { return new net.minecraft.network.chat.TranslatableComponent(str, args); }
return new net.minecraft.network.chat.TranslatableComponent(str, args);
}
#else #else
public static net.minecraft.network.chat.MutableComponent translate(String str, Object... args) public static net.minecraft.network.chat.MutableComponent translate(String str, Object... args)
{ { return net.minecraft.network.chat.Component.translatable(str, args); }
return net.minecraft.network.chat.Component.translatable(str, args); #endif
}
#endif
protected ConfigScreenRenderer(Screen parent, AbstractScreen screen, String translationName) protected ConfigScreenRenderer(Screen parent, AbstractScreen screen, String translationName)
{ {
super(translate(translationName)); super(translate(translationName));
#if MC_VER < MC_1_21_9
screen.minecraftWindow = Minecraft.getInstance().getWindow().getWindow(); screen.minecraftWindow = Minecraft.getInstance().getWindow().getWindow();
#else
screen.minecraftWindow = Minecraft.getInstance().getWindow().handle();
#endif
this.parent = parent; this.parent = parent;
this.screen = screen; this.screen = screen;
} }
@@ -53,20 +53,22 @@ public class MinecraftScreen
{ {
super.init(); // Init Minecraft's screen super.init(); // Init Minecraft's screen
Window mcWindow = this.minecraft.getWindow(); Window mcWindow = this.minecraft.getWindow();
screen.width = mcWindow.getWidth(); this.screen.width = mcWindow.getWidth();
screen.height = mcWindow.getHeight(); this.screen.height = mcWindow.getHeight();
screen.scaledWidth = this.width; this.screen.scaledWidth = this.width;
screen.scaledHeight = this.height; this.screen.scaledHeight = this.height;
screen.init(); // Init our own config screen this.screen.init(); // Init our own config screen
this.list = new ConfigListWidget(this.minecraft, this.width, this.height, 0, 0, 25); // Select the area to tint this.configListWidget = 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 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 if (this.minecraft != null && this.minecraft.level != null) // Check if in game
this.list.setRenderBackground(false); // Disable from rendering {
this.configListWidget.setRenderBackground(false); // Disable from rendering
}
#endif #endif
this.addWidget(this.list); // Add the tint to the things to be rendered this.addWidget(this.configListWidget); // Add the tint to the things to be rendered
} }
@Override @Override
@@ -78,58 +80,71 @@ public class MinecraftScreen
{ {
#if MC_VER < MC_1_20_2 #if MC_VER < MC_1_20_2
this.renderBackground(matrices); // Render background this.renderBackground(matrices); // Render background
#else #elif MC_VER < MC_1_21_6
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#else
// background blur is already being rendered, rendering again causes the game to crash
#endif #endif
this.list.render(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
screen.mouseX = mouseX; this.configListWidget.render(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
screen.mouseY = mouseY;
screen.render(delta); // Render everything on the main screen this.screen.mouseX = mouseX;
this.screen.mouseY = mouseY;
this.screen.render(delta); // Render everything on the main screen
super.render(matrices, mouseX, mouseY, delta); // Render the vanilla stuff (currently only used for the background and tint) super.render(matrices, mouseX, mouseY, delta); // Render the vanilla stuff (currently only used for the background and tint)
} }
#if MC_VER <= MC_1_21_10
@Override @Override
public void resize(Minecraft mc, int width, int height) public void resize(Minecraft mc, int width, int height)
#else
@Override
public void resize(int width, int height)
#endif
{ {
super.resize(mc, width, height); // Resize Minecraft's screen // Resize Minecraft's screen
#if MC_VER <= MC_1_21_10
super.resize(mc, width, height);
#else
super.resize(width, height);
#endif
Window mcWindow = this.minecraft.getWindow(); Window mcWindow = this.minecraft.getWindow();
screen.width = mcWindow.getWidth(); this.screen.width = mcWindow.getWidth();
screen.height = mcWindow.getHeight(); this.screen.height = mcWindow.getHeight();
screen.scaledWidth = this.width; this.screen.scaledWidth = this.width;
screen.scaledHeight = this.height; this.screen.scaledHeight = this.height;
screen.onResize(); // Resize our screen this.screen.onResize(); // Resize our screen
} }
@Override @Override
public void tick() public void tick()
{ {
super.tick(); // Tick Minecraft's screen super.tick(); // Tick Minecraft's screen
screen.tick(); // Tick our screen this.screen.tick(); // Tick our screen
if (screen.close) // If we decide to close the screen, then actually close the screen if (this.screen.close) // If we decide to close the screen, then actually close the screen
onClose(); {
this.onClose();
}
} }
@Override @Override
public void onClose() public void onClose()
{ {
screen.onClose(); // Close our screen this.screen.onClose(); // Close our screen
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Goto the parent screen
} }
@Override @Override
public void onFilesDrop(@NotNull List<Path> files) public void onFilesDrop(@NotNull List<Path> files)
{ { this.screen.onFilesDrop(files); }
screen.onFilesDrop(files);
}
// For checking if it should close when you press the escape key // For checking if it should close when you press the escape key
@Override @Override
public boolean shouldCloseOnEsc() public boolean shouldCloseOnEsc()
{ { return this.screen.shouldCloseOnEsc; }
return screen.shouldCloseOnEsc;
}
} }
@@ -20,7 +20,6 @@
package com.seibel.distanthorizons.common.wrappers.gui; package com.seibel.distanthorizons.common.wrappers.gui;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.Button;
@@ -42,10 +41,19 @@ import net.minecraft.client.gui.GuiGraphics;
#elif MC_VER < MC_1_21_6 #elif MC_VER < MC_1_21_6
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
#else #elif MC_VER <= MC_1_21_10
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.client.renderer.RenderPipelines;
#else
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.RenderPipelines;
#endif
#if MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
#endif #endif
/** /**
@@ -67,18 +75,33 @@ public class TexturedButtonWidget extends Button
private final int v; private final int v;
private final int hoveredVOffset; private final int hoveredVOffset;
#if MC_VER <= MC_1_21_10
private final ResourceLocation textureResourceLocation; private final ResourceLocation textureResourceLocation;
#else
private final Identifier textureResourceLocation;
#endif
private final int textureWidth; private final int textureWidth;
private final int textureHeight; private final int textureHeight;
#endif #endif
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text) public TexturedButtonWidget(
int x, int y, int width, int height, int u, int v, int hoveredVOffset,
#if MC_VER <= MC_1_21_10 ResourceLocation textureResourceLocation,
#else Identifier textureResourceLocation,
#endif
int textureWidth, int textureHeight, OnPress pressAction, Component text)
{ {
this(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text, true); this(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text, true);
} }
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground) public TexturedButtonWidget(
int x, int y, int width, int height, int u, int v, int hoveredVOffset,
#if MC_VER <= MC_1_21_10 ResourceLocation textureResourceLocation,
#else Identifier textureResourceLocation,
#endif
int textureWidth, int textureHeight, OnPress pressAction, Component text,
boolean renderBackground)
{ {
#if MC_VER < MC_1_20_2 #if MC_VER < MC_1_20_2
super(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text); super(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text);
@@ -169,8 +192,13 @@ public class TexturedButtonWidget extends Button
#endif #endif
#else #else
#if MC_VER < MC_1_21_11
@Override @Override
public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta) public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#else
@Override
protected void renderContents(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#endif
{ {
if (this.renderBackground) if (this.renderBackground)
{ {
@@ -0,0 +1,33 @@
package com.seibel.distanthorizons.common.wrappers.gui.config;
import com.seibel.distanthorizons.core.config.gui.IConfigGuiInfo;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.Nullable;
import java.util.AbstractMap;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* holds information needed by the config GUI for rendering.
*
* @see AbstractConfigBase
*/
public class ConfigGuiInfo implements IConfigGuiInfo
{
/**
* Used to display validation errors.
* Null if no error is present.
*/
@Nullable
public Component errorMessage;
public BiFunction<EditBox, Button, Predicate<String>> tooltipFunction;
/** determines which options the button will show */
public AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>> buttonOptionMap;
}
@@ -14,7 +14,7 @@ import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.narration.NarratableEntry;
@@ -41,7 +41,7 @@ import java.util.*;
// TODO: Make this // TODO: Make this
public class ChangelogScreen extends DhScreen public class ChangelogScreen extends DhScreen
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private Screen parent; private Screen parent;
@@ -74,6 +74,7 @@ public class ChangelogScreen extends DhScreen
{ {
return; return;
} }
try try
{ {
this.setupChangelog(versionID); this.setupChangelog(versionID);
@@ -175,9 +176,12 @@ public class ChangelogScreen extends DhScreen
{ {
#if MC_VER < MC_1_20_2 #if MC_VER < MC_1_20_2
this.renderBackground(matrices); // Render background this.renderBackground(matrices); // Render background
#else #elif MC_VER < MC_1_21_6
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#else
// background blur is already being rendered, rendering again causes the game to crash
#endif #endif
if (!this.usable) if (!this.usable)
{ {
return; return;
@@ -249,42 +253,35 @@ public class ChangelogScreen extends DhScreen
private final Component text; private final Component text;
private final List<AbstractWidget> children = new ArrayList<>(); private final List<AbstractWidget> children = new ArrayList<>();
private ButtonEntry(Component text) private ButtonEntry(Component text) { this.text = text; }
{
this.text = text;
}
public static ButtonEntry create(Component text) public static ButtonEntry create(Component text)
{ { return new ButtonEntry(text); }
return new ButtonEntry(text);
}
#if MC_VER < MC_1_20_1 #if MC_VER < MC_1_20_1
@Override @Override
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
{ { GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF); }
GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF); #elif MC_VER < MC_1_21_9
}
#else
@Override @Override
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
{ { matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF); }
matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF); #else
} @Override
public void renderContent(GuiGraphics matrices, int y, int x, boolean hovered, float tickDelta)
{ matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF); }
#endif #endif
@Override @Override
public List<? extends GuiEventListener> children() public List<? extends GuiEventListener> children() { return this.children; }
{
return this.children;
}
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
@Override @Override
public List<? extends NarratableEntry> narratables() public List<? extends NarratableEntry> narratables() { return this.children; }
{
return this.children;
}
#endif #endif
} }
} }
@@ -9,14 +9,21 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter; import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.logging.DhLogger;
import net.minecraft.client.gui.screens.Screen;
#if MC_VER >= MC_1_20_1 #if MC_VER >= MC_1_20_1
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
#else #else
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
#endif #endif
import net.minecraft.client.gui.screens.Screen;
#if MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import org.apache.logging.log4j.Logger; #else
import net.minecraft.resources.Identifier;
#endif
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*; import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
@@ -31,7 +38,7 @@ import java.util.*;
// and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192 // and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192
public class UpdateModScreen extends DhScreen public class UpdateModScreen extends DhScreen
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private Screen parent; private Screen parent;
@@ -88,10 +95,12 @@ public class UpdateModScreen extends DhScreen
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
0, 0,
#if MC_VER < MC_1_21_1 #if MC_VER <= MC_1_20_6
new ResourceLocation(ModInfo.ID, "logo.png"), new ResourceLocation(ModInfo.ID, "logo.png"),
#else #elif MC_VER <= MC_1_21_10
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"), ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
#else
Identifier.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
#endif #endif
195, 65, 195, 65,
// Create the button and tell it where to go // Create the button and tell it where to go
@@ -121,12 +130,14 @@ public class UpdateModScreen extends DhScreen
0, 0,
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21_1
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else #elif MC_VER <= MC_1_21_10
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"), ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
#else
Identifier.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
#endif #endif
20, 20, 20, 20,
// Create the button and tell it where to go // 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) (buttonWidget) -> Objects.requireNonNull(this.minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)),
// Add a title to the button // Add a title to the button
Translatable(ModInfo.ID + ".updater.title") Translatable(ModInfo.ID + ".updater.title")
)); ));
@@ -36,9 +36,9 @@ public class KeyedClientLevelManager implements IKeyedClientLevelManager
public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; } public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; }
@Override @Override
public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String levelKey) public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String serverKey, String levelKey)
{ {
IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevel((ClientLevel) clientLevel.getWrappedMcObject(), levelKey); IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevelWrapper((ClientLevel) clientLevel.getWrappedMcObject(), serverKey, levelKey);
this.serverKeyedLevel = keyedLevel; this.serverKeyedLevel = keyedLevel;
this.enabled = true; this.enabled = true;
return keyedLevel; return keyedLevel;
@@ -2,24 +2,36 @@ package com.seibel.distanthorizons.common.wrappers.level;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServerKeyedClientLevel public class ServerKeyedClientLevelWrapper extends ClientLevelWrapper implements IServerKeyedClientLevel
{ {
/** Returns the folder name the server wants the client to use. */
private final String serverKey;
/** A unique identifier (generally the level's name) for differentiating multiverse levels */ /** A unique identifier (generally the level's name) for differentiating multiverse levels */
private final String serverLevelKey; private final String serverLevelKey;
public ServerKeyedClientLevel(ClientLevel level, String serverLevelKey) //=============//
// constructor //
//=============//
public ServerKeyedClientLevelWrapper(ClientLevel level, String serverKey, String serverLevelKey)
{ {
super(level); super(level);
this.serverKey = serverKey;
this.serverLevelKey = serverLevelKey; this.serverLevelKey = serverLevelKey;
} }
@Override
public String getServerKey() { return this.serverKey; }
//======================//
// level identification //
//======================//
@Override @Override
public String getServerLevelKey() { return this.serverLevelKey; } public String getServerLevelKey() { return this.serverLevelKey; }
@@ -27,4 +39,6 @@ public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServe
@Override @Override
public String getDhIdentifier() { return this.getServerLevelKey(); } public String getDhIdentifier() { return this.getServerLevelKey(); }
} }
@@ -20,44 +20,35 @@
package com.seibel.distanthorizons.common.wrappers.minecraft; package com.seibel.distanthorizons.common.wrappers.minecraft;
import java.io.File; import java.io.File;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Objects;
import java.util.UUID;
import com.mojang.blaze3d.platform.NativeImage;
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.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.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.logging.DhLogger;
import net.minecraft.CrashReport; import net.minecraft.CrashReport;
import net.minecraft.client.CloudStatus;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.ChunkPos;
import org.jetbrains.annotations.Nullable;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TextComponent;
#endif #endif
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.ChunkPos;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
#else #else
@@ -71,95 +62,34 @@ import net.minecraft.util.profiling.Profiler;
*/ */
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final Minecraft MINECRAFT = Minecraft.getInstance(); private static final Minecraft MINECRAFT = Minecraft.getInstance();
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper(); public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
/**
* The lightmap for the current:
* Time, dimension, brightness setting, etc.
*/
private NativeImage lightMap = null;
private ProfilerWrapper profilerWrapper; private ProfilerWrapper profilerWrapper;
private MinecraftClientWrapper()
{
} //======================//
// multiplayer handling //
//======================//
//================//
// helper methods //
//================//
/**
* This should be called at the beginning of every frame to
* clear any Minecraft data that becomes out of date after a frame. <br> <br>
* <p>
* LightMaps and other time sensitive objects fall in this category. <br> <br>
* <p>
* This doesn't affect OpenGL objects in any way.
*/
@Override
public void clearFrameObjectCache() { this.lightMap = null; }
//=================//
// method wrappers //
//=================//
@Override
public float getShade(EDhDirection lodDirection)
{
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.Quality.lodShading.get();
switch (lodShading)
{
default:
case AUTO:
if (MINECRAFT.level != null)
{
Direction mcDir = McObjectConverter.Convert(lodDirection);
return MINECRAFT.level.getShade(mcDir, true);
}
else
{
return 0.0f;
}
case ENABLED:
switch (lodDirection)
{
case DOWN:
return 0.5F;
default:
case UP:
return 1.0F;
case NORTH:
case SOUTH:
return 0.8F;
case WEST:
case EAST:
return 0.6F;
}
case DISABLED:
return 1.0F;
}
}
@Override @Override
public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); } public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); }
@Override @Override
public boolean clientConnectedToDedicatedServer() { return MINECRAFT.getCurrentServer() != null && !this.hasSinglePlayerServer(); } public boolean clientConnectedToDedicatedServer()
{
return MINECRAFT.getCurrentServer() != null
&& !this.hasSinglePlayerServer();
}
@Override @Override
public boolean connectedToReplay() { return !MINECRAFT.hasSingleplayerServer() && MINECRAFT.getCurrentServer() == null; } public boolean connectedToReplay()
{
return MINECRAFT.getCurrentServer() == null
&& !this.hasSinglePlayerServer() ;
}
@Override @Override
public String getCurrentServerName() public String getCurrentServerName()
@@ -174,7 +104,6 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
return (server != null) ? server.name : "NULL"; return (server != null) ? server.name : "NULL";
} }
} }
@Override @Override
public String getCurrentServerIp() public String getCurrentServerIp()
{ {
@@ -188,7 +117,6 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
return (server != null) ? server.ip : "NA"; return (server != null) ? server.ip : "NA";
} }
} }
@Override @Override
public String getCurrentServerVersion() public String getCurrentServerVersion()
{ {
@@ -196,21 +124,17 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
return (server != null) ? server.version.getString() : "UNKOWN"; return (server != null) ? server.version.getString() : "UNKOWN";
} }
//=============//
// Simple gets //
//=============// //=================//
// player handling //
//=================//
public LocalPlayer getPlayer() { return MINECRAFT.player; } public LocalPlayer getPlayer() { return MINECRAFT.player; }
@Override @Override
public boolean playerExists() { return MINECRAFT.player != null; } public boolean playerExists() { return MINECRAFT.player != null; }
@Override
public UUID getPlayerUUID() { return this.getPlayer().getUUID(); }
@Override
public String getUsername() { return MINECRAFT.getUser().getName(); }
@Override @Override
public DhBlockPos getPlayerBlockPos() public DhBlockPos getPlayerBlockPos()
{ {
@@ -241,6 +165,12 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
return new DhChunkPos(playerPos.x, playerPos.z); return new DhChunkPos(playerPos.x, playerPos.z);
} }
//================//
// level handling //
//================//
@Nullable @Nullable
@Override @Override
public IClientLevelWrapper getWrappedClientLevel() { return this.getWrappedClientLevel(false); } public IClientLevelWrapper getWrappedClientLevel() { return this.getWrappedClientLevel(false); }
@@ -258,6 +188,80 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
return ClientLevelWrapper.getWrapper(level, bypassLevelKeyManager); return ClientLevelWrapper.getWrapper(level, bypassLevelKeyManager);
} }
//===========//
// messaging //
//===========//
@Override
public void sendChatMessage(String string)
{
LocalPlayer player = this.getPlayer();
if (player == null)
{
return;
}
#if MC_VER < MC_1_19_2
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
#elif MC_VER < MC_1_21_9
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
#else
GLProxy.queueRunningOnRenderThread(() ->
{
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
});
#endif
}
@Override
public void sendOverlayMessage(String string)
{
LocalPlayer player = this.getPlayer();
if (player == null)
{
return;
}
#if MC_VER < MC_1_19_2
player.displayClientMessage(new TextComponent(string), /*isOverlay*/true);
#else
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/true);
#endif
}
//==========================//
// vanilla option overrides //
//==========================//
public void disableVanillaClouds()
{
#if MC_VER <= MC_1_18_2
MINECRAFT.options.renderClouds = CloudStatus.OFF;
#else
MINECRAFT.options.cloudStatus().set(CloudStatus.OFF);
#endif
}
public void disableVanillaChunkFadeIn()
{
#if MC_VER <= MC_1_21_10
// chunk fade in was added MC 1.21.11
#else
MINECRAFT.options.chunkSectionFadeInTime().set(0.0);
#endif
}
//======//
// misc //
//======//
@Override @Override
public IProfilerWrapper getProfiler() public IProfilerWrapper getProfiler()
{ {
@@ -280,87 +284,39 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
return this.profilerWrapper; return this.profilerWrapper;
} }
/** Returns all worlds available to the server */
@Override
public ArrayList<ILevelWrapper> getAllServerWorlds()
{
ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>();
Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels();
for (ServerLevel world : serverWorlds)
{
worlds.add(ServerLevelWrapper.getWrapper(world));
}
return worlds;
}
@Override
public void sendChatMessage(String string)
{
LocalPlayer player = this.getPlayer();
if (player == null)
{
return;
}
#if MC_VER < MC_1_19_2
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
#else
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
#endif
}
@Override
public void sendOverlayMessage(String string)
{
LocalPlayer player = this.getPlayer();
if (player == null)
{
return;
}
#if MC_VER < MC_1_19_2
player.displayClientMessage(new TextComponent(string), /*isOverlay*/true);
#else
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/true);
#endif
}
/**
* Crashes Minecraft, displaying the given errorMessage <br> <br>
* In the following format: <br>
*
* The game crashed whilst <strong>errorMessage</strong> <br>
* Error: <strong>ExceptionClass: exceptionErrorMessage</strong> <br>
* Exit Code: -1 <br>
*/
@Override @Override
public void crashMinecraft(String errorMessage, Throwable exception) public void crashMinecraft(String errorMessage, Throwable exception)
{ {
LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception); LOGGER.fatal(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception);
CrashReport report = new CrashReport(errorMessage, exception); CrashReport report = new CrashReport(errorMessage, exception);
#if MC_VER < MC_1_20_4 #if MC_VER < MC_1_20_4
Minecraft.crash(report); Minecraft.crash(report);
#else #else
Minecraft.getInstance().delayCrash(report); MINECRAFT.delayCrash(report);
#endif #endif
} }
//=============//
// mod support //
//=============//
@Override @Override
public Object getOptionsObject() { return MINECRAFT.options; } public Object getOptionsObject() { return MINECRAFT.options; }
//========//
// shared //
//========//
@Override @Override
public boolean isDedicatedServer() { return false; } public boolean isDedicatedServer() { return false; }
@Override @Override
public File getInstallationDirectory() { return MINECRAFT.gameDirectory; } public File getInstallationDirectory() { return MINECRAFT.gameDirectory; }
@Override
public void executeOnRenderThread(Runnable runnable) { MINECRAFT.execute(runnable); }
@Override @Override
public int getPlayerCount() public int getPlayerCount()
{ {
@@ -375,4 +331,6 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
} }
} }
} }
@@ -21,14 +21,14 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
#if MC_VER < MC_1_21_5 #if MC_VER < MC_1_21_5
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
#elif MC_VER >= MC_1_21_5 #else
import com.mojang.blaze3d.opengl.GlStateManager; import com.mojang.blaze3d.opengl.GlStateManager;
#endif #endif
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
@@ -54,7 +54,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper
{ {
public static final MinecraftGLWrapper INSTANCE = new MinecraftGLWrapper(); public static final MinecraftGLWrapper INSTANCE = new MinecraftGLWrapper();
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@@ -25,10 +25,13 @@ import java.util.concurrent.ConcurrentHashMap;
import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.WrapperFactory; import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.ColorUtil; import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
@@ -53,13 +56,19 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWra
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.util.math.Vec3d; import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3f; 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.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.effect.MobEffects; import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.phys.Vec3;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.joml.Vector4f;
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
import net.minecraft.tags.FluidTags; import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
@@ -68,31 +77,27 @@ import org.lwjgl.opengl.GL15;
#else #else
import net.minecraft.world.level.material.FogType; import net.minecraft.world.level.material.FogType;
#endif #endif
import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger;
import org.joml.Vector4f;
#if MC_VER >= MC_1_21_5 #if MC_VER >= MC_1_21_5
import com.mojang.blaze3d.opengl.GlTexture; import com.mojang.blaze3d.opengl.GlTexture;
#else
#endif
#if MC_VER <= MC_1_21_10
#else
import net.minecraft.world.attribute.EnvironmentAttributes;
#endif #endif
/** /**
* A singleton that contains everything * A singleton that contains everything
* related to rendering in Minecraft. * related to rendering in Minecraft.
*
* @author James Seibel
* @version 12-12-2021
*/ */
//@Environment(EnvType.CLIENT)
public class MinecraftRenderWrapper implements IMinecraftRenderWrapper public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
{ {
public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper(); public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper();
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final Minecraft MC = Minecraft.getInstance(); private static final Minecraft MC = Minecraft.getInstance();
private static final IWrapperFactory FACTORY = WrapperFactory.INSTANCE;
private static final IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
/** /**
* In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps. <br> * In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps. <br>
@@ -123,12 +128,20 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override @Override
public Vec3f getLookAtVector() public Vec3f getLookAtVector()
{ {
#if MC_VER <= MC_1_21_10
Camera camera = MC.gameRenderer.getMainCamera(); Camera camera = MC.gameRenderer.getMainCamera();
return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z()); return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z());
#else
Camera camera = MC.gameRenderer.getMainCamera();
return new Vec3f(camera.forwardVector().x(), camera.forwardVector().y(), camera.forwardVector().z());
#endif
} }
/**
* Unless you really need to know if the player is blind,
* use {@link MinecraftRenderWrapper#isFogStateSpecial()} or {@link IMinecraftRenderWrapper#isFogStateSpecial()} instead
*/
@Override @Override
/** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */
public boolean playerHasBlindingEffect() public boolean playerHasBlindingEffect()
{ {
if (MC.player == null) if (MC.player == null)
@@ -153,7 +166,11 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
public Vec3d getCameraExactPosition() public Vec3d getCameraExactPosition()
{ {
Camera camera = MC.gameRenderer.getMainCamera(); Camera camera = MC.gameRenderer.getMainCamera();
#if MC_VER <= MC_1_21_10
Vec3 projectedView = camera.getPosition(); Vec3 projectedView = camera.getPosition();
#else
Vec3 projectedView = camera.position();
#endif
return new Vec3d(projectedView.x, projectedView.y, projectedView.z); return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
} }
@@ -187,8 +204,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
Math.max(0f, Math.min(colorValues.z, 1f)), // b Math.max(0f, Math.min(colorValues.z, 1f)), // b
Math.max(0f, Math.min(colorValues.w, 1f)) // a Math.max(0f, Math.min(colorValues.w, 1f)) // a
); );
#else #elif MC_VER <= MC_1_21_10
if (mcFogRenderer == null) if (mcFogRenderer == null)
{ {
mcFogRenderer = new FogRenderer(); mcFogRenderer = new FogRenderer();
@@ -212,9 +228,33 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
Math.max(0f, Math.min(colorValues.z, 1f)), // b Math.max(0f, Math.min(colorValues.z, 1f)), // b
Math.max(0f, Math.min(colorValues.w, 1f)) // a Math.max(0f, Math.min(colorValues.w, 1f)) // a
); );
#else
if (mcFogRenderer == null)
{
mcFogRenderer = new FogRenderer();
}
if (MC.level == null)
{
// shouldn't happen, but just in case
return Color.white;
}
Vector4f colorValues = mcFogRenderer.setupFog(
MC.gameRenderer.getMainCamera(),
MC.options.getEffectiveRenderDistance(),
MC.deltaTracker,
MC.gameRenderer.getDarkenWorldAmount(MC.deltaTracker.getGameTimeDeltaPartialTick(true)),
MC.level);
return new Color(
Math.max(0f, Math.min(colorValues.x, 1f)), // r
Math.max(0f, Math.min(colorValues.y, 1f)), // g
Math.max(0f, Math.min(colorValues.z, 1f)), // b
Math.max(0f, Math.min(colorValues.w, 1f)) // a
);
#endif #endif
} }
// getSpecialFogColor() is the same as getFogColor()
@Override @Override
public Color getSkyColor() public Color getSkyColor()
@@ -226,8 +266,10 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
frameTime = MC.getFrameTime(); frameTime = MC.getFrameTime();
#elif MC_VER < MC_1_21_3 #elif MC_VER < MC_1_21_3
frameTime = MC.getTimer().getRealtimeDeltaTicks(); frameTime = MC.getTimer().getRealtimeDeltaTicks();
#else #elif MC_VER <= MC_1_21_10
frameTime = MC.deltaTracker.getGameTimeDeltaTicks(); frameTime = MC.deltaTracker.getGameTimeDeltaTicks();
#else
frameTime = 0f; // unused
#endif #endif
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
@@ -236,9 +278,12 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
#elif MC_VER < MC_1_21_3 #elif MC_VER < MC_1_21_3
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime); Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z); return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
#elif MC_VER <= MC_1_21_10
int argbColorInt = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
return ColorUtil.toColorObjARGB(argbColorInt);
#else #else
int argbColorInt = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);; int argbColor = MC.level.environmentAttributes().getValue(EnvironmentAttributes.SKY_COLOR, BlockPos.ZERO);
return ColorUtil.toColorObjARGB(argbColorInt); // TODO MC changed color formats return new Color(ColorUtil.getRed(argbColor), ColorUtil.getGreen(argbColor), ColorUtil.getBlue(argbColor), 255 /* ignore alpha since DH clouds don't render correctly with transparency */);
#endif #endif
} }
else else
@@ -248,10 +293,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
} }
@Override @Override
public double getFov(float partialTicks) public double getFov(float partialTicks) { return MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true); }
{
return MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true);
}
/** Measured in chunks */ /** Measured in chunks */
@Override @Override
@@ -265,42 +307,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
#endif #endif
} }
@Override protected RenderTarget getRenderTarget() { return MC.getMainRenderTarget(); }
public int getScreenWidth()
{
// alternate ways of getting the window's resolution,
// using one of these methods may fix the optifine render resolution bug
// TODO: test these once we can run with Optifine again
// int[] heightArray = new int[1];
// int[] widthArray = new int[1];
//
// long window = GLProxy.getInstance().minecraftGlContext;
// GLFW.glfwGetWindowSize(window, widthArray, heightArray); // option 1
// GLFW.glfwGetFramebufferSize(window, widthArray, heightArray); // option 2
int width = MC.getWindow().getWidth();
if (OPTIFINE_ACCESSOR != null)
{
// TODO remove comment after testing:
// this should fix the issue where different optifine render resolutions screw up the LOD rendering
width *= OPTIFINE_ACCESSOR.getRenderResolutionMultiplier();
}
return width;
}
@Override
public int getScreenHeight()
{
int height = MC.getWindow().getHeight();
if (OPTIFINE_ACCESSOR != null)
{
height *= OPTIFINE_ACCESSOR.getRenderResolutionMultiplier();
}
return height;
}
private RenderTarget getRenderTarget() { return MC.getMainRenderTarget(); }
@Override @Override
public boolean mcRendersToFrameBuffer() public boolean mcRendersToFrameBuffer()
@@ -323,7 +330,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
} }
@Override @Override
public int getTargetFrameBuffer() public int getTargetFramebuffer()
{ {
// used so we can access the framebuffer shaders end up rendering to // used so we can access the framebuffer shaders end up rendering to
if (AbstractOptifineAccessor.optifinePresent()) if (AbstractOptifineAccessor.optifinePresent())
@@ -359,8 +366,9 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
} }
return glTexture.glId(); return glTexture.glId();
} }
catch (ClassCastException e) catch (Exception e)
{ {
// only log this error once per session // only log this error once per session
if (!this.depthTextureCastFailLogged) if (!this.depthTextureCastFailLogged)
@@ -389,7 +397,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return glTexture.glId(); return glTexture.glId();
} }
catch (ClassCastException e) catch (Exception e)
{ {
// only log this error once per session // only log this error once per session
if (!this.colorTextureCastFailLogged) if (!this.colorTextureCastFailLogged)
@@ -403,19 +411,27 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
} }
@Override @Override
public int getTargetFrameBufferViewportWidth() public int getTargetFramebufferViewportWidth()
{ {
#if MC_VER < MC_1_21_9
return this.getRenderTarget().viewWidth; return this.getRenderTarget().viewWidth;
#else
return this.getRenderTarget().width;
#endif
} }
@Override @Override
public int getTargetFrameBufferViewportHeight() public int getTargetFramebufferViewportHeight()
{ {
#if MC_VER < MC_1_21_9
return this.getRenderTarget().viewHeight; return this.getRenderTarget().viewHeight;
#else
return this.getRenderTarget().height;
#endif
} }
@Override @Override
public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); } public ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
@Override @Override
public boolean isFogStateSpecial() public boolean isFogStateSpecial()
@@ -459,4 +475,45 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
wrapper.setLightmapId(tetxureId); wrapper.setLightmapId(tetxureId);
} }
@Override
public float getShade(EDhDirection lodDirection)
{
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.Quality.lodShading.get();
switch (lodShading)
{
default:
case AUTO:
if (MC.level != null)
{
Direction mcDir = McObjectConverter.Convert(lodDirection);
return MC.level.getShade(mcDir, true);
}
else
{
return 0.0f;
}
case ENABLED:
switch (lodDirection)
{
case DOWN:
return 0.5F;
default:
case UP:
return 1.0F;
case NORTH:
case SOUTH:
return 0.8F;
case WEST:
case EAST:
return 0.6F;
}
case DISABLED:
return 1.0F;
}
}
} }
@@ -2,17 +2,20 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
//@Environment(EnvType.SERVER)
public class MinecraftServerWrapper implements IMinecraftSharedWrapper public class MinecraftServerWrapper implements IMinecraftSharedWrapper
{ {
public static final MinecraftServerWrapper INSTANCE = new MinecraftServerWrapper(); public static final MinecraftServerWrapper INSTANCE = new MinecraftServerWrapper();
/** set during server startup */
@Nullable
public DedicatedServer dedicatedServer = null; public DedicatedServer dedicatedServer = null;
//=============// //=============//
// constructor // // constructor //
//=============// //=============//
@@ -33,7 +36,7 @@ public class MinecraftServerWrapper implements IMinecraftSharedWrapper
{ {
if (this.dedicatedServer == null) if (this.dedicatedServer == null)
{ {
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server completed initialization!"); throw new IllegalStateException("Trying to get Installation Direction before dedicated server completed initialization!");
} }
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21_1
@@ -46,7 +49,14 @@ public class MinecraftServerWrapper implements IMinecraftSharedWrapper
@Override @Override
public int getPlayerCount() public int getPlayerCount()
{ {
if (this.dedicatedServer == null)
{
throw new IllegalStateException("Trying to get player count before dedicated server completed initialization!");
}
return this.dedicatedServer.getPlayerCount(); return this.dedicatedServer.getPlayerCount();
} }
} }
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -32,7 +32,7 @@ import java.nio.ByteBuffer;
public class LightMapWrapper implements ILightMapWrapper public class LightMapWrapper implements ILightMapWrapper
{ {
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class); private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private int textureId = 0; private int textureId = 0;
@@ -71,26 +71,6 @@ public class ServerPlayerWrapper implements IServerPlayerWrapper
return new Vec3d(position.x, position.y, position.z); return new Vec3d(position.x, position.y, position.z);
} }
@Override
public int getViewDistance()
{
#if MC_VER < MC_1_21_6
return this.getServerPlayer().server.getPlayerList().getViewDistance();
#else
return this.getServerPlayer().getServer().getPlayerList().getViewDistance();
#endif
}
@Override
public SocketAddress getRemoteAddress()
{
#if MC_VER >= MC_1_19_4
return this.getServerPlayer().connection.getRemoteAddress();
#else // < 1.19.4
return this.getServerPlayer().connection.connection.getRemoteAddress();
#endif
}
//================// //================//
@@ -2,11 +2,11 @@ package com.seibel.distanthorizons.common.wrappers.world;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; 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.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.ClientBlockStateColorCache; import com.seibel.distanthorizons.common.wrappers.block.ClientBlockStateColorCache;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.*; import com.seibel.distanthorizons.core.level.*;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
@@ -21,11 +21,11 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWra
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -51,9 +51,15 @@ import net.minecraft.world.phys.Vec3;
import com.seibel.distanthorizons.core.util.ColorUtil; import com.seibel.distanthorizons.core.util.ColorUtil;
#endif #endif
#if MC_VER <= MC_1_21_10
#else
import net.minecraft.world.attribute.EnvironmentAttributes;
#endif
public class ClientLevelWrapper implements IClientLevelWrapper public class ClientLevelWrapper implements IClientLevelWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** /**
* weak references are to prevent rare issues * weak references are to prevent rare issues
* where, upon world closure, some levels aren't shutdown/removed properly * where, upon world closure, some levels aren't shutdown/removed properly
@@ -65,16 +71,14 @@ public class ClientLevelWrapper implements IClientLevelWrapper
private static final Minecraft MINECRAFT = Minecraft.getInstance(); private static final Minecraft MINECRAFT = Minecraft.getInstance();
private final ClientLevel level; private final ClientLevel level;
private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockCache = new ConcurrentHashMap<>(); private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockColorCacheByBlockState = new ConcurrentHashMap<>();
/** cached method reference to reduce GC overhead */ /** cached method reference to reduce GC overhead */
private final Function<BlockState, ClientBlockStateColorCache> cachedBlockColorCacheFunction = (blockState) -> this.createBlockColorCache(blockState); private final Function<BlockState, ClientBlockStateColorCache> createCachedBlockColorCacheFunc = (blockState) -> new ClientBlockStateColorCache(blockState, this);
private BlockStateWrapper dirtBlockWrapper; private BlockStateWrapper dirtBlockWrapper;
private BiomeWrapper plainsBiomeWrapper; private IDhLevel dhLevel;
@Deprecated // TODO circular references are bad
private IDhLevel parentDhLevel;
@@ -90,6 +94,29 @@ public class ClientLevelWrapper implements IClientLevelWrapper
// instance methods // // instance methods //
//==================// //==================//
/**
* can be used when speed is important and the same level is likely to be passed in,
* IE rendering.
*/
@Nullable
public static IClientLevelWrapper getWrapperIfDifferent(@Nullable IClientLevelWrapper levelWrapper, @NotNull ClientLevel level) // TODO handle null level
{
if (KEYED_CLIENT_LEVEL_MANAGER.isEnabled() && KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel() != levelWrapper)
{
return getWrapper(level);
}
ClientLevelWrapper clientLevelWrapper = (ClientLevelWrapper)levelWrapper;
if (clientLevelWrapper == null
|| clientLevelWrapper.level != level)
{
return getWrapper(level);
}
return clientLevelWrapper;
}
@Nullable
public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level) { return getWrapper(level, false); } public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level) { return getWrapper(level, false); }
@Nullable @Nullable
@@ -143,13 +170,17 @@ public class ClientLevelWrapper implements IClientLevelWrapper
{ {
try try
{ {
// this method only makes sense if we are running a single-player server
if (MINECRAFT.getSingleplayerServer() == null)
{
return null;
}
Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels(); Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels();
// attempt to find the server level with the same dimension type // 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 // Note: this assumes only one level per dimension type, multiverse servers may not behave correctly
ServerLevelWrapper foundLevelWrapper = null; ServerLevelWrapper foundLevelWrapper = null;
// TODO: Surely there is a more efficient way to write this code
for (ServerLevel serverLevel : serverLevels) for (ServerLevel serverLevel : serverLevels)
{ {
if (serverLevel.dimension() == this.level.dimension()) if (serverLevel.dimension() == this.level.dimension())
@@ -175,16 +206,18 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//====================// //====================//
@Override @Override
public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockWrapper) public int getBlockColor(DhBlockPos blockPos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockWrapper)
{ {
ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent( ClientBlockStateColorCache blockColorCache = this.blockColorCacheByBlockState.get(((BlockStateWrapper) blockWrapper).blockState);
((BlockStateWrapper) blockWrapper).blockState, if (blockColorCache == null)
this.cachedBlockColorCacheFunction); {
blockColorCache = this.blockColorCacheByBlockState.computeIfAbsent(
((BlockStateWrapper) blockWrapper).blockState,
this.createCachedBlockColorCacheFunc);
}
return blockColorCache.getColor((BiomeWrapper) biome, pos); return blockColorCache.getColor((BiomeWrapper) biome, fullDataSource, blockPos);
} }
/** used by {@link ClientLevelWrapper#cachedBlockColorCacheFunction} */
private ClientBlockStateColorCache createBlockColorCache(BlockState block) { return new ClientBlockStateColorCache(block, this); }
@Override @Override
@@ -204,38 +237,31 @@ public class ClientLevelWrapper implements IClientLevelWrapper
} }
} }
return this.getBlockColor(DhBlockPos.ZERO,BiomeWrapper.EMPTY_WRAPPER, this.dirtBlockWrapper); return this.getBlockColor(DhBlockPos.ZERO, BiomeWrapper.EMPTY_WRAPPER, null, this.dirtBlockWrapper);
} }
@Override @Override
public void clearBlockColorCache() { this.blockCache.clear(); } public void clearBlockColorCache() { this.blockColorCacheByBlockState.clear(); }
@Override @Override
public IBiomeWrapper getPlainsBiomeWrapper() public IDimensionTypeWrapper getDimensionType()
{ {
if (this.plainsBiomeWrapper == null) #if MC_VER <= MC_1_21_10
{ return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
try #else
{ return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType(), this.getDimensionName());
this.plainsBiomeWrapper = (BiomeWrapper) BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, this); #endif
}
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 @Override
public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); } public String getDimensionName()
{
#if MC_VER <= MC_1_21_10
@Override return this.level.dimension().location().toString();
public String getDimensionName() { return this.level.dimension().location().toString(); } #else
return this.level.dimension().identifier().toString();
#endif
}
@Override @Override
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; } public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
@@ -269,37 +295,6 @@ public class ClientLevelWrapper implements IClientLevelWrapper
#endif #endif
} }
@Override
public IChunkWrapper tryGetChunk(DhChunkPos pos)
{
if (!this.level.hasChunk(pos.getX(), pos.getZ()))
{
return null;
}
ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.EMPTY, false);
if (chunk == null)
{
return null;
}
return new ChunkWrapper(chunk, this);
}
@Override
public boolean hasChunkLoaded(int chunkX, int chunkZ)
{
ChunkSource source = this.level.getChunkSource();
return source.hasChunk(chunkX, chunkZ);
}
@Override
public IBlockStateWrapper getBlockState(DhBlockPos pos)
{ return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this); }
@Override
public IBiomeWrapper getBiome(DhBlockPos pos) { return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this); }
@Override @Override
public ClientLevel getWrappedMcObject() { return this.level; } public ClientLevel getWrappedMcObject() { return this.level; }
@@ -307,18 +302,18 @@ public class ClientLevelWrapper implements IClientLevelWrapper
public void onUnload() public void onUnload()
{ {
LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.remove(this.level); LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.remove(this.level);
this.parentDhLevel = null; this.dhLevel = null;
} }
@Override @Override
public File getDhSaveFolder() public File getDhSaveFolder()
{ {
if (this.parentDhLevel == null) if (this.dhLevel == null)
{ {
return null; return null;
} }
return this.parentDhLevel.getSaveStructure().getSaveFolder(this); return this.dhLevel.getSaveStructure().getSaveFolder(this);
} }
@@ -329,17 +324,19 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//===================// //===================//
@Override @Override
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; } public void setDhLevel(IDhLevel dhLevel) { this.dhLevel = dhLevel; }
@Override
public IDhLevel getDhLevel() { return this.dhLevel; }
@Override @Override
public IDhApiCustomRenderRegister getRenderRegister() public IDhApiCustomRenderRegister getRenderRegister()
{ {
if (this.parentDhLevel == null) if (this.dhLevel == null)
{ {
return null; return null;
} }
return this.parentDhLevel.getGenericRenderer(); return this.dhLevel.getGenericRenderer();
} }
@Override @Override
@@ -348,9 +345,12 @@ public class ClientLevelWrapper implements IClientLevelWrapper
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
Vec3 colorVec3 = this.level.getCloudColor(tickDelta); Vec3 colorVec3 = this.level.getCloudColor(tickDelta);
return new Color((float)colorVec3.x, (float)colorVec3.y, (float)colorVec3.z); return new Color((float)colorVec3.x, (float)colorVec3.y, (float)colorVec3.z);
#else #elif MC_VER <= MC_1_21_10
int argbColor = this.level.getCloudColor(tickDelta); int argbColor = this.level.getCloudColor(tickDelta);
return ColorUtil.toColorObjARGB(argbColor); return ColorUtil.toColorObjARGB(argbColor);
#else
int argbColor = this.level.environmentAttributes().getValue(EnvironmentAttributes.CLOUD_COLOR, BlockPos.ZERO);
return new Color(ColorUtil.getRed(argbColor), ColorUtil.getGreen(argbColor), ColorUtil.getBlue(argbColor), 255 /* ignore alpha since DH clouds don't render correctly with transparency */);
#endif #endif
} }
@@ -26,25 +26,45 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWra
import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.dimension.DimensionType;
/**
* @author James Seibel
*/
public class DimensionTypeWrapper implements IDimensionTypeWrapper public class DimensionTypeWrapper implements IDimensionTypeWrapper
{ {
private static final ConcurrentMap<String, DimensionTypeWrapper> DIMENSION_WRAPPER_BY_NAME = new ConcurrentHashMap<>(); private static final ConcurrentMap<String, DimensionTypeWrapper> DIMENSION_WRAPPER_BY_NAME = new ConcurrentHashMap<>();
private final DimensionType dimensionType; private final DimensionType dimensionType;
private final String name;
//=============// //=============//
// Constructor // // Constructor //
//=============// //=============//
public DimensionTypeWrapper(DimensionType dimensionType) { this.dimensionType = dimensionType; } #if MC_VER <= MC_1_21_10
public DimensionTypeWrapper(DimensionType dimensionType)
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType) #else
public DimensionTypeWrapper(DimensionType dimensionType, String name)
#endif
{ {
String dimName = getName(dimensionType); this.dimensionType = dimensionType;
#if MC_VER <= MC_1_21_10
this.name = determineName(dimensionType);
#else
this.name = name;
#endif
}
#if MC_VER <= MC_1_21_10
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
#else
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType, String name)
#endif
{
#if MC_VER <= MC_1_21_10
String dimName = determineName(dimensionType);
#else
String dimName = name;
#endif
// check if the dimension has already been wrapped // check if the dimension has already been wrapped
if (DIMENSION_WRAPPER_BY_NAME.containsKey(dimName) if (DIMENSION_WRAPPER_BY_NAME.containsKey(dimName)
@@ -55,10 +75,26 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
// create the missing wrapper // create the missing wrapper
#if MC_VER <= MC_1_21_10
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType); DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
#else
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType, dimName);
#endif
DIMENSION_WRAPPER_BY_NAME.put(dimName, dimensionTypeWrapper); DIMENSION_WRAPPER_BY_NAME.put(dimName, dimensionTypeWrapper);
return dimensionTypeWrapper; return dimensionTypeWrapper;
} }
private static String determineName(DimensionType dimensionType)
{
#if MC_VER <= MC_1_16_5
// effectsLocation() is marked as client only, so using the backing field directly
return dimensionType.effectsLocation.getPath();
#elif MC_VER <= MC_1_21_10
return dimensionType.effectsLocation().getPath();
#else
throw new UnsupportedOperationException("As of MC 1.21.11 the dimension type no longer stores it's name and must be determined from the level.");
#endif
}
public static void clearMap() { DIMENSION_WRAPPER_BY_NAME.clear(); } public static void clearMap() { DIMENSION_WRAPPER_BY_NAME.clear(); }
@@ -69,16 +105,7 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
//=================// //=================//
@Override @Override
public String getName() { return getName(this.dimensionType); } public String getName() { return this.name; }
public static String getName(DimensionType dimensionType)
{
#if MC_VER <= MC_1_16_5
// effectsLocation() is marked as client only, so using the backing field directly
return dimensionType.effectsLocation.getPath();
#else
return dimensionType.effectsLocation().getPath();
#endif
}
@Override @Override
public boolean hasCeiling() { return this.dimensionType.hasCeiling(); } public boolean hasCeiling() { return this.dimensionType.hasCeiling(); }
@@ -89,7 +116,6 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
@Override @Override
public Object getWrappedMcObject() { return this.dimensionType; } public Object getWrappedMcObject() { return this.dimensionType; }
// there's definitely a better way of doing this, but it should work well enough for now
@Override @Override
public boolean isTheEnd() { return this.getName().equalsIgnoreCase("the_end"); } public boolean isTheEnd() { return this.getName().equalsIgnoreCase("the_end"); }
@@ -98,7 +124,6 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
//================// //================//
// base overrides // // base overrides //
//================// //================//
@@ -24,21 +24,18 @@ import java.lang.ref.WeakReference;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; 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.chunk.ChunkWrapper; 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.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.world.EWorldEnvironment;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@@ -52,16 +49,12 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
#if MC_VER < MC_1_21_3 import com.seibel.distanthorizons.core.logging.DhLogger;
#else import org.jetbrains.annotations.Nullable;
import java.nio.file.Path;
#endif
import org.apache.logging.log4j.Logger;
public class ServerLevelWrapper implements IServerLevelWrapper public class ServerLevelWrapper implements IServerLevelWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** /**
* weak references are to prevent rare issues * weak references are to prevent rare issues
* where, upon world closure, some levels aren't shutdown/removed properly * where, upon world closure, some levels aren't shutdown/removed properly
@@ -69,8 +62,13 @@ public class ServerLevelWrapper implements IServerLevelWrapper
private static final Map<ServerLevel, WeakReference<ServerLevelWrapper>> LEVEL_WRAPPER_REF_BY_SERVER_LEVEL = Collections.synchronizedMap(new WeakHashMap<>()); private static final Map<ServerLevel, WeakReference<ServerLevelWrapper>> LEVEL_WRAPPER_REF_BY_SERVER_LEVEL = Collections.synchronizedMap(new WeakHashMap<>());
private final ServerLevel level; private final ServerLevel level;
@Deprecated // TODO circular references are bad private IDhLevel dhLevel;
private IDhLevel parentDhLevel;
/**
* this name is cached to prevent issues during shutdown where
* the server variables needed may no longer be available.
*/
private final String KeyedLevelDimensionName;
@@ -95,7 +93,11 @@ public class ServerLevelWrapper implements IServerLevelWrapper
}).get(); }).get();
} }
public ServerLevelWrapper(ServerLevel level) { this.level = level; } public ServerLevelWrapper(ServerLevel level)
{
this.level = level;
this.KeyedLevelDimensionName = this.createKeyedLevelDimensionName();
}
@@ -114,22 +116,80 @@ public class ServerLevelWrapper implements IServerLevelWrapper
} }
@Override @Override
public String getWorldFolderName() public String getKeyedLevelDimensionName() { return this.KeyedLevelDimensionName; }
{
// Need specifically overworld since it's the only dimension that is stored in a server root folder
#if MC_VER >= MC_1_21_3 private String createKeyedLevelDimensionName()
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParent().getFileName().toString(); {
#else // <= 1.21.3 String dimensionName = this.getDhIdentifier();
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParentFile().getName();
if (Config.Server.sendLevelKeys.get())
{
String levelKeyPrefix = Config.Server.levelKeyPrefix.get();
if (SharedApi.getEnvironment() == EWorldEnvironment.CLIENT_SERVER)
{
String cleanWorldFolderName = this.getWorldFolderName()
.replaceAll("[^" + LevelInitMessage.ALLOWED_CHARS_REGEX + " ]", "")
.replaceAll(" ", "_");
levelKeyPrefix += (!levelKeyPrefix.isEmpty() ? "_" : "") + cleanWorldFolderName
+ "_" + this.getHashedSeedEncoded();
}
if (levelKeyPrefix.isEmpty())
{
levelKeyPrefix = this.getHashedSeedEncoded();
}
String mainPart = "@" + dimensionName;
return levelKeyPrefix.substring(0, Math.min(
LevelInitMessage.MAX_LENGTH - mainPart.length(),
levelKeyPrefix.length()
)) + mainPart;
}
return dimensionName;
}
private String getWorldFolderName()
{
try
{
// We use the overworld since it's the only dimension that is stored in the server root folder
#if MC_VER >= MC_1_21_3
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParent().getFileName().toString();
#else // <= 1.21.3
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParentFile().getName();
#endif
}
catch (Exception e)
{
LOGGER.warn("Unable to get world folder name. LODs may not load or save correctly. Error: ["+e.getMessage()+"].", e);
return "unknown_world";
}
}
@Override
public DimensionTypeWrapper getDimensionType()
{
#if MC_VER <= MC_1_21_10
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
#else
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType(), this.getDimensionName());
#endif #endif
} }
@Override @Override
public DimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); } public String getDimensionName()
{
@Override #if MC_VER <= MC_1_21_10
public String getDimensionName() { return this.level.dimension().location().toString(); } return this.level.dimension().location().toString();
#else
return this.level.dimension().identifier().toString();
#endif
}
@Override @Override
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; } public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
@@ -163,43 +223,6 @@ public class ServerLevelWrapper implements IServerLevelWrapper
#endif #endif
} }
@Override
public IChunkWrapper tryGetChunk(DhChunkPos pos)
{
if (!this.level.hasChunk(pos.getX(), pos.getZ()))
{
return null;
}
ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FULL, false);
if (chunk == null)
{
return null;
}
return new ChunkWrapper(chunk, this);
}
@Override
public boolean hasChunkLoaded(int chunkX, int chunkZ)
{
// world.hasChunk(chunkX, chunkZ); THIS DOES NOT WORK FOR CLIENT LEVEL CAUSE MOJANG ALWAYS RETURN TRUE FOR THAT!
ChunkSource source = this.level.getChunkSource();
return source.hasChunk(chunkX, chunkZ);
}
@Override
public IBlockStateWrapper getBlockState(DhBlockPos pos)
{
return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this);
}
@Override
public IBiomeWrapper getBiome(DhBlockPos pos)
{
return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this);
}
@Override @Override
public ServerLevel getWrappedMcObject() { return this.level; } public ServerLevel getWrappedMcObject() { return this.level; }
@@ -208,28 +231,31 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@Override @Override
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; } public void setDhLevel(IDhLevel dhLevel) { this.dhLevel = dhLevel; }
@Override
@Nullable
public IDhLevel getDhLevel() { return this.dhLevel; }
@Override @Override
public IDhApiCustomRenderRegister getRenderRegister() public IDhApiCustomRenderRegister getRenderRegister()
{ {
if (this.parentDhLevel == null) if (this.dhLevel == null)
{ {
return null; return null;
} }
return this.parentDhLevel.getGenericRenderer(); return this.dhLevel.getGenericRenderer();
} }
@Override @Override
public File getDhSaveFolder() public File getDhSaveFolder()
{ {
if (this.parentDhLevel == null) if (this.dhLevel == null)
{ {
return null; return null;
} }
return this.parentDhLevel.getSaveStructure().getSaveFolder(this); return this.dhLevel.getSaveStructure().getSaveFolder(this);
} }
@@ -0,0 +1,92 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import net.minecraft.world.level.ChunkPos;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class ChunkPosGenStream
{
public static Iterator<ChunkPos> getIterator(int genMinX, int genMinZ, int width, int extraRadius)
{ return getStream(genMinX, genMinZ, width, extraRadius).iterator(); }
/** @param extraRadius in both the positive and negative directions */
public static Stream<ChunkPos> getStream(int genMinX, int genMinZ, int width, int extraRadius)
{ return StreamSupport.stream(new InclusiveChunkPosIterator(genMinX, genMinZ, width, extraRadius), false); }
private static class InclusiveChunkPosIterator extends Spliterators.AbstractSpliterator<ChunkPos>
{
private final int minX;
private final int minZ;
private final int maxX;
private final int maxZ;
/** current X pos */
int x;
/** current Z pos */
private int z;
//=============//
// constructor //
//=============//
protected InclusiveChunkPosIterator(int genMinX, int genMinZ, int width, int extraRadius)
{
super(getCount(width, extraRadius), Spliterator.SIZED);
this.minX = genMinX - extraRadius;
this.minZ = genMinZ - extraRadius;
this.maxX = genMinX + (width - 1) + extraRadius;
this.maxZ = genMinZ + (width - 1) + extraRadius;
// X starts at 1 minus the minX so we can immediately re-add 1 in the tryAdvance() loop
this.x = this.minX - 1;
this.z = this.minZ;
}
private static int getCount(int width, int extraRadius)
{
int widthPlusExtra = width + (extraRadius * 2);
return widthPlusExtra * widthPlusExtra;
}
//=================//
// iterator method //
//=================//
@Override
public boolean tryAdvance(Consumer<? super ChunkPos> consumer)
{
if (this.x == this.maxX && this.z == this.maxZ)
{
// the last returned position was the final valid position
return false;
}
if (this.x == this.maxX)
{
// we reached the max X position, loop back around in the next Z row
this.x = this.minX;
this.z++;
}
else
{
this.x++;
}
consumer.accept(new ChunkPos(this.x, this.z));
return true;
}
}
}
@@ -19,114 +19,118 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration; package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.core.util.ExceptionUtil;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.objects.EventTimer;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
public final class GenerationEvent public final class GenerationEvent
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();;
private static int generationFutureDebugIDs = 0;
private static final AtomicInteger DEBUG_ID_REF = new AtomicInteger(0);
/** can be used for troubleshooting */
public final int id; public final int id;
public final ThreadedParameters threadedParam;
public final ThreadWorldGenParams threadedParam;
public final DhChunkPos minPos; public final DhChunkPos minPos;
/** the number of chunks wide this event is */ public final int widthInChunks;
public final int size;
public final EDhApiWorldGenerationStep targetGenerationStep; public final EDhApiWorldGenerationStep targetGenerationStep;
public final EDhApiDistantGeneratorMode generatorMode; public final EDhApiDistantGeneratorMode generatorMode;
public EventTimer timer = null; public final CompletableFuture<Void> future;
public long inQueueTime;
public long timeoutTime = -1;
public CompletableFuture<Void> future = null;
public final Consumer<IChunkWrapper> resultConsumer; public final Consumer<IChunkWrapper> resultConsumer;
public GenerationEvent( //=============//
DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup, // constructor //
//=============//
private GenerationEvent(
DhChunkPos minPos, int widthInChunks, BatchGenerationEnvironment generationGroup,
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer) EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
{ {
this.inQueueTime = System.nanoTime(); this.id = DEBUG_ID_REF.getAndIncrement();
this.id = generationFutureDebugIDs++;
this.minPos = minPos; this.minPos = minPos;
this.size = size; this.widthInChunks = widthInChunks;
this.generatorMode = generatorMode;
this.targetGenerationStep = targetGenerationStep; this.targetGenerationStep = targetGenerationStep;
this.threadedParam = ThreadedParameters.getOrMake(generationGroup.params); this.generatorMode = generatorMode;
this.threadedParam = ThreadWorldGenParams.getOrMake(generationGroup.globalParams);
this.future = new CompletableFuture<>();
this.resultConsumer = resultConsumer; this.resultConsumer = resultConsumer;
} }
public static GenerationEvent startEvent( //=======//
DhChunkPos minPos, int size, BatchGenerationEnvironment genEnvironment, // start //
//=======//
public static GenerationEvent start(
DhChunkPos minPos, int widthInChunks, BatchGenerationEnvironment genEnvironment,
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer, EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
ExecutorService worldGeneratorThreadPool) ExecutorService worldGeneratorThreadPool)
{ {
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, generatorMode, target, resultConsumer); GenerationEvent genEvent = new GenerationEvent(minPos, widthInChunks, genEnvironment, generatorMode, target, resultConsumer);
generationEvent.future = CompletableFuture.supplyAsync(() ->
try
{ {
long runStartTime = System.nanoTime(); worldGeneratorThreadPool.execute(() ->
generationEvent.timeoutTime = runStartTime;
generationEvent.inQueueTime = runStartTime - generationEvent.inQueueTime;
generationEvent.timer = new EventTimer("setup");
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
try
{ {
genEnvironment.generateLodFromListAsync(generationEvent, (runnable) -> try
{ {
worldGeneratorThreadPool.execute(() -> BatchGenerationEnvironment.isDhWorldGenThreadRef.set(true);
{
boolean alreadyMarked = BatchGenerationEnvironment.isCurrentThreadDistantGeneratorThread();
if (!alreadyMarked)
{
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
}
if (genEvent.generatorMode == EDhApiDistantGeneratorMode.INTERNAL_SERVER)
{
genEnvironment.internalServerGenerator.generateChunksViaInternalServer(genEvent);
genEvent.future.complete(null);
}
else
{
try try
{ {
runnable.run(); genEnvironment.generateEvent(genEvent);
} }
catch (Throwable throwable) catch (Throwable throwable)
{ {
handleWorldGenThrowable(generationEvent, throwable); handleWorldGenThrowable(genEvent, throwable);
} }
finally finally
{ {
if (!alreadyMarked) genEvent.future.complete(null);
{
BatchGenerationEnvironment.isDistantGeneratorThread.set(false);
}
} }
}); }
}); }
} catch (Throwable initialThrowable)
catch (Throwable initialThrowable) {
{ handleWorldGenThrowable(genEvent, initialThrowable);
handleWorldGenThrowable(generationEvent, initialThrowable); }
} finally
finally {
{ BatchGenerationEnvironment.isDhWorldGenThreadRef.remove();
BatchGenerationEnvironment.isDistantGeneratorThread.remove(); }
} });
}
catch (RejectedExecutionException e)
{
genEvent.future.completeExceptionally(e);
}
return null; return genEvent;
}, worldGeneratorThreadPool);
return generationEvent;
} }
/** There's probably a better way to handle this, but it'll work for now */ /** There's probably a better way to handle this, but it'll work for now */
private static void handleWorldGenThrowable(GenerationEvent generationEvent, Throwable initialThrowable) private static void handleWorldGenThrowable(GenerationEvent generationEvent, Throwable initialThrowable)
@@ -137,9 +141,8 @@ public final class GenerationEvent
throwable = throwable.getCause(); throwable = throwable.getCause();
} }
if (throwable instanceof InterruptedException boolean isShutdownException = ExceptionUtil.isShutdownException(throwable);
|| throwable instanceof UncheckedInterruptedException if (isShutdownException)
|| throwable instanceof RejectedExecutionException)
{ {
// these exceptions can be ignored, generally they just mean // these exceptions can be ignored, generally they just mean
// the thread is busy so it'll need to try again later. // the thread is busy so it'll need to try again later.
@@ -154,35 +157,15 @@ public final class GenerationEvent
} }
} }
public boolean isComplete() { return this.future.isDone(); }
public boolean hasTimeout(int duration, TimeUnit unit)
{
if (this.timeoutTime == -1)
{
return false;
}
long currentTime = System.nanoTime(); //================//
long delta = currentTime - this.timeoutTime; // base overrides //
return (delta > TimeUnit.NANOSECONDS.convert(duration, unit)); //================//
}
public boolean terminate()
{
LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
ThreadPoolUtil.WORLD_GEN_THREAD_FACTORY.dumpAllThreadStacks();
this.future.cancel(true);
return this.future.isCancelled();
}
public void refreshTimeout()
{
this.timeoutTime = System.nanoTime();
UncheckedInterruptedException.throwIfInterrupted();
}
@Override @Override
public String toString() { return this.id + ":" + this.size + "@" + this.minPos + "(" + this.targetGenerationStep + ")"; } public String toString() { return this.id + ":" + this.widthInChunks + "@" + this.minPos + "(" + this.targetGenerationStep + ")"; }
} }
@@ -0,0 +1,311 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.ExceptionUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
#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 java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.Function;
public class InternalServerGenerator
{
public static final DhLogger LOGGER = new DhLoggerBuilder()
.name("LOD World Gen - Internal Server")
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
.build();
public static final DhLogger CHUNK_LOAD_LOGGER = new DhLoggerBuilder()
.name("LOD Chunk Loading")
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
.build();
private static final IC2meAccessor C2ME_ACCESSOR = ModAccessorInjector.INSTANCE.get(IC2meAccessor.class);
/**
* Used to revert the ignore logic in {@link SharedApi} so
* that a given chunk pos can be handled again.
* A timer is used so we don't have to inject into MC's code and it works sell enough
* most of the time.
* If a chunk does get through due the timeout not being long enough that isn't the end of the world.
*/
private static final int MS_TO_IGNORE_CHUNK_AFTER_COMPLETION = 5_000;
#if MC_VER < MC_1_21_5
private static final TicketType<ChunkPos> DH_SERVER_GEN_TICKET = TicketType.create("dh_server_gen_ticket", Comparator.comparingLong(ChunkPos::toLong));
#elif MC_VER < MC_1_21_9
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* persist */ false, TicketType.TicketUse.LOADING);
#else
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* flags */TicketType.FLAG_LOADING);
#endif
private static boolean c2meMissingWarningLogged = false;
private final GlobalWorldGenParams params;
private final IDhServerLevel dhServerLevel;
private final Timer chunkSaveIgnoreTimer = TimerUtil.CreateTimer("ChunkSaveIgnoreTimer");
//=============//
// constructor //
//=============//
public InternalServerGenerator(GlobalWorldGenParams params, IDhServerLevel dhServerLevel)
{
this.params = params;
this.dhServerLevel = dhServerLevel;
}
//============//
// generation //
//============//
public void generateChunksViaInternalServer(GenerationEvent genEvent)
{
this.runValidation();
try
{
//=====================//
// create gen requests //
//=====================//
ArrayList<CompletableFuture<ChunkAccess>> getChunkFutureList = new ArrayList<>();
{
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
while (chunkPosIterator.hasNext())
{
ChunkPos chunkPos = chunkPosIterator.next();
CompletableFuture<ChunkAccess> requestChunkFuture =
this.requestChunkFromServerAsync(chunkPos)
// log errors if necessary
.whenCompleteAsync(
(chunk, throwable) ->
{
// unwrap the CompletionException if necessary
Throwable actualThrowable = throwable;
while (actualThrowable instanceof CompletionException)
{
actualThrowable = actualThrowable.getCause();
}
if (actualThrowable != null)
{
// ignore expected shutdown exceptions
boolean isShutdownException =
ExceptionUtil.isShutdownException(actualThrowable)
|| actualThrowable.getMessage().contains("Unloaded chunk");
if (!isShutdownException)
{
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load chunk [" + chunkPos + "] from server, error: [" + actualThrowable.getMessage() + "].", actualThrowable);
}
}
});
getChunkFutureList.add(requestChunkFuture);
}
}
//==============================//
// wait for generation requests //
//==============================//
// Join-ing each thread will prevent DH from working on anything else
// but will also prevent over-queuing world gen tasks.
// If C2ME is present the CPU will still be well utilized.
ArrayList<IChunkWrapper> chunkWrappers = new ArrayList<>();
for (int i = 0; i < getChunkFutureList.size(); i++)
{
CompletableFuture<ChunkAccess> getChunkFuture = getChunkFutureList.get(i);
ChunkAccess chunk = getChunkFuture.join();
if (chunk != null)
{
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.dhServerLevel.getLevelWrapper());
chunkWrapper.createDhHeightMaps();
chunkWrappers.add(chunkWrapper);
}
}
//==========================//
// process generated chunks //
//==========================//
int maxSkyLight = this.dhServerLevel.getServerLevelWrapper().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT;
for (int i = 0; i < chunkWrappers.size(); i++)
{
ChunkWrapper chunkWrapper = (ChunkWrapper)chunkWrappers.get(i);
// pre-generated chunks should have lighting but new ones won't
if (!chunkWrapper.isDhBlockLightingCorrect())
{
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(chunkWrapper, chunkWrappers, maxSkyLight);
}
this.dhServerLevel.updateBeaconBeamsForChunk(chunkWrapper, chunkWrappers);
genEvent.resultConsumer.accept(chunkWrapper);
}
}
finally
{
// release all chunks from the server to prevent out of memory issues
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
while (chunkPosIterator.hasNext())
{
ChunkPos chunkPos = chunkPosIterator.next();
this.releaseChunkFromServer(this.params.mcServerLevel, chunkPos);
}
}
}
private void runValidation()
{
// DH thread check
if (!DhApi.isDhThread()
&& ModInfo.IS_DEV_BUILD)
{
throw new IllegalStateException("Internal server generation should be called from one of DH's world gen thread. Current thread: ["+Thread.currentThread().getName()+"]");
}
// C2ME present?
if (C2ME_ACCESSOR == null
&& !c2meMissingWarningLogged)
{
c2meMissingWarningLogged = true;
String c2meWarning = "C2ME missing, \n" +
"low CPU usage and slow world gen speeds expected. \n" +
"DH is set to use MC's internal server for world gen \n" +
"this mode is less efficient unless a mod like C2ME is present."
;
if (Config.Common.Logging.Warning.showSlowWorldGenSettingWarnings.get())
{
String message =
EMinecraftColor.ORANGE + "Distant Horizons: slow world gen." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
c2meWarning;
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
LOGGER.warn(c2meWarning);
}
}
private CompletableFuture<ChunkAccess> requestChunkFromServerAsync(ChunkPos chunkPos)
{
return CompletableFuture.supplyAsync(() ->
{
ServerLevel level = this.params.mcServerLevel;
// ignore chunk update events for this position
SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.addPosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z));
#if MC_VER < MC_1_21_5
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
level.getChunkSource().distanceManager.addTicket(DH_SERVER_GEN_TICKET, chunkPos, chunkLevel, chunkPos);
#else
level.getChunkSource().addTicketWithRadius(DH_SERVER_GEN_TICKET, chunkPos, 0);
#endif
// probably not the most optimal to run updates here, but fast enough
level.getChunkSource().distanceManager.runAllUpdates(level.getChunkSource().chunkMap);
ChunkHolder chunkHolder = level.getChunkSource().chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong());
if (chunkHolder == null)
{
throw new IllegalStateException("No chunk chunkHolder for pos ["+chunkPos+"] after ticket has been added.");
}
#if MC_VER <= MC_1_20_4
return chunkHolder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
.thenApply(result -> result.left().orElseThrow(() -> new RuntimeException(result.right().get().toString()))); // can throw if the server is shutting down
#elif MC_VER <= MC_1_20_6
return chunkHolder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
.thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.toString()))); // can throw if the server is shutting down
#else
return chunkHolder.scheduleChunkGenerationTask(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
.thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.getError()))); // can throw if the server is shutting down
#endif
}, this.params.mcServerLevel.getChunkSource().chunkMap.mainThreadExecutor)
.thenCompose(Function.identity());
}
/**
* mitigates out of memory issues in the vanilla chunk system. <br>
* See: https://github.com/pop4959/Chunky/pull/383
*/
private void releaseChunkFromServer(ServerLevel level, ChunkPos chunkPos)
{
level.getChunkSource().chunkMap.mainThreadExecutor.execute(() ->
{
try
{
#if MC_VER < MC_1_21_5
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
level.getChunkSource().distanceManager.removeTicket(DH_SERVER_GEN_TICKET, chunkPos, chunkLevel, chunkPos);
#else
level.getChunkSource().removeTicketWithRadius(DH_SERVER_GEN_TICKET, chunkPos, 0);
#endif
level.getChunkSource().chunkMap.tick(() -> false);
#if MC_VER > MC_1_16_5
level.entityManager.tick();
#endif
// give MC a few seconds to save the chunk before
// we can process update events there again
this.chunkSaveIgnoreTimer.schedule(new TimerTask()
{
@Override
public void run() { SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.removePosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z)); }
}, MS_TO_IGNORE_CHUNK_AFTER_COMPLETION);
}
catch (Exception e)
{
LOGGER.warn("Failed to release chunk back to internal server. Error: ["+e.getMessage()+"]", e);
}
});
}
}
@@ -1,109 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment.PerfCalculator;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.WorldGenStructFeatManager;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.WorldGenLevel;
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.structure.StructureCheck;
#endif
public final class ThreadedParameters
{
private static final ThreadLocal<ThreadedParameters> LOCAL_PARAM = new ThreadLocal<>();
final ServerLevel level;
public WorldGenStructFeatManager structFeat = null;
#if MC_VER >= MC_1_18_2
public StructureCheck structCheck;
#endif
boolean isValid = true;
public final PerfCalculator perf = new PerfCalculator();
private static GlobalParameters previousGlobalParameters = null;
public static ThreadedParameters getOrMake(GlobalParameters param)
{
ThreadedParameters tParam = LOCAL_PARAM.get();
if (tParam != null && tParam.isValid && tParam.level == param.level)
{
return tParam;
}
tParam = new ThreadedParameters(param);
LOCAL_PARAM.set(tParam);
return tParam;
}
private ThreadedParameters(GlobalParameters param)
{
previousGlobalParameters = param;
this.level = param.level;
#if MC_VER < MC_1_18_2
this.structFeat = new WorldGenStructFeatManager(param.worldGenSettings, level);
#elif MC_VER < MC_1_19_2
this.structCheck = this.createStructureCheck(param);
#else
this.structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
param.level.dimension(), param.generator, param.randomState, level, param.generator.getBiomeSource(), param.worldSeed,
param.fixerUpper);
#endif
}
public void markAsInvalid() { isValid = false; }
public void makeStructFeat(WorldGenLevel genLevel, GlobalParameters param)
{
#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 MC_VER >= MC_1_18_2 && MC_VER < MC_1_19_2
public void recreateStructureCheck()
{
if (previousGlobalParameters != null)
{
this.structCheck = createStructureCheck(previousGlobalParameters);
}
}
private StructureCheck createStructureCheck(GlobalParameters param)
{
return new StructureCheck(param.chunkScanner, param.registry, param.structures,
param.level.dimension(), param.generator, this.level, param.generator.getBiomeSource(), param.worldSeed,
param.fixerUpper);
}
#else
public void recreateStructureCheck() { /* do nothing */ }
#endif
}
@@ -0,0 +1,745 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
import com.mojang.serialization.Codec;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.core.Registry;
#if MC_VER >= MC_1_19_4
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.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.*;
#if MC_VER < MC_1_21_3
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
#else
#endif
#if MC_VER < MC_1_21_9
import net.minecraft.world.level.block.Blocks;
#else
#endif
import net.minecraft.world.level.levelgen.Heightmap;
#if MC_VER >= MC_1_18_2
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
import net.minecraft.world.ticks.LevelChunkTicks;
#endif
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
#endif
#endif
#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_1
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
import net.minecraft.world.level.material.Fluid;
public class ChunkCompoundTagParser
{
public static final DhLogger LOGGER = new DhLoggerBuilder()
.name("LOD Chunk Reader")
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
.build();
private static final AtomicBoolean ZERO_CHUNK_POS_ERROR_LOGGED_REF = new AtomicBoolean(false);
private static final ConcurrentHashMap<String, Object> LOGGED_ERROR_MESSAGE_MAP = new ConcurrentHashMap<>();
private static boolean lightingSectionErrorLogged = false;
//============//
// read chunk //
//============//
public static ChunkWrapper createFromTag(
WorldGenLevel mcWorldGenLevel, IDhServerLevel dhServerLevel,
ChunkPos chunkPos, CompoundTag chunkData)
{
#if MC_VER < MC_1_18_2
CompoundTag tagLevel = chunkData.getCompound("Level");
#else
CompoundTag tagLevel = chunkData;
#endif
//=======================//
// validate the chunkPos //
//=======================//
int chunkX = CompoundTagUtil.getInt(tagLevel,"xPos");
int chunkZ = CompoundTagUtil.getInt(tagLevel, "zPos");
ChunkPos actualChunkPos = new ChunkPos(chunkX, chunkZ);
// confirm chunk pos is correct
if (!Objects.equals(chunkPos, actualChunkPos))
{
if (chunkX == 0 && chunkZ == 0)
{
if (!ZERO_CHUNK_POS_ERROR_LOGGED_REF.getAndSet(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
{
LOGGER.error("Chunk file at ["+chunkPos.toString()+"] is in the wrong location. \n" +
"Please try optimizing your world to fix this issue. \n" +
"World optimization can be done from the singleplayer world selection screen. \n" +
"(Expected pos: ["+chunkPos.toString()+"], actual ["+actualChunkPos.toString()+"])" +
" ");
return null;
}
}
//===========//
// get ticks //
//===========//
#if MC_VER < MC_1_18_2
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
mcWorldGenLevel.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), #if MC_VER >= MC_1_17_1 mcWorldGenLevel, #endif
chunkPos, mcWorldGenLevel.getLevel().getChunkSource().getGenerator().getBiomeSource(),
tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null);
String BLOCK_TICKS_TAG_PRE18 = "TileTicks";
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 MC_VER >= MC_1_17_1 , mcWorldGenLevel #endif );
String FLUID_TICKS_TAG_PRE18 = "LiquidTicks";
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 MC_VER >= MC_1_17_1 , mcWorldGenLevel #endif );
#else
// ticks shouldn't be needed so ignore them for MC versions after 1.18.2
LevelChunkTicks<Block> blockTicks = new LevelChunkTicks<>();
LevelChunkTicks<Fluid> fluidTicks = new LevelChunkTicks<>();
#endif
//=====================//
// get misc properties //
//=====================//
int sectionYCount = #if MC_VER < MC_1_17_1 16; #else mcWorldGenLevel.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYCount];
boolean hasBlocks = readAndPopulateSections(mcWorldGenLevel, chunkPos, tagLevel, chunkSections);
if (!hasBlocks)
{
return null;
}
long inhabitedTime = CompoundTagUtil.getLong(tagLevel, "InhabitedTime");
boolean isLightOn = CompoundTagUtil.getBoolean(tagLevel, "isLightOn");
//============//
// make chunk //
//============//
#if MC_VER < MC_1_18_2
LevelChunk chunk = new LevelChunk((Level) mcWorldGenLevel.getLevel(), chunkPos, chunkBiomeContainer, UpgradeData.EMPTY, blockTicks,
fluidTicks, inhabitedTime, chunkSections, null);
#else
LevelChunk chunk = new LevelChunk((Level) mcWorldGenLevel, chunkPos, UpgradeData.EMPTY, blockTicks,
fluidTicks, inhabitedTime, chunkSections, null, null);
#endif
// Set some states after object creation
chunk.setLightCorrect(isLightOn);
boolean hasHeightmapData = readHeightmaps(chunk, chunkData);
// chunk wrapper so we can pass along extra data more easily
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, dhServerLevel.getServerLevelWrapper());
chunkWrapper.createDhHeightMaps();
//===========================//
// check if chunk has blocks //
//===========================//
// in some MC versions all the NBT data will be there
// but the chunk will be totally empty,
// usually this means the chunk was only partially generated.
// If that happens we should try to generate the chunk from scratch
// otherwise we can end up with large empty holes in the world.
// walking through the heightmap (recreated by DH if missing)
// is a fast way to check if there are any blocks in the chunk
boolean chunkHasBlocks = false;
int serverMinHeight = dhServerLevel.getServerLevelWrapper().getMinHeight();
for (int x = 0; x < 16 && !chunkHasBlocks; x++)
{
for (int z = 0; z < 16 && !chunkHasBlocks; z++)
{
int heightMap = Math.max(
// max between both heightmaps just in case there's a discrepancy
chunkWrapper.getLightBlockingHeightMapValue(x, z),
chunkWrapper.getSolidHeightMapValue(x, z)
);
if (heightMap != serverMinHeight)
{
chunkHasBlocks = true;
}
}
}
if (chunkHasBlocks)
{
return chunkWrapper;
}
else
{
// no blocks detected, this chunk should be generated from scratch
return null;
}
}
//=================//
// chunk sections //
// (Blocks/biomes) //
//=================//
/** handles both blocks and biomes */
private static boolean readAndPopulateSections(
LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData,
LevelChunkSection[] chunkSections)
{
int sectionYCount = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
ListTag tagSections = CompoundTagUtil.getListTag(chunkData, "Sections", 10);
// try lower-case "sections" if capital "Sections" is missing
if (tagSections == null
|| tagSections.isEmpty())
{
tagSections = CompoundTagUtil.getListTag(chunkData, "sections", 10);
}
boolean blocksFound = false;
if (tagSections != null)
{
for (int i = 0; i < tagSections.size(); ++i)
{
CompoundTag tagSection = CompoundTagUtil.getCompoundTag(tagSections, i);
if (tagSection == null)
{
continue;
}
final int sectionYPos = CompoundTagUtil.getByte(tagSection, "Y");
//===================//
// get blocks/biomes //
//===================//
#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())
{
int sectionIndex;
#if MC_VER < MC_1_17_1
sectionIndex = sectionYPos;
#else
sectionIndex = level.getSectionIndexFromSectionY(sectionYPos);
#endif
chunkSections[sectionIndex] = levelChunkSection;
}
}
#else
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
if (sectionId >= 0
&& sectionId < chunkSections.length)
{
//========//
// blocks //
//========//
PalettedContainer<BlockState> blockStateContainer;
boolean containsBlockStates = CompoundTagUtil.contains(tagSection, "block_states", 10);
if (containsBlockStates)
{
Codec<PalettedContainer<BlockState>> blockStateCodec = getBlockStateCodec(level);
#if MC_VER < MC_1_20_6
blockStateContainer = blockStateCodec
.parse(NbtOps.INSTANCE, CompoundTagUtil.getCompoundTag(tagSection, "block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
.getOrThrow(false, (message) -> logParsingWarningOnce(message));
#else
blockStateContainer = blockStateCodec
.parse(NbtOps.INSTANCE, CompoundTagUtil.getCompoundTag(tagSection, "block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
blocksFound = true;
}
else
{
#if MC_VER < MC_1_21_9
blockStateContainer = new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#else
blockStateContainer = PalettedContainerFactory.create(level.registryAccess()).createForBlockStates();
#endif
}
//========//
// biomes //
//========//
Registry<Biome> biomeRegistry = getBiomeRegistry(level);
#if MC_VER < MC_1_19_2
Codec<PalettedContainer<Biome>> biomeCodec;
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec;
#endif
biomeCodec = getBiomeCodec(level, biomeRegistry);
#if MC_VER < MC_1_18_2
PalettedContainer<Biome> biomeContainer;
#else
PalettedContainer<Holder<Biome>> biomeContainer;
#endif
#if MC_VER < MC_1_18_2
biomeContainer = tagSection.contains("biomeRegistry", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomeRegistry")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, (message) -> logWarningOnce(message))
: new PalettedContainer<Biome>(biomeRegistry, biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
{
CompoundTag biomeTag = CompoundTagUtil.getCompoundTag(tagSection, "biomeRegistry");
if (biomeTag == null)
{
biomeTag = CompoundTagUtil.getCompoundTag(tagSection, "biomes");
}
if (biomeTag != null
&& !biomeTag.isEmpty())
{
#if MC_VER < MC_1_20_6
biomeContainer = new PalettedContainer<Holder<Biome>>(
biomeRegistry.asHolderIdMap(),
biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, biomeTag)
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYCount, (String) string))
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
}
else
{
// no biomes found, use the default (probably plains)
#if MC_VER < MC_1_21_3
biomeContainer = new PalettedContainer<Holder<Biome>>(
biomeRegistry.asHolderIdMap(),
biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#elif MC_VER < MC_1_21_9
biomeContainer = new PalettedContainer<Holder<Biome>>(biomeRegistry.asHolderIdMap(),
biomeRegistry.getOrThrow(Biomes.PLAINS),
PalettedContainer.Strategy.SECTION_BIOMES);
#else
biomeContainer = PalettedContainerFactory.create(level.registryAccess()).createForBiomes();
#endif
}
}
#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 blocksFound;
}
private static Codec<PalettedContainer<BlockState>> getBlockStateCodec(LevelAccessor level)
{
#if MC_VER < MC_1_18_2
return null; // unused for older MC versions
#elif MC_VER < MC_1_19_2
return PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#elif MC_VER <= MC_1_21_8
return PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#else
return PalettedContainerFactory.create(level.registryAccess()).blockStatesContainerCodec();
#endif
}
private static Registry<Biome> getBiomeRegistry(LevelAccessor level)
{
#if MC_VER < MC_1_18_2
// not needed
return null;
#elif MC_VER < MC_1_19_4
return level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#elif MC_VER < MC_1_21_3
return level.registryAccess().registryOrThrow(Registries.BIOME);
#else
return level.registryAccess().lookupOrThrow(Registries.BIOME);
#endif
}
private static
#if MC_VER < MC_1_19_2 Codec<PalettedContainer<Biome>>
#else Codec<PalettedContainer<Holder<Biome>>>
#endif
getBiomeCodec(LevelAccessor level, Registry<Biome> biomeRegistry)
{
#if MC_VER < MC_1_18_2
return null; // unused for older MC versions
#elif MC_VER < MC_1_19_2
return PalettedContainer.codec(
biomeRegistry, biomeRegistry.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_19_2
return PalettedContainer.codec(
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_21_3
return PalettedContainer.codecRW(
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_21_9
return PalettedContainer.codecRW(
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS));
#else
return PalettedContainer.codecRW(
biomeRegistry.holderByNameCodec(), PalettedContainerFactory.create(level.registryAccess()).biomeStrategy(), biomeRegistry.getOrThrow(Biomes.PLAINS));
#endif
}
//============//
// heightmaps //
//============//
private static boolean readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
{
CompoundTag tagHeightmaps = CompoundTagUtil.getCompoundTag(chunkData, "Heightmaps");
if (tagHeightmaps == null)
{
return false;
}
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
{
String heightmapKey = type.getSerializationKey();
#if MC_VER < MC_1_21_5
if (tagHeightmaps.contains(heightmapKey, 12))
{
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmapKey));
}
#else
if (tagHeightmaps.contains(heightmapKey))
{
Optional<long[]> optionalHeightmap = tagHeightmaps.getLongArray(heightmapKey);
if (optionalHeightmap.isPresent())
{
chunk.setHeightmap(type, optionalHeightmap.get());
}
}
#endif
}
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
return true;
}
//================//
// chunk lighting //
//================//
/** source: 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.getInclusiveMinBuildHeight(chunk), ChunkWrapper.getExclusiveMaxBuildHeight(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))
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("Chunk section tag has an unexpected type ["+chunkSectionTag.getClass().getName()+"], expected ["+CompoundTag.class.getName()+"].");
}
return null;
}
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
// if null all lights = 0
byte[] blockLightNibbleArray = CompoundTagUtil.getByteArray(chunkSectionCompoundTag, "BlockLight");
byte[] skyLightNibbleArray = CompoundTagUtil.getByteArray(chunkSectionCompoundTag, "SkyLight");
if (blockLightNibbleArray != null
&& skyLightNibbleArray != null)
{
// 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.getInclusiveMinBuildHeight(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);
}
}
//=========//
// logging //
//=========//
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
"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.");
return newMessage;
});
}
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
"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.");
return newMessage;
});
}
private static void logParsingWarningOnce(String message) { logParsingWarningOnce(message, null); }
private static void logParsingWarningOnce(String message, Exception e)
{
if (message == null)
{
return;
}
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
"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.",
e);
return newMessage;
});
}
private static RuntimeException logErrorAndReturnException(String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
"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.");
return newMessage;
});
// Currently we want to ignore these errors, if returning null is a problem, we can change this later
return null; //new RuntimeException(message);
}
//================//
// 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,343 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.RegionFileStorageExternalCache;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.ExceptionUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
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 java.io.IOException;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicReference;
#if MC_VER <= MC_1_17_1
import net.minecraft.world.level.chunk.ChunkStatus;
#elif MC_VER <= MC_1_19_2
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.core.Registry;
#elif MC_VER <= MC_1_19_4
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.chunk.ChunkStatus;
#elif MC_VER <= MC_1_20_6
import net.minecraft.core.registries.Registries;
#elif MC_VER <= MC_1_21_3
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.chunk.status.ChunkStatus;
#elif MC_VER <= MC_1_21_8
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.chunk.status.ChunkStatus;
#elif MC_VER <= MC_1_21_9
import net.minecraft.world.level.chunk.PalettedContainerFactory;
#else
import net.minecraft.world.level.chunk.PalettedContainerFactory;
#endif
public class ChunkFileReader implements AutoCloseable
{
public static final DhLogger LOGGER = new DhLoggerBuilder()
.name("LOD World Gen")
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
.build();
public static final DhLogger CHUNK_LOAD_LOGGER = new DhLoggerBuilder()
.name("LOD Chunk Loading")
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
.build();
private static final IModChecker MOD_CHECKER = SingletonInjector.INSTANCE.get(IModChecker.class);
public final GlobalWorldGenParams params;
/**
* will be true if C2ME is installed (since they require us to
* pull chunks using their async method), or if there
* was an issue with the sync pulling method.
*/
private boolean pullExistingChunkUsingMcAsyncMethod = false;
private final AtomicReference<RegionFileStorageExternalCache> regionFileStorageCacheRef = new AtomicReference<>();
public RegionFileStorageExternalCache getOrCreateRegionFileCache(RegionFileStorage storage)
{
RegionFileStorageExternalCache cache = this.regionFileStorageCacheRef.get();
if (cache == null)
{
cache = new RegionFileStorageExternalCache(storage);
if (!this.regionFileStorageCacheRef.compareAndSet(null, cache))
{
cache = this.regionFileStorageCacheRef.get();
}
}
return cache;
}
//=============//
// constructor //
//=============//
public ChunkFileReader(GlobalWorldGenParams params)
{
this.params = params;
if (MOD_CHECKER.isModLoaded("c2me"))
{
LOGGER.info("C2ME detected: DH's pre-existing chunk accessing will use methods handled by C2ME.");
this.pullExistingChunkUsingMcAsyncMethod = true;
}
}
//=============//
// constructor //
//=============//
/**
* If the given chunk pos already exists in the world, that chunk will be returned,
* otherwise this will return an empty chunk.
*/
public CompletableFuture<ChunkWrapper> createEmptyOrPreExistingChunkWrapperAsync(
int chunkX, int chunkZ,
Map<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos,
Map<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos,
Map<DhChunkPos, ChunkWrapper> generatedChunkWrapperByDhPos)
{
ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
DhChunkPos dhChunkPos = new DhChunkPos(chunkX, chunkZ);
if (generatedChunkWrapperByDhPos.containsKey(dhChunkPos))
{
return CompletableFuture.completedFuture(generatedChunkWrapperByDhPos.get(dhChunkPos));
}
return this.getChunkNbtDataAsync(chunkPos)
.thenApply((CompoundTag chunkData) ->
{
ChunkWrapper newChunkWrapper = this.loadOrMakeChunkWrapper(chunkPos, chunkData);
// attempt to get chunk lighting
ChunkCompoundTagParser.CombinedChunkLightStorage combinedLights = ChunkCompoundTagParser.readLight(newChunkWrapper.getChunk(), chunkData);
if (combinedLights != null)
{
// may be empty, empty checks are handled later
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
}
return newChunkWrapper;
})
// separate handle so we can cleanly handle missing chunks and/or thrown errors
.handle((ChunkWrapper newChunkWrapper, Throwable throwable) ->
{
if (newChunkWrapper != null)
{
return newChunkWrapper;
}
else
{
return this.CreateProtoChunkWrapper(this.params.mcServerLevel, chunkPos);
}
})
.thenApply((ChunkWrapper newChunkWrapper) ->
{
generatedChunkWrapperByDhPos.put(dhChunkPos, newChunkWrapper);
return newChunkWrapper;
});
}
private CompletableFuture<CompoundTag> getChunkNbtDataAsync(ChunkPos chunkPos)
{
ServerLevel level = this.params.mcServerLevel;
try
{
IOWorker ioWorker = level.getChunkSource().chunkMap.worker;
#if MC_VER <= MC_1_18_2
return CompletableFuture.completedFuture(ioWorker.load(chunkPos));
#else
// storage will be null if C2ME is installed
if (!this.pullExistingChunkUsingMcAsyncMethod
&& ioWorker.storage != null)
{
try
{
RegionFileStorage storage = this.params.mcServerLevel.getChunkSource().chunkMap.worker.storage;
RegionFileStorageExternalCache cache = this.getOrCreateRegionFileCache(storage);
return CompletableFuture.completedFuture(cache.read(chunkPos));
}
catch (NullPointerException e)
{
// this shouldn't happen, if anything is null it should be
// ioWorker.storage
// but just in case
LOGGER.error("Unexpected issue pulling pre-existing chunk ["+chunkPos+"], falling back to async chunk pulling. This may cause server-tick lag.", e);
this.pullExistingChunkUsingMcAsyncMethod = true;
// try again now using the async method
return this.getChunkNbtDataAsync(chunkPos);
}
}
else
{
// log if we unexpectedly weren't able to run the sync chunk pulling
if (!this.pullExistingChunkUsingMcAsyncMethod)
{
// this shouldn't happen, but just in case
LOGGER.info("Unable to pull pre-existing chunk using synchronous method. Falling back to async method. this may cause server-tick lag.");
this.pullExistingChunkUsingMcAsyncMethod = true;
}
//GET_CHUNK_COUNT_REF.incrementAndGet();
// When running in vanilla MC on versions before 1.21.4,
// DH would attempt to run loadAsync on this same thread via a threading mixin,
// to prevent causing lag on the server thread.
// However, if a mod like C2ME is installed this will run on a C2ME thread instead.
return ioWorker.loadAsync(chunkPos)
.thenApply(optional ->
{
// Debugging note:
// If there are reports of extreme memory use when C2ME is installed, that probably means
// this method is queuing a lot of tasks (1,000+), which causes C2ME to explode.
//GET_CHUNK_COUNT_REF.decrementAndGet();
//PREF_LOGGER.info("chunk getter count ["+F3Screen.NUMBER_FORMAT.format(GET_CHUNK_COUNT_REF.get())+"]");
return optional.orElse(null);
})
.exceptionally((throwable) ->
{
// unwrap the CompletionException if necessary
Throwable actualThrowable = throwable;
while (actualThrowable instanceof CompletionException completionException)
{
actualThrowable = completionException.getCause();
}
boolean isShutdownException = ExceptionUtil.isShutdownException(actualThrowable);
if (!isShutdownException)
{
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load or make chunk ["+chunkPos+"], error: ["+actualThrowable.getMessage()+"].", actualThrowable);
}
return null;
});
}
#endif
}
catch (ClosedByInterruptException ignore)
{
// this just means the world generator is being shut down
return CompletableFuture.completedFuture(null);
}
catch (Exception e)
{
CHUNK_LOAD_LOGGER.warn("Couldn't load or make chunk [" + chunkPos + "]. Error: [" + e.getMessage() + "].", e);
return CompletableFuture.completedFuture(null);
}
}
private ChunkWrapper loadOrMakeChunkWrapper(ChunkPos chunkPos, CompoundTag chunkTagData)
{
ServerLevel mcServerLevel = this.params.mcServerLevel;
if (chunkTagData == null)
{
return this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
}
else
{
try
{
ChunkWrapper chunkWrapper = ChunkCompoundTagParser.createFromTag(mcServerLevel, this.params.dhServerLevel, chunkPos, chunkTagData);
if (chunkWrapper == null)
{
chunkWrapper = this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
}
return chunkWrapper;
}
catch (Exception e)
{
CHUNK_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 this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
}
}
}
public ChunkWrapper CreateProtoChunkWrapper(ServerLevel level, ChunkPos chunkPos)
{
ProtoChunk chunk = CreateProtoChunk(level, chunkPos);
return new ChunkWrapper(chunk, this.params.dhServerLevel.getLevelWrapper());
}
public static ProtoChunk CreateProtoChunk(ServerLevel level, ChunkPos chunkPos)
{
#if MC_VER <= MC_1_16_5
return new ProtoChunk(chunkPos, UpgradeData.EMPTY);
#elif MC_VER <= MC_1_17_1
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level);
#elif MC_VER <= MC_1_19_2
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null);
#elif MC_VER <= MC_1_19_4
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registries.BIOME), null);
#elif MC_VER < MC_1_21_3
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registries.BIOME), null);
#elif MC_VER < MC_1_21_9
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().lookupOrThrow(Registries.BIOME), null);
#else
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, PalettedContainerFactory.create(level.registryAccess()), null);
#endif
}
//================//
// base overrides //
//================//
@Override
public void close()
{
RegionFileStorageExternalCache regionStorage = this.regionFileStorageCacheRef.get();
if (regionStorage != null)
{
try
{
regionStorage.close();
}
catch (ClosedChannelException ignore) { /* world generator is being shut down */ }
catch (IOException e)
{
LOGGER.error("Failed to close region file storage cache, error: ["+e.getMessage()+"].", e);
}
}
}
}
@@ -0,0 +1,148 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import org.jetbrains.annotations.Nullable;
/**
* these tag helpers are usedd to simplify tag accessing between MC versions
*/
public class CompoundTagUtil
{
/** defaults to "false" if the tag isn't present */
public static boolean getBoolean(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getBoolean(key);
#else
return tag.getBoolean(key).orElse(false);
#endif
}
/** defaults to "0" if the tag isn't present */
public static byte getByte(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getByte(key);
#else
return tag.getByte(key).orElse((byte)0);
#endif
}
/** defaults to "0" if the tag isn't present */
public static short getShort(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getShort(index);
#else
return tag.getShort(index).orElse((short)0);
#endif
}
/** defaults to "0" if the tag isn't present */
public static int getInt(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getInt(key);
#else
return tag.getInt(key).orElse(0);
#endif
}
/** defaults to "0" if the tag isn't present */
public static long getLong(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getInt(key);
#else
return tag.getLong(key).orElse(0L);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
public static String getString(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getString(key);
#else
return tag.getString(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
public static byte[] getByteArray(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getByteArray(key);
#else
return tag.getByteArray(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
public static CompoundTag getCompoundTag(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getCompound(key);
#else
return tag.getCompound(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
public static CompoundTag getCompoundTag(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getCompound(index);
#else
return tag.getCompound(index).orElse(null);
#endif
}
/**
* defaults to null if the tag isn't present
* @param elementType unused after MC 1.21.5
*/
@Nullable
public static ListTag getListTag(CompoundTag tag, String key, int elementType)
{
#if MC_VER < MC_1_21_5
return tag.getList(key, elementType);
#else
return tag.getList(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
public static ListTag getListTag(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getList(index);
#else
return tag.getList(index).orElse(null);
#endif
}
public static boolean contains(CompoundTag tag, String key, int index)
{
#if MC_VER < MC_1_21_5
return tag.contains(key, index);
#else
return tag.contains(key);
#endif
}
}
@@ -1,851 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
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.chunk.ChunkLightStorage;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import it.unimi.dsi.fastutil.shorts.ShortList;
import net.minecraft.core.Registry;
#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.ResourceLocation;
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.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.*;
#if MC_VER < MC_1_21_3
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
#else
#endif
import net.minecraft.world.level.levelgen.Heightmap;
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.BlendingData;
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
import net.minecraft.world.ticks.LevelChunkTicks;
#endif
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
#endif
#endif
#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_1
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
#endif
import net.minecraft.world.level.material.Fluid;
import org.jetbrains.annotations.Nullable;
public class ChunkLoader
{
private static final AtomicBoolean ZERO_CHUNK_POS_ERROR_LOGGED_REF = new AtomicBoolean(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 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";
private static final String BLOCK_TICKS_TAG_18 = "block_ticks";
private static final String FLUID_TICKS_TAG_18 = "fluid_ticks";
private static final String BLOCK_TICKS_TAG_PRE18 = "TileTicks";
private static final String FLUID_TICKS_TAG_PRE18 = "LiquidTicks";
private static final ConfigBasedLogger LOGGER = BatchGenerationEnvironment.LOAD_LOGGER;
private static boolean lightingSectionErrorLogged = false;
private static final ConcurrentHashMap<String, Object> LOGGED_ERROR_MESSAGE_MAP = new ConcurrentHashMap<>();
//============//
// read chunk //
//============//
public static LevelChunk read(WorldGenLevel level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if MC_VER < MC_1_18_2
CompoundTag tagLevel = chunkData.getCompound("Level");
#else
CompoundTag tagLevel = chunkData;
#endif
int chunkX = tagGetInt(tagLevel,"xPos");
int chunkZ = tagGetInt(tagLevel, "zPos");
ChunkPos actualPos = new ChunkPos(chunkX, chunkZ);
if (!Objects.equals(chunkPos, actualPos))
{
if (chunkX == 0 && chunkZ == 0)
{
if (!ZERO_CHUNK_POS_ERROR_LOGGED_REF.getAndSet(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;
}
}
#if MC_VER < MC_1_20_6
ChunkStatus.ChunkType chunkType;
#else
ChunkType chunkType;
#endif
chunkType = readChunkType(tagLevel);
#if MC_VER < MC_1_18_2
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
return null;
#elif MC_VER < MC_1_21_6
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
#else
// ignore blending data, there appears to be an issue with parsing it in 1.21.6
BlendingData blendingData = null;
if (chunkType == ChunkType.PROTOCHUNK)
{
return null;
}
#endif
long inhabitedTime = tagGetLong(tagLevel, "InhabitedTime");
//================== Read params for making the LevelChunk ==================
UpgradeData upgradeData = UpgradeData.EMPTY;
// commented out 2025-06-04 as a test to see if the upgrade data
// is actually necessary for DH or if it can be ignored
// (if it can't be ignored we'll need to handle null responses from tagGetCompoundTag())
//
//#if MC_VER < MC_1_17_1
//upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
// ? new UpgradeData(tagGetCompoundTag(tagLevel, TAG_UPGRADE_DATA))
// : UpgradeData.EMPTY;
//#elif MC_VER < MC_1_21_5
//upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
// ? new UpgradeData(tagGetCompoundTag(tagLevel, TAG_UPGRADE_DATA), level)
// : UpgradeData.EMPTY;
//#else
//upgradeData = tagLevel.contains(TAG_UPGRADE_DATA)
// ? new UpgradeData(tagGetCompoundTag(tagLevel, TAG_UPGRADE_DATA), level)
// : UpgradeData.EMPTY;
//#endif
boolean isLightOn = tagGetBoolean(tagLevel, "isLightOn");
#if MC_VER < MC_1_18_2
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
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 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 MC_VER >= MC_1_17_1 , level #endif );
#else
#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),
string -> Registry.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos);
#elif MC_VER < MC_1_21_4
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
(string -> BuiltInRegistries.BLOCK.getOptional(ResourceLocation.tryParse(string))), chunkPos);
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
string -> BuiltInRegistries.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos);
#else
// do we need the ticks for what we're doing?
LevelChunkTicks<Block> blockTicks = new LevelChunkTicks<>();
LevelChunkTicks<Fluid> fluidTicks = new LevelChunkTicks<>();
#endif
#endif
LevelChunkSection[] levelChunkSections = readSections(level, chunkPos, tagLevel);
// ====================== Make the chunk =========================
#if MC_VER < MC_1_18_2
LevelChunk chunk = new LevelChunk((Level) level.getLevel(), chunkPos, chunkBiomeContainer, upgradeData, blockTicks,
fluidTicks, inhabitedTime, levelChunkSections, null);
#else
LevelChunk chunk = new LevelChunk((Level) level, chunkPos, upgradeData, blockTicks,
fluidTicks, inhabitedTime, levelChunkSections, null, blendingData);
#endif
// Set some states after object creation
chunk.setLightCorrect(isLightOn);
readHeightmaps(chunk, chunkData);
//readPostPocessings(chunk, chunkData);
return chunk;
}
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if MC_VER >= MC_1_18_2
#if MC_VER < MC_1_19_4
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#elif MC_VER < MC_1_21_3
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#else
Registry<Biome> biomes = level.registryAccess().lookupOrThrow(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));
#elif MC_VER < MC_1_21_3
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
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.getOrThrow(Biomes.PLAINS));
#endif
#endif
int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex];
ListTag tagSections = tagGetListTag(chunkData, "Sections", 10);
if (tagSections == null || tagSections.isEmpty())
{
tagSections = tagGetListTag(chunkData, "sections", 10);
}
if (tagSections != null)
{
for (int j = 0; j < tagSections.size(); ++j)
{
CompoundTag tagSection = tagGetCompoundTag(tagSections, j);
if (tagSection == null)
{
continue;
}
final int sectionYPos = tagGetByte(tagSection, "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
boolean containsBlockStates;
#if MC_VER < MC_1_21_5
containsBlockStates = tagSection.contains("block_states", 10);
#else
containsBlockStates = tagSection.contains("block_states");
#endif
if (containsBlockStates)
{
#if MC_VER < MC_1_20_6
blockStateContainer = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
.getOrThrow(false, (message) -> logParsingWarningOnce(message));
#else
blockStateContainer = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
}
else
{
blockStateContainer = new PalettedContainer<BlockState>(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, (message) -> logWarningOnce(message))
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
boolean containsBiomes;
#if MC_VER < MC_1_21_5
containsBiomes = tagSection.contains("biomes", 10);
#else
containsBiomes = tagSection.contains("biomes");
#endif
if (containsBiomes)
{
#if MC_VER < MC_1_20_6
biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "biomes"))
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
.getOrThrow(false, (message) -> logParsingWarningOnce(message));
#else
biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "biomes"))
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
}
else
{
biomeContainer = new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(),
#if MC_VER < MC_1_21_3
biomes.getHolderOrThrow(Biomes.PLAINS),
#else
biomes.getOrThrow(Biomes.PLAINS),
#endif
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_1 ChunkType
#else ChunkType #endif
readChunkType(CompoundTag tagLevel)
{
String statusString = tagGetString(tagLevel,"Status");
if (statusString != null)
{
ChunkStatus chunkStatus = ChunkStatus.byName(statusString);
if (chunkStatus != null)
{
return chunkStatus.getChunkType();
}
}
#if MC_VER <= MC_1_20_4
return ChunkStatus.ChunkType.PROTOCHUNK;
#else
return ChunkType.PROTOCHUNK;
#endif
}
private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
{
CompoundTag tagHeightmaps = tagGetCompoundTag(chunkData, "Heightmaps");
if (tagHeightmaps != null)
{
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
{
String heightmap = type.getSerializationKey();
#if MC_VER < MC_1_21_5
if (tagHeightmaps.contains(heightmap, 12))
{
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
}
#else
if (tagHeightmaps.contains(heightmap))
{
Optional<long[]> optionalHeightmap = tagHeightmaps.getLongArray(heightmap);
if (optionalHeightmap.isPresent())
{
chunk.setHeightmap(type, optionalHeightmap.get());
}
}
#endif
}
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
}
}
// commented out as a test as of 2025-06-04 to see if this is actually necessary for DH
// DH probably doesn't need any chunk post-processing data
//private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
//{
// ListTag tagPostProcessings = tagGetListTag(chunkData,"PostProcessing", 9);
// if (tagPostProcessings != null)
// {
// for (int i = 0; i < tagPostProcessings.size(); ++i)
// {
// ListTag listTag3 = tagGetListTag(tagPostProcessings, i);
// for (int j = 0; j < listTag3.size(); ++j)
// {
// #if MC_VER < MC_1_21_3
// chunk.addPackedPostProcess(listTag3.getShort(j), i);
// #else
// chunk.addPackedPostProcess(ShortList.of(tagGetShort(listTag3, j)), i);
// #endif
// }
// }
// }
//}
#if MC_VER >= MC_1_18_2
private static BlendingData readBlendingData(CompoundTag chunkData)
{
BlendingData blendingData = null;
boolean containsBlendingData;
#if MC_VER < MC_1_21_5
containsBlendingData = chunkData.contains("blending_data", 10);
#else
containsBlendingData = chunkData.contains("blending_data");
#endif
if (containsBlendingData)
{
@SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
try
{
#if MC_VER < MC_1_21_3
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logParsingWarningOnce(message)).orElse(null);
#else
// blending data appears to have changed as of 1.21.6 causing a class cast exception here due to it being wrapped in a Java.Optional
blendingData = BlendingData.unpack(BlendingData.Packed.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logParsingWarningOnce(message)).orElse(null));
#endif
}
catch (Exception e)
{
String message = e.getMessage();
if (message == null || message.trim().isEmpty())
{
message = "Failed to parse blending data";
}
logParsingWarningOnce(message, e);
}
}
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.getInclusiveMinBuildHeight(chunk), ChunkWrapper.getExclusiveMaxBuildHeight(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))
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("Chunk section tag has an unexpected type ["+chunkSectionTag.getClass().getName()+"], expected ["+CompoundTag.class.getName()+"].");
}
return null;
}
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
// if null all lights = 0
byte[] blockLightNibbleArray = tagGetByteArray(chunkSectionCompoundTag, "BlockLight");
byte[] skyLightNibbleArray = tagGetByteArray(chunkSectionCompoundTag, "SkyLight");
if (blockLightNibbleArray != null
&& skyLightNibbleArray != null)
{
// 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.getInclusiveMinBuildHeight(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);
}
}
//=========//
// logging //
//=========//
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
"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.");
return newMessage;
});
}
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
"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.");
return newMessage;
});
}
private static void logParsingWarningOnce(String message) { logParsingWarningOnce(message, null); }
private static void logParsingWarningOnce(String message, Exception e)
{
if (message == null)
{
return;
}
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
"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.",
e);
return newMessage;
});
}
private static RuntimeException logErrorAndReturnException(String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
"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.");
return newMessage;
});
// Currently we want to ignore these errors, if returning null is a problem, we can change this later
return null; //new RuntimeException(message);
}
//====================//
// tag helper methods //
//====================//
// TODO move into separate file (this file is getting long)
// these tag helpers are to simplify tag accessing between MC versions
/** defaults to "false" if the tag isn't present */
private static boolean tagGetBoolean(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getBoolean(key);
#else
return tag.getBoolean(key).orElse(false);
#endif
}
/** defaults to "0" if the tag isn't present */
private static byte tagGetByte(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getByte(key);
#else
return tag.getByte(key).orElse((byte)0);
#endif
}
/** defaults to "0" if the tag isn't present */
private static short tagGetShort(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getShort(index);
#else
return tag.getShort(index).orElse((short)0);
#endif
}
/** defaults to "0" if the tag isn't present */
private static int tagGetInt(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getInt(key);
#else
return tag.getInt(key).orElse(0);
#endif
}
/** defaults to "0" if the tag isn't present */
private static long tagGetLong(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getInt(key);
#else
return tag.getLong(key).orElse(0L);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
private static String tagGetString(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getString(key);
#else
return tag.getString(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
private static byte[] tagGetByteArray(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getByteArray(key);
#else
return tag.getByteArray(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
private static CompoundTag tagGetCompoundTag(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getCompound(key);
#else
return tag.getCompound(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
private static CompoundTag tagGetCompoundTag(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getCompound(index);
#else
return tag.getCompound(index).orElse(null);
#endif
}
/**
* defaults to null if the tag isn't present
* @param elementType unused after MC 1.21.5
*/
@Nullable
private static ListTag tagGetListTag(CompoundTag tag, String key, int elementType)
{
#if MC_VER < MC_1_21_5
return tag.getList(key, elementType);
#else
return tag.getList(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
private static ListTag tagGetListTag(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getList(index);
#else
return tag.getList(index).orElse(null);
#endif
}
//================//
// 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);
}
}
}
@@ -28,7 +28,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.SpawnerBlock; import net.minecraft.world.level.block.SpawnerBlock;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -78,7 +78,7 @@ import net.minecraft.world.ticks.LevelTickAccess;
public class DhLitWorldGenRegion extends WorldGenRegion public class DhLitWorldGenRegion extends WorldGenRegion
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static ChunkStatus debugTriggeredForStatus = null; private static ChunkStatus debugTriggeredForStatus = null;
@@ -352,7 +352,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
ChunkAccess chunk = this.getChunkAccess(chunkX, chunkZ, chunkStatus, returnNonNull); ChunkAccess chunk = this.getChunkAccess(chunkX, chunkZ, chunkStatus, returnNonNull);
if (chunk instanceof LevelChunk) if (chunk instanceof LevelChunk)
{ {
chunk = new ImposterProtoChunk((LevelChunk) chunk #if MC_VER >= MC_1_18_2 , true #endif ); chunk = new ImposterProtoChunk((LevelChunk) chunk #if MC_VER >= MC_1_18_2 ,/* allow writes */ false #endif );
} }
return chunk; return chunk;
} }
@@ -392,10 +392,11 @@ public class DhLitWorldGenRegion extends WorldGenRegion
} }
} }
if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus) if (chunkStatus != ChunkStatus.EMPTY
&& chunkStatus != debugTriggeredForStatus)
{ {
LOGGER.info("WorldGen requiring " + chunkStatus LOGGER.info("WorldGen requiring [" + chunkStatus + "]"
+ " outside expected range detected. Force passing EMPTY chunk and seeing if it works."); + " is outside the expected range. Returning EMPTY chunk.");
debugTriggeredForStatus = chunkStatus; debugTriggeredForStatus = chunkStatus;
} }
@@ -1,13 +1,13 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject; package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling.ChunkFileReader;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFile;
import net.minecraft.world.level.chunk.storage.RegionFileStorage; import net.minecraft.world.level.chunk.storage.RegionFileStorage;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.DataInputStream; import java.io.DataInputStream;
@@ -29,7 +29,7 @@ import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
*/ */
public class RegionFileStorageExternalCache implements AutoCloseable public class RegionFileStorageExternalCache implements AutoCloseable
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** Can be null due to the C2ME mod */ /** Can be null due to the C2ME mod */
@Nullable @Nullable
@@ -55,7 +55,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
public RegionFileStorageExternalCache(RegionFileStorage storage) { this.storage = storage; } public RegionFileStorageExternalCache(RegionFileStorage storage) { this.storage = storage; }
@Nullable @Nullable
public RegionFile getRegionFile(ChunkPos pos) throws IOException public RegionFile getRegionFile(ChunkPos chunkPos) throws IOException
{ {
if (this.storage == null) if (this.storage == null)
{ {
@@ -70,8 +70,8 @@ public class RegionFileStorageExternalCache implements AutoCloseable
long posLong = ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ()); long chunkPosLong = ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ());
RegionFile rFile = null; RegionFile regionFile = null;
// Check vanilla cache // Check vanilla cache
int retryCount = 0; int retryCount = 0;
@@ -85,7 +85,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
this.getRegionFileLock.lock(); this.getRegionFileLock.lock();
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
rFile = this.storage.getRegionFile(pos); regionFile = this.storage.getRegionFile(chunkPos);
// keeping the region cache size low helps prevent concurrency issues // keeping the region cache size low helps prevent concurrency issues
if (this.storage.regionCache.size() > 150) // max 256 if (this.storage.regionCache.size() > 150) // max 256
@@ -97,7 +97,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
} }
} }
#else #else
rFile = this.storage.regionCache.getOrDefault(posLong, null); regionFile = this.storage.regionCache.getOrDefault(chunkPosLong, null);
#endif #endif
break; break;
@@ -139,19 +139,19 @@ public class RegionFileStorageExternalCache implements AutoCloseable
if (retryCount >= maxRetryCount) if (retryCount >= maxRetryCount)
{ {
BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at [" + pos + "]."); ChunkFileReader.CHUNK_LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at [" + chunkPos + "].");
} }
if (rFile != null) if (regionFile != null)
{ {
return rFile; return regionFile;
} }
// Then check our custom cache // Then check our custom cache
for (RegionFileCache cache : this.regionFileCache) for (RegionFileCache cache : this.regionFileCache)
{ {
if (cache.pos == posLong) if (cache.pos == chunkPosLong)
{ {
return cache.file; return cache.file;
} }
@@ -170,22 +170,22 @@ public class RegionFileStorageExternalCache implements AutoCloseable
return null; return null;
} }
Path regionFilePath = storageFolderPath.resolve("r." + pos.getRegionX() + "." + pos.getRegionZ() + ".mca"); Path regionFilePath = storageFolderPath.resolve("r." + chunkPos.getRegionX() + "." + chunkPos.getRegionZ() + ".mca");
#if MC_VER == MC_1_16_5 || MC_VER == 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); regionFile = new RegionFile(regionFilePath.toFile(), storageFolderPath.toFile(), false);
#elif MC_VER <= MC_1_20_4 #elif MC_VER <= MC_1_20_4
rFile = new RegionFile(regionFilePath, storageFolderPath, false); regionFile = new RegionFile(regionFilePath, storageFolderPath, false);
#else #else
rFile = new RegionFile(new RegionStorageInfo("level", null, "level type"), regionFilePath, storageFolderPath, false); regionFile = new RegionFile(new RegionStorageInfo("level", null, "level type"), regionFilePath, storageFolderPath, false);
#endif #endif
this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ()), rFile)); this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ()), regionFile));
while (this.regionFileCache.size() > MAX_CACHE_SIZE) while (this.regionFileCache.size() > MAX_CACHE_SIZE)
{ {
this.regionFileCache.poll().file.close(); this.regionFileCache.poll().file.close();
} }
return rFile; return regionFile;
} }
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.seibel.distanthorizons.common.wrappers.worldGeneration; package com.seibel.distanthorizons.common.wrappers.worldGeneration.params;
import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.DataFixer;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
@@ -27,35 +27,50 @@ import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess; import net.minecraft.core.RegistryAccess;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.chunk.ChunkGenerator;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.world.level.chunk.storage.ChunkScanAccess; import net.minecraft.world.level.chunk.storage.ChunkScanAccess;
#endif #endif
import net.minecraft.world.level.levelgen.WorldGenSettings;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
#elif MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
#else #else
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.levelgen.RandomState; import net.minecraft.world.level.levelgen.RandomState;
#if MC_VER >= MC_1_19_4 import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.core.registries.Registries;
#endif
#endif #endif
import net.minecraft.world.level.storage.WorldData; import net.minecraft.world.level.storage.WorldData;
public final class GlobalParameters #if MC_VER < MC_1_19_4
#elif MC_VER < MC_1_21_3
import net.minecraft.core.registries.Registries;
#else
import net.minecraft.core.registries.Registries;
#endif
#if MC_VER < MC_1_19_4
import net.minecraft.world.level.levelgen.WorldGenSettings;
#else
import net.minecraft.world.level.levelgen.WorldOptions;
#endif
/**
* Handles parameters that are relevant for the entire MC world.
*
* @see ThreadWorldGenParams
*/
public final class GlobalWorldGenParams
{ {
public final ChunkGenerator generator; public final ChunkGenerator generator;
public final IDhServerLevel lodLevel; public final IDhServerLevel dhServerLevel;
public final ServerLevel level; public final ServerLevel mcServerLevel;
public final Registry<Biome> biomes; public final Registry<Biome> biomes;
public final RegistryAccess registry; public final RegistryAccess registry;
public final long worldSeed; public final long worldSeed;
public final DataFixer fixerUpper; public final DataFixer dataFixer;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
public final StructureManager structures; public final StructureManager structures;
@@ -72,15 +87,21 @@ public final class GlobalParameters
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
public final BiomeManager biomeManager; public final BiomeManager biomeManager;
public final ChunkScanAccess chunkScanner; // FIXME: Figure out if this is actually needed public final ChunkScanAccess chunkScanner;
#endif #endif
public GlobalParameters(IDhServerLevel lodLevel)
{
this.lodLevel = lodLevel;
this.level = ((ServerLevelWrapper) lodLevel.getServerLevelWrapper()).getWrappedMcObject();
MinecraftServer server = this.level.getServer(); //=============//
// constructor //
//=============//
public GlobalWorldGenParams(IDhServerLevel dhServerLevel)
{
this.dhServerLevel = dhServerLevel;
this.mcServerLevel = ((ServerLevelWrapper) dhServerLevel.getServerLevelWrapper()).getWrappedMcObject();
MinecraftServer server = this.mcServerLevel.getServer();
WorldData worldData = server.getWorldData(); WorldData worldData = server.getWorldData();
this.registry = server.registryAccess(); this.registry = server.registryAccess();
@@ -99,15 +120,19 @@ public final class GlobalParameters
#endif #endif
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
this.biomeManager = new BiomeManager(this.level, BiomeManager.obfuscateSeed(this.worldSeed)); this.biomeManager = new BiomeManager(this.mcServerLevel, BiomeManager.obfuscateSeed(this.worldSeed));
this.chunkScanner = this.level.getChunkSource().chunkScanner(); this.chunkScanner = this.mcServerLevel.getChunkSource().chunkScanner();
#endif #endif
this.structures = server.getStructureManager(); this.structures = server.getStructureManager();
this.generator = this.level.getChunkSource().getGenerator(); this.generator = this.mcServerLevel.getChunkSource().getGenerator();
this.fixerUpper = server.getFixerUpper(); this.dataFixer = server.getFixerUpper();
#if MC_VER >= MC_1_19_2 #if MC_VER >= MC_1_19_2
this.randomState = this.level.getChunkSource().randomState(); this.randomState = this.mcServerLevel.getChunkSource().randomState();
#endif #endif
} }
} }
@@ -0,0 +1,124 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.worldGeneration.params;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.WorldGenStructFeatManager;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.WorldGenLevel;
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.structure.StructureCheck;
#endif
public final class ThreadWorldGenParams
{
private static final ThreadLocal<ThreadWorldGenParams> LOCAL_PARAM_REF = new ThreadLocal<>();
final ServerLevel level;
public WorldGenStructFeatManager structFeatManager = null;
#if MC_VER >= MC_1_18_2
public StructureCheck structCheck;
#endif
boolean isValid = true;
// used for some older MC versions
private static GlobalWorldGenParams previousGlobalWorldGenParams = null;
//=============//
// constructor //
//=============//
public static ThreadWorldGenParams getOrMake(GlobalWorldGenParams globalParams)
{
ThreadWorldGenParams threadParam = LOCAL_PARAM_REF.get();
if (threadParam != null
&& threadParam.isValid
&& threadParam.level == globalParams.mcServerLevel)
{
return threadParam;
}
threadParam = new ThreadWorldGenParams(globalParams);
LOCAL_PARAM_REF.set(threadParam);
return threadParam;
}
private ThreadWorldGenParams(GlobalWorldGenParams param)
{
previousGlobalWorldGenParams = param;
this.level = param.mcServerLevel;
#if MC_VER < MC_1_18_2
this.structFeatManager = new WorldGenStructFeatManager(param.worldGenSettings, this.level);
#elif MC_VER < MC_1_19_2
this.structCheck = this.createStructureCheck(param);
#else
this.structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
param.mcServerLevel.dimension(), param.generator, param.randomState, this.level, param.generator.getBiomeSource(), param.worldSeed,
param.dataFixer);
#endif
}
//==========//
// builders //
//==========//
public void makeStructFeatManager(WorldGenLevel genLevel, GlobalWorldGenParams param)
{
#if MC_VER < MC_1_18_2
this.structFeatManager = new WorldGenStructFeatManager(param.worldGenSettings, genLevel);
#elif MC_VER < MC_1_19_4
this.structFeatManager = new WorldGenStructFeatManager(param.worldGenSettings, genLevel, this.structCheck);
#else
this.structFeatManager = new WorldGenStructFeatManager(param.worldOptions, genLevel, this.structCheck);
#endif
}
#if MC_VER < MC_1_18_2
#elif MC_VER < MC_1_19_2
public void recreateStructureCheck()
{
if (previousGlobalWorldGenParams != null)
{
this.structCheck = this.createStructureCheck(previousGlobalWorldGenParams);
}
}
private StructureCheck createStructureCheck(GlobalWorldGenParams param)
{
return new StructureCheck(param.chunkScanner, param.registry, param.structures,
param.mcServerLevel.dimension(), param.generator, this.level, param.generator.getBiomeSource(), param.worldSeed,
param.dataFixer);
}
#else
public void recreateStructureCheck() { /* do nothing */ }
#endif
}
@@ -0,0 +1,54 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
import java.util.ArrayList;
import java.util.List;
#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 abstract class AbstractWorldGenStep
{
public abstract void generateGroup(
ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers);
public abstract ChunkStatus getChunkStatus();
/** @return the list of chunks that have an earlier status and can be generated */
protected ArrayList<ChunkWrapper> getChunkWrappersToGenerate(List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkWrapper> chunkWrappersToGenerate = new ArrayList<>(chunkWrappers.size());
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(this.getChunkStatus()))
{
// this chunk has already been generated up to this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(this.getChunkStatus());
chunkWrappersToGenerate.add(chunkWrapper);
}
}
return chunkWrappersToGenerate;
}
}
@@ -20,15 +20,14 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step; package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import net.minecraft.server.level.WorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.Blender; import net.minecraft.world.level.levelgen.blending.Blender;
@@ -40,76 +39,75 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
public final class StepBiomes public final class StepBiomes extends AbstractWorldGenStep
{ {
public static final ChunkStatus STATUS = ChunkStatus.BIOMES;
private final BatchGenerationEnvironment environment; private final BatchGenerationEnvironment environment;
public static final ChunkStatus STATUS = ChunkStatus.BIOMES;
//=============//
// constructor //
//=============//
public StepBiomes(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; } public StepBiomes(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
//==================//
// abstract methods //
//==================//
@Override
public ChunkStatus getChunkStatus() { return STATUS; }
@Override
public void generateGroup( public void generateGroup(
ThreadedParameters tParams, WorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers) ArrayGridList<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>(); for (ChunkWrapper chunkWrapper : chunksToDo)
for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
chunksToDo.add(chunk);
}
}
for (ChunkAccess chunk : chunksToDo)
{
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
this.environment.params.generator.createBiomes(this.environment.params.biomes, chunk); this.environment.globalParams.generator.createBiomes(this.environment.globalParams.biomes, chunk);
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes( this.environment.globalParams.generator.createBiomes(
this.environment.params.biomes, this.environment.globalParams.biomes,
Runnable::run, Runnable::run,
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk) chunk)
); );
#elif MC_VER < MC_1_19_4 #elif MC_VER < MC_1_19_4
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes( this.environment.globalParams.generator.createBiomes(
this.environment.params.biomes, this.environment.globalParams.biomes,
Runnable::run, Runnable::run,
this.environment.params.randomState, Blender.of(worldGenRegion), this.environment.globalParams.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk) chunk)
); );
#elif MC_VER < MC_1_21_1 #elif MC_VER < MC_1_21_1
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes( this.environment.globalParams.generator.createBiomes(
Runnable::run, Runnable::run,
this.environment.params.randomState, this.environment.globalParams.randomState,
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk) chunk)
); );
#else #else
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes( this.environment.globalParams.generator.createBiomes(
this.environment.params.randomState, this.environment.globalParams.randomState,
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk) chunk)
); );
#endif #endif
@@ -21,15 +21,14 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.Heightmap;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
#if MC_VER <= MC_1_20_4 #if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
@@ -37,10 +36,13 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
public final class StepFeatures
public final class StepFeatures extends AbstractWorldGenStep
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static final ChunkStatus STATUS = ChunkStatus.FEATURES; public static final ChunkStatus STATUS = ChunkStatus.FEATURES;
@@ -48,37 +50,41 @@ public final class StepFeatures
//=============//
// constructor //
//=============//
public StepFeatures(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; } public StepFeatures(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
//==================//
// abstract methods //
//==================//
@Override
public ChunkStatus getChunkStatus() { return STATUS; }
@Override
public void generateGroup( public void generateGroup(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers) ArrayGridList<ChunkWrapper> chunkWrappers)
{ {
for (ChunkWrapper chunkWrapper : chunkWrappers) ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
for (ChunkWrapper chunkWrapper : chunksToDo)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
}
try try
{ {
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
worldGenRegion.setOverrideCenter(chunk.getPos()); worldGenRegion.setOverrideCenter(chunk.getPos());
environment.params.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeat); environment.globalParams.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeatManager);
#else #else
if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().getX(), chunkWrapper.getChunkPos().getZ())) if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().getX(), chunkWrapper.getChunkPos().getZ()))
{ {
this.environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeat.forWorldGenRegion(worldGenRegion)); this.environment.globalParams.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeatManager.forWorldGenRegion(worldGenRegion));
} }
else else
{ {
@@ -88,6 +94,10 @@ public final class StepFeatures
Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter()); Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter());
} }
catch (ConcurrentModificationException e) // ReportedException
{
// TODO
}
catch (Exception e) catch (Exception e)
{ {
LOGGER.warn("Unexpected issue when generating features for chunk at pos ["+chunkWrapper.getChunkPos()+"], error: ["+e.getMessage()+"].", e); LOGGER.warn("Unexpected issue when generating features for chunk at pos ["+chunkWrapper.getChunkPos()+"], error: ["+e.getMessage()+"].", e);
@@ -20,16 +20,14 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step; package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import net.minecraft.server.level.WorldGenRegion; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.Blender; import net.minecraft.world.level.levelgen.blending.Blender;
@@ -41,7 +39,7 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
public final class StepNoise public final class StepNoise extends AbstractWorldGenStep
{ {
private static final ChunkStatus STATUS = ChunkStatus.NOISE; private static final ChunkStatus STATUS = ChunkStatus.NOISE;
@@ -49,62 +47,62 @@ public final class StepNoise
//=============//
// constructor //
//=============//
public StepNoise(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; } public StepNoise(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
//==================//
// abstract methods //
//==================//
@Override
public ChunkStatus getChunkStatus() { return STATUS; }
@Override
public void generateGroup( public void generateGroup(
ThreadedParameters tParams, WorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers) ArrayGridList<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>(); for (ChunkWrapper chunkWrapper : chunksToDo)
for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
continue;
}
chunkWrapper.trySetStatus(STATUS);
chunksToDo.add(chunk);
}
for (ChunkAccess chunk : chunksToDo)
{
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
this.environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk); this.environment.globalParams.generator.fillFromNoise(worldGenRegion, tParams.structFeatManager, chunk);
#elif MC_VER < MC_1_18_2 #elif MC_VER < MC_1_18_2
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise( this.environment.globalParams.generator.fillFromNoise(
Runnable::run, Runnable::run,
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk)); chunk));
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise( this.environment.globalParams.generator.fillFromNoise(
Runnable::run, Runnable::run,
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk)); chunk));
#elif MC_VER < MC_1_21_1 #elif MC_VER < MC_1_21_1
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise( this.environment.globalParams.generator.fillFromNoise(
Runnable::run, Runnable::run,
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
this.environment.params.randomState, this.environment.globalParams.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk)); chunk));
#else #else
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise( this.environment.globalParams.generator.fillFromNoise(
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
this.environment.params.randomState, this.environment.globalParams.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk)); chunk));
#endif #endif
UncheckedInterruptedException.throwIfInterrupted();
} }
} }
@@ -20,15 +20,14 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step; package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import net.minecraft.server.level.WorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
#if MC_VER <= MC_1_20_4 #if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
@@ -37,7 +36,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
public final class StepStructureReference public final class StepStructureReference extends AbstractWorldGenStep
{ {
private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES; private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES;
@@ -45,35 +44,31 @@ public final class StepStructureReference
//=============//
// constructor //
//=============//
public StepStructureReference(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; } public StepStructureReference(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
//==================//
// abstract methods //
//==================//
@Override
public ChunkStatus getChunkStatus() { return STATUS; }
@Override
public void generateGroup( public void generateGroup(
ThreadedParameters tParams, WorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers) ArrayGridList<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>(); for (ChunkWrapper chunkWrapper : chunksToDo)
for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS)) this.environment.globalParams.generator.createReferences(worldGenRegion, tParams.structFeatManager.forWorldGenRegion(worldGenRegion), chunk);
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
}
}
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepStructureReference: "+chunk.getPos());
this.environment.params.generator.createReferences(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
} }
} }
@@ -20,20 +20,17 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step; package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.resources.ResourceKey; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.apache.logging.log4j.Logger;
#if MC_VER <= MC_1_20_4 #if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
@@ -42,9 +39,9 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
public final class StepStructureStart public final class StepStructureStart extends AbstractWorldGenStep
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS; private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS;
private static final ReentrantLock STRUCTURE_PLACEMENT_LOCK = new ReentrantLock(); private static final ReentrantLock STRUCTURE_PLACEMENT_LOCK = new ReentrantLock();
@@ -52,114 +49,97 @@ public final class StepStructureStart
//=============//
// constructor //
//=============//
public StepStructureStart(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; } public StepStructureStart(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
public static class StructStartCorruptedException extends RuntimeException //==================//
{ // abstract methods //
private static final long serialVersionUID = -8987434342051563358L; //==================//
public StructStartCorruptedException(ArrayIndexOutOfBoundsException e) @Override
public ChunkStatus getChunkStatus() { return STATUS; }
@Override
public void generateGroup(
ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
// TODO should be put in wrapped environment so we can skip some other world gen steps
// SURFACE wouldn't need structure generation either
#if MC_VER < MC_1_19_2
if (!this.environment.globalParams.worldGenSettings.generateFeatures())
#elif MC_VER < MC_1_19_4
if (!this.environment.globalParams.worldGenSettings.generateStructures())
#else
if (!this.environment.globalParams.worldOptions.generateStructures())
#endif
{ {
super("StructStartCorruptedException"); return;
super.initCause(e);
fillInStackTrace();
} }
}
public void generateGroup(
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers) throws InterruptedException
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunksToDo)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS))
// hopefully this shouldn't cause any performance issues (this step is generally quite quick so hopefully it should be fine)
// and should prevent some concurrency issues
STRUCTURE_PLACEMENT_LOCK.lock();
#if MC_VER < MC_1_19_2
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry, tParams.structFeatManager, chunk, this.environment.globalParams.structures,
this.environment.globalParams.worldSeed);
#elif MC_VER < MC_1_19_4
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry, this.environment.globalParams.randomState, tParams.structFeatManager, chunk, this.environment.globalParams.structures,
this.environment.globalParams.worldSeed);
#elif MC_VER <= MC_1_21_3
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry,
this.environment.globalParams.mcServerLevel.getChunkSource().getGeneratorState(),
tParams.structFeatManager, chunk, this.environment.globalParams.structures);
#else
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry,
this.environment.globalParams.mcServerLevel.getChunkSource().getGeneratorState(),
tParams.structFeatManager, chunk, this.environment.globalParams.structures,
this.environment.globalParams.mcServerLevel.dimension());
#endif
#if MC_VER >= MC_1_18_2
try
{ {
// this chunk has already generated this step tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
continue;
} }
else if (chunk instanceof ProtoChunk) catch (ArrayIndexOutOfBoundsException firstEx)
{ {
chunkWrapper.trySetStatus(STATUS); // There's a rare issue with StructStart where it throws ArrayIndexOutOfBounds
} // This means the structFeat is corrupted (For some reason) and I need to reset it.
} // TODO: Figure out in the future why this happens even though I am using new structFeat - OLD
#if MC_VER < MC_1_19_2 // reset the structureStart
if (this.environment.params.worldGenSettings.generateFeatures()) tParams.recreateStructureCheck();
{
#elif MC_VER < MC_1_19_4
if (this.environment.params.worldGenSettings.generateStructures())
{
#else
if (this.environment.params.worldOptions.generateStructures())
{
#endif
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepStructureStart: "+chunk.getPos());
// there are a few cases where the structure generator call may lock up (either due to teleporting or leaving the world).
// hopefully allowing interrupts here will prevent that from happening.
BatchGenerationEnvironment.throwIfThreadInterrupted();
// hopefully this shouldn't cause any performance issues (this step is generally quite quick so hopefully it should be fine)
// and should prevent some concurrency issues
STRUCTURE_PLACEMENT_LOCK.lock();
#if MC_VER < MC_1_19_2
this.environment.params.generator.createStructures(this.environment.params.registry, tParams.structFeat, chunk, this.environment.params.structures,
this.environment.params.worldSeed);
#elif MC_VER < MC_1_19_4
this.environment.params.generator.createStructures(this.environment.params.registry, this.environment.params.randomState, tParams.structFeat, chunk, this.environment.params.structures,
this.environment.params.worldSeed);
#elif MC_VER <= MC_1_21_3
this.environment.params.generator.createStructures(this.environment.params.registry,
this.environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, this.environment.params.structures);
#else
this.environment.params.generator.createStructures(this.environment.params.registry,
this.environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, this.environment.params.structures,
this.environment.params.level.dimension());
#endif
#if MC_VER >= MC_1_18_2
try try
{ {
// try running the structure logic again
tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts()); tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
} }
catch (ArrayIndexOutOfBoundsException firstEx) catch (ArrayIndexOutOfBoundsException secondEx)
{ {
// There's a rare issue with StructStart where it throws ArrayIndexOutOfBounds // the structure logic failed again, log it and move on
// This means the structFeat is corrupted (For some reason) and I need to reset it. LOGGER.error("Unable to create structure starts for " + chunk.getPos() + ". This is an error with MC's world generation. Ignoring and continuing generation. Error: " + secondEx.getMessage()); // don't log the full stack trace since it is long and will generally end up in MC's code
// TODO: Figure out in the future why this happens even though I am using new structFeat - OLD
// reset the structureStart
tParams.recreateStructureCheck();
try
{
// try running the structure logic again
tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
}
catch (ArrayIndexOutOfBoundsException secondEx)
{
// the structure logic failed again, log it and move on
LOGGER.error("Unable to create structure starts for " + chunk.getPos() + ". This is an error with MC's world generation. Ignoring and continuing generation. Error: " + secondEx.getMessage()); // don't log the full stack trace since it is long and will generally end up in MC's code
//throw new StepStructureStart.StructStartCorruptedException(secondEx);
}
} }
#endif
STRUCTURE_PLACEMENT_LOCK.unlock();
} }
#endif
STRUCTURE_PLACEMENT_LOCK.unlock();
} }
} }
@@ -20,15 +20,14 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step; package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import net.minecraft.server.level.WorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
#if MC_VER <= MC_1_20_4 #if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
@@ -37,7 +36,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
public final class StepSurface public final class StepSurface extends AbstractWorldGenStep
{ {
private static final ChunkStatus STATUS = ChunkStatus.SURFACE; private static final ChunkStatus STATUS = ChunkStatus.SURFACE;
@@ -45,42 +44,41 @@ public final class StepSurface
//=============//
// constructor //
//=============//
public StepSurface(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; } public StepSurface(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
public void generateGroup( //==================//
ThreadedParameters tParams, WorldGenRegion worldGenRegion, // abstract methods //
List<ChunkWrapper> chunkWrappers) //==================//
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers) @Override
public ChunkStatus getChunkStatus() { return STATUS; }
@Override
public void generateGroup(
ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
for (ChunkWrapper chunkWrapper : chunksToDo)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
chunksToDo.add(chunk);
}
}
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepSurface: "+chunk.getPos());
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
environment.params.generator.buildSurfaceAndBedrock(worldGenRegion, chunk); this.environment.globalParams.generator.buildSurfaceAndBedrock(worldGenRegion, chunk);
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk); this.environment.globalParams.generator.buildSurface(worldGenRegion, tParams.structFeatManager.forWorldGenRegion(worldGenRegion), chunk);
#else #else
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), environment.params.randomState, chunk); this.environment.globalParams.generator.buildSurface(worldGenRegion, tParams.structFeatManager.forWorldGenRegion(worldGenRegion), this.environment.globalParams.randomState, chunk);
#endif #endif
} }
} }
} }
@@ -18,7 +18,6 @@ accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chu
# used for grabbing vanilla rendered chunks # used for grabbing vanilla rendered chunks
accessible class net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo accessible class net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo
accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chunk Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk; accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chunk Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;
#accessible field net/minecraft/world/entity/Entity blockPosition Lnet/minecraft/core/BlockPos;
# lighting # lighting
accessible field net/minecraft/client/renderer/LightTexture lightPixels Lcom/mojang/blaze3d/platform/NativeImage; accessible field net/minecraft/client/renderer/LightTexture lightPixels Lcom/mojang/blaze3d/platform/NativeImage;
@@ -30,8 +29,6 @@ accessible field net/minecraft/world/level/lighting/LevelLightEngine skyEngine L
accessible method net/minecraft/world/level/levelgen/Heightmap setHeight (III)V accessible method net/minecraft/world/level/levelgen/Heightmap setHeight (III)V
accessible field net/minecraft/world/level/biome/Biome generationSettings Lnet/minecraft/world/level/biome/BiomeGenerationSettings; accessible field net/minecraft/world/level/biome/Biome generationSettings Lnet/minecraft/world/level/biome/BiomeGenerationSettings;
accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecraft/world/level/biome/Biome$BiomeCategory; accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecraft/world/level/biome/Biome$BiomeCategory;
#accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder;
#accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doCreateBiomes (Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)V
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager; accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder; accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
@@ -29,8 +29,6 @@ accessible field net/minecraft/world/level/lighting/LevelLightEngine skyEngine L
accessible method net/minecraft/world/level/levelgen/Heightmap setHeight (III)V accessible method net/minecraft/world/level/levelgen/Heightmap setHeight (III)V
accessible field net/minecraft/world/level/biome/Biome generationSettings Lnet/minecraft/world/level/biome/BiomeGenerationSettings; accessible field net/minecraft/world/level/biome/Biome generationSettings Lnet/minecraft/world/level/biome/BiomeGenerationSettings;
accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecraft/world/level/biome/Biome$BiomeCategory; accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecraft/world/level/biome/Biome$BiomeCategory;
# accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder;
#accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doCreateBiomes (Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)V
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager; accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder; accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
@@ -15,7 +15,6 @@ accessible class net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo
accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chunk Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk; accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chunk Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;
# world generation # 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/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage; 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 accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
@@ -11,10 +11,7 @@ accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecr
# used for grabbing vanilla rendered chunks # used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList; 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 # 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/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage; 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 accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
@@ -11,10 +11,7 @@ accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecr
# used for grabbing vanilla rendered chunks # used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList; 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 # 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/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage; 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 accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
@@ -0,0 +1,53 @@
accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/nio/file/Path;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)F
accessible field net/minecraft/client/Minecraft deltaTracker Lnet/minecraft/client/DeltaTracker$Timer;
accessible field net/minecraft/client/renderer/LevelRenderer level Lnet/minecraft/client/multiplayer/ClientLevel;
# used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList;
# world generation
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
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# 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;
accessible method net/minecraft/client/gui/components/debug/DebugScreenEntries register (Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/gui/components/debug/DebugScreenEntry;)Lnet/minecraft/resources/ResourceLocation;
# hacky stuff
accessible field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
mutable field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
@@ -0,0 +1,56 @@
accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/nio/file/Path;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)F
accessible field net/minecraft/client/Minecraft deltaTracker Lnet/minecraft/client/DeltaTracker$Timer;
accessible field net/minecraft/client/renderer/LevelRenderer level Lnet/minecraft/client/multiplayer/ClientLevel;
# used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList;
# world generation
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
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# getting existing chunks outside the main thread
accessible method net/minecraft/server/level/ChunkMap getVisibleChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/SimpleRegionStorage 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;
accessible method net/minecraft/client/gui/components/debug/DebugScreenEntries register (Ljava/lang/String;Lnet/minecraft/client/gui/components/debug/DebugScreenEntry;)Lnet/minecraft/resources/Identifier;
# hacky stuff
accessible field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
mutable field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
@@ -14,7 +14,6 @@ accessible field net/minecraft/client/Minecraft deltaTracker Lnet/minecraft/clie
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList; accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList;
# world generation # 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/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage; 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 accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
@@ -14,7 +14,6 @@ accessible field net/minecraft/client/Minecraft deltaTracker Lnet/minecraft/clie
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList; accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList;
# world generation # 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/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage; 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 accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
+51 -7
View File
@@ -19,7 +19,9 @@ loom {
"-Dminecraft.api.session.host=https://nope.invalid", "-Dminecraft.api.session.host=https://nope.invalid",
"-Dminecraft.api.services.host=https://nope.invalid", "-Dminecraft.api.services.host=https://nope.invalid",
// https://netty.io/wiki/reference-counted-objects.html#leak-detection-levels // https://netty.io/wiki/reference-counted-objects.html#leak-detection-levels
"-Dio.netty.leakDetection.level=advanced" "-Dio.netty.leakDetection.level=advanced",
"-XX:+UseZGC",
"-XX:+ZGenerational"
) )
programArgs("--username", "Dev") programArgs("--username", "Dev")
} }
@@ -76,15 +78,23 @@ dependencies {
// Fabric API // Fabric API
addModJar(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) addModJar(fabricApi.module("fabric-api-base", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version)) addModJar(fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) if (buildVersionBefore(minecraft_version, "1.21.9"))
{
addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version))
}
else // > 1.21.9
{
addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-resource-loader-v1", rootProject.fabric_api_version))
}
addModJar(fabricApi.module("fabric-events-interaction-v0", rootProject.fabric_api_version)) 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-rendering-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version)) addModJar(fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-entity-events-v1", rootProject.fabric_api_version)) addModJar(fabricApi.module("fabric-entity-events-v1", rootProject.fabric_api_version))
if (minecraft_version >= "1.19.2") if (buildVersionBefore(minecraft_version, "1.19.2"))
addModJar(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version))
else // < 1.19.2
addModJar(fabricApi.module("fabric-command-api-v1", rootProject.fabric_api_version)) addModJar(fabricApi.module("fabric-command-api-v1", rootProject.fabric_api_version))
else
addModJar(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version))
// used by mod menu in MC 1.20.6+ // used by mod menu in MC 1.20.6+
addModJar(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) addModJar(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version))
@@ -92,7 +102,7 @@ dependencies {
// Mod Menu // Mod Menu
modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") addMod("com.terraformersmc:modmenu:${rootProject.modmenu_version}", rootProject.enable_mod_menu)
// Starlight // Starlight
addMod("curse.maven:starlight-521783:${rootProject.starlight_version_fabric}", rootProject.enable_starlight) addMod("curse.maven:starlight-521783:${rootProject.starlight_version_fabric}", rootProject.enable_starlight)
@@ -143,6 +153,40 @@ dependencies {
} }
} }
private static boolean buildVersionBefore(String minecraft_version, String compareVersion)
{
int sortValue = sortSemanticVersionOldestToNewest(minecraft_version, compareVersion);
return sortValue == -1;
}
/**
* input format: "major.minor.patch"
* needed so we can sort versions with different length strings
* IE: 1.21.1 should come before 1.21.10
*/
private static int sortSemanticVersionOldestToNewest(String version1, String version2)
{
String[] parts1 = version1.split("\\.");
String[] parts2 = version2.split("\\.");
int major1 = Integer.parseInt(parts1[0]);
int major2 = Integer.parseInt(parts2[0]);
if (major1 != major2)
{
return Integer.compare(major1, major2);
}
int minor1 = Integer.parseInt(parts1[1]);
int minor2 = Integer.parseInt(parts2[1]);
if (minor1 != minor2)
{
return Integer.compare(minor1, minor2);
}
int patch1 = Integer.parseInt(parts1[2]);
int patch2 = Integer.parseInt(parts2[2]);
return Integer.compare(patch1, patch2);
}
task deleteResources(type: Delete) { task deleteResources(type: Delete) {
delete file("build/resources/main") delete file("build/resources/main")
@@ -24,26 +24,24 @@ import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.mojang.blaze3d.platform.InputConstants;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.fabric.wrappers.modAccessor.SodiumAccessor; import com.seibel.distanthorizons.fabric.wrappers.modAccessor.SodiumAccessor;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents; 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.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 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.AttackBlockCallback;
import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@@ -61,13 +59,16 @@ import java.nio.FloatBuffer;
#endif #endif
import java.util.HashSet; import java.util.HashSet;
import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
#if MC_VER < MC_1_21_9
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
#endif
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
/** /**
@@ -84,7 +85,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
private final ClientApi clientApi = ClientApi.INSTANCE; private final ClientApi clientApi = ClientApi.INSTANCE;
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class); private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
// TODO we shouldn't be filtering keys on the Forge/Fabric side, only in ClientApi // TODO we shouldn't be filtering keys on the Forge/Fabric side, only in ClientApi
private static final int[] KEY_TO_CHECK_FOR = { GLFW.GLFW_KEY_F6, GLFW.GLFW_KEY_F8, GLFW.GLFW_KEY_P}; private static final int[] KEY_TO_CHECK_FOR = { GLFW.GLFW_KEY_F6, GLFW.GLFW_KEY_F8, GLFW.GLFW_KEY_P};
@@ -103,6 +104,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
LOGGER.info("Registering Fabric Client Events"); LOGGER.info("Registering Fabric Client Events");
//========================// //========================//
// register mod accessors // // register mod accessors //
//========================// //========================//
@@ -111,14 +113,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
//=============//
// tick events //
//=============//
ClientTickEvents.START_CLIENT_TICK.register((client) -> { ClientApi.INSTANCE.clientTickEvent(); });
//==============// //==============//
// chunk events // // chunk events //
//==============// //==============//
@@ -128,8 +122,16 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
{ {
if (MC.clientConnectedToDedicatedServer()) if (MC.clientConnectedToDedicatedServer())
{ {
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level); // executor to prevent locking up the render/event thread
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, wrappedLevel), wrappedLevel); AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, wrappedLevel), wrappedLevel);
});
}
} }
}); });
@@ -143,8 +145,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(blockPos.getX(), blockPos.getZ())) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(blockPos.getX(), blockPos.getZ()))
{ {
// executor to prevent locking up the render/event thread // executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor(); AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null) if (executor != null)
{ {
@@ -183,8 +183,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(hitResult.getBlockPos().getX(), hitResult.getBlockPos().getZ())) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(hitResult.getBlockPos().getX(), hitResult.getBlockPos().getZ()))
{ {
// executor to prevent locking up the render/event thread // executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor(); AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null) if (executor != null)
{ {
@@ -217,95 +215,83 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// render event // // render event //
//==============// //==============//
#if MC_VER < MC_1_21_9
WorldRenderEvents.AFTER_SETUP.register((renderContext) -> WorldRenderEvents.AFTER_SETUP.register((renderContext) ->
{ {
Mat4f projectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix()); ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f modelViewMatrix;
#if MC_VER < MC_1_20_6 #if MC_VER < MC_1_20_6
modelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose()); ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
#else #else
modelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix()); ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
#endif #endif
#if MC_VER < MC_1_21_1
ClientApi.RENDER_STATE.frameTime = renderContext.tickDelta();
#else
ClientApi.RENDER_STATE.frameTime = renderContext.tickCounter().getGameTimeDeltaTicks();
#endif
//LOGGER.info("\n\n" + ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, renderContext.world());
// "Level Render\n" +
// "Mc MVM: \n" + modelViewMatrix.toString() + "\n" +
// "Mc Proj: \n" + projectionMatrix.toString()
//);
this.clientApi.renderLods(ClientLevelWrapper.getWrapper(renderContext.world()), this.clientApi.renderLods();
modelViewMatrix,
projectionMatrix,
#if MC_VER < MC_1_21_1
renderContext.tickDelta()
#else
renderContext.tickCounter().getGameTimeDeltaTicks()
#endif
);
}); });
// TODO add to forge and neo
WorldRenderEvents.AFTER_ENTITIES.register((renderContext) -> WorldRenderEvents.AFTER_ENTITIES.register((renderContext) ->
{ {
Mat4f projectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix()); ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f modelViewMatrix;
#if MC_VER < MC_1_20_6 #if MC_VER < MC_1_20_6
modelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose()); ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
#else #else
modelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix()); ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
#endif #endif
this.clientApi.renderFadeOpaque( #if MC_VER < MC_1_21_1
modelViewMatrix, ClientApi.RENDER_STATE.frameTime = renderContext.tickDelta();
projectionMatrix, #else
#if MC_VER < MC_1_21_1 ClientApi.RENDER_STATE.frameTime = renderContext.tickCounter().getGameTimeDeltaTicks();
renderContext.tickDelta(), #endif
#else
renderContext.tickCounter().getGameTimeDeltaTicks(), ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, renderContext.world());
#endif
ClientLevelWrapper.getWrapper(renderContext.world())
); this.clientApi.renderFadeOpaque();
}); });
// TODO add to forge and neo
WorldRenderEvents.AFTER_TRANSLUCENT.register((renderContext) -> WorldRenderEvents.AFTER_TRANSLUCENT.register((renderContext) ->
{ {
Mat4f projectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix()); ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f modelViewMatrix;
#if MC_VER < MC_1_20_6 #if MC_VER < MC_1_20_6
modelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose()); ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
#else #else
modelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix()); ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
#endif #endif
#if MC_VER < MC_1_21_1
ClientApi.RENDER_STATE.frameTime = renderContext.tickDelta();
#else
ClientApi.RENDER_STATE.frameTime = renderContext.tickCounter().getGameTimeDeltaTicks();
#endif
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, renderContext.world());
#if MC_VER < MC_1_21_6 #if MC_VER < MC_1_21_6
// rendered in MixinLevelRenderer // rendered in MixinLevelRenderer
#else #else
ClientApi.INSTANCE.renderDeferredLodsForShaders(ClientLevelWrapper.getWrapper(renderContext.world()), ClientApi.INSTANCE.renderDeferredLodsForShaders();
ClientApi.RENDER_STATE.mcModelViewMatrix,
ClientApi.RENDER_STATE.mcProjectionMatrix,
ClientApi.RENDER_STATE.frameTime
);
#endif #endif
this.clientApi.renderFade( this.clientApi.renderFadeTransparent();
modelViewMatrix,
projectionMatrix,
#if MC_VER < MC_1_21_1
renderContext.tickDelta(),
#else
renderContext.tickCounter().getGameTimeDeltaTicks(),
#endif
ClientLevelWrapper.getWrapper(renderContext.world())
);
}); });
#endif
// Debug keyboard event // Debug keyboard event
@@ -355,18 +341,18 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// Check all keys we need // Check all keys we need
for (int keyCode = GLFW.GLFW_KEY_A; keyCode <= GLFW.GLFW_KEY_Z; keyCode++) for (int keyCode = GLFW.GLFW_KEY_A; keyCode <= GLFW.GLFW_KEY_Z; keyCode++)
{ {
if (InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), keyCode)) //if (InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), keyCode))
{ //{
currentKeyDown.add(keyCode); // currentKeyDown.add(keyCode);
} //}
} }
for (int keyCode : KEY_TO_CHECK_FOR) for (int keyCode : KEY_TO_CHECK_FOR)
{ {
if (InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), keyCode)) //if (InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), keyCode))
{ //{
currentKeyDown.add(keyCode); // currentKeyDown.add(keyCode);
} //}
} }
// Diff and trigger events // Diff and trigger events
@@ -20,13 +20,12 @@
package com.seibel.distanthorizons.fabric; package com.seibel.distanthorizons.fabric;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.NativeDialogUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.*; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.*;
@@ -38,10 +37,8 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
#if MC_VER >= MC_1_19_2 #if MC_VER >= MC_1_19_2
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
@@ -49,8 +46,12 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
#endif #endif
import javax.swing.*; #if MC_VER <= MC_1_21_10
import java.awt.*; import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
#endif
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@@ -60,22 +61,27 @@ import java.util.function.Consumer;
*/ */
public class FabricMain extends AbstractModInitializer implements ClientModInitializer, DedicatedServerModInitializer public class FabricMain extends AbstractModInitializer implements ClientModInitializer, DedicatedServerModInitializer
{ {
#if MC_VER >= MC_1_21_1 #if MC_VER <= MC_1_20_6
private static final ResourceLocation INITIAL_PHASE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH);
#elif MC_VER <= MC_1_21_10
private static final ResourceLocation INITIAL_PHASE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH); private static final ResourceLocation INITIAL_PHASE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH);
#else #else
private static final ResourceLocation INITIAL_PHASE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH); private static final Identifier INITIAL_PHASE = Identifier.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH);
#endif #endif
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@Override @Override
protected void createInitialBindings() protected void createInitialSharedBindings()
{ {
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE); SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new FabricPluginPacketSender()); SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new FabricPluginPacketSender());
} }
@Override
protected void createInitialClientBindings() { /* no additional setup needed currently */ }
@Override @Override
protected IEventProxy createClientProxy() { return new FabricClientProxy(); } protected IEventProxy createClientProxy() { return new FabricClientProxy(); }
@@ -97,7 +103,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
String indiumMissingMessage = ModInfo.READABLE_NAME + " needs Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium"; String indiumMissingMessage = ModInfo.READABLE_NAME + " needs Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium";
LOGGER.fatal(indiumMissingMessage); LOGGER.fatal(indiumMissingMessage);
TinyFileDialogs.tinyfd_messageBox(ModInfo.READABLE_NAME, indiumMissingMessage, "ok", "error", false); NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, indiumMissingMessage, "ok", "error");
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
String errorMessage = "loading Distant Horizons. Distant Horizons requires Indium in order to run with Sodium."; String errorMessage = "loading Distant Horizons. Distant Horizons requires Indium in order to run with Sodium.";
@@ -109,6 +115,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
this.tryCreateModCompatAccessor("starlight", IStarlightAccessor.class, StarlightAccessor::new); this.tryCreateModCompatAccessor("starlight", IStarlightAccessor.class, StarlightAccessor::new);
this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new); this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new);
this.tryCreateModCompatAccessor("bclib", IBCLibAccessor.class, BCLibAccessor::new); this.tryCreateModCompatAccessor("bclib", IBCLibAccessor.class, BCLibAccessor::new);
this.tryCreateModCompatAccessor("c2me", IC2meAccessor.class, C2meAccessor::new);
#if MC_VER >= MC_1_19_4 #if MC_VER >= MC_1_19_4
// 1.19.4 is the lowest version Iris supports DH // 1.19.4 is the lowest version Iris supports DH
this.tryCreateModCompatAccessor("iris", IIrisAccessor.class, IrisAccessor::new); this.tryCreateModCompatAccessor("iris", IIrisAccessor.class, IrisAccessor::new);
@@ -127,7 +134,8 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
} }
@Override @Override
protected void subscribeClientStartedEvent(Runnable eventHandler) { ClientLifecycleEvents.CLIENT_STARTED.register((mc) -> eventHandler.run()); } protected void subscribeClientStartedEvent(Runnable eventHandler)
{ ClientLifecycleEvents.CLIENT_STARTED.register((mc) -> eventHandler.run()); }
@Override @Override
protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler) protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler)
@@ -152,11 +160,6 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false); ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false);
} }
#endif #endif
if (ConfigBase.INSTANCE == null)
{
throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method.");
}
} }
} }
@@ -1,5 +1,7 @@
package com.seibel.distanthorizons.fabric; package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkProcessingEvent;
import com.seibel.distanthorizons.api.methods.events.DhApiEventRegister; import com.seibel.distanthorizons.api.methods.events.DhApiEventRegister;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
@@ -7,7 +9,6 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper; import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; 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.api.internal.ServerApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender; import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
@@ -15,6 +16,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.fabric.testing.TestChunkInputReplacerEvent;
import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent; import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent;
import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents; import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
@@ -28,7 +30,7 @@ import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
#if MC_VER >= MC_1_20_6 #if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.common.CommonPacketPayload; import com.seibel.distanthorizons.common.CommonPacketPayload;
@@ -37,8 +39,6 @@ import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage; import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
#endif #endif
import java.util.function.Supplier;
/** /**
* This handles all events sent to the server, * This handles all events sent to the server,
* and is the starting point for most of the mod. * and is the starting point for most of the mod.
@@ -52,7 +52,7 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
private static final ServerApi SERVER_API = ServerApi.INSTANCE; private static final ServerApi SERVER_API = ServerApi.INSTANCE;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class); private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private final boolean isDedicatedServer; private final boolean isDedicatedServer;
@@ -93,14 +93,11 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
/* Register the mod needed event callbacks */ /* Register the mod needed event callbacks */
// ServerTickEvent // can be enabled to test overrides/events without having to build a separate API project
ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent());
// can be enabled to test world gen overrides without having to build a separate API project
if (false) if (false)
{ {
DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent()); DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent());
DhApi.events.bind(DhApiChunkProcessingEvent.class, new TestChunkInputReplacerEvent());
} }
@@ -0,0 +1,82 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.fabric.mixins.client;
#if MC_VER < MC_1_21_9
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(Entity.class)
public class MixinChunkSectionsToRender
{ /* rendering before was handled via Fabric API events */ }
#else
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.chunk.ChunkSectionLayerGroup;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#if MC_VER <= MC_1_21_10
#else
import com.mojang.blaze3d.textures.GpuSampler;
#endif
@Mixin(ChunkSectionsToRender.class)
public class MixinChunkSectionsToRender
{
#if MC_VER <= MC_1_21_10
// needs to fire at HEAD with a lower than normal order (less than 1000)
// otherwise it will be canceled by Sodium
@Inject(at = @At("HEAD"), method = "renderGroup", order = 800)
private void renderDeferredLayer(ChunkSectionLayerGroup chunkSectionLayerGroup, CallbackInfo ci)
#else
// needs to fire at HEAD with a lower than normal order (less than 1000)
// otherwise it will be canceled by Sodium
@Inject(at = @At("HEAD"), method = "renderGroup", order = 800)
private void renderDeferredLayer(ChunkSectionLayerGroup chunkSectionLayerGroup, GpuSampler gpuSampler, CallbackInfo ci)
#endif
{
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, Minecraft.getInstance().levelRenderer.level);
if (chunkSectionLayerGroup == ChunkSectionLayerGroup.TRANSLUCENT)
{
ClientApi.INSTANCE.renderFadeTransparent();
ClientApi.INSTANCE.renderDeferredLodsForShaders();
}
else if (chunkSectionLayerGroup == ChunkSectionLayerGroup.TRIPWIRE)
{
ClientApi.INSTANCE.renderFadeOpaque();
}
}
}
#endif
@@ -3,8 +3,10 @@ package com.seibel.distanthorizons.fabric.mixins.client;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -16,6 +18,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import java.util.concurrent.AbstractExecutorService;
#endif #endif
@Mixin(ClientPacketListener.class) @Mixin(ClientPacketListener.class)
@@ -45,8 +49,24 @@ public class MixinClientPacketListener
@Inject(method = "enableChunkLight", at = @At("TAIL")) @Inject(method = "enableChunkLight", at = @At("TAIL"))
void onEnableChunkLight(LevelChunk chunk, int x, int z, CallbackInfo ci) void onEnableChunkLight(LevelChunk chunk, int x, int z, CallbackInfo ci)
{ {
IClientLevelWrapper clientLevel = ClientLevelWrapper.getWrapper((ClientLevel) chunk.getLevel()); if (chunk == null)
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, clientLevel), clientLevel); {
return;
}
// executor to prevent locking up the render thread
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor == null)
{
return;
}
executor.execute(() ->
{
IClientLevelWrapper clientLevel = ClientLevelWrapper.getWrapper((ClientLevel) this.level);
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, clientLevel), clientLevel);
});
} }
#endif #endif
@@ -12,12 +12,15 @@ import java.util.List;
@Mixin(DebugScreenOverlay.class) @Mixin(DebugScreenOverlay.class)
public class MixinDebugScreenOverlay public class MixinDebugScreenOverlay
{ {
#if MC_VER < MC_1_21_9
@Inject(method = "getSystemInformation", at = @At("RETURN")) @Inject(method = "getSystemInformation", at = @At("RETURN"))
private void addCustomF3(CallbackInfoReturnable<List<String>> cir) private void addCustomF3(CallbackInfoReturnable<List<String>> cir)
{ {
List<String> messages = cir.getReturnValue(); List<String> messages = cir.getReturnValue();
F3Screen.addStringToDisplay(messages); F3Screen.addStringToDisplay(messages);
} }
#else
// handled by DhDebugScreenEntry for MC versions after 1.21.10
#endif
} }
@@ -160,9 +160,12 @@ public class MixinFogRenderer
{ {
#if MC_VER < MC_1_21_6 #if MC_VER < MC_1_21_6
Entity entity = camera.getEntity(); Entity entity = camera.getEntity();
#else #elif MC_VER <= MC_1_21_10
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.getEntity(); Entity entity = camera.getEntity();
#else
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.entity();
#endif #endif
@@ -30,9 +30,34 @@ import net.minecraft.client.renderer.RenderType;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#else #elif MC_VER < MC_1_21_9
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.framegraph.FrameGraphBuilder;
import com.mojang.blaze3d.resource.GraphicsResourceAllocator;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender; import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.util.profiling.ProfilerFiller;
import org.joml.Matrix4f;
import org.joml.Matrix4fc; import org.joml.Matrix4fc;
import org.joml.Vector4f;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
#else
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.framegraph.FrameGraphBuilder;
import com.mojang.blaze3d.resource.GraphicsResourceAllocator;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.state.LevelRenderState;
import net.minecraft.util.profiling.ProfilerFiller;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Vector4f;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
#endif #endif
@@ -41,11 +66,9 @@ import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.fabric.FabricClientProxy;
import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import com.seibel.distanthorizons.core.config.Config;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -54,7 +77,8 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
@Mixin(LevelRenderer.class) @Mixin(LevelRenderer.class)
@@ -64,7 +88,7 @@ public class MixinLevelRenderer
private ClientLevel level; private ClientLevel level;
@Unique @Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
@@ -92,9 +116,16 @@ public class MixinLevelRenderer
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;DDDLorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V", method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;DDDLorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V",
cancellable = true) cancellable = true)
private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f projectionMatrix, Matrix4f frustumMatrix, CallbackInfo callback) private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f projectionMatrix, Matrix4f frustumMatrix, CallbackInfo callback)
#else #elif MC_VER < MC_1_21_9
@Inject(at = @At("HEAD"), method = "prepareChunkRenders", cancellable = true) @Inject(at = @At("HEAD"), method = "prepareChunkRenders", cancellable = true)
private void prepareChunkRenders(Matrix4fc projectionMatrix, double d, double e, double f, CallbackInfoReturnable<ChunkSectionsToRender> callback) private void prepareChunkRenders(Matrix4fc projectionMatrix, double d, double e, double f, CallbackInfoReturnable<ChunkSectionsToRender> callback)
#else
@Inject(at = @At("HEAD"), method = "renderLevel")
private void renderLevel(
GraphicsResourceAllocator resourceAllocator, DeltaTracker deltaTracker,
boolean renderBlockOutline, Camera camera,
Matrix4f positionMatrix, Matrix4f projectionMatrix, Matrix4f idkMatrix, GpuBufferSlice gpuBufferSlice,
Vector4f skyColor, boolean thinFog, CallbackInfo callback)
#endif #endif
{ {
#if MC_VER == MC_1_16_5 #if MC_VER == MC_1_16_5
@@ -110,11 +141,14 @@ public class MixinLevelRenderer
// get the matrices directly from MC // get the matrices directly from MC
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose()); ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix); ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#else #elif MC_VER < MC_1_21_9
// MC combined the model view and projection matricies // MC combined the model view and projection matricies
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(projectionMatrix); ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(projectionMatrix);
ClientApi.RENDER_STATE.mcProjectionMatrix = new Mat4f(); ClientApi.RENDER_STATE.mcProjectionMatrix = new Mat4f();
ClientApi.RENDER_STATE.mcProjectionMatrix.setIdentity(); ClientApi.RENDER_STATE.mcProjectionMatrix.setIdentity();
#else
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(positionMatrix);
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#endif #endif
// TODO move this into a common place // TODO move this into a common place
@@ -126,34 +160,45 @@ public class MixinLevelRenderer
ClientApi.RENDER_STATE.frameTime = Minecraft.getInstance().deltaTracker.getRealtimeDeltaTicks(); ClientApi.RENDER_STATE.frameTime = Minecraft.getInstance().deltaTracker.getRealtimeDeltaTicks();
#endif #endif
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, this.level);
//LOGGER.info("\n\n" +
// "Level Mixin\n" +
// "Mc MVM: \n" + mcModelViewMatrix.toString() + "\n" +
// "Mc Proj: \n" + mcProjectionMatrix.toString()
//);
#if MC_VER < MC_1_21_6 #if MC_VER < MC_1_21_6
if (renderType.equals(RenderType.translucent())) if (renderType.equals(RenderType.translucent()))
{ {
ClientApi.INSTANCE.renderDeferredLodsForShaders(ClientLevelWrapper.getWrapper(this.level), ClientApi.INSTANCE.renderDeferredLodsForShaders();
ClientApi.RENDER_STATE.mcModelViewMatrix,
ClientApi.RENDER_STATE.mcProjectionMatrix,
ClientApi.RENDER_STATE.frameTime
);
} }
#elif MC_VER < MC_1_21_9
// rendering handled via Fabric Api render event
#else #else
// rendering handled via Fabric Api render event // handled here and in MixinChunkSectionsToRender
#endif #endif
// FIXME completely disables rendering when sodium is installed
if (Config.Client.Advanced.Debugging.lodOnlyMode.get())
{
callback.cancel();
}
} }
#if MC_VER < MC_1_21_9
// rendering handled via Fabric Api render event
#else
@Inject(at = @At("HEAD"), method = "prepareChunkRenders")
private void prepareChunkRenders(Matrix4fc modelViewMatrix, double d, double e, double f, CallbackInfoReturnable<ChunkSectionsToRender> callback)
{
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrix);
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, this.level);
// only crash during development
if (ModInfo.IS_DEV_BUILD)
{
ClientApi.RENDER_STATE.canRenderOrThrow();
}
ClientApi.INSTANCE.renderLods();
}
#endif
} }
@@ -22,12 +22,14 @@ package com.seibel.distanthorizons.fabric.mixins.client;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.LightTexture;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; 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.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -60,27 +62,40 @@ public class MixinLightTexture
private GpuTexture texture; private GpuTexture texture;
#endif #endif
@Unique
private MinecraftRenderWrapper renderWrapper = null;
@Inject(method = "updateLightTexture(F)V", at = @At("RETURN")) @Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci) public void updateLightTexture(float partialTicks, CallbackInfo ci)
{ {
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (mc == null || mc.getWrappedClientLevel() == null) if (mc == null)
{ {
return; return;
} }
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel(); IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
if (clientLevel == null)
{
return;
}
// lazy initialization to make sure we don't call this too early
if (this.renderWrapper == null)
{
this.renderWrapper = (MinecraftRenderWrapper)SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
}
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel); this.renderWrapper.updateLightmap(this.lightPixels, clientLevel);
#elif MC_VER < MC_1_21_5 #elif MC_VER < MC_1_21_5
MinecraftRenderWrapper.INSTANCE.setLightmapId(this.target.getColorTextureId(), clientLevel); this.renderWrapper.setLightmapId(this.target.getColorTextureId(), clientLevel);
#else #else
GlTexture glTexture = (GlTexture) this.texture; GlTexture glTexture = (GlTexture) this.texture;
MinecraftRenderWrapper.INSTANCE.setLightmapId(glTexture.glId(), clientLevel); this.renderWrapper.setLightmapId(glTexture.glId(), clientLevel);
#endif #endif
} }
@@ -16,7 +16,7 @@ import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -34,7 +34,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class MixinMinecraft public abstract class MixinMinecraft
{ {
@Unique @Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MixinMinecraft.class.getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@Shadow @Shadow
@@ -28,7 +28,6 @@ import net.minecraft.network.chat.Component;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.network.chat.TranslatableComponent;
#endif #endif
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -51,6 +50,11 @@ import net.minecraft.client.gui.screens.OptionsScreen;
import net.minecraft.client.gui.screens.options.OptionsScreen; import net.minecraft.client.gui.screens.options.OptionsScreen;
#endif #endif
#if MC_VER <= MC_1_21_10
import net.minecraft.resources.ResourceLocation;
#else
import net.minecraft.resources.Identifier;
#endif
/** /**
* Adds a button to the menu to goto the config * Adds a button to the menu to goto the config
@@ -62,13 +66,16 @@ import net.minecraft.client.gui.screens.options.OptionsScreen;
public class MixinOptionsScreen extends Screen public class MixinOptionsScreen extends Screen
{ {
/** Texture used for the config opening button */ /** Texture used for the config opening button */
#if MC_VER <= MC_1_20_6
@Unique @Unique
private static final ResourceLocation ICON_TEXTURE = private static final ResourceLocation ICON_TEXTURE = new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
#if MC_VER < MC_1_21_1 #elif MC_VER <= MC_1_21_10
new ResourceLocation(ModInfo.ID, "textures/gui/button.png"); @Unique
#else private static final ResourceLocation ICON_TEXTURE = ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png");
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png"); #else
#endif @Unique
private static final Identifier ICON_TEXTURE = Identifier.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png");
#endif
@Unique @Unique
@@ -13,8 +13,7 @@ public class MixinLevelTicks<T>
#else #else
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import net.minecraft.world.ticks.LevelTicks; import net.minecraft.world.ticks.LevelTicks;
import net.minecraft.world.ticks.ScheduledTick; import net.minecraft.world.ticks.ScheduledTick;
@@ -26,18 +25,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LevelTicks.class) // available in 1.18.2+, but only needed in 1.21.4+ @Mixin(LevelTicks.class) // available in 1.18.2+, but only needed in 1.21.4+
public class MixinLevelTicks<T> public class MixinLevelTicks<T>
{ {
// TODO put in a common location
private static boolean isWorldGenThread()
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
@Inject(method = "schedule", at = @At(value = "HEAD"), cancellable = true) @Inject(method = "schedule", at = @At(value = "HEAD"), cancellable = true)
private void onChunkSave(ScheduledTick<T> tick, CallbackInfo ci) private void onChunkSave(ScheduledTick<T> tick, CallbackInfo ci)
{ {
// In MC 1.21.4 an error check was added to log attempting to schedule ticks for unloaded chunks // In MC 1.21.4 an error check was added to log attempting to schedule ticks for unloaded chunks
// this caused a lot of unnecessary errors when generating sand (FallingBlock.class). // this caused a lot of unnecessary errors when generating sand (FallingBlock.class).
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
ci.cancel(); ci.cancel();
} }
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.fabric.mixins.server; package com.seibel.distanthorizons.fabric.mixins.server;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
@@ -35,10 +36,8 @@ public class MixinTracingExecutor
} }
#else #else
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService; import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService;
import net.minecraft.TracingExecutor; import net.minecraft.TracingExecutor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -55,16 +54,11 @@ import java.util.concurrent.Executor;
@Mixin(TracingExecutor.class) @Mixin(TracingExecutor.class)
public class MixinTracingExecutor public class MixinTracingExecutor
{ {
// TODO put in a common location
private static boolean isWorldGenThread()
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
// replaced with TracingExecutor in MC 1.21.3+ // replaced with TracingExecutor in MC 1.21.3+
@Inject(method = "forName(Ljava/lang/String;)Ljava/util/concurrent/Executor;", at = @At("HEAD"), cancellable = true) @Inject(method = "forName(Ljava/lang/String;)Ljava/util/concurrent/Executor;", at = @At("HEAD"), cancellable = true)
private void forName(String executorName, CallbackInfoReturnable<Executor> ci) private void forName(String executorName, CallbackInfoReturnable<Executor> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
// run this task on the current DH thread instead of a new MC thread // run this task on the current DH thread instead of a new MC thread
ci.setReturnValue(new RunOnThisThreadExecutorService()); ci.setReturnValue(new RunOnThisThreadExecutorService());
@@ -19,19 +19,23 @@
package com.seibel.distanthorizons.fabric.mixins.server; package com.seibel.distanthorizons.fabric.mixins.server;
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService; import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
#if MC_VER <= MC_1_21_10
import net.minecraft.Util;
#else
import net.minecraft.util.Util;
#endif
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.Util;
import java.util.concurrent.ExecutorService;
#if MC_VER < MC_1_16_5 #if MC_VER < MC_1_16_5
#elif MC_VER < MC_1_21_3 #elif MC_VER < MC_1_21_3
import java.util.concurrent.ExecutorService;
import java.util.function.Supplier; import java.util.function.Supplier;
#else #else
#endif #endif
@@ -46,15 +50,11 @@ import java.util.function.Supplier;
@Mixin(Util.class) @Mixin(Util.class)
public class MixinUtilBackgroundThread public class MixinUtilBackgroundThread
{ {
private static boolean isWorldGenThread()
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
@Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true) @Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true)
private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable<ExecutorService> ci) private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable<ExecutorService> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
// run this task on the current DH thread instead of a new MC thread // run this task on the current DH thread instead of a new MC thread
ci.setReturnValue(new RunOnThisThreadExecutorService()); ci.setReturnValue(new RunOnThisThreadExecutorService());
@@ -70,7 +70,7 @@ public class MixinUtilBackgroundThread
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci) private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
//ApiShared.LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered"); //ApiShared.LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered");
ci.setReturnValue(r); ci.setReturnValue(r);
@@ -86,7 +86,7 @@ public class MixinUtilBackgroundThread
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci) private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
//ApiShared.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered"); //ApiShared.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered");
ci.setReturnValue(r); ci.setReturnValue(r);
@@ -0,0 +1,64 @@
package com.seibel.distanthorizons.fabric.testing;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkProcessingEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.io.IOException;
public class TestChunkInputReplacerEvent extends DhApiChunkProcessingEvent
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final String REPLACEMENT_BLOCK_STATE_NAMESPACE = "minecraft:stone";
private IDhApiBlockStateWrapper stoneBlockWrapper = null;
private boolean initialBlockSetupComplete = false;
@Override
public void blockOrBiomeChangedDuringChunkProcessing(DhApiEventParam<EventParam> event)
{
if (!this.initialBlockSetupComplete)
{
// this method can be called on multiple threads
synchronized (this)
{
this.initialBlockSetupComplete = true;
try
{
this.stoneBlockWrapper = DhApi.Delayed.wrapperFactory.getDefaultBlockStateWrapper(REPLACEMENT_BLOCK_STATE_NAMESPACE, event.value.levelWrapper);
}
catch (IOException e)
{
LOGGER.error("Unable to get ["+REPLACEMENT_BLOCK_STATE_NAMESPACE+"] block replacement cannot continue and is now disabled, error: ["+e.getMessage()+"].", e);
DhApi.events.unbind(DhApiChunkProcessingEvent.class, this.getClass());
}
}
}
// will happen if the initial setup fails until the unbind call is processed
// which likely won't happen until the current chunk has finished processing
if (this.stoneBlockWrapper == null)
{
return;
}
// replace any dirt or grass block with stone
IDhApiBlockStateWrapper block = event.value.currentBlock;
if (block.getSerialString().contains("grass_block")
|| block.getSerialString().contains("dirt"))
{
event.value.setBlockOverride(this.stoneBlockWrapper);
}
}
}
@@ -10,11 +10,9 @@ import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.objects.data.DhApiChunk; import com.seibel.distanthorizons.api.objects.data.DhApiChunk;
import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint; import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList; import java.util.ArrayList;
@@ -13,7 +13,7 @@ import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -23,7 +23,7 @@ import java.util.function.Consumer;
public class TestGenericWorldGenerator implements IDhApiWorldGenerator public class TestGenericWorldGenerator implements IDhApiWorldGenerator
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private final IDhApiLevelWrapper levelWrapper; private final IDhApiLevelWrapper levelWrapper;
@@ -6,12 +6,12 @@ import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLo
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
// TODO add to API example once Builderb0y has given the all-clear // TODO add to API example once Builderb0y has given the all-clear
public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@Override @Override
public void onLevelLoad(DhApiEventParam<DhApiLevelLoadEvent.EventParam> event) public void onLevelLoad(DhApiEventParam<DhApiLevelLoadEvent.EventParam> event)
@@ -23,9 +23,7 @@ import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi; import com.terraformersmc.modmenu.api.ModMenuApi;
/** /** For making the config show up in modmenu */
* For making the config show up in modmenu
*/
public class ModMenuIntegration implements ModMenuApi public class ModMenuIntegration implements ModMenuApi
{ {
// For the custom config code // For the custom config code
@@ -17,20 +17,16 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.seibel.distanthorizons.common.wrappers; package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
import java.util.function.Supplier; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
public class DependencySetupDoneCheck public class C2meAccessor implements IC2meAccessor
{ {
// TODO move to DependencySetup @Override
public static boolean isDone = false; public String getModName()
/** {
* This is used so we can override some MC logic when running return "c2me";
* in DH's world generator. }
* Specifically so we can redirect threads to run on DH threads instead
* of MC threads.
*/
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; });
} }
@@ -34,22 +34,14 @@ import net.irisshaders.iris.api.v0.IrisApi;
public class IrisAccessor implements IIrisAccessor public class IrisAccessor implements IIrisAccessor
{ {
@Override @Override
public String getModName() public String getModName() { return Iris.MODID; }
{
return Iris.MODID;
}
@Override @Override
public boolean isShaderPackInUse() public boolean isShaderPackInUse() { return IrisApi.getInstance().isShaderPackInUse(); }
{
return IrisApi.getInstance().isShaderPackInUse();
}
@Override @Override
public boolean isRenderingShadowPass() public boolean isRenderingShadowPass() { return IrisApi.getInstance().isRenderingShadowPass(); }
{
return IrisApi.getInstance().isRenderingShadowPass();
}
} }
#endif #endif
@@ -28,9 +28,6 @@ public class OptifineAccessor extends AbstractOptifineAccessor
{ {
@Override @Override
public String getModName() public String getModName() { return "Optifine-Fabric"; }
{
return "Optifine-Fabric-1.18.X";
}
} }
@@ -26,6 +26,7 @@ import java.lang.invoke.MethodType;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import net.minecraft.client.Minecraft;
#if MC_VER < MC_1_20_1 #if MC_VER < MC_1_20_1
@@ -50,9 +51,11 @@ public class SodiumAccessor implements ISodiumAccessor
*/ */
public static final boolean isSodiumV5OrLess; public static final boolean isSodiumV5OrLess;
#if MC_VER >= MC_1_20_1 #if MC_VER <= MC_1_19_4
#elif MC_VER <= MC_1_21_10
private static MethodHandle setFogOcclusionMethod; private static MethodHandle setFogOcclusionMethod;
private static Object sodiumPerformanceOptions; private static Object sodiumPerformanceOptions;
#else
#endif #endif
static { static {
@@ -78,7 +81,8 @@ public class SodiumAccessor implements ISodiumAccessor
@Override @Override
public void setFogOcclusion(boolean occlusionEnabled) public void setFogOcclusion(boolean occlusionEnabled)
{ {
#if MC_VER >= MC_1_20_1 #if MC_VER <= MC_1_19_4
#elif MC_VER <= MC_1_21_10
try try
{ {
if (sodiumPerformanceOptions == null) if (sodiumPerformanceOptions == null)
@@ -122,6 +126,8 @@ public class SodiumAccessor implements ISodiumAccessor
{ {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
#else
// in newer versions of Sodium this doesn't appear to be an issue so it can probably just be ignored
#endif #endif
} }
@@ -17,6 +17,7 @@
"client.MixinDebugScreenOverlay", "client.MixinDebugScreenOverlay",
"client.MixinFogRenderer", "client.MixinFogRenderer",
"client.MixinLevelRenderer", "client.MixinLevelRenderer",
"client.MixinChunkSectionsToRender",
"client.MixinLightTexture", "client.MixinLightTexture",
"client.MixinMinecraft", "client.MixinMinecraft",
"client.MixinOptionsScreen", "client.MixinOptionsScreen",
@@ -55,7 +55,7 @@ import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@@ -81,7 +81,7 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
{ {
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final ForgePluginPacketSender PACKET_SENDER = (ForgePluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class); private static final ForgePluginPacketSender PACKET_SENDER = (ForgePluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
@@ -107,21 +107,6 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
//=============//
// tick events //
//=============//
@SubscribeEvent
public void clientTickEvent(TickEvent.ClientTickEvent event)
{
if (event.phase == TickEvent.Phase.START)
{
ClientApi.INSTANCE.clientTickEvent();
}
}
//==============// //==============//
// world events // // world events //
//==============// //==============//
@@ -22,17 +22,16 @@ package com.seibel.distanthorizons.forge;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen; import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.forge.wrappers.modAccessor.ModChecker; import com.seibel.distanthorizons.forge.wrappers.modAccessor.ModChecker;
import com.seibel.distanthorizons.forge.wrappers.modAccessor.OptifineAccessor; import com.seibel.distanthorizons.forge.wrappers.modAccessor.OptifineAccessor;
import com.seibel.distanthorizons.forge.wrappers.modAccessor.OculusAccessor;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
@@ -86,11 +85,13 @@ public class ForgeMain extends AbstractModInitializer
} }
@Override @Override
protected void createInitialBindings() protected void createInitialSharedBindings()
{ {
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE); SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new ForgePluginPacketSender()); SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new ForgePluginPacketSender());
} }
@Override
protected void createInitialClientBindings() { /* no additional setup needed currently */ }
@Override @Override
protected IEventProxy createClientProxy() { return new ForgeClientProxy(); } protected IEventProxy createClientProxy() { return new ForgeClientProxy(); }
@@ -102,6 +103,7 @@ public class ForgeMain extends AbstractModInitializer
protected void initializeModCompat() protected void initializeModCompat()
{ {
this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new); this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new);
this.tryCreateModCompatAccessor("oculus", IIrisAccessor.class, OculusAccessor::new);
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.CONFIGGUIFACTORY, ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.CONFIGGUIFACTORY,
@@ -118,14 +120,14 @@ public class ForgeMain extends AbstractModInitializer
@Override @Override
protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler) protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler)
{ { MinecraftForge.EVENT_BUS.addListener((RegisterCommandsEvent e) -> { eventHandler.accept(e.getDispatcher()); }); }
MinecraftForge.EVENT_BUS.addListener((RegisterCommandsEvent e) -> { eventHandler.accept(e.getDispatcher()); });
}
@Override @Override
protected void subscribeClientStartedEvent(Runnable eventHandler) protected void subscribeClientStartedEvent(Runnable eventHandler)
{ {
// FIXME What event is this? // Just run the event handler, since there are no proper ClientLifecycleEvent for the client
// to signify readiness other than FmlClientSetupEvent
eventHandler.run();
} }
@Override @Override
@@ -47,7 +47,7 @@ import net.minecraftforge.event.server.ServerStoppingEvent;
#endif #endif
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -90,16 +90,6 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
// events // // events //
//========// //========//
// ServerTickEvent (at end)
@SubscribeEvent
public void serverTickEvent(TickEvent.ServerTickEvent event)
{
if (event.phase == TickEvent.Phase.END)
{
this.serverApi.serverTickEvent();
}
}
// ServerWorldLoadEvent // ServerWorldLoadEvent
@SubscribeEvent @SubscribeEvent
public void dedicatedWorldLoadEvent(#if MC_VER == MC_1_16_5 || MC_VER == 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)
@@ -67,9 +67,11 @@ public class MixinFogRenderer
Entity entity = camera.getEntity(); Entity entity = camera.getEntity();
boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS); boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
if (!isSpecialFog && cameraNotInFluid && fogMode == FogMode.FOG_TERRAIN if (!isSpecialFog
&& !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial() && cameraNotInFluid
&& Config.Client.Advanced.Graphics.Fog.disableVanillaFog.get()) && fogMode == FogMode.FOG_TERRAIN
&& !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial()
&& !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get())
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE); RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE);

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