Compare commits

..

698 Commits

Author SHA1 Message Date
James Seibel 1787d2c6d9 Fix BlockStateWrapper compiling 2023-12-03 18:38:17 -06:00
James Seibel 1a07fb83b6 Revert Sqlite update 3.44.1.0 -> 3.43.0.0
The new version caused java module issues. I'll have to look into it again another time.
2023-12-03 17:55:01 -06:00
James Seibel 4d2ee292bb Remove "-dev" from the version number 2023-12-03 17:19:30 -06:00
James Seibel 0fdde61fe5 Add a config to enable/disable using MC's lighting engine 2023-12-02 12:37:30 -06:00
James Seibel ae8a4912a6 Add a limit to the number of queued update chunks 2023-12-02 12:24:22 -06:00
James Seibel ffd8ea8751 Fix snow light rendering 2023-12-02 12:07:35 -06:00
James Seibel 4cd10a82fd Fix DH lighting failing for water 2023-12-01 07:50:14 -06:00
James Seibel 4955d22649 Fix block lighting being ignored in MC 1.20.2 2023-12-01 07:49:36 -06:00
James Seibel 425b761f8e Fix pack.mcmeta typo 2023-11-30 07:37:13 -06:00
James Seibel ab3bfd457f Merge branch 'Lorenzo_Giannini-main-patch-20902' into 'main'
multiversion vanilla support

See merge request jeseibel/distant-horizons!43
2023-11-30 13:30:46 +00:00
James Seibel 0faa64112a Refactor, comment, and rename 2023-11-30 07:25:57 -06:00
James Seibel 46519b096c Merge branch 'distant-horizons-lightmapfix' 2023-11-30 07:08:46 -06:00
James Seibel cb7d980e15 Fix a typo in the bug issue template 2023-11-30 07:03:15 -06:00
James Seibel 6c1562ac33 Merge Null's fragment shader culling !38 2023-11-29 07:44:27 -06:00
James Seibel 5e17e4ea8c Update Sqlite 3.43.0.0 -> =3.44.1.0 2023-11-28 07:13:44 -06:00
Fourmisain b8e7b14fbb fix light map update being done on non-light-map textures 2023-11-27 16:58:54 +01:00
James Seibel 5d8eb185bc Merge branch 'Lorenzo_Giannini-main-patch-41483' into 'main'
fix links

See merge request jeseibel/distant-horizons!42
2023-11-25 21:08:43 +00:00
Lorenzo Giannini 7325cedba6 Update pack.mcmeta 2023-11-25 18:31:10 +00:00
Lorenzo Giannini e5043d6d9b fix links 2023-11-25 18:21:21 +00:00
James Seibel 13e53a18e3 Change the default MC version from 1.20.1 -> 1.20.2 2023-11-25 12:13:18 -06:00
James Seibel dd60c7620f Fix null pointer when joining some multiverse servers 2023-11-25 11:39:53 -06:00
James Seibel 83c01cabfb Fix lighting on some servers 2023-11-25 11:08:44 -06:00
James Seibel a95171dbbe Add constants to BlockStateWrapper 2023-11-25 08:46:03 -06:00
James Seibel 10d542ed14 Fix incorrect ChunkWrapper IndexOutOfBounds checking 2023-11-25 08:45:23 -06:00
James Seibel 7e1c55a0c5 Fix merging for transparent LODs over the void 2023-11-24 14:02:33 -06:00
James Seibel 4e25d318ec Fix a potential concurrent error on DhClientServerWorld shutdown 2023-11-24 09:46:51 -06:00
James Seibel c4228f4e63 Improve multiverse similarity logic and fix incorrect log 2023-11-21 07:44:28 -06:00
James Seibel 1411091f60 Fix null pointers when moving between multiverse levels 2023-11-20 07:35:26 -06:00
coolGi 6a2278949e Updated core submodule 2023-11-20 21:59:12 +10:30
coolGi 374b859882 Fixed Indium dialog not showing 2023-11-20 21:47:35 +10:30
coolGi a98bdb94b8 Fixed <br/> not being parsed correctly 2023-11-20 00:40:16 +10:30
coolGi de390f5d70 Fixed updater's back up method deleting the wrong jar 2023-11-19 22:48:46 +10:30
coolGi 9d4968351b Fixed updater crashing without network 2023-11-19 20:07:35 +10:30
coolGi e99fbb76bf Added temporary solution to file update from 1.6 2023-11-18 23:52:30 +10:30
coolGi fd175d2f36 Updated core sub-project 2023-11-18 21:45:52 +10:30
coolGi 0db862e42b Added a notification when Indium is not installed (cus people don't read the crash logs!!!) 2023-11-18 21:32:38 +10:30
coolGi 58f5d64f91 Bumped version number to 2.0.1-a-dev 2023-11-18 20:34:22 +10:30
James Seibel bb54a94acd re-add "-dev" to the version number
For future releases the version number should be changed immediately beforehand.
2023-11-16 19:09:37 -06:00
James Seibel e34a7eef06 Remove "-dev" from the version number 2023-11-16 18:54:44 -06:00
James Seibel 3d177369ab Fix Optifine rendering a blank screen until resized 2023-11-14 07:36:49 -06:00
James Seibel 946aff24af Update coreSubProjects 2023-11-10 07:02:58 -06:00
James Seibel 424429bff1 Minor core refactor 2023-11-08 19:42:43 -06:00
coolGi 55df8daa35 Added key detection through the new test screen 2023-11-08 01:26:37 +10:30
James Seibel 734edeaba9 Fix transparent water when transparency is disabled 2023-11-06 07:49:06 -06:00
James Seibel 48452e6fe7 Fix some chunks not updating 2023-11-05 15:54:23 -06:00
coolGi 314c88a452 Merge remote-tracking branch 'origin/main' 2023-11-05 17:19:26 +10:30
coolGi 3a782a1c4b Fixed forge 1.16 & 1.17 2023-11-05 17:19:16 +10:30
James Seibel 924b2b7e8e Update coreSubProjects 2023-11-04 15:42:32 -05:00
James Seibel 34565992ea Only log broken BlockState and Biomes deserializations once 2023-11-04 15:42:16 -05:00
James Seibel dbc84cc0f3 Add invalid fullData ID handling 2023-11-03 19:43:00 -05:00
coolGi 15de27eafd Fixed compilation for 1.16 & 1.17 (missed last time) 2023-11-04 02:30:31 +10:30
coolGi 8a882d7bf4 Fixed compolation on older mc versions 2023-11-03 23:20:26 +10:30
coolGi 04ee9d58bc Equal was notted accidentally 2023-11-03 23:05:59 +10:30
coolGi ee867e972f Fix for when there is a chance of mc being null 2023-11-03 23:02:43 +10:30
James Seibel a227547daa Fix Optifine shaders not rendering 2023-11-02 21:45:12 -05:00
James Seibel ef1699f2e9 re-add CPU option I_PAID_FOR_THE_WHOLE_CPU 2023-11-01 21:14:02 -05:00
James Seibel 108f692c82 Add additional safeguards to RegionFileStorageExternalCache storage.getRegionFile() 2023-11-01 21:11:24 -05:00
James Seibel a3026fbb61 Add a FIXME comment 2023-10-31 20:29:30 -05:00
James Seibel 33c2ca839e Fix incorrect chunk saving for MC 1.16/1.17 2023-10-31 20:21:22 -05:00
James Seibel 4ac4daeac1 Reduce the chance of concurrent Long2ObjectLinkedOpenHashMap issues 2023-10-31 19:56:04 -05:00
James Seibel 49f90aa5c9 Fix memory leak in MC 1.17 and 1.16 from ChunkWrapper 2023-10-31 19:53:59 -05:00
coolGi 6788ac41c5 Fixed building for pre 1.20 2023-10-31 20:22:30 +10:30
coolGi 8e25deb33d Refactor of GitInfo. Removed core commit, and added Build Source 2023-10-31 19:35:10 +10:30
coolGi 3d31d20a46 Refactor of GitInfo. Removed core commit, and added Build Source 2023-10-31 19:35:07 +10:30
James Seibel 7b902a4be1 Fix world gen freezing for certain MC versions 2023-10-30 22:18:24 -05:00
James Seibel fbd48b3cfa fix server shutdown null pointer 2023-10-30 18:43:20 -05:00
coolGi 9475040627 Updated dependancies 2023-10-31 01:04:57 +10:30
coolGi a673a3be4b Merge remote-tracking branch 'origin/main' 2023-10-31 00:34:04 +10:30
coolGi 2d444ab0bf Fixed Alex's Caves compat 2023-10-31 00:31:38 +10:30
James Seibel 95e29f36ef minor ClientBlockStateCache refactor 2023-10-30 07:46:46 -05:00
James Seibel ad97f322b2 Fix aether block tinting without breaking vanilla oceans 2023-10-30 07:46:21 -05:00
James Seibel f19281e832 Update coreSubProjects 2023-10-29 16:10:00 -05:00
James Seibel 3c91e53228 Merge branch 'minecraft-lod-mod-refactor/client-world-close' 2023-10-29 16:09:47 -05:00
James Seibel 3f35909641 Reduce CPU load when moving around the world
Should've made this multiple commits, but too late now

- Create ConfigThreadPool to remove duplicate thread setup/config code
- Move configurable thread pools into their own ThreadPools class
- Add a semaphore to limit how many LOD builder/lighting/bufferBuilder threads can be active at once
2023-10-29 15:04:46 -05:00
James Seibel 384ba30a61 Update coreSubProjects 2023-10-28 12:19:46 -05:00
James Seibel 33b5c225a4 Remove broken chunkSaveEvent, was actually chunkUnloadEvent 2023-10-28 11:53:32 -05:00
James Seibel 1a3560feef comment out GL message logging for MC 1.20.2 2023-10-28 10:23:28 -05:00
James Seibel ae2e45b52f Fix GPU Upload methods and some minor memory leaks 2023-10-28 10:18:59 -05:00
s809 1bb2ef29f5 Move closing of client world to a proper place 2023-10-28 19:43:22 +05:00
James Seibel 59ad956ba1 Fix index out of bounds 2023-10-27 20:01:49 -05:00
James Seibel 4af6ec7428 Fix GLVertexBuffer memory leak 2023-10-27 19:42:21 -05:00
James Seibel 32b59ee8a4 Revert "Fix broken LOD building when the Aether is installed"
This reverts commit 070d7e23d5.
2023-10-27 07:49:31 -05:00
James Seibel 0d5a8dee73 Attempt to reduce memory leaking when flying 2023-10-25 07:52:18 -05:00
James Seibel 91863034e8 Fix Optifine shader rendering 2023-10-24 20:12:56 -05:00
James Seibel f705fee90f Fix ClientBlockStateCache missing import 2023-10-24 20:04:47 -05:00
James Seibel d10a9e08d7 Merge in IMS' GL object changes 2023-10-24 07:31:43 -05:00
James Seibel 070d7e23d5 Fix broken LOD building when the Aether is installed 2023-10-23 20:32:05 -05:00
James Seibel 1b200259c5 Add Sqlite to the readme 2023-10-23 20:29:47 -05:00
James Seibel e21149466e update localization files 2023-10-21 11:26:13 -05:00
James Seibel f6528be87f Fix missing import 2023-10-21 11:17:40 -05:00
James Seibel e8c2271db8 Fix 1.16 compiling and double closing buffers 2023-10-21 11:14:34 -05:00
James Seibel 9f5c63d9e2 Fix LodRenderSection memory leak 2023-10-21 09:34:43 -05:00
James Seibel 539048bfac Fix missing localization for noiseSteps.@tooltip 2023-10-20 20:11:17 -05:00
James Seibel e155e68289 Add a config to change the number of light baking threads 2023-10-20 20:09:21 -05:00
James Seibel 083fab7323 Ops, missed one of the lightmap changes 2023-10-20 19:34:05 -05:00
James Seibel 5450a7efe4 Fix memory leaks when moving between dimensions 2023-10-20 19:24:05 -05:00
James Seibel f6860ed4af Update coreSubProjects 2023-10-19 22:15:42 -05:00
James Seibel 15ed0c2392 bandaid fix for World gen holes 2023-10-19 22:13:44 -05:00
James Seibel 22f290c8a3 minor refactor 2023-10-19 21:45:11 -05:00
James Seibel 6d39fb23d6 Fix never ending world gen tasks 2023-10-19 20:47:41 -05:00
James Seibel 0a523b99c7 Fix Sqlite memory leak 2023-10-19 20:11:49 -05:00
James Seibel c537ab2082 Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-10-19 18:28:36 -05:00
James Seibel 63005035d7 Disable MC 1.17 lighting (always use DH lighting)
Something in MC 1.17 is broken so chunks that DH gets may be broken about 25% of the time.
2023-10-19 18:28:23 -05:00
coolGi c8d03a625a Updated sub-project 2023-10-20 00:57:57 +10:30
James Seibel d11d26dc9f fix localization and render startup for 1.16 2023-10-19 07:28:01 -05:00
James Seibel 1932cf9e76 Fix chunkwrapper null pointer 2023-10-19 07:27:31 -05:00
James Seibel 97e6797fea Fix MC 1.16/1.17 compiling 2023-10-19 07:27:23 -05:00
James Seibel c4105c3014 Fix Forge not rendering 2023-10-19 07:04:24 -05:00
James Seibel 826b4a6305 Fix section null pointer error for MC 1.16 2023-10-18 22:01:36 -05:00
James Seibel 0cb0874c54 Fix black chunks on fabric 1.20.2 2023-10-18 21:16:05 -05:00
James Seibel b831dd84e4 Fix LOD render distance measured as the diameter instead of radius
Closes #565
2023-10-18 07:51:25 -05:00
James Seibel 6775bf8a3d Fix transparent fog rendering 2023-10-18 07:24:24 -05:00
James Seibel 61402afd93 Fix DH auto update jar deletion and add more logging 2023-10-17 21:43:01 -05:00
James Seibel c5cff674fe Add GLStates around the DhApplyShader 2023-10-16 21:58:01 -05:00
James Seibel 679b5c082a Fix transparent LODs overwriting opaque LOD alpha values 2023-10-16 21:33:50 -05:00
James Seibel e00faa8e3c Fix crashing on Mac and GL4.3 and older 2023-10-16 20:45:05 -05:00
James Seibel e5265f3dbb Add (mac broken) FrameBuffer application 2023-10-15 18:05:38 -05:00
coolGi 71e3e10280 Updated core sub-module 2023-10-14 22:48:10 +10:30
James Seibel 9443f6d1e5 Have GLProxy attempt creating contexts with all supported GL versions 2023-10-13 21:56:10 -05:00
James Seibel 415c553308 Add IDhApiRenderProxy for getting/setting framebuffers and textures 2023-10-13 19:58:12 -05:00
coolGi 4b783df11e Fixed lod's being rendered underwater 2023-10-13 23:26:06 +10:30
James Seibel 6d7135ef29 Fix black chunks when Sodium is installed 2023-10-13 07:36:44 -05:00
James Seibel c0f8b6729b Fix missing MC sky 2023-10-12 21:35:09 -05:00
James Seibel a5a01b8a76 Disable the russian translation 2023-10-12 19:47:22 -05:00
James Seibel ecd44d9d31 Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-10-12 19:43:00 -05:00
James Seibel fb4f0b0440 Improve SSAO class readability 2023-10-12 19:42:53 -05:00
coolGi c0bd3dda19 Surrounded the Java location with quotations so paths with spaces work 2023-10-13 02:54:32 +10:30
coolGi d4c94ea5e4 Attempt to find Java's binary to use for the self updater on Windows 2023-10-13 02:00:16 +10:30
coolGi 1a48d161d9 Updater now works with spaces in file paths 2023-10-13 00:07:02 +10:30
James Seibel adbb9cbc40 Fix SSAO Rendering 2023-10-12 07:49:11 -05:00
coolGi 40db006d62 Fixed compilation for older mc versions (again) 2023-10-12 23:08:12 +10:30
James Seibel ed64e83807 Re-add OpenGL debug config options, also remove debug LOD update configs 2023-10-10 20:40:55 -05:00
James Seibel 644b5462f1 Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-10-10 19:35:40 -05:00
James Seibel 33aded7345 Fix Light map flickering when multiple client levels are active 2023-10-10 19:33:10 -05:00
coolGi d04314a9ee Fixed compilation for older mc versions 2023-10-11 01:40:15 +10:30
coolGi fadc2e7ab4 Disabled relocation for sqlite (relocation bug on their end) 2023-10-11 01:19:31 +10:30
coolGi 8fc6d4a1de Changed self updater location to use the class' path 2023-10-10 23:05:42 +10:30
coolGi 87c3f5549c Updated core subproject 2023-10-10 22:57:59 +10:30
coolGi 1e9120c6d9 Relocated the sqlite dependency in the jar 2023-10-10 22:48:20 +10:30
James Seibel 53a6b9fcd0 merge in Steveplays Frame/Depth buffer changes 2023-10-10 07:01:27 -05:00
James Seibel 682fb11e9e Fix render cache clearing deleting the database instead of removing the cache data 2023-10-08 21:17:44 -05:00
James Seibel 17f515ef06 Move some ForgeApi code into common 2023-10-08 21:00:54 -05:00
James Seibel 3f6bc262cd Fix forge (and fabric) not updating LODs when placing/breaking blocks 2023-10-08 20:56:48 -05:00
James Seibel 5f025808ab Fix singleplayer Forge worlds not updating loaded/saved chunks 2023-10-07 21:29:58 -05:00
James Seibel b93c5815c7 Update coreSubProjects 2023-10-07 20:21:06 -05:00
James Seibel 1482bfcd07 Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-10-07 18:51:18 -05:00
James Seibel 71634f509a Update coreSubProjects 2023-10-07 18:50:46 -05:00
coolGi 608e2d5723 Used .renameTo instead of .canWrite to check if file is unlocked 2023-10-06 18:02:30 +10:30
coolGi f39d2d92f7 Updated core submodule 2023-10-06 17:24:37 +10:30
coolGi 264a9b66fa Fixed (hopefully) updater on Windows 2023-10-06 17:23:17 +10:30
coolGi bee9cce881 Temporary fix for windows file locking 2023-10-06 16:26:44 +10:30
coolGi ec63a025fd Disabled updating to the same commit 2023-10-05 13:12:39 +10:30
coolGi 69c5f49c72 Seeing if this will fix DH auto-updates on windows 2023-10-05 13:09:45 +10:30
coolGi 64d2363050 Fixed fabric mod json 2023-10-04 13:02:24 +10:30
coolGi 796f450091 Revert "Renamed cursedforge reference in gradle"
This reverts commit 7fb021010c.
2023-10-04 13:02:01 +10:30
coolGi aa0f274b87 Updated root subproject 2023-10-03 23:43:07 +10:30
coolGi c3103810db Moved run directory to root of the project 2023-10-03 23:41:23 +10:30
coolGi 7fb021010c Renamed cursedforge reference in gradle 2023-10-03 23:36:42 +10:30
coolGi b5267a0658 Added "provides" for our old modid 2023-10-03 23:09:08 +10:30
coolGi 1775a58938 Updated core submodule 2023-10-03 22:48:13 +10:30
coolGi 76f7371057 Fixed building for all mc versions 2023-10-03 16:44:18 +10:30
coolGi 727d7ee346 used .equals for string 2023-10-03 16:21:42 +10:30
coolGi 40618bc35a Fixed up button for 1.20.2 2023-10-03 16:01:30 +10:30
coolGi 2463d8c024 Added option to allow button background to not appear 2023-10-03 14:38:52 +10:30
coolGi 90c53d8116 Fixed button textured stuff 1.20.1 and prior 2023-10-03 14:26:24 +10:30
coolGi eb4e3bc793 Fixes to screen class not being imported 2023-10-03 02:18:59 +00:00
James Seibel 172d43a11b git ignore sqlite databases 2023-10-02 19:37:58 -05:00
coolGi 75419ed5ae Updated core submodule 2023-10-02 14:16:50 +10:30
coolGi 94978fe10a Fixed updater on 1.20.2 2023-10-02 14:16:08 +10:30
coolGi 71493e79d5 Hopefully fixed 1.20.2 building 2023-10-02 12:33:44 +10:30
coolGi 48fc9d13ad Updated core sub-project 2023-10-02 11:40:46 +10:30
coolGi 867152fdf2 Fixed renderChunkLayer injection point (1.20.2) 2023-10-02 11:40:33 +10:30
coolGi 7d13cb4905 bumped manifold version 2023-10-02 11:26:41 +10:30
coolGi 115c531be2 Fixed building on forge 2023-10-02 10:58:36 +10:30
coolGi fee8dc9b13 1.20.2 now builds (config looks odd, and updater disabled) 2023-10-01 22:32:19 +10:30
James Seibel 0bbaa15579 add basic sqlite and unit tests 2023-09-30 15:25:48 -05:00
James Seibel aeb7cf6ff1 Fix multiplayer similarity percent 2023-09-29 23:17:38 -05:00
James Seibel 0be45abc28 change ClientLevelWrapper getDimType to IDimensionTypeWrapper 2023-09-29 22:42:41 -05:00
James Seibel a6bff2abb0 Improve world gen for partially generated sections 2023-09-28 07:26:30 -05:00
James Seibel f4d6e63c47 Fix MixinLevelRenderer compling for 1.19.2 and lower MC versions 2023-09-26 19:03:55 -05:00
James Seibel 2edabc7168 Merge branch 'vivecraft' 2023-09-26 18:05:26 -05:00
coolGi 46e5bf24ca Close IO steams when done using them 2023-09-27 00:02:15 +09:30
coolGi ba014fc581 Updated core subproject 2023-09-27 00:00:19 +09:30
coolGi 225dad9d84 Disabled changelog button if using nightly 2023-09-26 23:11:54 +09:30
coolGi ca2914d3a9 Changed update screen to use short sha 2023-09-26 23:11:31 +09:30
coolGi 85203847d5 Updated core sub-project (again) 2023-09-26 23:01:03 +09:30
coolGi db2cdbaecd Updated core submodule 2023-09-26 22:58:05 +09:30
coolGi 3a80c6f59e Added nightly to be auto-updated 2023-09-26 22:53:12 +09:30
James Seibel 0137a6bfd0 Fix full data sampling 2023-09-24 21:22:15 -05:00
AquaticLava 8b4c647fd2 change method to private 2023-09-24 12:56:39 -06:00
AquaticLava d4ba61e341 Fix vivecraft incompatibility - forge 2023-09-24 12:56:22 -06:00
AquaticLava 634df3ba06 Fix vivecraft incompatibility 2023-09-23 23:33:24 -06:00
coolGi 88766f30e4 Updated core submodule 2023-09-24 11:20:41 +09:30
coolGi 14a15754fb Made config use our lang for everything 2023-09-24 11:18:31 +09:30
coolGi c919fe9ab0 Updated manifold version 2023-09-24 10:42:03 +09:30
James Seibel 13bf04ef2d Remove Sodium < 0.5.1 incompatability
Fixed via using Indium
2023-09-23 16:11:12 -05:00
James Seibel e42919c37f Add a config for synchronous GPU uploading
hopefully to help with Sodium/AMD issues
2023-09-23 16:06:04 -05:00
James Seibel 5a3bd0c9dd remove ReflectionHandler 2023-09-23 11:54:10 -05:00
James Seibel e1907245e2 Replace sodium mixins with an Indium dependency 2023-09-23 11:11:24 -05:00
James Seibel e476b56ac1 BatchGenerationEnvironment refactor and spelling fix 2023-09-23 08:31:02 -05:00
James Seibel 776ec93d09 Fix potential concurrency issue in BatchGenerationEnvironment 2023-09-23 08:30:23 -05:00
James Seibel fc0aafb070 Improve world gen GC pressure, break dataSource thread safety, and fix a sampling bug 2023-09-21 22:02:39 -05:00
James Seibel 54fa033e07 Add GL Context creation debug configs 2023-09-16 08:03:48 -05:00
James Seibel cbd0521a0c Add a mutable DhSectionPos 2023-09-15 07:34:42 -05:00
James Seibel e958790f76 Fix compiling for MC 1.16 - 1.19 2023-09-15 07:05:15 -05:00
James Seibel 06bb4a9cb3 Refactor and Optimize DhSectionPos 2023-09-14 22:05:05 -05:00
James Seibel b38a8d5e62 Add the ability to set Minecraft's memory JVM argument in gradle.properties 2023-09-13 18:53:23 -05:00
James Seibel 84028f78b8 Hopefully fix Structure load locking up the world gen threads (again) 2023-09-13 18:30:22 -05:00
James Seibel 515c45b4c8 Add unused data source automatic freeing 2023-09-12 20:16:45 -05:00
James Seibel 0cac09aec1 reformat ChunkLightStorage 2023-09-11 21:02:15 -05:00
James Seibel 695809d573 Merge branch 'minecraft-lod-mod-betterLightStorage' 2023-09-11 20:39:56 -05:00
James Seibel e12f33a938 DhLightingEngine Optimization from Builderb0y 2023-09-11 20:35:46 -05:00
Builderb0y 8e69174d5a Update 2 files
- /common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkLightStorage.java
- /common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java
2023-09-12 00:44:58 +00:00
James Seibel 3ea6bee3cf Merge branch 'minecraft-lod-mod-iris.shadow.fix' 2023-09-11 07:38:04 -05:00
James Seibel c1fda715d0 Improve LOD Building and Lighting speed for BigGlobe worlds
Thanks Builderb0y!
2023-09-11 07:26:09 -05:00
James Seibel e4a7056d48 Optimize ChunkWrapper.getBlockState() 2023-09-10 19:39:34 -05:00
James Seibel 2107d3cbbd Optimize BlockState/Biome Wrapper getter methods
The lambdas were being newly created for each get() which became difficult for the GC to handle.
2023-09-10 19:35:37 -05:00
James Seibel ef6fc07cd3 Minor ChunkWrapper.getBlockState() GC optimization 2023-09-10 17:10:01 -05:00
James Seibel 56172c69a4 Fix incompatible sodium version 0.5.0 -> 0.5.1 2023-09-09 19:57:33 -05:00
James Seibel a92aa1eca8 Fix SSAO rendering when Sodium is installed 2023-09-09 18:26:16 -05:00
James Seibel f2931e8204 Fix MC 1.16 compiling fail due to logger import 2023-09-09 17:37:49 -05:00
James Seibel 5d47a5f391 Fix shadow static rendering 2023-09-09 17:03:47 -05:00
James Seibel 299fbe7336 Use MC's projection matrix in SSAO instead of creating a new one 2023-09-09 16:45:46 -05:00
James Seibel b30c8ea413 Fix crashing due to Chunk Wrapper concurrency 2023-09-09 16:28:56 -05:00
James Seibel 64dea2d730 Reduce duplicate Client Chunk update calls 2023-09-09 13:37:04 -05:00
James Seibel 9edba26910 Potentially fix stuttering when flying around the world 2023-09-09 12:49:31 -05:00
James Seibel cf6b3d9a89 Add debug options to skip some full data update events 2023-09-08 22:32:37 -05:00
James Seibel 47f297809b add blend-alpha to GLState (thanks Null!) 2023-09-08 20:01:08 -05:00
James Seibel 12b4521df6 Remove LodQuadBuilder transparent void fix 2023-09-08 19:56:22 -05:00
James Seibel 48291e261e Optimize Buffer building 2023-09-08 07:54:28 -05:00
James Seibel 7c9fd5be9c Fix Fabric UI mixin fail for MC 1.19 and below 2023-09-07 07:05:05 -05:00
James Seibel 8d822d7f3c Add Null's SSAO improvements 2023-09-06 21:25:57 -05:00
James Seibel f42a76c8fd Render file handling refactors 2023-09-02 21:07:08 -05:00
coolGi 8765f9effa Updated core subproject 2023-09-02 21:05:32 +09:30
coolGi 276b839668 Fixed fog not applying correctly with blindness or darkness 2023-09-02 21:03:24 +09:30
coolGi 507b045eb9 Fixed deep dark not blackening lods 2023-09-02 20:59:31 +09:30
coolGi 1de8cfb001 Forgot forge was weird with mixins 2023-09-02 20:55:02 +09:30
coolGi 1ab8483df5 Added tripwire to ignored blocks 2023-09-02 20:48:04 +09:30
coolGi 969edef3b2 Updated core submodule 2023-09-02 20:11:03 +09:30
coolGi 20be303dd0 Fixed crash ran on version not on Modrinth 2023-09-02 20:10:56 +09:30
James Seibel fc43f866a1 Add missing licensing headers 2023-09-01 20:43:31 -05:00
James Seibel eca04899de Update existing licensing headers 2023-09-01 07:44:23 -05:00
James Seibel 7b7fb8db44 Add file write locking to AbstractMetaDataContainerFile 2023-08-31 22:01:48 -05:00
James Seibel 313b660334 Remove debug disable lighting engine configs 2023-08-31 21:43:21 -05:00
James Seibel 5b84b26b67 Move Fog and SSAO matrix inversions to the CPU 2023-08-31 21:16:09 -05:00
James Seibel 6dc567e079 Make kelp opaque in MC 1.19 and below to match MC 1.20 2023-08-31 19:42:35 -05:00
James Seibel 13b26d5902 Fix GLProxy null pointer 2023-08-31 19:08:38 -05:00
James Seibel 3d7b21c444 Fix lighting engine setting lighting incorrectly 2023-08-31 19:02:28 -05:00
James Seibel fc5879beab Reduce DhLightingEngine GC impact 2023-08-31 07:56:00 -05:00
James Seibel 251c9d1134 Fix corrupt chunk saving for fabric 1.20 2023-08-31 07:12:56 -05:00
James Seibel 0a1bf750ba Fix disappearing LOD sections 2023-08-30 07:44:11 -05:00
James Seibel a236264d14 Add temp debug option to disable lighting engine and light baking 2023-08-30 07:08:00 -05:00
James Seibel ba45a7c2f5 Fix fabric corrupted LODs on MC 1.19 2023-08-28 06:41:19 -05:00
James Seibel 95b1ecff0d Add Fabric incompatibility list to versionProperties
also mark Sodium <0.5 as incompatible for 1.20
2023-08-27 14:14:00 -05:00
James Seibel 8ad5565148 Convert WorldGenQueue to use SectionPos instead of LodPos 2023-08-27 07:53:44 -05:00
James Seibel 5cda6a6f2c fix render source null pointer 2023-08-26 21:53:07 -05:00
James Seibel 6f35d0710e fix quality preset cache reset firing too often 2023-08-26 17:57:40 -05:00
James Seibel 0c87a2a9ea Fix api:javadoc compiling and warnings 2023-08-26 17:32:55 -05:00
James Seibel 3ddc315826 Fix 1.16 compiling 2023-08-26 15:19:19 -05:00
James Seibel b888103c80 Add IDhApiConfigValue.addChangeListener() 2023-08-26 13:11:21 -05:00
James Seibel 09e689dc2f Add a ReadMe to DhApi 2023-08-26 11:19:54 -05:00
coolGi 0f61affac8 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	coreSubProjects
2023-08-27 01:38:27 +09:30
coolGi 57e44cc4a7 Updated core sub-project 2023-08-27 01:37:32 +09:30
coolGi 876c4f2510 Moved fabric api stuff 2023-08-27 01:37:16 +09:30
James Seibel 6c4364f009 Remove unused IDhApiConfigValue.getApiValue() 2023-08-26 10:56:36 -05:00
James Seibel d96fc3c73b Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-08-26 10:43:43 -05:00
James Seibel e3da644b1c Optimize SSAO rendering 2023-08-26 10:43:17 -05:00
coolGi 34bb512f03 Disabled SVG from being included in the jar 2023-08-27 01:02:05 +09:30
coolGi fae58503a8 Removed flatlaf 2023-08-27 00:59:36 +09:30
James Seibel 0ea69d0ca4 Add OpenGL error log config 2023-08-26 08:51:27 -05:00
coolGi 3144e9d957 Updated core-subproject 2023-08-26 17:29:48 +09:30
James Seibel 30fac5e5ce Remove deprecated getX() and getZ() from DhChunkPos 2023-08-25 21:17:54 -05:00
James Seibel 37cbeebaa5 Fix BCLib compiling for MC 1.16 and 1.17 2023-08-25 18:26:52 -05:00
James Seibel 3aa6cc3383 Attempt to fix LodRenderer freeing GL objects while they are in use 2023-08-25 07:52:20 -05:00
James Seibel 56dd3c352e Fix BCLib compiling for MC 1.16 and disable 1.17 2023-08-25 07:19:12 -05:00
James Seibel 83fa1a0281 Require a ILevelWrapper when deserializing BiomeWrappers 2023-08-24 21:38:01 -05:00
James Seibel 5b4049e0ca Require a ILevelWrapper when deserializing BlockStateWrappers 2023-08-24 20:10:59 -05:00
coolGi 888651ef52 Fixed duplicate bclib code from previous commit 2023-08-25 00:14:37 +09:30
coolGi a122b2c143 Fixed javadocs reference, and updated core 2023-08-25 00:10:34 +09:30
coolGi a41afa0dbf Fixed BCLib compat 1.19.4+ 2023-08-25 00:09:57 +09:30
coolGi d9b969e7a7 Fixed up git on ci (well, kinda) 2023-08-24 23:35:20 +09:30
James Seibel 9297ac4c35 Fix config presets rewriting the config file before it loads 2023-08-24 07:41:14 -05:00
James Seibel 7ee42a9529 Add missing render cache config listeners 2023-08-24 07:21:05 -05:00
James Seibel 640559239c Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-08-24 07:08:39 -05:00
coolGi 263574bf38 Added string (tripwire) to the ignored blocks 2023-08-24 21:26:24 +09:30
coolGi 60cbbb0393 Updated core sub-project 2023-08-24 21:20:46 +09:30
coolGi 91f9f17989 Merge branch 'main' into 'main'
feat: Update rendering block ignores

See merge request jeseibel/minecraft-lod-mod!35
2023-08-24 11:49:33 +00:00
coolGi a96e345fbe Updated core subproject 2023-08-24 21:13:23 +09:30
coolGi 9ffc54f0b1 Updated readme 2023-08-24 21:12:56 +09:30
coolGi bf3428b53c Added version number to bottom left of config (#558) 2023-08-24 21:07:05 +09:30
coolGi c78f6eb66d Added blendium to mod suggestion on fabric 2023-08-24 20:58:43 +09:30
coolGi 73f4bc3108 Formatted some resource files 2023-08-24 20:28:25 +09:30
James Seibel 73a42284f1 Fix MC 16, 17, 18, and 19 compiling 2023-08-23 22:15:51 -05:00
James Seibel 90accf01db Fix a null pointer 2023-08-23 22:00:13 -05:00
James Seibel 1cb60f6a6d Add core merge 2023-08-23 21:28:46 -05:00
James Seibel bfcc4b001d Merge branch 'steve-ignore-blocks' 2023-08-23 21:28:29 -05:00
James Seibel 2f7852f103 Revert Air Block handling and use the old (de)serialization logic
While having the ability to deserialize blockstates without a level is desired, I'm not sure if the method suggested here would work between MC versions or would support modded blocks (in the eventual case where someone wants to do that).
2023-08-23 21:25:40 -05:00
James Seibel 944b3c05ab Fix Fabric 1.18.2 saving black/empty chunks 2023-08-22 17:45:54 -05:00
James Seibel 531a308b39 Fix file saving 2023-08-22 07:45:33 -05:00
Steveplays28 27cd001680 feat: Update rendering block ignores
Barrier blocks, structure void blocks, light blocks, and air blocks now share 2 `HashMap`s that define blocks that should be ignored by the LOD builder.
2023-08-22 11:36:45 +02:00
James Seibel e5ee46335d Fix potential null pointers in DhWorld shutdown 2023-08-21 07:46:58 -05:00
James Seibel ef72cdc0e6 Add a profiler section for debug wireframe rendering 2023-08-20 18:38:27 -05:00
James Seibel 246bd54b55 Update coreSubProjects 2023-08-20 18:22:14 -05:00
James Seibel e1147f3f60 Replace ChunkWrapper LinkedList with ArrayList 2023-08-20 17:07:30 -05:00
James Seibel 3bc0104268 hide unused multiplayer configs 2023-08-20 15:13:37 -05:00
James Seibel 50e9a51f56 Fix LevelWrappers not unloading correctly 2023-08-20 14:47:36 -05:00
James Seibel 87fec5276d Add world gen task count to the F3 menu 2023-08-20 14:15:06 -05:00
coolGi 2b734bb8d4 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	coreSubProjects
2023-08-20 02:54:43 +09:30
coolGi 2d93a23da9 Updated core subproject 2023-08-20 02:54:27 +09:30
coolGi 1ac1b7ee2d Fixed ui modify, and made file handeler use its own set function 2023-08-20 02:36:48 +09:30
James Seibel 81adca92c0 Add a flag to enable/disable ChunkWrapper Index validation 2023-08-19 11:18:49 -05:00
coolGi d509694ba9 Re-arranged the git info 2023-08-20 01:35:48 +09:30
James Seibel 7f658bd310 Fix lighting and LOD generation for snow layers 2023-08-19 11:02:00 -05:00
James Seibel d7728cce45 Update manifold 2023.1.11 -> 2023.1.15 2023-08-19 07:18:52 -05:00
tom lee 4d649d8a33 Implement cache invalidation, and have render cache be used. Can expect faster load times. 2023-08-19 16:49:29 +08:00
James Seibel c3d0cc2da8 Potentially fix custom world generator snow lighting 2023-08-18 20:51:36 -05:00
James Seibel 0b67d64ff4 Fix compiling for MC 1.19 and lower 2023-08-18 18:55:27 -05:00
James Seibel 386dc38150 Remove height map optimization in LodDataBuilder 2023-08-18 18:44:46 -05:00
James Seibel ba6b08b818 Fix compiling for MC 1.19 and lower 2023-08-18 18:21:59 -05:00
James Seibel 2cfef3a84d Fix snow rendering take 2 2023-08-18 18:15:13 -05:00
James Seibel 8b20e1ee6d Attempt to fix a potential StructureLocator concurrency issue 2023-08-18 18:06:04 -05:00
James Seibel b254fde3ae Attempt to fix snow lighting 2023-08-18 17:22:39 -05:00
James Seibel 876989346b Fix biome non-existent exception when saving chunks on Fabric 2023-08-18 16:49:41 -05:00
James Seibel 7278bc1548 World gen step reformatting 2023-08-18 16:06:55 -05:00
James Seibel 1728c3e898 remove part of temp fix for block/sky light indexes in ChunkWrapper 2023-08-18 16:03:16 -05:00
James Seibel 80393529f1 minor world gen step refactor 2023-08-18 15:56:40 -05:00
James Seibel b9d635ac69 Fix potential missing heightmap when generating chunks 2023-08-18 15:56:21 -05:00
James Seibel cca668979f Remove MC's lighting engine from the world generator 2023-08-18 11:55:40 -05:00
James Seibel 0e017cf512 hopefully prevent world gen structure lockup when leaving the world 2023-08-18 09:11:00 -05:00
James Seibel b1e75431ae Fix WorldGen ThreadFactory not being used 2023-08-18 08:25:23 -05:00
James Seibel e12d7766d6 Fix a null pointer in ClassicConfigGui 2023-08-18 07:59:28 -05:00
James Seibel e43ee68508 Fix preset configs not applying after the UI screen changes 2023-08-18 07:51:15 -05:00
James Seibel 9a9aac64af Update coreSubProjects 2023-08-17 22:18:00 -05:00
James Seibel 5193a3313d Merge branch 'main' into 'main'
Fix querying block colors at constant position.

See merge request jeseibel/minecraft-lod-mod!34
2023-08-18 03:05:23 +00:00
James Seibel 2ff1b80650 Fix weird rendering on Intel GPUs 2023-08-17 21:05:05 -05:00
Builderb0y 7e0e51103b Fix querying block colors at constant position. 2023-08-18 01:57:08 +00:00
coolGi 2977486866 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	coreSubProjects
2023-08-17 23:27:48 +09:30
coolGi bb9df60004 Updated core subproject 2023-08-17 23:25:36 +09:30
James Seibel a1b42f61fb Update coreSubProjects 2023-08-17 07:23:25 -05:00
coolGi b394c01f47 Changed inject to the correct point (thx @Steveplays) 2023-08-16 20:02:49 +09:30
coolGi c047996592 This revert commit 21144a7c 2023-08-16 19:38:49 +09:30
James Seibel cc2340d1b9 Add a .editorconfig to core 2023-08-15 20:50:52 -05:00
James Seibel 01b233e968 Potentially fix lag when moving across chunk borders 2023-08-15 20:49:29 -05:00
coolGi c53cf3f870 Fixed fabric 1.16.5 2023-08-15 14:51:57 +09:30
coolGi 4f0da248e3 Fixed git submodule not getting commit 2023-08-15 14:44:10 +09:30
coolGi 44688afeb9 Updated settings.gradle to 1.20.1 2023-08-15 13:23:07 +09:30
coolGi 4301ed9917 Fixed building for pre 1.19 2023-08-15 13:16:39 +09:30
coolGi 0b26f8ce3c Updated core subproject 2023-08-15 13:00:51 +09:30
coolGi 815aed53fc Removed unused accessors 2023-08-15 13:00:41 +09:30
coolGi 9cde0edfa3 Didnt merge it correctly 2023-08-15 12:52:59 +09:30
coolGi 70b3ba0040 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	coreSubProjects
#	fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java
#	fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLevelRenderer.java
#	fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/mods/sodium/MixinSodiumRenderer.java
#	fabric/src/main/java/com/seibel/distanthorizons/fabric/wrappers/modAccessor/SodiumAccessor.java
2023-08-15 12:50:20 +09:30
coolGi 32c89b1af9 Updated core submodule 2023-08-15 12:47:43 +09:30
coolGi 21144a7ce4 Fix to sodium, now no longer requiring Mixins 2023-08-15 12:47:13 +09:30
James Seibel 0935a6e94b Fix ClientBlockDetailMap.getBlockStateData not passing the BlockPos
Thanks Builderb0y
2023-08-14 22:01:03 -05:00
coolGi f28f09dd40 Tmp fix for sodium 2023-08-15 11:54:41 +09:30
James Seibel f564755d66 Apply editorconfig auto formatting 2023-08-14 21:16:13 -05:00
James Seibel 2af274171c Create .editorconfig 2023-08-14 20:48:14 -05:00
James Seibel de726b7669 Fix ModGitInfo crashing on release MC 2023-08-14 07:30:48 -05:00
James Seibel 22d134e786 Fix MC 1.17 compiling 2023-08-14 07:12:56 -05:00
James Seibel 642b040f65 change the default MC version to 1.20.1 2023-08-13 18:07:32 -05:00
James Seibel 0cc883b6c3 Fix unnecessary region file concurrency warnings in MC 1.16 and 1.17 2023-08-13 17:51:10 -05:00
James Seibel 902362f54f Fix 1.16 and 1.17 compiling 2023-08-13 17:23:36 -05:00
James Seibel f9c946e3ce Improve DhLightingEngine performance 2023-08-13 17:10:45 -05:00
James Seibel 9020d5bbe6 Replace ChunkWrapper light hash maps with 1D arrays 2023-08-13 17:02:45 -05:00
James Seibel fe973d27b9 Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-08-13 13:37:43 -05:00
James Seibel e9c1f41f50 Fix the game instantly crashing due to running out of memory 2023-08-13 13:37:35 -05:00
James Seibel 630e3bf16f Merge branch 'local.docker.compile' into 'main'
Local docker compile

See merge request jeseibel/minecraft-lod-mod!33
2023-08-12 22:05:29 +00:00
Null 511 27df83f8e7 Local docker compile 2023-08-12 22:05:29 +00:00
James Seibel 4ad9bb0d71 Fix FullDataPointIdMap (de)serialization duplicate Entries
Potentially fix serializing Biomes/Blocks after a level has been shut down.
Also increase DATA_FORMAT_VERSION 2 -> 3
2023-08-12 16:17:59 -05:00
coolGi 4fcd4cb2f7 Merge branch 'cicd.cleanup' into 'main'
CICD Improvements

See merge request jeseibel/minecraft-lod-mod!32
2023-08-08 22:09:51 +00:00
Null 511 15beb792d1 CICD Improvements 2023-08-08 22:09:51 +00:00
coolGi 401daf907c Updated core sub-module 2023-08-08 21:23:38 +09:30
coolGi a3f6f51a7a Temporary fix for sodium 0.5's fog occlusion 2023-08-08 21:23:25 +09:30
coolGi e4cd89131e Removed test git stage 2023-08-08 20:41:23 +09:30
coolGi 32d8553b9b Added a temporary ci stage to test git 2023-08-08 20:36:35 +09:30
coolGi 092fc45a36 Updated core submodule 2023-08-07 22:05:13 +09:30
coolGi 9f58cf305f Added a warning if value doesn't have a way to display it 2023-08-07 22:05:04 +09:30
coolGi c1370e74a2 Updated core sub-module 2023-08-07 21:10:54 +09:30
coolGi 3b3ec1eee2 DH Jar now knows git version that it was built from 2023-08-07 21:10:45 +09:30
coolGi 803d26c7c7 Maybe fixed game not compiling for 1.18.2 2023-08-07 20:22:17 +09:30
James Seibel 9244f29daa Update coreSubProjects 2023-08-06 19:26:46 -05:00
James Seibel 0a9e789cdc Remove IMinecraftRenderWrapper.tryDisableVanillaFog()
They are now handled via mixins
2023-08-06 19:21:58 -05:00
James Seibel a761ef4b6b Add setUseDhLighting to ChunkWrapper 2023-08-06 19:19:45 -05:00
James Seibel 3f4df4289c minor ForgeServerProxy reformat 2023-08-06 19:19:23 -05:00
James Seibel 879c70a7a0 Fix forge vanilla fog removal 2023-08-06 19:11:09 -05:00
James Seibel b0c1f69023 add a todo comment to fabric MixinThreadingDetector to match forge 2023-08-06 17:49:14 -05:00
James Seibel 889474fb42 Add SeamlessOverdraw support for 1.16 2023-08-06 17:48:12 -05:00
James Seibel 4ad6c854c8 update comment in ChunkWrapper 2023-08-06 16:09:42 -05:00
James Seibel 521549a349 Fix MixinTextureUtil mixin crash due to incorrect package path 2023-08-06 15:37:55 -05:00
James Seibel ee06d81434 Add missing simicolons to ChunkWrapper 2023-08-06 15:20:28 -05:00
James Seibel 3f6ee90b4e Fix SodiumAccessor breaking 1.16 2023-08-06 09:23:43 -05:00
James Seibel a017586c0e Add 1.17.1 to the CI script 2023-08-06 09:09:24 -05:00
James Seibel 027b867afd Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-08-06 09:08:28 -05:00
James Seibel 67b61b55a3 Fix 1.17 compiling 2023-08-06 09:08:12 -05:00
coolGi b2efeb77e1 Started work on fix for sodium 0.5 for 1.20.x 2023-08-06 23:22:51 +09:30
coolGi 2929009e63 Merge remote-tracking branch 'origin/main' 2023-08-06 23:04:34 +09:30
James Seibel 4604751f80 Add 1.16.5 to the CI script 2023-08-06 08:32:40 -05:00
James Seibel 6906597165 Fix 1.19.2 compiling 2023-08-06 08:32:21 -05:00
coolGi 8235a911ab Fixed sodium not working on post 1.16 2023-08-06 22:18:48 +09:30
coolGi 8af8039a00 Updated core sub-module 2023-08-06 19:01:32 +09:30
coolGi b399121f20 Added a ui button option + debug menu option 2023-08-06 18:59:39 +09:30
James Seibel bf519301f5 Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-08-05 22:36:18 -05:00
James Seibel 9f0378f969 comment out netty code
It broke MC 1.16
2023-08-05 22:32:57 -05:00
James Seibel ea9cb7fcac remove unused imports in BiomeWrapper 2023-08-05 22:06:09 -05:00
James Seibel 7ba1f77a19 Fix Biome/BlockState Wrapper in 1.18 and 1.19 2023-08-05 21:58:16 -05:00
James Seibel 739947e008 Fix compiling for 1.20.1 2023-08-05 21:21:32 -05:00
James Seibel cfb0dd4096 Fix Server start/end forge events 1.16 2023-08-05 19:42:17 -05:00
James Seibel 92ff5beebc Fix null biomes onChunkSave in 1.16 2023-08-05 19:39:24 -05:00
James Seibel 8bb1251dc9 Fix Sodium rendering 1.16 2023-08-05 19:15:17 -05:00
James Seibel a52a122f9f Update MixinThreadingDetector.java 2023-08-05 18:17:01 -05:00
James Seibel c90efed0fe Remove/fix Terraforged mixin 1.16 2023-08-05 18:14:46 -05:00
James Seibel b41119232e Remove BCLibAccessor from 1.16 2023-08-05 17:27:30 -05:00
James Seibel 9fefaf6eca Fix some rendering mixins 1.16 2023-08-05 16:28:10 -05:00
James Seibel b5f3be4fcc Fix BlockStateWrapper logger null pointer 2023-08-05 12:21:49 -05:00
James Seibel f6c05303ea Fix ChunkWrapper.updateClientLightReady() for 1.16 2023-08-05 11:27:38 -05:00
James Seibel 18b0b8c6f4 Fix world gen for 1.16 2023-08-05 11:27:23 -05:00
James Seibel 9c73443aa2 BatchGenEnv Java 8 fix, MC 1.16 fix, and minor reformat 2023-08-05 11:20:17 -05:00
James Seibel 8fd5c6e3a4 update manifold 2023.1.10 -> 2023.1.11 2023-08-05 11:18:11 -05:00
James Seibel fb57fbbe8d Fix GUI screens for 1.16 2023-08-05 11:17:30 -05:00
James Seibel 98d2b0db3f Fix several mixins for 1.16 2023-08-05 11:15:48 -05:00
James Seibel c895dff2c8 Convert Biome/Block wrapper serialization methods to use ResourceLocations 2023-08-05 11:12:46 -05:00
James Seibel 5cd6111e92 Fix TintWithoutLevel... objects for 1.16 2023-08-05 11:11:41 -05:00
James Seibel a57a34ab58 Fix GetConfigScreen and WrapperFactory using Java 8+ code 2023-08-05 11:10:34 -05:00
coolGi 49baaaab85 Updated core sub-module 2023-08-05 16:53:50 +09:30
coolGi f8a0ce84de Some general fixes 2023-08-05 16:53:41 +09:30
coolGi 7408187507 Updated core submodule 2023-08-05 15:07:52 +09:30
James Seibel f2c2c5dd92 Add config for Lod Shading so Old Lighting can be enabled for shaders 2023-08-01 20:11:30 -05:00
James Seibel dbefea35c2 temporarily comment out LodQuadTree concurrency warning
The warning doesn't appear to be causing any ill effects for now, but it will still need to be fixed in the future
2023-08-01 07:44:55 -05:00
James Seibel f887f38e17 Add a 1 sec timeout before applying config presets 2023-08-01 07:42:18 -05:00
James Seibel 080a99d6db Remove thread preset "paid for whole cpu" and tweak CPU preset options 2023-08-01 07:27:41 -05:00
James Seibel 7fee50883a Slightly increase the default world gen thread count and activities 2023-07-31 21:44:23 -05:00
James Seibel 9bc97780a4 temporary fix for some LODs not appearing 2023-07-31 20:51:51 -05:00
James Seibel 7b3f63a2f2 Fix a crash when the ForgeConfigApiPort mod is included 2023-07-29 17:35:00 -05:00
James Seibel 60be8302f4 Fix multiplayer networking configs and add missing localizations 2023-07-29 10:01:35 -05:00
James Seibel c2ec60c6e5 Fix Forge networked multiverse and connecting to Forge servers
This change require an additional empty byte at the beginning of the multiverse packets
2023-07-29 09:21:44 -05:00
James Seibel fef7369338 Fix default mcVer from 1.18.4 -> 1.18.2
We don't have a 1.18.4 properties file
2023-07-28 07:25:51 -05:00
James Seibel 036754c54a Fix 1.19 and 1.20 compiling 2023-07-28 07:22:45 -05:00
James Seibel 8e0ccd781b refactor FabricClientProxy 2023-07-27 21:38:58 -05:00
James Seibel 1ee02211b9 Fix Forge client-side rendering 2023-07-27 21:37:00 -05:00
James Seibel a2ac2e00da update forge 1.18.2 40.2.9 -> 40.2.10 2023-07-25 21:27:21 -05:00
James Seibel a33ecec9d3 Add missing callAfterRunUpdates to forge mixingLevelRenderer 2023-07-25 21:27:01 -05:00
James Seibel b0937fd9d0 Fix a native code crash in MC 1.20 when using seamless overdraw 2023-07-23 18:21:01 -05:00
James Seibel d1283db786 Add a config to disable multiverse networking 2023-07-23 17:35:33 -05:00
James Seibel dc556efe0b Fix blocking the server thread in 1.20.1 world generation 2023-07-23 15:32:40 -05:00
James Seibel 0ff59e49a1 Fix lighting propagation when connected to a server 2023-07-22 21:02:58 -05:00
James Seibel febf5c31cc Remove an un-needed error log in tryGetClientLevelWrapper() 2023-07-22 20:43:31 -05:00
James Seibel 1665999111 Fix BlockStateWrapper deserialization
For some reason using "JsonOps.COMPRESSED" caused issues in a few cases when trying to deserialize the JSON string.
This also causes all old Full data to be deprecated since the method of serialization was changed.
2023-07-22 18:19:31 -05:00
James Seibel 14dd26349a Fix 1.20.1 missing imports 2023-07-22 12:57:35 -05:00
James Seibel 54e356d98c Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-07-22 12:40:19 -05:00
James Seibel 959dd4e718 Add a config for world gen timeout 2023-07-22 12:34:20 -05:00
James Seibel a61b2cb2fc Rename DLodDirection -> EDhDirection and rename several methods 2023-07-22 12:20:39 -05:00
James Seibel 6ac5b7f9a3 Default to using the new lighting engine for world generation 2023-07-22 12:17:23 -05:00
coolGi 3f26a70169 Fixed fog not being properly removed on forged 2023-07-21 17:06:14 +09:30
coolGi 084bf635cb Fixed fabric crash 2023-07-21 03:18:11 +09:30
coolGi f1a7e69c63 Updated core submodule 2023-07-20 21:06:20 +09:30
coolGi 93cfc64493 Fixed parchment version 2023-07-20 20:35:29 +09:30
coolGi eeaa836c45 Fixed improper location for remap (fixing forge 1.20.1) 2023-07-20 20:13:44 +09:30
coolGi 30d3d8c52e Updated parchment version 2023-07-20 20:12:53 +09:30
coolGi eaa61098fa Made ci bit smaller 2023-07-19 23:37:21 +09:30
coolGi 2ee087f29d Fixed bclib for newer versions 2023-07-19 23:36:25 +09:30
coolGi d8861f6100 Fixed bclib crash 2023-07-19 22:41:31 +09:30
coolGi 8802ce0a32 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	coreSubProjects
2023-07-19 22:20:02 +09:30
coolGi 7405839718 Updated core submodule 2023-07-19 22:19:15 +09:30
coolGi 2d77efd8ea Save commit for fog fix 2023-07-19 22:19:01 +09:30
James Seibel 9c73926cd2 Update coreSubProjects 2023-07-17 21:44:07 -05:00
James Seibel 0c7b1e06fd Fix Fabric 1.20.1 crash on boot due to Client Networking cast failure
Not sure why the compiler didn't complain, but this explicit conversion fixes the issue.
2023-07-17 20:30:03 -05:00
James Seibel 113bd75154 Update Fabric API 0.83.1 -> 0.85.0 2023-07-17 20:29:15 -05:00
coolGi 3d268adeea Attempt to make ci smaller 2023-07-17 14:39:22 +00:00
coolGi 3ac2392edb Testing to see if i can reduce the size of the buildscript 2023-07-17 23:55:23 +09:30
coolGi 008f5cf915 Changed order of ci build 2023-07-17 23:40:07 +09:30
coolGi 77cc7fe669 Fixed up buildAll scripts 2023-07-17 23:33:25 +09:30
coolGi b6d352fbce Fixed some typos in the gradle script 2023-07-17 22:37:33 +09:30
coolGi fc8426a2ee Merge remote-tracking branch 'origin/main' 2023-07-17 22:31:01 +09:30
James Seibel cd78ffc069 Fix Netty not being embedded in the Jar correctly (Thanks coolGi!) 2023-07-17 07:55:09 -05:00
coolGi 5230a4abf9 Merge remote-tracking branch 'origin/main' 2023-07-17 22:10:11 +09:30
James Seibel c37304d114 Temporary Forge retail MC Netty library fix 2023-07-17 07:39:01 -05:00
coolGi 0144619ed4 Changed homepage from cursedforge to modrinth 2023-07-17 21:41:58 +09:30
TomTheFurry b7cc51bb3e Fixed the mem leak issue by nuking the whole save-when-exit 2023-07-17 18:46:30 +08:00
James Seibel 96c844c6e8 Update coreSubProjects 2023-07-16 22:08:08 -05:00
James Seibel 09930b1754 Reduce CPU load for lower thread presets 2023-07-16 17:41:51 -05:00
coolGi 8cf51d0f77 Merge remote-tracking branch 'origin/main' 2023-07-17 02:17:44 +09:30
coolGi d3667b0e3a Updated core subproject 2023-07-17 02:17:10 +09:30
TomTheFurry 3ae7f3a299 Update core 2023-07-17 02:17:10 +09:30
coolGi 800d64c923 Slight fix to the reliability of the auto updater 2023-07-17 02:17:10 +09:30
coolGi fd2a1ca036 Removed spaces from version properties 2023-07-17 02:17:10 +09:30
TomTheFurry d8e631f52b Update core 2023-07-17 00:06:57 +08:00
coolGi f41e182f86 Revert "Updated TexturedButtonWidget with one from stable, is it still needed?"
This reverts commit 4876ee5d7c.
2023-07-17 01:19:00 +09:30
coolGi 4876ee5d7c Updated TexturedButtonWidget with one from stable, is it still needed? 2023-07-17 01:15:09 +09:30
coolGi 58d5f16845 Merge remote-tracking branch 'origin/main' 2023-07-17 00:29:26 +09:30
coolGi b26b072332 Updated core submodule 2023-07-17 00:29:01 +09:30
coolGi ab62c62079 Fixed values being able to go over number limit 2023-07-17 00:27:32 +09:30
James Seibel dacf573548 Update coreSubProjects 2023-07-16 09:29:40 -05:00
James Seibel 11baa7956d Hopefully fix massive CPU use when world gen is enabled 2023-07-15 18:22:01 -05:00
James Seibel 88bab7736b Update coreSubProjects 2023-07-15 12:31:52 -05:00
James Seibel cf5dcb63d1 Fix RenderSourceFileHandler threadPools not closing
Closes #543 (slowdown when moving between dimensions)
2023-07-15 11:50:59 -05:00
James Seibel fe92203201 Update coreSubProjects 2023-07-15 08:51:26 -05:00
James Seibel e9c2a6e0e4 Fix debug Renderer Mode localization 2023-07-14 07:03:18 -05:00
James Seibel 21f665c941 Make preprocessors exclusive by default and add inclusive options
This was done since previously the POST processors were inclusive but the PRE processors were exclusive, which is confusing.
2023-07-11 21:33:55 -05:00
James Seibel c9c96ff2e8 Fix 1.20.1 compiling 2023-07-11 19:53:08 -05:00
James Seibel 1e8964a162 Fix pointing to the wrong core sub-module 2023-07-11 19:41:44 -05:00
James Seibel e5cbcf2614 Update coreSubProjects 2023-07-11 19:02:21 -05:00
James Seibel 995f30b7e2 Merge branch 'minecraft-lod-mod-main' 2023-07-11 18:52:11 -05:00
James Seibel 64e0f9b48f refactoring 2023-07-11 08:50:32 -05:00
James Seibel db7001dd6f Fix option and changelog button missing backgrounds 2023-07-10 20:43:30 -05:00
James Seibel d70de5dac6 Fix forge running in the IDE 2023-07-08 11:55:15 -05:00
James Seibel 0a45192030 remove unused imports 2023-07-08 11:53:29 -05:00
s809 bdda96854e Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-07-08 16:23:17 +05:00
s809 638e7c19a3 Update core 2023-07-08 12:25:39 +05:00
James Seibel cedb63a505 Fix 1.19 and 1.20 compiling 2023-07-07 07:55:01 -05:00
coolGi 84941639ff Merge branch 'buildAll_windows' into 'main'
Update buildAll.bat

See merge request jeseibel/minecraft-lod-mod!30
2023-07-07 08:16:37 +00:00
Dominik Marcinowski cb8e4231a5 Update buildAll.bat 2023-07-07 08:03:00 +00:00
James Seibel 447759304a Add experimental Seamless Overdraw option 2023-07-06 22:17:05 -05:00
s809 0b8f57a952 Update core 2023-07-06 21:57:19 +05:00
s809 5808179abb Outline what interaction should somewhat look like
(not an actual interaction)
Fix client repeatedly registering handlers on reconnects
2023-07-06 21:57:02 +05:00
James Seibel 87f3a718b4 Refactoring 2023-07-04 15:41:22 -05:00
James Seibel 6c79c6b1ff Merge branch 'minecraft-lod-mod-serverCommunication' 2023-07-04 13:43:08 -05:00
James Seibel 889daecc86 refactor and rename 2023-07-04 10:29:43 -05:00
Cailin Smith 10de377081 Somehow missed this 2023-07-03 23:27:46 +02:00
James Seibel 310b237b7d Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-07-03 06:50:34 -05:00
James Seibel e71727c6f9 Update coreSubProjects 2023-07-03 06:50:21 -05:00
Cailin Smith 213d7a30b4 Rename class 2023-07-02 22:35:02 +02:00
Cailin Smith bc1a0123b2 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-07-02 22:10:05 +02:00
Cailin Smith b3c4c790dc Add ability for servers to communicate with the client to set the world.
This prevents the client from accidentally selected the wrong world
folder to load LODs from, since levels of the same dimension can't
naturally be distinguished from each other. With level similarity
detection, this can sometimes work, but in general is not reliable. This
mechanism instead allows servers to send a packet to the client on load,
enabling the override system, and then a second packet on world change,
which specifically sets the world key, based on knowledge that only the
server has, leading to a reliable way of detecting the correct world.
2023-07-02 21:46:34 +02:00
Cailin Smith d96c96fc6e Add ability for servers to communicate with the client to set the world.
This prevents the client from accidentally selected the wrong world
folder to load LODs from, since levels of the same dimension can't
naturally be distinguished from each other. With level similarity
detection, this can sometimes work, but in general is not reliable. This
mechanism instead allows servers to send a packet to the client on load,
enabling the override system, and then a second packet on world change,
which specifically sets the world key, based on knowledge that only the
server has, leading to a reliable way of detecting the correct world.
2023-07-02 21:27:06 +02:00
TomTheFurry 79513b6c52 Fix build issues 2023-07-02 18:33:28 +08:00
TomTheFurry 6fca33e496 Fix a crash on game startup, and fix world gen compat with TFC (TerraFirmaCraft) 2023-07-02 18:32:15 +08:00
s809 0184396b6a Update core 2023-06-30 22:05:40 +05:00
s809 47963fba43 The real server side
(not tested)
2023-06-30 22:05:03 +05:00
James Seibel cd95405c6a update chunk wrapper docs 2023-06-30 07:32:53 -05:00
James Seibel 3936b63580 Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-06-29 20:58:01 -05:00
James Seibel c1c4a91bbf add api version and CI building 2023-06-29 20:56:40 -05:00
TomTheFurry 51b93a7e3f Prob fix the gl error? (might add mem leak tho.) & fix 1.18.2 build 2023-06-29 21:40:25 +05:00
TomTheFurry bde11b5950 Trash remaining 1.19 & 1.18.1 unsupported mc version, and fixup 1.19.2 2023-06-29 21:40:25 +05:00
James Seibel 3aab0469ab Set the javadoc's title to their project names
In this case "DistantHorizons-api"
2023-06-29 21:40:25 +05:00
James Seibel dd2c9d1e56 Fix javadoc landing page 2023-06-29 21:40:25 +05:00
James Seibel 4dbae72eae JavaDoc API auto deploy attempt 2
(Fix typo)
2023-06-29 21:40:25 +05:00
James Seibel 2aac43485a JavaDoc API auto deploy attempt 2023-06-29 21:40:25 +05:00
James Seibel 6aa94d1ec5 add build_api_docs to the CI 2023-06-29 21:40:25 +05:00
James Seibel a9b1ca888a re-add horizontal micro-offset in vertex shader 2023-06-29 21:40:19 +05:00
TomTheFurry 6477408858 Prob fix the gl error? (might add mem leak tho.) & fix 1.18.2 build 2023-06-29 16:27:42 +08:00
TomTheFurry 1b66672166 Trash remaining 1.19 & 1.18.1 unsupported mc version, and fixup 1.19.2 2023-06-29 16:04:20 +08:00
James Seibel 207d5cb949 Update coreSubProjects 2023-06-28 18:17:33 -05:00
s809 3b90855781 Update core 2023-06-28 23:09:22 +05:00
s809 61c4b50528 Some changes to stop server from crashing on startup 2023-06-28 23:08:09 +05:00
James Seibel 38085c3e7d Set the javadoc's title to their project names
In this case "DistantHorizons-api"
2023-06-26 21:26:42 -05:00
James Seibel a16099bc98 Fix javadoc landing page 2023-06-27 01:36:36 +00:00
James Seibel 0342b4b5a9 JavaDoc API auto deploy attempt 2
(Fix typo)
2023-06-27 01:27:06 +00:00
James Seibel 9be222f25f JavaDoc API auto deploy attempt 2023-06-27 01:20:56 +00:00
James Seibel 5e242561bd add build_api_docs to the CI 2023-06-26 19:26:22 -05:00
s809 014b49246d Clean up a bit and add netty to core 2023-06-26 21:16:39 +05:00
James Seibel d57192f76a Update coreSubProjects 2023-06-26 21:16:39 +05:00
TomTheFurry a52c286241 Ops. 2023-06-26 21:16:39 +05:00
TomTheFurry 16bcf30092 Add in 1.20.1 builds! But, sky light for world gen isn't working...? 2023-06-26 21:16:39 +05:00
coolGi b2ba94f6e5 Added clean to the ci 2023-06-26 21:16:39 +05:00
TomTheFurry 7d8ab781cb Optimization on load time, fix gl bug, and improve transparency 2023-06-26 23:20:02 +08:00
James Seibel 52f52b1c1b re-add horizontal micro-offset in vertex shader 2023-06-26 07:59:01 -05:00
s809 9a5ebce51e Fork core 2023-06-26 16:16:31 +05:00
James Seibel 960488bef4 Update coreSubProjects 2023-06-25 18:50:53 -05:00
TomTheFurry 318d514b41 Ops. 2023-06-25 14:58:50 +08:00
TomTheFurry 01f767486d Add in 1.20.1 builds! But, sky light for world gen isn't working...? 2023-06-25 14:56:50 +08:00
coolGi eba2036718 Added clean to the ci 2023-06-25 00:01:37 +00:00
s809 f9a7aa3c62 Not even working 2023-06-24 22:26:58 +05:00
TomTheFurry 064c69d66c Fix up 1.18.2 builds 2023-06-24 23:13:53 +08:00
TomTheFurry 1ef2084e3b Fix a missing stuff in forge 2023-06-24 22:49:34 +08:00
TomTheFurry 0396c5e384 CI for 1.19.4??? 2023-06-24 22:43:07 +08:00
TomTheFurry a02fb42490 1.19 is back up and running! (A few... bugs though) 2023-06-24 22:41:19 +08:00
coolGi b82d9d6d9a Updated core submodule 2023-06-24 23:20:27 +09:30
TomTheFurry e2855e8984 Start to cleanup and work on 1.19 2023-06-24 19:30:25 +08:00
James Seibel 2bbf081f99 Overhaul Dh API event definition system 2023-06-23 23:07:48 -05:00
TomTheFurry f4784840fd Greatly improve Gen queue stability & improve loading times and performance 2023-06-24 00:24:27 +08:00
James Seibel 3464341c04 Update coreSubProjects 2023-06-22 21:37:05 -05:00
James Seibel ff16a97ce8 Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-06-22 07:57:24 -05:00
James Seibel 56c666ead8 Add AbstractDhApiChunkWorldGenerator 2023-06-22 07:57:14 -05:00
coolGi 12ef1f30f1 Fixed missing fullstop in readme 2023-06-21 14:27:40 +00:00
James Seibel 5d7abd5f70 Make WrapperFactory compiling fail if not implemented for current MC version 2023-06-21 07:58:06 -05:00
James Seibel 047dc0d4f5 Update coreSubProjects 2023-06-21 07:30:36 -05:00
coolGi a1f9fdc715 Fixed tooltips for linked tooltips 2023-06-21 19:52:26 +09:30
coolGi 4554052471 Fixed a broken lang in the config 2023-06-21 19:46:41 +09:30
coolGi 17762a9e49 Updated core submodule 2023-06-21 19:40:42 +09:30
James Seibel bdfc053596 API event exception fix 2023-06-20 20:55:35 -05:00
TomTheFurry 68e93ac19a Fixed up last of the bugs 2023-06-20 19:04:34 +08:00
James Seibel bdd01eef2b Update coreSubProjects 2023-06-19 22:04:15 -05:00
James Seibel fc68645ba3 Update coreSubProjects 2023-06-19 21:42:05 -05:00
James Seibel 4c2d1458de Fix config default thread counts being 0 2023-06-19 20:41:04 -05:00
coolGi 5f51cb11fe Forgot to turn down default noise intensity 2023-06-20 00:03:56 +09:30
coolGi 661aa37d71 Updated core submodule 2023-06-20 00:02:03 +09:30
coolGi 7756bd4fa1 Fixed removing unused files form jar 2023-06-19 23:07:17 +09:30
TomTheFurry 19daa14f94 Update core 2023-06-19 14:41:36 +08:00
James Seibel afbb0e05aa Update coreSubProjects 2023-06-18 08:51:17 -05:00
James Seibel 1de329ed9b Fix some config setup not running if the UI isn't opened 2023-06-17 21:53:22 -05:00
James Seibel 5915bb1f70 Update coreSubProjects 2023-06-17 21:03:46 -05:00
James Seibel 38c368d9fe rename namespace lod -> distanthorizons 2023-06-17 17:45:38 -05:00
James Seibel 637a8cfe65 rename IDhApiUnsafeWrapper method 2023-06-17 16:52:27 -05:00
TomTheFurry ef47e1d85d Merge remote-tracking branch 'origin/main' 2023-06-18 00:27:29 +08:00
TomTheFurry 97f19915bd Refactor the DhLevel class structure.
Note: Known issue where sections need reload before gen is queued up. Will fix next
2023-06-18 00:24:12 +08:00
TomTheFurry a487653444 Refactor the DhLevel class structure.
Note: Known issue where sections need reload before gen is queued up. Will fix next
2023-06-18 00:23:51 +08:00
coolGi b649b1c279 Updated readme 2023-06-17 14:49:51 +09:30
coolGi 1a0f1244fa Fixed 1.19, 1.19.2, and 1.19.4 compile properties 2023-06-17 14:07:05 +09:30
TomTheFurry 67612e1e92 Update core 2023-06-17 01:01:00 +08:00
TomTheFurry 650c4b30a5 First baby step towards a pre-lod-loader. 2023-06-17 01:00:34 +08:00
TomTheFurry 3be9848da3 Update core 2023-06-16 23:37:29 +08:00
TomTheFurry d339a86b92 Quick fix debug chat msg crashing the game 2023-06-16 23:37:11 +08:00
James Seibel 64d6e2ea55 Update coreSubProjects 2023-06-15 07:42:34 -05:00
coolGi 77a5906ee5 Merge remote-tracking branch 'origin/main' 2023-06-15 18:30:26 +09:30
coolGi ff0547f2b9 Updated more mod versions 2023-06-15 18:28:51 +09:30
TomTheFurry b9aa2e1cca Merge remote-tracking branch 'origin/main'
# Conflicts:
#	coreSubProjects
2023-06-15 16:42:07 +08:00
TomTheFurry 7624764ee0 Fix lighting issues in both client-only and single player world 2023-06-15 16:41:18 +08:00
coolGi 08d15f7728 Disabled immersive portals compat as it doesnt fully work 2023-06-15 17:57:22 +09:30
coolGi ebd6aa0d74 Updated mod and loader versions 2023-06-15 17:55:38 +09:30
coolGi 1b84d8bf0b Updated gradle script to be easier to add loaders 2023-06-15 17:41:18 +09:30
James Seibel 2b1b54a646 config renaming and add new configs to DhApi 2023-06-14 21:32:21 -05:00
James Seibel a65dc83721 Update James' contact in the code_of_conduct 2023-06-14 20:18:07 -05:00
James Seibel 78c6fea352 Add LZ4 to the readme 2023-06-14 20:17:48 -05:00
James Seibel 03da6d87b5 Update coreSubProjects 2023-06-14 19:43:04 -05:00
James Seibel e0e6ac402e Implement the Quick enable render toggle
Also improve the logic for setting UI only config values
2023-06-12 22:19:11 -05:00
James Seibel 288459c88b Allow skipping config enums 2023-06-12 20:24:56 -05:00
coolGi fe41d03437 Updated core subproject 2023-06-12 20:19:13 +09:30
coolGi 940933eff4 Merge branch 'detached' into 'main'
Screen Space Ambient Occlusion

See merge request jeseibel/minecraft-lod-mod!28
2023-06-12 10:48:06 +00:00
notalpha e45b479ae3 feat: SSAO 2023-06-12 11:37:18 +02:00
TomTheFurry d3ec5a9ac2 Update core 2023-06-12 13:26:10 +08:00
James Seibel 7d20354ba5 rename config extendedNearClip -> overdrawPrevention 2023-06-11 22:16:48 -05:00
James Seibel 12548099f1 Fix BlockToAvoid and TintWithAvoidedBlocks 2023-06-11 21:24:38 -05:00
James Seibel 643c66a12f Update coreSubProjects 2023-06-11 18:03:55 -05:00
James Seibel da719b4a7e Update coreSubProjects 2023-06-11 15:57:53 -05:00
TomTheFurry 2a3ae34294 Fix (well, lower the chance of) reading regions that is actively being saved, reduce that chunk pos error chance. 2023-06-11 19:52:59 +08:00
TomTheFurry 27f7440165 update core 2023-06-11 18:32:42 +08:00
TomTheFurry 73ff60a0f6 Update manifold version so it stop spamming complaints 2023-06-11 18:31:50 +08:00
James Seibel a80843ce77 Update the config lang file
Also rename and refactor a few items
2023-06-09 22:10:43 -05:00
coolGi 4a9dc216e7 Updated lang generator to whitelist linked options 2023-06-08 23:06:48 +09:30
James Seibel 72c9de354e Overhaul the config and start adding summary options
also:
- add DH vs MC lighting engine option
- add a toggle for wireframe rendering
2023-06-07 22:59:49 -05:00
coolGi ae269b3a74 Updated core submodule 2023-06-06 19:06:14 +09:30
coolGi f3bc22cdc8 Added linking config options 2023-06-06 19:06:04 +09:30
coolGi 92cc8bf6f6 Updated core submodule 2023-06-06 18:02:35 +09:30
coolGi 05b6e74482 Fixed and checked support for all available config types 2023-06-06 18:01:49 +09:30
James Seibel 90fb950fc3 rename LodThreadFactory -> DhThreadFactory 2023-06-05 20:02:50 -05:00
James Seibel 005f1ed8a0 Move the world gen thread pool into the WorldGenQueue
We want Core to handle the world gen threads, not the individual world generators
2023-06-05 19:50:32 -05:00
James Seibel bb4497dd80 fix config listener onValueSet and add ConfigChangeListener 2023-06-04 22:05:17 -05:00
James Seibel 8956bc4980 Revert "remove ClassicConfigGUI.setWithoutSaving()"
This reverts commit 0a91473402.
2023-06-04 21:41:37 -05:00
James Seibel 0a91473402 remove ClassicConfigGUI.setWithoutSaving() 2023-06-04 21:07:54 -05:00
James Seibel 4df12e7316 add missing thread configs 2023-06-03 22:29:58 -05:00
James Seibel 3ff4e0f9a6 World generator reformatting 2023-06-03 21:37:12 -05:00
coolGi 6c5b1501e9 Updated core submodule 2023-06-03 21:40:04 +09:30
coolGi c1fddd1b82 Added a comment to add DimensionWrapper (maybe to replace DimensionTypeWrapper) 2023-06-03 21:39:47 +09:30
coolGi 256e3a1dbb Checked dimension instead of the dimension type 2023-06-03 21:32:59 +09:30
coolGi 2fbc69cb34 Updated log4j version 2023-06-03 21:00:37 +09:30
coolGi d3a06b8597 Fixed merged jars and added it to the ci 2023-06-02 23:32:12 +09:30
coolGi 9d9370f2f5 Moved version for lz4 to gradle.properties 2023-06-02 23:09:21 +09:30
coolGi aa6ae3084e Merge remote-tracking branch 'origin/main' 2023-06-02 22:38:50 +09:30
coolGi 9bd66aa54b FORGE FINALLY WORKS!!!! Server Load event called too late, and duplicate Client level load events. 2023-06-02 22:38:44 +09:30
TomTheFurry c33c0a4acb Fix the chunk loading by using an alternative method. It works very nicely now! 2023-06-01 19:22:27 +08:00
James Seibel fc9ce8243a Add a basic lighting engine for generated chunks 2023-05-29 19:44:56 -05:00
James Seibel ddf8950cc0 add BatchGenerationEnvironment warn comment 2023-05-29 14:10:15 -05:00
James Seibel f2832a0fd6 (hopefully) improve world gen shutdown time 2023-05-27 13:42:28 -05:00
coolGi 4660c4d5bf Merge remote-tracking branch 'origin/main' 2023-05-22 23:51:27 +09:30
coolGi 1b60c66eec Only use the Modrinth api if the user clicks the changelog menu instead of when the config is opened 2023-05-22 23:51:19 +09:30
James Seibel ac939a5e95 drop fixed thread pools to 1 for easier testing 2023-05-22 07:49:46 -05:00
James Seibel ea0a98c9f0 Fix a crash if transparency is disabled 2023-05-22 07:18:21 -05:00
James Seibel baa11f481e improve a couple batch generator error messages 2023-05-21 18:41:48 -05:00
James Seibel 1cbb124ba0 fix unit tests and add data compression 2023-05-21 17:47:51 -05:00
coolGi 883685de83 Updated core submodule 2023-05-21 20:40:12 +09:30
coolGi 91864443d5 Made the changelog screen look a bit nicer 2023-05-21 20:36:01 +09:30
coolGi b48d6bfd4b Added changelog button to the config menu 2023-05-21 20:18:25 +09:30
coolGi a1ede75450 Fixed up the lz4 compression location in jar 2023-05-21 20:00:59 +09:30
James Seibel db73aaeabb add LZ4 for compression 2023-05-20 11:44:39 -05:00
James Seibel 284690338c ForgeServerProxy minor reformat 2023-05-19 22:42:15 -05:00
James Seibel 550a1a44ef Update coreSubProjects 2023-05-13 11:08:26 -05:00
James Seibel 5203c03257 Merge branch 'main' of gitlab.com:jeseibel/minecraft-lod-mod 2023-05-12 22:15:03 -05:00
James Seibel 1a9f56c03e Update coreSubProjects 2023-05-12 22:14:48 -05:00
coolGi 3cf0c724f4 Added a fixme comment 2023-05-12 20:45:40 +09:30
coolGi 3e96b01981 Reverted the MixinFogRenderer to fix fog not disabling 2023-05-12 19:09:28 +09:30
coolGi 59658441ff Made the MixinUtilBackgroundThread slightly easier to deal with 2023-05-12 19:04:28 +09:30
coolGi a4e20114a7 Updated forge's mixins 2023-05-12 19:02:32 +09:30
coolGi aa1315dd57 Moved the modAccessors to the wrappers folder on forge 2023-05-12 18:47:49 +09:30
coolGi 271149baa4 Made several Subscribed Events public 2023-05-12 18:43:54 +09:30
James Seibel 680f6dc317 Update coreSubProjects 2023-05-06 19:40:14 -05:00
coolGi 0e6282e90e Updated core submodule 2023-05-07 08:55:48 +09:30
coolGi e9f66dc091 Removed getFogColor from the BCLibAccessor 2023-05-07 08:55:10 +09:30
coolGi 88f6acb618 Generalised the fog fix 2023-05-07 08:48:03 +09:30
coolGi 6f36cd3305 Fixed up gradle to be able to not run immersive portals 2023-05-07 08:32:46 +09:30
coolGi b476da8ba4 Worked more on immersive portals compatibility 2023-05-06 23:04:56 +09:30
coolGi 873187e210 Hard-coded the authors in mods.toml to fix architectury error 2023-05-06 13:04:11 +09:30
James Seibel 5ec21065cc Create new world gen tasks when moving into new areas 2023-05-04 07:31:56 -05:00
James Seibel dbdbb14bac Fix renderSection flickering 2023-05-02 19:46:34 -05:00
coolGi 2cbc98a26a Removed quotations around authors in mod infos 2023-05-02 23:21:40 +09:30
coolGi 5739eb9a85 Started work on adding immersive portal support 2023-05-02 23:21:09 +09:30
coolGi f6887ee7d5 Merge remote-tracking branch 'origin/main' 2023-05-02 22:28:54 +09:30
James Seibel 12698c5d39 rename lod.core.api -> lod.coreapi.api to file compiler issues 2023-05-02 07:33:27 -05:00
James Seibel 8244932247 temporary fabric/mod.json and forge/mods.toml author fix 2023-05-02 07:32:10 -05:00
Ran 39ef896138 Made forge actually run! Renamed com.seibel.lod.core to com.seibel.lod.coreapi 2023-05-01 19:27:52 +06:00
coolGi 258a2a8ee7 Updated core submodule 2023-05-01 15:15:10 +09:30
James Seibel 0b570ba15d Update coreSubProjects 2023-04-30 21:52:37 -05:00
James Seibel 7d4e6e59d9 Update coreSubProjects 2023-04-30 21:30:02 -05:00
coolGi 9c543e2ec8 Updated forge version 2023-05-01 02:38:32 +09:30
coolGi d9ef9282ad Renamed some comments on the root build.gradle 2023-05-01 00:57:00 +09:30
coolGi 5c1058ca76 Moved fabric loom back to its own build.gradle 2023-05-01 00:52:41 +09:30
Ran c30cb1c04d Forge runs! With a thousand errors... 2023-04-30 14:41:04 +06:00
Ran 5caf11789f Try to fix Forge 2023-04-30 14:15:04 +06:00
coolGi ef02856b45 Removed fabricLike and quilt from the main branch. Development moved to the common_mixins branch. 2023-04-29 23:18:48 +09:30
235 changed files with 12876 additions and 9994 deletions
+19
View File
@@ -0,0 +1,19 @@
**/.git
**/.gitlab
**/.cache
buildAllJars
**/_Misc Files
*.bat
*.md
*.sh
*.txt
coreSubProjects/*.md
coreSubProjects/*.txt
**/.gitignore
**/.gitattributes
**/.gitlab-cy.yml
**/.gitmodules
+701
View File
@@ -0,0 +1,701 @@
# DH Main
root = true
# Note: please keep this and the core .editorconfig in sync
[*]
charset = utf-8
end_of_line = crlf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 1000
tab_width = 4
trim_trailing_whitespace = false
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = true
ij_smart_tabs = false
ij_visual_guides = none
ij_wrap_on_typing = false
[*.java]
indent_style = tab
ij_smart_tabs = true
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false
ij_java_align_multiline_annotation_parameters = false
ij_java_align_multiline_array_initializer_expression = false
ij_java_align_multiline_assignment = false
ij_java_align_multiline_binary_operation = false
ij_java_align_multiline_chained_methods = false
ij_java_align_multiline_deconstruction_list_components = false
ij_java_align_multiline_extends_list = false
ij_java_align_multiline_for = false
ij_java_align_multiline_method_parentheses = false
ij_java_align_multiline_parameters = false
ij_java_align_multiline_parameters_in_calls = false
ij_java_align_multiline_parenthesized_expression = false
ij_java_align_multiline_records = false
ij_java_align_multiline_resources = false
ij_java_align_multiline_ternary_operation = false
ij_java_align_multiline_text_blocks = false
ij_java_align_multiline_throws_list = false
ij_java_align_subsequent_simple_methods = false
ij_java_align_throws_keyword = false
ij_java_align_types_in_multi_catch = false
ij_java_annotation_parameter_wrap = off
ij_java_array_initializer_new_line_after_left_brace = false
ij_java_array_initializer_right_brace_on_new_line = false
ij_java_array_initializer_wrap = normal
ij_java_assert_statement_colon_on_next_line = false
ij_java_assert_statement_wrap = off
ij_java_assignment_wrap = off
ij_java_binary_operation_sign_on_next_line = false
ij_java_binary_operation_wrap = off
ij_java_blank_lines_after_anonymous_class_header = 0
ij_java_blank_lines_after_class_header = 0
ij_java_blank_lines_after_imports = 1
ij_java_blank_lines_after_package = 1
ij_java_blank_lines_around_class = 1
ij_java_blank_lines_around_field = 0
ij_java_blank_lines_around_field_in_interface = 0
ij_java_blank_lines_around_initializer = 0
ij_java_blank_lines_around_method = 0
ij_java_blank_lines_around_method_in_interface = 0
ij_java_blank_lines_before_class_end = 1
ij_java_blank_lines_before_imports = 1
ij_java_blank_lines_before_method_body = 0
ij_java_blank_lines_before_package = 1
ij_java_block_brace_style = next_line
ij_java_block_comment_add_space = false
ij_java_block_comment_at_first_column = true
ij_java_builder_methods = none
ij_java_call_parameters_new_line_after_left_paren = false
ij_java_call_parameters_right_paren_on_new_line = false
ij_java_call_parameters_wrap = normal
ij_java_case_statement_on_separate_line = true
ij_java_catch_on_new_line = true
ij_java_class_annotation_wrap = off
ij_java_class_brace_style = next_line
ij_java_class_count_to_use_import_on_demand = 5
ij_java_class_names_in_javadoc = 1
ij_java_deconstruction_list_wrap = normal
ij_java_do_not_indent_top_level_class_members = false
ij_java_do_not_wrap_after_single_annotation = false
ij_java_do_not_wrap_after_single_annotation_in_parameter = false
ij_java_do_while_brace_force = never
ij_java_doc_add_blank_line_after_description = true
ij_java_doc_add_blank_line_after_param_comments = false
ij_java_doc_add_blank_line_after_return = false
ij_java_doc_add_p_tag_on_empty_lines = false
ij_java_doc_align_exception_comments = false
ij_java_doc_align_param_comments = false
ij_java_doc_do_not_wrap_if_one_line = true
ij_java_doc_enable_formatting = true
ij_java_doc_enable_leading_asterisks = true
ij_java_doc_indent_on_continuation = false
ij_java_doc_keep_empty_lines = true
ij_java_doc_keep_empty_parameter_tag = true
ij_java_doc_keep_empty_return_tag = false
ij_java_doc_keep_empty_throws_tag = true
ij_java_doc_keep_invalid_tags = true
ij_java_doc_param_description_on_new_line = false
ij_java_doc_preserve_line_breaks = false
ij_java_doc_use_throws_not_exception_tag = true
ij_java_else_on_new_line = true
ij_java_enum_constants_wrap = off
ij_java_extends_keyword_wrap = normal
ij_java_extends_list_wrap = normal
ij_java_field_annotation_wrap = off
ij_java_finally_on_new_line = true
ij_java_for_brace_force = always
ij_java_for_statement_new_line_after_left_paren = false
ij_java_for_statement_right_paren_on_new_line = false
ij_java_for_statement_wrap = off
ij_java_generate_final_locals = false
ij_java_generate_final_parameters = false
ij_java_if_brace_force = never
ij_java_imports_layout = *,|,javax.**,java.**,|,$*
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
ij_java_keep_blank_lines_before_right_brace = 10
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
ij_java_keep_blank_lines_in_code = 10
ij_java_keep_blank_lines_in_declarations = 10
ij_java_keep_builder_methods_indents = false
ij_java_keep_control_statement_in_one_line = true
ij_java_keep_first_column_comment = true
ij_java_keep_indents_on_empty_lines = true
ij_java_keep_line_breaks = true
ij_java_keep_multiple_expressions_in_one_line = true
ij_java_keep_simple_blocks_in_one_line = false
ij_java_keep_simple_classes_in_one_line = true
ij_java_keep_simple_lambdas_in_one_line = true
ij_java_keep_simple_methods_in_one_line = true
ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = false
ij_java_line_comment_add_space_on_reformat = false
ij_java_line_comment_at_first_column = false
ij_java_method_annotation_wrap = off
ij_java_method_brace_style = next_line
ij_java_method_call_chain_wrap = normal
ij_java_method_parameters_new_line_after_left_paren = true
ij_java_method_parameters_right_paren_on_new_line = false
ij_java_method_parameters_wrap = normal
ij_java_modifier_list_wrap = false
ij_java_multi_catch_types_wrap = normal
ij_java_names_count_to_use_import_on_demand = 3
ij_java_new_line_after_lparen_in_annotation = false
ij_java_new_line_after_lparen_in_deconstruction_pattern = true
ij_java_new_line_after_lparen_in_record_header = false
ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
ij_java_parameter_annotation_wrap = off
ij_java_parentheses_expression_new_line_after_left_paren = false
ij_java_parentheses_expression_right_paren_on_new_line = false
ij_java_place_assignment_sign_on_next_line = false
ij_java_prefer_longer_names = true
ij_java_prefer_parameters_wrap = false
ij_java_record_components_wrap = normal
ij_java_repeat_synchronized = true
ij_java_replace_instanceof_and_cast = false
ij_java_replace_null_check = false
ij_java_replace_sum_lambda_with_method_ref = false
ij_java_resource_list_new_line_after_left_paren = false
ij_java_resource_list_right_paren_on_new_line = false
ij_java_resource_list_wrap = on_every_item
ij_java_rparen_on_new_line_in_annotation = false
ij_java_rparen_on_new_line_in_deconstruction_pattern = true
ij_java_rparen_on_new_line_in_record_header = false
ij_java_space_after_closing_angle_bracket_in_type_argument = false
ij_java_space_after_colon = true
ij_java_space_after_comma = true
ij_java_space_after_comma_in_type_arguments = true
ij_java_space_after_for_semicolon = true
ij_java_space_after_quest = true
ij_java_space_after_type_cast = true
ij_java_space_before_annotation_array_initializer_left_brace = false
ij_java_space_before_annotation_parameter_list = false
ij_java_space_before_array_initializer_left_brace = false
ij_java_space_before_catch_keyword = true
ij_java_space_before_catch_left_brace = true
ij_java_space_before_catch_parentheses = true
ij_java_space_before_class_left_brace = true
ij_java_space_before_colon = true
ij_java_space_before_colon_in_foreach = true
ij_java_space_before_comma = false
ij_java_space_before_deconstruction_list = false
ij_java_space_before_do_left_brace = true
ij_java_space_before_else_keyword = true
ij_java_space_before_else_left_brace = true
ij_java_space_before_finally_keyword = true
ij_java_space_before_finally_left_brace = true
ij_java_space_before_for_left_brace = true
ij_java_space_before_for_parentheses = true
ij_java_space_before_for_semicolon = false
ij_java_space_before_if_left_brace = true
ij_java_space_before_if_parentheses = true
ij_java_space_before_method_call_parentheses = false
ij_java_space_before_method_left_brace = true
ij_java_space_before_method_parentheses = false
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
ij_java_space_before_quest = true
ij_java_space_before_switch_left_brace = true
ij_java_space_before_switch_parentheses = true
ij_java_space_before_synchronized_left_brace = false
ij_java_space_before_synchronized_parentheses = true
ij_java_space_before_try_left_brace = true
ij_java_space_before_try_parentheses = true
ij_java_space_before_type_parameter_list = false
ij_java_space_before_while_keyword = true
ij_java_space_before_while_left_brace = true
ij_java_space_before_while_parentheses = true
ij_java_space_inside_one_line_enum_braces = false
ij_java_space_within_empty_array_initializer_braces = true
ij_java_space_within_empty_method_call_parentheses = false
ij_java_space_within_empty_method_parentheses = false
ij_java_spaces_around_additive_operators = true
ij_java_spaces_around_annotation_eq = true
ij_java_spaces_around_assignment_operators = true
ij_java_spaces_around_bitwise_operators = true
ij_java_spaces_around_equality_operators = true
ij_java_spaces_around_lambda_arrow = true
ij_java_spaces_around_logical_operators = true
ij_java_spaces_around_method_ref_dbl_colon = false
ij_java_spaces_around_multiplicative_operators = true
ij_java_spaces_around_relational_operators = true
ij_java_spaces_around_shift_operators = true
ij_java_spaces_around_type_bounds_in_type_parameters = true
ij_java_spaces_around_unary_operator = false
ij_java_spaces_within_angle_brackets = false
ij_java_spaces_within_annotation_parentheses = false
ij_java_spaces_within_array_initializer_braces = false
ij_java_spaces_within_braces = true
ij_java_spaces_within_brackets = false
ij_java_spaces_within_cast_parentheses = false
ij_java_spaces_within_catch_parentheses = false
ij_java_spaces_within_deconstruction_list = false
ij_java_spaces_within_for_parentheses = false
ij_java_spaces_within_if_parentheses = false
ij_java_spaces_within_method_call_parentheses = false
ij_java_spaces_within_method_parentheses = false
ij_java_spaces_within_parentheses = false
ij_java_spaces_within_record_header = false
ij_java_spaces_within_switch_parentheses = false
ij_java_spaces_within_synchronized_parentheses = false
ij_java_spaces_within_try_parentheses = false
ij_java_spaces_within_while_parentheses = false
ij_java_special_else_if_treatment = true
ij_java_subclass_name_suffix = Impl
ij_java_ternary_operation_signs_on_next_line = false
ij_java_ternary_operation_wrap = on_every_item
ij_java_test_name_suffix = Test
ij_java_throws_keyword_wrap = normal
ij_java_throws_list_wrap = normal
ij_java_use_external_annotations = false
ij_java_use_fq_class_names = false
ij_java_use_relative_indents = false
ij_java_use_single_class_imports = true
ij_java_variable_annotation_wrap = off
ij_java_visibility = public
ij_java_while_brace_force = always
ij_java_while_on_new_line = true
ij_java_wrap_comments = false
ij_java_wrap_first_method_in_call_chain = false
ij_java_wrap_long_lines = false
[*.nbtt]
indent_style = tab
max_line_length = 150
ij_continuation_indent_size = 4
ij_nbtt_keep_indents_on_empty_lines = false
ij_nbtt_space_after_colon = true
ij_nbtt_space_after_comma = true
ij_nbtt_space_before_colon = true
ij_nbtt_space_before_comma = false
ij_nbtt_spaces_within_brackets = false
ij_nbtt_spaces_within_parentheses = false
[*.properties]
ij_properties_align_group_field_declarations = false
ij_properties_keep_blank_lines = false
ij_properties_key_value_delimiter = equals
ij_properties_spaces_around_key_value_delimiter = false
[.editorconfig]
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.jspx,*.pom,*.rng,*.tagx,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
ij_xml_align_attributes = true
ij_xml_align_text = false
ij_xml_attribute_wrap = normal
ij_xml_block_comment_add_space = false
ij_xml_block_comment_at_first_column = true
ij_xml_keep_blank_lines = 2
ij_xml_keep_indents_on_empty_lines = false
ij_xml_keep_line_breaks = true
ij_xml_keep_line_breaks_in_text = true
ij_xml_keep_whitespaces = false
ij_xml_keep_whitespaces_around_cdata = preserve
ij_xml_keep_whitespaces_inside_cdata = false
ij_xml_line_comment_at_first_column = true
ij_xml_space_after_tag_name = false
ij_xml_space_around_equals_in_attribute = false
ij_xml_space_inside_empty_tag = false
ij_xml_text_wrap = normal
ij_xml_use_custom_settings = false
[{*.bash,*.sh,*.zsh}]
indent_size = 4
tab_width = 4
ij_shell_binary_ops_start_line = false
ij_shell_keep_column_alignment_padding = false
ij_shell_minify_program = false
ij_shell_redirect_followed_by_space = false
ij_shell_switch_cases_indented = false
ij_shell_use_unix_line_separator = true
[{*.comp,*.frag,*.fsh,*.geom,*.glsl,*.gsh,*.tesc,*.tese,*.vert,*.vsh}]
ij_glsl_space_after_colon = true
ij_glsl_space_after_comma = true
ij_glsl_space_after_for_semicolon = true
ij_glsl_space_after_quest = true
ij_glsl_space_before_colon = false
ij_glsl_space_before_comma = false
ij_glsl_space_before_for_semicolon = false
ij_glsl_space_before_quest = true
ij_glsl_spaces_around_additive_operators = true
ij_glsl_spaces_around_assignment_operators = true
ij_glsl_spaces_around_bitwise_operators = true
ij_glsl_spaces_around_equality_operators = true
ij_glsl_spaces_around_logical_operators = true
ij_glsl_spaces_around_multiplicative_operators = true
ij_glsl_spaces_around_relational_operators = true
ij_glsl_spaces_around_shift_operators = true
ij_glsl_spaces_within_brackets = false
ij_glsl_spaces_within_parentheses = false
[{*.gant,*.groovy,*.gy}]
ij_groovy_align_group_field_declarations = false
ij_groovy_align_multiline_array_initializer_expression = false
ij_groovy_align_multiline_assignment = false
ij_groovy_align_multiline_binary_operation = false
ij_groovy_align_multiline_chained_methods = false
ij_groovy_align_multiline_extends_list = false
ij_groovy_align_multiline_for = true
ij_groovy_align_multiline_list_or_map = true
ij_groovy_align_multiline_method_parentheses = false
ij_groovy_align_multiline_parameters = true
ij_groovy_align_multiline_parameters_in_calls = false
ij_groovy_align_multiline_resources = true
ij_groovy_align_multiline_ternary_operation = false
ij_groovy_align_multiline_throws_list = false
ij_groovy_align_named_args_in_map = true
ij_groovy_align_throws_keyword = false
ij_groovy_array_initializer_new_line_after_left_brace = false
ij_groovy_array_initializer_right_brace_on_new_line = false
ij_groovy_array_initializer_wrap = off
ij_groovy_assert_statement_wrap = off
ij_groovy_assignment_wrap = off
ij_groovy_binary_operation_wrap = off
ij_groovy_blank_lines_after_class_header = 0
ij_groovy_blank_lines_after_imports = 1
ij_groovy_blank_lines_after_package = 1
ij_groovy_blank_lines_around_class = 1
ij_groovy_blank_lines_around_field = 0
ij_groovy_blank_lines_around_field_in_interface = 0
ij_groovy_blank_lines_around_method = 1
ij_groovy_blank_lines_around_method_in_interface = 1
ij_groovy_blank_lines_before_imports = 1
ij_groovy_blank_lines_before_method_body = 0
ij_groovy_blank_lines_before_package = 0
ij_groovy_block_brace_style = end_of_line
ij_groovy_block_comment_add_space = false
ij_groovy_block_comment_at_first_column = true
ij_groovy_call_parameters_new_line_after_left_paren = false
ij_groovy_call_parameters_right_paren_on_new_line = false
ij_groovy_call_parameters_wrap = off
ij_groovy_catch_on_new_line = false
ij_groovy_class_annotation_wrap = split_into_lines
ij_groovy_class_brace_style = end_of_line
ij_groovy_class_count_to_use_import_on_demand = 5
ij_groovy_do_while_brace_force = never
ij_groovy_else_on_new_line = false
ij_groovy_enable_groovydoc_formatting = true
ij_groovy_enum_constants_wrap = off
ij_groovy_extends_keyword_wrap = off
ij_groovy_extends_list_wrap = off
ij_groovy_field_annotation_wrap = split_into_lines
ij_groovy_finally_on_new_line = false
ij_groovy_for_brace_force = never
ij_groovy_for_statement_new_line_after_left_paren = false
ij_groovy_for_statement_right_paren_on_new_line = false
ij_groovy_for_statement_wrap = off
ij_groovy_ginq_general_clause_wrap_policy = 2
ij_groovy_ginq_having_wrap_policy = 1
ij_groovy_ginq_indent_having_clause = true
ij_groovy_ginq_indent_on_clause = true
ij_groovy_ginq_on_wrap_policy = 1
ij_groovy_ginq_space_after_keyword = true
ij_groovy_if_brace_force = never
ij_groovy_import_annotation_wrap = 2
ij_groovy_imports_layout = *,|,javax.**,java.**,|,$*
ij_groovy_indent_case_from_switch = true
ij_groovy_indent_label_blocks = true
ij_groovy_insert_inner_class_imports = false
ij_groovy_keep_blank_lines_before_right_brace = 2
ij_groovy_keep_blank_lines_in_code = 2
ij_groovy_keep_blank_lines_in_declarations = 2
ij_groovy_keep_control_statement_in_one_line = true
ij_groovy_keep_first_column_comment = true
ij_groovy_keep_indents_on_empty_lines = false
ij_groovy_keep_line_breaks = true
ij_groovy_keep_multiple_expressions_in_one_line = false
ij_groovy_keep_simple_blocks_in_one_line = false
ij_groovy_keep_simple_classes_in_one_line = true
ij_groovy_keep_simple_lambdas_in_one_line = true
ij_groovy_keep_simple_methods_in_one_line = true
ij_groovy_label_indent_absolute = false
ij_groovy_label_indent_size = 0
ij_groovy_lambda_brace_style = end_of_line
ij_groovy_layout_static_imports_separately = true
ij_groovy_line_comment_add_space = false
ij_groovy_line_comment_add_space_on_reformat = false
ij_groovy_line_comment_at_first_column = true
ij_groovy_method_annotation_wrap = split_into_lines
ij_groovy_method_brace_style = end_of_line
ij_groovy_method_call_chain_wrap = off
ij_groovy_method_parameters_new_line_after_left_paren = false
ij_groovy_method_parameters_right_paren_on_new_line = false
ij_groovy_method_parameters_wrap = off
ij_groovy_modifier_list_wrap = false
ij_groovy_names_count_to_use_import_on_demand = 3
ij_groovy_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
ij_groovy_parameter_annotation_wrap = off
ij_groovy_parentheses_expression_new_line_after_left_paren = false
ij_groovy_parentheses_expression_right_paren_on_new_line = false
ij_groovy_prefer_parameters_wrap = false
ij_groovy_resource_list_new_line_after_left_paren = false
ij_groovy_resource_list_right_paren_on_new_line = false
ij_groovy_resource_list_wrap = off
ij_groovy_space_after_assert_separator = true
ij_groovy_space_after_colon = true
ij_groovy_space_after_comma = true
ij_groovy_space_after_comma_in_type_arguments = true
ij_groovy_space_after_for_semicolon = true
ij_groovy_space_after_quest = true
ij_groovy_space_after_type_cast = true
ij_groovy_space_before_annotation_parameter_list = false
ij_groovy_space_before_array_initializer_left_brace = false
ij_groovy_space_before_assert_separator = false
ij_groovy_space_before_catch_keyword = true
ij_groovy_space_before_catch_left_brace = true
ij_groovy_space_before_catch_parentheses = true
ij_groovy_space_before_class_left_brace = true
ij_groovy_space_before_closure_left_brace = true
ij_groovy_space_before_colon = true
ij_groovy_space_before_comma = false
ij_groovy_space_before_do_left_brace = true
ij_groovy_space_before_else_keyword = true
ij_groovy_space_before_else_left_brace = true
ij_groovy_space_before_finally_keyword = true
ij_groovy_space_before_finally_left_brace = true
ij_groovy_space_before_for_left_brace = true
ij_groovy_space_before_for_parentheses = true
ij_groovy_space_before_for_semicolon = false
ij_groovy_space_before_if_left_brace = true
ij_groovy_space_before_if_parentheses = true
ij_groovy_space_before_method_call_parentheses = false
ij_groovy_space_before_method_left_brace = true
ij_groovy_space_before_method_parentheses = false
ij_groovy_space_before_quest = true
ij_groovy_space_before_record_parentheses = false
ij_groovy_space_before_switch_left_brace = true
ij_groovy_space_before_switch_parentheses = true
ij_groovy_space_before_synchronized_left_brace = true
ij_groovy_space_before_synchronized_parentheses = true
ij_groovy_space_before_try_left_brace = true
ij_groovy_space_before_try_parentheses = true
ij_groovy_space_before_while_keyword = true
ij_groovy_space_before_while_left_brace = true
ij_groovy_space_before_while_parentheses = true
ij_groovy_space_in_named_argument = true
ij_groovy_space_in_named_argument_before_colon = false
ij_groovy_space_within_empty_array_initializer_braces = false
ij_groovy_space_within_empty_method_call_parentheses = false
ij_groovy_spaces_around_additive_operators = true
ij_groovy_spaces_around_assignment_operators = true
ij_groovy_spaces_around_bitwise_operators = true
ij_groovy_spaces_around_equality_operators = true
ij_groovy_spaces_around_lambda_arrow = true
ij_groovy_spaces_around_logical_operators = true
ij_groovy_spaces_around_multiplicative_operators = true
ij_groovy_spaces_around_regex_operators = true
ij_groovy_spaces_around_relational_operators = true
ij_groovy_spaces_around_shift_operators = true
ij_groovy_spaces_within_annotation_parentheses = false
ij_groovy_spaces_within_array_initializer_braces = false
ij_groovy_spaces_within_braces = true
ij_groovy_spaces_within_brackets = false
ij_groovy_spaces_within_cast_parentheses = false
ij_groovy_spaces_within_catch_parentheses = false
ij_groovy_spaces_within_for_parentheses = false
ij_groovy_spaces_within_gstring_injection_braces = false
ij_groovy_spaces_within_if_parentheses = false
ij_groovy_spaces_within_list_or_map = false
ij_groovy_spaces_within_method_call_parentheses = false
ij_groovy_spaces_within_method_parentheses = false
ij_groovy_spaces_within_parentheses = false
ij_groovy_spaces_within_switch_parentheses = false
ij_groovy_spaces_within_synchronized_parentheses = false
ij_groovy_spaces_within_try_parentheses = false
ij_groovy_spaces_within_tuple_expression = false
ij_groovy_spaces_within_while_parentheses = false
ij_groovy_special_else_if_treatment = true
ij_groovy_ternary_operation_wrap = off
ij_groovy_throws_keyword_wrap = off
ij_groovy_throws_list_wrap = off
ij_groovy_use_flying_geese_braces = false
ij_groovy_use_fq_class_names = false
ij_groovy_use_fq_class_names_in_javadoc = true
ij_groovy_use_relative_indents = false
ij_groovy_use_single_class_imports = true
ij_groovy_variable_annotation_wrap = off
ij_groovy_while_brace_force = never
ij_groovy_while_on_new_line = false
ij_groovy_wrap_chain_calls_after_dot = false
ij_groovy_wrap_long_lines = false
[{*.har,*.json,*.png.mcmeta,mcmod.info,pack.mcmeta}]
indent_size = 4
ij_json_array_wrapping = split_into_lines
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_keep_trailing_comma = false
ij_json_object_wrapping = split_into_lines
ij_json_property_alignment = do_not_align
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = false
ij_json_space_before_comma = false
ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
ij_html_align_attributes = true
ij_html_align_text = false
ij_html_attribute_wrap = normal
ij_html_block_comment_add_space = false
ij_html_block_comment_at_first_column = true
ij_html_do_not_align_children_of_min_lines = 0
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
ij_html_enforce_quotes = false
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
ij_html_keep_blank_lines = 2
ij_html_keep_indents_on_empty_lines = false
ij_html_keep_line_breaks = true
ij_html_keep_line_breaks_in_text = true
ij_html_keep_whitespaces = false
ij_html_keep_whitespaces_inside = span,pre,textarea
ij_html_line_comment_at_first_column = true
ij_html_new_line_after_last_attribute = never
ij_html_new_line_before_first_attribute = never
ij_html_quote_style = double
ij_html_remove_new_line_before_tags = br
ij_html_space_after_tag_name = false
ij_html_space_around_equality_in_attribute = false
ij_html_space_inside_empty_tag = false
ij_html_text_wrap = normal
[{*.kt,*.kts}]
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
ij_kotlin_align_multiline_method_parentheses = false
ij_kotlin_align_multiline_parameters = true
ij_kotlin_align_multiline_parameters_in_calls = false
ij_kotlin_allow_trailing_comma = false
ij_kotlin_allow_trailing_comma_on_call_site = false
ij_kotlin_assignment_wrap = off
ij_kotlin_blank_lines_after_class_header = 0
ij_kotlin_blank_lines_around_block_when_branches = 0
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
ij_kotlin_block_comment_add_space = false
ij_kotlin_block_comment_at_first_column = true
ij_kotlin_call_parameters_new_line_after_left_paren = false
ij_kotlin_call_parameters_right_paren_on_new_line = false
ij_kotlin_call_parameters_wrap = off
ij_kotlin_catch_on_new_line = false
ij_kotlin_class_annotation_wrap = split_into_lines
ij_kotlin_continuation_indent_for_chained_calls = true
ij_kotlin_continuation_indent_for_expression_bodies = true
ij_kotlin_continuation_indent_in_argument_lists = true
ij_kotlin_continuation_indent_in_elvis = true
ij_kotlin_continuation_indent_in_if_conditions = true
ij_kotlin_continuation_indent_in_parameter_lists = true
ij_kotlin_continuation_indent_in_supertype_lists = true
ij_kotlin_else_on_new_line = false
ij_kotlin_enum_constants_wrap = off
ij_kotlin_extends_list_wrap = off
ij_kotlin_field_annotation_wrap = split_into_lines
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = false
ij_kotlin_import_nested_classes = false
ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 2
ij_kotlin_keep_blank_lines_in_code = 2
ij_kotlin_keep_blank_lines_in_declarations = 2
ij_kotlin_keep_first_column_comment = true
ij_kotlin_keep_indents_on_empty_lines = false
ij_kotlin_keep_line_breaks = true
ij_kotlin_lbrace_on_next_line = false
ij_kotlin_line_break_after_multiline_when_entry = true
ij_kotlin_line_comment_add_space = false
ij_kotlin_line_comment_add_space_on_reformat = false
ij_kotlin_line_comment_at_first_column = true
ij_kotlin_method_annotation_wrap = split_into_lines
ij_kotlin_method_call_chain_wrap = off
ij_kotlin_method_parameters_new_line_after_left_paren = false
ij_kotlin_method_parameters_right_paren_on_new_line = false
ij_kotlin_method_parameters_wrap = off
ij_kotlin_name_count_to_use_star_import = 5
ij_kotlin_name_count_to_use_star_import_for_members = 3
ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.**
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
ij_kotlin_space_after_type_colon = true
ij_kotlin_space_before_catch_parentheses = true
ij_kotlin_space_before_comma = false
ij_kotlin_space_before_extend_colon = true
ij_kotlin_space_before_for_parentheses = true
ij_kotlin_space_before_if_parentheses = true
ij_kotlin_space_before_lambda_arrow = true
ij_kotlin_space_before_type_colon = false
ij_kotlin_space_before_when_parentheses = true
ij_kotlin_space_before_while_parentheses = true
ij_kotlin_spaces_around_additive_operators = true
ij_kotlin_spaces_around_assignment_operators = true
ij_kotlin_spaces_around_equality_operators = true
ij_kotlin_spaces_around_function_type_arrow = true
ij_kotlin_spaces_around_logical_operators = true
ij_kotlin_spaces_around_multiplicative_operators = true
ij_kotlin_spaces_around_range = false
ij_kotlin_spaces_around_relational_operators = true
ij_kotlin_spaces_around_unary_operator = false
ij_kotlin_spaces_around_when_arrow = true
ij_kotlin_variable_annotation_wrap = off
ij_kotlin_while_on_new_line = false
ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 0
ij_kotlin_wrap_first_method_in_call_chain = false
[{*.markdown,*.md}]
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
ij_markdown_force_one_space_after_list_bullet = true
ij_markdown_force_one_space_between_words = true
ij_markdown_format_tables = true
ij_markdown_insert_quote_arrows_on_wrap = true
ij_markdown_keep_indents_on_empty_lines = false
ij_markdown_keep_line_breaks_inside_text_blocks = true
ij_markdown_max_lines_around_block_elements = 1
ij_markdown_max_lines_around_header = 1
ij_markdown_max_lines_between_paragraphs = 1
ij_markdown_min_lines_around_block_elements = 1
ij_markdown_min_lines_around_header = 1
ij_markdown_min_lines_between_paragraphs = 1
ij_markdown_wrap_text_if_long = true
ij_markdown_wrap_text_inside_blockquotes = true
[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}]
ij_toml_keep_indents_on_empty_lines = false
[{*.yaml,*.yml}]
indent_size = 4
ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false
ij_yaml_indent_sequence_value = true
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_sequence_on_new_line = false
ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true
+6
View File
@@ -5,6 +5,7 @@ run/
out/
*.iml
.gradle/
.gradle-cache/
output/
bin/
libs/
@@ -22,9 +23,14 @@ hs_err_pid*
**/src/generated/
Merged/
# Folder created by the buildAll scripts
buildAllJars/
# file from notepad++
*.bak
# file genearated via MC version switching using preprocessor
build.properties
# Sqlite databases
*.sqlite
+77 -226
View File
@@ -1,41 +1,22 @@
# use Eclipse's JDK
image: gradle:eclipse-temurin
# The ci should always use an unix/unix-like OS to work
# The ci should always use a unix(-like) OS to work
image: eclipse-temurin:17
# all stages need to be defined here
# TODO: Make stages depend on what is in versionProperties
stages:
# TODO: Make stages depending on what is in versionProperties
- build_1_18_2
- build
- api
- pages
variables:
# Pull core when building
GIT_SUBMODULE_STRATEGY: recursive
# Pull core when building
GIT_SUBMODULE_STRATEGY: recursive
# 1.18.2 build
build_1_18_2:
stage: build_1_18_2
script:
# this both runs the unit tests and assembles the code
- ./gradlew build -PmcVer="1.18.2" --gradle-user-home cache/;
image: eclipse-temurin:17
artifacts:
name: "NightlyBuild_1_18_2-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
- fabric/build/libs/*.jar
- forge/build/libs/*.jar
- quilt/build/libs/*.jar
exclude:
# TODO: There is a lot of duplicate stuff here, fix it later
- fabric/build/libs/*-all.jar
- fabric/build/libs/*-sources.jar
- forge/build/libs/*-all.jar
- forge/build/libs/*-sources.jar
- quilt/build/libs/*-all.jar
- quilt/build/libs/*-sources.jar
expire_in: 1 day
when: always
# These can be extended so code is a bit less duplicated
.build_java:
#image: eclipse-temurin:17
cache:
key: "gradleCache"
policy: pull-push
@@ -45,203 +26,73 @@ build_1_18_2:
allow_failure: true
build:
stage: build
parallel:
matrix:
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2"]
script:
# this both runs the unit tests and assembles the code
- ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
- ./gradlew build -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
artifacts:
name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
- Merged/*.jar
- fabric/build/libs/*.jar
- forge/build/libs/*.jar
- quilt/build/libs/*.jar
exclude:
# TODO: There is a lot of duplicate stuff here, try to maybe make it smaller
- fabric/build/libs/*-all.jar
- fabric/build/libs/*-sources.jar
- forge/build/libs/*-all.jar
- forge/build/libs/*-sources.jar
- quilt/build/libs/*-all.jar
- quilt/build/libs/*-sources.jar
expire_in: 14 days
when: always
extends: .build_java
api:
stage: api
needs: []
script:
# this should only run for the API
- ./gradlew api:clean --gradle-user-home cache/;
# this also runs unit tests
- ./gradlew api:build --gradle-user-home cache/;
- ./gradlew api:addSourcesToCompiledJar --gradle-user-home cache/;
artifacts:
name: "Api_NightlyBuild-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths:
- coreSubProjects/api/build/libs/merged/*.jar
# can be uncommented if we don't want a jar with the source code
# - coreSubProjects/api/build/libs/*.jar
exclude:
- coreSubProjects/api/build/libs/merged/*-all.jar
- coreSubProjects/api/build/libs/merged/*-sources.jar
expire_in: 1 day
when: always
extends: .build_java
# ============================== Previos CI for future reference ============================== #
## all stages need to be defined here
# # Don't build the standalone jar yet because it isn't done yet
# # - build_standalone
#stages:
# - build_19
# - build_18_2
# - build_18_1
# - build_17_1
# - build_16_5
#
#variables:
# # Pull core when building
# GIT_SUBMODULE_STRATEGY: recursive
#
#
#before_script:
# - echo $CI_JOB_ID
# # Writing GE_JOB_ID variable to environment file, will need the value in the next stage.
# - echo GE_JOB_ID=$CI_JOB_ID >> generate_jars.env
#
#
## The standalone build
##build_standalone:
## stage: build_standalone
## script:
## # make sure any previously merged jars are removed before running this job.
## # note: if the merged folder doesn't exist "rm -R Merged" will throw an error, which can be ignored
## # the "|| true" makes that step always succeed.
## - ./gradlew core:build --gradle-user-home cache/;
##
## # Copy the file with the shortest name to the root DistantHorizons.jar so it can be sent off
## - cp $(find core/build/libs/ | awk 'function base(f){sub(".*/", "", f); return f;} {print length(base($0)), $0}'| sort -n | head -2 | grep -P "[0-9][0-9] core/build/libs/*" | #sed -r "s/([0-9][0-9] )//g") DistantHorizons.jar
## # build using Java 16
## image: eclipse-temurin:16
## artifacts:
## name: "NightlyBuild_standalone-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
## paths:
## # Get the standalone jar
## - DistantHorizons.jar
## expire_in: 1 day
## # even if one build fails, upload the successful jars
## when: always
## cache:
## key: "gradleCache"
## policy: pull-push
## paths:
## - .gradle
## - cache/
## allow_failure: true
#
#
## 1.16.5 build
#build_16_5:
# stage: build_16_5
# script:
# # make sure any previously merged jars are removed before running this job.
# # note: if the merged folder doesn't exist "rm -R Merged" will throw an error, which can be ignored
# # the "|| true" makes that step always succeed.
# - ./gradlew test --gradle-user-home cache/;
# - ./gradlew build -PmcVer="1.16.5" --gradle-user-home cache/;
# # build using Java 16
# image: eclipse-temurin:16
# artifacts:
# name: "Merged_NightlyBuild_1_16_5-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
# paths:
# # relative to the root directory
# - Merged
# expire_in: 1 day
# # even if one build fails, upload the successful jars
# when: always
# cache:
# key: "gradleCache"
# policy: pull-push
# paths:
# - .gradle
# - cache/
# allow_failure: true
#
## 1.17.1 build
#build_17_1:
# stage: build_17_1
# script:
# - ./gradlew test --gradle-user-home cache/;
# - ./gradlew build -PmcVer="1.17.1" --gradle-user-home cache/;
# image: eclipse-temurin:16
# artifacts:
# name: "Merged_NightlyBuild_1_17_1-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
# paths:
# - Merged
# expire_in: 1 day
# when: always
# cache:
# key: "gradleCache"
# policy: pull-push
# paths:
# - .gradle
# - cache/
# allow_failure: true
#
## 1.18.1 build
#build_18_1:
# stage: build_18_1
# script:
# - ./gradlew test --gradle-user-home cache/;
# - ./gradlew build -PmcVer="1.18.1" --gradle-user-home cache/;
# # build using Java 17
# image: eclipse-temurin:17
# artifacts:
# name: "Merged_NightlyBuild_1_18_1-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
# paths:
# - Merged
# expire_in: 1 day
# when: always
# cache:
# key: "gradleCache"
# policy: pull-push
# paths:
# - .gradle
# - cache/
# allow_failure: true
#
## 1.18.2 build
#build_18_2:
# stage: build_18_2
# script:
# - ./gradlew test --gradle-user-home cache/;
# - ./gradlew build -PmcVer="1.18.2" --gradle-user-home cache/;
# image: eclipse-temurin:17
# artifacts:
# name: "Merged_NightlyBuild_1_18_2-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
# paths:
# - Merged
# expire_in: 1 day
# when: always
# cache:
# key: "gradleCache"
# policy: pull-push
# paths:
# - .gradle
# - cache/
# allow_failure: true
#
## 1.19 build
#build_19:
# stage: build_19
# script:
# - ./gradlew test --gradle-user-home cache/;
# - ./gradlew build -PmcVer="1.19" --gradle-user-home cache/;
# image: eclipse-temurin:17
# artifacts:
# name: "Merged_NightlyBuild_1_19-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
# paths:
# - Merged
# expire_in: 1 day
# when: always
# cache:
# key: "gradleCache"
# policy: pull-push
# paths:
# - .gradle
# - cache/
# allow_failure: true
# unused deployment stage
#deploy:
# stage: deploy
# image: registry.gitlab.com/gitlab-org/release-cli:latest
# script:
# - echo 'running release_job'
# - echo 'Previous Job ID is printed below'
# - echo $GE_JOB_ID
# # Specifying that this job requires artifacts from the previous job to succeed
# needs:
# - job: build
# artifacts: true
# release:
# name: 'Unstable Jars for Latest Commit' #: $CI_COMMIT_SHORT_SHA'
# description: 'Created automatically using the release-cli.'
# # tag_name is a mendatory field and can not be an empty string
# tag_name: 'Unstable-$CI_COMMIT_SHORT_SHA'
# assets:
# links:
# - name: 'Fabric Jars'
# url: 'https://gitlab.com/jeseibel/minecraft-lod-mod/cw/-/jobs/${GE_JOB_ID}/artifacts/file/fabric/build/libs'
# - name: 'Forge Jars'
# url: 'https://gitlab.com/jeseibel/minecraft-lod-mod/cw/-/jobs/${GE_JOB_ID}/artifacts/file/forge/build/libs'
# generate and publish API javadocs
pages:
stage: pages
needs: []
script:
# this should only run for the API
- ./gradlew api:clean --gradle-user-home cache/;
# this also runs unit tests
- ./gradlew api:build --gradle-user-home cache/;
- ./gradlew api:javadoc --gradle-user-home cache/;
- mkdir public
- cp -r $CI_PROJECT_DIR/coreSubProjects/api/build/docs/javadoc/. public
artifacts:
paths:
- public
allow_failure: false
extends: .build_java
+1 -1
View File
@@ -1,4 +1,4 @@
## Chcek off each item in this list before submitting:
## 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]"
+12
View File
@@ -0,0 +1,12 @@
FROM eclipse-temurin:17-jdk
WORKDIR /home/build/
COPY ./gradlew .
RUN chmod +x ./gradlew
CMD echo "\r========== [CLEAN: $MC_VER] ==========" && \
./gradlew clean -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
echo "\r========== [BUILD: $MC_VER] ==========" && \
./gradlew build -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
echo "\r========== [MERGE: $MC_VER] ==========" && \
./gradlew mergeJars -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
echo "\r========== [DONE: $MC_VER] =========="
+73 -40
View File
@@ -18,49 +18,70 @@ If you want to see a quick demo, check out a video covering the mod here:
## Minecraft and Library Versions
This branch supports the following versions of Minecraft:
### This branch supports the following versions of Minecraft:
> **NOTE: At the moment only 1.18.2 fabric works/build/runs**
#### 1.20.2
Fabric: 0.14.24\
Fabric API: 0.90.4+1.20.2\
Forge: 48.0.13\
Parchment: 1.20.1:2023.09.03\
Modmenu: 8.0.0
#### 1.19 (BROKE)
Forge: 41.0.19\
Fabric: 0.14.7\
Fabric API: 0.55.3+1.19\
Modmenu: 4.0.0
#### 1.20.1, 1.20 (Default)
Fabric: 0.14.24\
Fabric API: 0.90.4+1.20.1\
Forge: 47.2.1\
Parchment: 1.20.1:2023.09.03\
Modmenu: 7.2.2
#### 1.18.2 (ONLY FABRIC WORKS)
Fabric: 0.14.14\
Fabric API: 0.67.1+1.18.2\
Forge: 40.0.32\
Parchment=2022.11.06\
#### 1.19.4
Fabric: 0.14.24\
Fabric API: 0.87.1+1.19.4\
Forge: 45.2.4\
Parchment: 1.19.4:2023.06.26\
Modmenu: 6.3.1
#### 1.19.2
Fabric: 0.14.24\
Fabric API: 0.76.1+1.19.2\
Forge: 43.3.2\
Parchment: 1.19.2:2022.11.27\
Modmenu: 4.2.0-beta.2
#### 1.18.2
Fabric: 0.14.24\
Fabric API: 0.76.0+1.18.2\
Forge: 40.2.10\
Parchment: 1.18.2:2022.11.06\
Modmenu: 3.2.5
#### 1.18.1, 1.18 (CEASING DEVELOPMENT SOON)
Forge: 39.1.2\
Fabric: 0.13.3\
Fabric API: 0.42.6+1.18\
Modmenu: 3.0.1
#### 1.17.1, 1.17 (BROKE)
Forge: 37.1.1\
Fabric: 0.13.2\
#### 1.17.1, 1.17
Fabric: 0.14.24\
Fabric API: 0.46.1+1.17\
Forge: 37.1.1\
Parchment: 1.17.1:2021.12.12\
Modmenu: 2.0.14
#### 1.16.5, 1.16.5 (BROKE)
Forge: 36.2.28\
Fabric: 0.13.2\
#### 1.16.5, 1.16.4
Fabric: 0.14.24\
Fabric API: 0.42.0+1.16\
Forge: 36.2.39\
Parchment: 1.16.5:2022.03.06\
Modmenu: 1.16.22
### Versions no longer supported
- 1.18.1, 1.18
- 1.19.1, 1.19
- 1.19.3
<br><br>
### Plugin and Library versions
Fabric loom: 1.1-snapshot\
Forge gradle: 5.1.67\
Fabric loom: 1.1.+\
Forge gradle (Using Architectury): 3.4-SNAPSHOT\
Sponge vanilla gradle: 0.2.1-SNAPSHOT\
Sponge mixin gradle: 0.7-SNAPSHOT\
Java Compiler plugin: Manifold Preprocessor
Sponge mixin: 0.8.5\
Java Preprocessor plugin: Manifold Preprocessor
<br>
@@ -95,7 +116,8 @@ If running in an IDE, to ensure the IDE noticed the version change, run any grad
>Note: There may be a `java.nio.file.FileSystemException` thrown when running the command after switching versions. To fix it, either restart your IDE (as your IDE is probably locking a file) or use a tool like LockHunter to unlock the linked file(s). (Generally it is a lib file under `common\build\lib`, `forge\build\lib`, or `fabric\build\lib`). \
> If anyone knows how to solve this issue please let us know here: \
> https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/233
<br>
## Compiling
@@ -107,14 +129,16 @@ From the File Explorer:
1. Download and extract the project zip
2. Download the core from https://gitlab.com/jeseibel/distant-horizons-core and extract into a folder called `coreSubProjects`
3. Open a terminal emulator in the project folder (On Windows you can type `cmd` in the title bar)
4. Run the commands: `./gradlew assemble`
5. The compiled jar file will be in the folder `Merged`
4. Run the commands: `./gradlew assemble` (You may need to use a `.\` on Windows)
5. Merge the jars wih `./gradlew mergeJars`
6. The compiled jar file will be in the folder `Merged`
From the command line:
1. `git clone --recurse-submodules https://gitlab.com/jeseibel/minecraft-lod-mod.git`
2. `cd minecraft-lod-mod`
3. `./gradlew assemble`
4. The compiled jar file will be in the folder `Merged`
4. `./gradlew mergeJars`
5. The compiled jar file will be in the folder `Merged`
Run tests with: `./gradlew test`
@@ -122,7 +146,16 @@ Run tests with: `./gradlew test`
> Example: `./gradlew assemble -PmcVer=1.18.2`
<Br>
<br>
## Compiling with Docker
`./compile <version>`
You can also locally compile the DH jars without a Java environment by using Docker. Where `<version>` is the version of Minecraft to compile for (ie `1.20.1`), or the keyword `all`. See [Versions](#minecraft-and-library-versions) for a list of all supported values.
<br>
## Other commands
@@ -158,17 +191,17 @@ To build all versions: `./buildAll` (all builds will end up in the `Merged` fold
## Open Source Acknowledgements
Forgix (To merge multiple mod versions into one jar) [_Used to be_ [_DHJarMerger_](https://github.com/Ran-helo/DHJarMerger)]\
Forgix (To merge multiple mod versions into one jar) [_Formerly_ [_DHJarMerger_](https://github.com/Ran-helo/DHJarMerger)]\
https://github.com/PacifistMC/Forgix
XZ for Java (data compression)\
https://tukaani.org/xz/java.html
LZ4 for Java (data compression)\
https://github.com/lz4/lz4-java
Json & Toml for Java (config handling)\
NightConfig for Json & Toml (config handling)\
https://github.com/TheElectronWill/night-config
SVG Salamander for SVG support\
SVG Salamander for SVG support (not being used atm)\
https://github.com/blackears/svgSalamander
FlatLaf for theming (for development testing, may remove later)\
https://www.formdev.com/flatlaf/
sqlite-jdbc\
https://github.com/xerial/sqlite-jdbc
+154 -66
View File
@@ -12,6 +12,9 @@ plugins {
// Provides mc libraries to core
// id "org.spongepowered.gradle.vanilla" version '0.2.1-SNAPSHOT' apply false
// Architectury is used here only as a replacement for forge's own loom
id "dev.architectury.loom" version "1.1.+" apply false
}
/**
@@ -25,13 +28,28 @@ def writeBuildGradlePredefine(List<String> mcVers, int mcIndex) {
for (int i = 0; i < mcVers.size(); i++) {
String mcStr = mcVers[i].replace(".", "_")
if (mcIndex < i) {
// exclusive before
// FIXME doesn't function correctly for 1.16.5 (IE the first item in the list)
redefineList.add("PRE_MC_" + mcStr)
}
if (mcIndex <= i) {
// inclusive before
redefineList.add("PRE_AND_MC_" + mcStr)
}
if (mcIndex == i) {
// exact
redefineList.add("MC_" + mcStr)
}
if (mcIndex > i) {
// inclusive after
redefineList.add("POST_AND_MC_" + mcStr)
}
if (mcIndex >= i) {
// exclusive after
redefineList.add("POST_MC_" + mcStr)
}
}
@@ -73,19 +91,25 @@ writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm")
// Forgix settings (used for merging jars)
forgix {
group = "com.seibel.lod"
group = "com.seibel.distanthorizons"
mergedJarName = "DistantHorizons-${rootProject.versionStr}.jar"
forge {
jarLocation = "build/libs/DistantHorizons-forge-${rootProject.versionStr}.jar"
}
fabric {
jarLocation = "build/libs/DistantHorizons-fabric-${rootProject.versionStr}.jar"
}
if (findProject(":forge"))
forge {
jarLocation = "build/libs/DistantHorizons-forge-${rootProject.versionStr}.jar"
}
removeDuplicate "com.seibel.lod.api"
removeDuplicate "com.seibel.lod.core"
if (findProject(":fabric"))
fabric {
jarLocation = "build/libs/DistantHorizons-fabric-${rootProject.versionStr}.jar"
}
if (findProject(":quilt"))
quilt {
jarLocation = "build/libs/DistantHorizons-quilt-${rootProject.versionStr}.jar"
}
removeDuplicate "com.seibel.distanthorizons"
}
subprojects { p ->
@@ -103,6 +127,10 @@ subprojects { p ->
apply plugin: "application"
// apply plugin: "org.spongepowered.gradle.vanilla" // Provides minecraft libraries
// Apply forge's loom
if (findProject(":forge") && p == project(":forge"))
apply plugin: "dev.architectury.loom"
// Set the manifold version (may not be required tough)
manifold {
@@ -117,6 +145,21 @@ subprojects { p ->
// have implemented dependencies automatically embedded in the final jar
implementation.extendsFrom(shadowMe)
// Configuration fpr core & api
coreProjects
shadowMe.extendsFrom(coreProjects)
// FIXME this additional configuration is necessary because forge
// needs forgeRuntimeLibrary, although adding it to shadowMe
// causes runtime issues where the libraries aren't properly added
forgeShadowMe
// this should match shadowMe pretty closely
implementation.extendsFrom(forgeShadowMe)
shadowMe.extendsFrom(forgeShadowMe)
forgeRuntimeLibrary.extendsFrom(forgeShadowMe)
if (isMinecraftSubProject && p != project(":common")) {
// Shadow common
common
@@ -124,6 +167,9 @@ subprojects { p ->
compileClasspath.extendsFrom common
runtimeClasspath.extendsFrom common
developmentForge.extendsFrom common
compileClasspath.extendsFrom coreProjects
runtimeClasspath.extendsFrom coreProjects
developmentForge.extendsFrom coreProjects
if (findProject(":fabricLike") && p != project(":fabricLike")) {
// Shadow fabricLike
@@ -141,7 +187,7 @@ subprojects { p ->
// (This will point to a non-existent class in all sub-projects except "Core")
if (p == project(":core")) {
application {
mainClass.set("com.seibel.lod.core.jar.JarMain")
mainClass.set("com.seibel.distanthorizons.core.jar.JarMain")
}
}
@@ -150,13 +196,15 @@ subprojects { p ->
// shared dependencies //
//=====================//
// Manifold
if (isMinecraftSubProject)
if (isMinecraftSubProject) {
annotationProcessor("systems.manifold:manifold-preprocessor:${rootProject.manifold_version}")
}
// Log4j
// TODO: Change to shadowMe later to work in the standalone jar
// We cannot do this now as it would break Quilt
implementation("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
@@ -169,22 +217,24 @@ subprojects { p ->
implementation("junit:junit:4.13")
// Compression
shadowMe("org.tukaani:xz:1.9")
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}")
// Sqlite Database
forgeShadowMe("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}")
// NightConfig (includes Toml & Json)
shadowMe("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
shadowMe("com.electronwill.night-config:json:${rootProject.nightconfig_version}")
forgeShadowMe("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
forgeShadowMe("com.electronwill.night-config:json:${rootProject.nightconfig_version}")
// Theming
shadowMe("com.formdev:flatlaf:${rootProject.flatlaf_version}")
// SVG (not needed atm)
// forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}")
// Netty
// Breaks 1.16.5
//forgeShadowMe("io.netty:netty-all:${rootProject.netty_version}")
// SVG
shadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}")
shadowMe("com.formdev:flatlaf-extras:${rootProject.flatlaf_version}")
// Remember, for lwjgl dependancies that arent included in Minecraft, you need to also need to add it to the ShadowJar thing
shadowMe("org.lwjgl:lwjgl-jawt:3.2.2") {
// Remember, for lwjgl dependencies that arent included in Minecraft, you need to also need to add it to the ShadowJar thing
forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") {
exclude group: "org.lwjgl", module: "lwjgl" // This module is imported by Minecraft so exclude it
}
@@ -197,7 +247,7 @@ subprojects { p ->
// Add core
if (isMinecraftSubProject) {
shadowMe(project(":core")) {
coreProjects(project(":core")) {
// Remove Junit test libraries
exclude group: "org.junit.jupiter", module: "junit-jupiter"
exclude group: "org.junit.jupiter", module: "junit-jupiter-engine"
@@ -209,7 +259,7 @@ subprojects { p ->
// Add the api
if (p != project(":api")) {
shadowMe(project(":api")) {
coreProjects(project(":api")) {
// Remove Junit test libraries
exclude group: "org.junit.jupiter", module: "junit-jupiter"
exclude group: "org.junit.jupiter", module: "junit-jupiter-engine"
@@ -222,8 +272,8 @@ subprojects { p ->
// Add common
if (isMinecraftSubProject && p != project(":common")) {
// Common
common(project(path: ":common")) { transitive false }
shadowCommon(project(path: ":common")) { transitive false }
common(project(":common")) { transitive false }
shadowCommon(project(":common")) { transitive false }
// FabricLike
if (findProject(":fabricLike") && p != project(":fabricLike")) {
@@ -238,11 +288,11 @@ subprojects { p ->
configurations = [project.configurations.shadowMe]
if (isMinecraftSubProject && p != project(":common")) {
configurations.push(project.configurations.shadowCommon) // Shadow the common subproject
relocate "com.seibel.lod.common", "loaderCommon.${p.name}.com.seibel.lod.common" // Move the loader files to a different location
relocate "com.seibel.distanthorizons.common", "loaderCommon.${p.name}.com.seibel.distanthorizons.common" // Move the loader files to a different location
if (findProject(":fabricLike") && p != project(":fabricLike")) {
configurations.push(project.configurations.shadowFabricLike) // Shadow the fabricLike subproject
relocate "com.seibel.lod.fabriclike", "loaderCommon.${p.name}.com.seibel.lod.fabriclike" // Move the loader files to a different location
relocate "com.seibel.distanthorizons.fabriclike", "loaderCommon.${p.name}.com.seibel.distanthorizons.fabriclike" // Move the loader files to a different location
}
}
def librariesLocation = "distanthorizons.libraries"
@@ -251,17 +301,21 @@ subprojects { p ->
// Only ever shadow the dependencies we use otherwise some stuff would break when running on an external client
relocate "org.lwjgl.system.jawt", "${librariesLocation}.lwjgl.system.jawt"
// Compression
relocate "org.tukaani", "${librariesLocation}.tukaani"
// Compression (LZ4)
relocate "net.jpountz", "${librariesLocation}.jpountz"
// Sqlite Database
//At the moment, there is a bug in this library which doesnt allow it to be relocated
// relocate "org.sqlite", "${librariesLocation}.sqlite"
// NightConfig (includes Toml & Json)
relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig"
// Theming
relocate 'com.formdev.flatlaf', 'distanthorizons.libraries.formdev.flatlaf'
// SVG
relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg"
// SVG (not needed atm)
// relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg"
// Netty
relocate "io.netty", "${librariesLocation}.netty"
mergeServiceFiles()
}
@@ -272,13 +326,17 @@ subprojects { p ->
// Put stuff from gradle.properties into the mod info
processResources {
def resourceTargets = [ // Location of where to inject the properties
// Properties for each of the loaders
"fabric.mod.json",
"quilt.mod.json",
"META-INF/mods.toml",
// Holds info like git commit
// TODO: For some reason this script doesnt work with the core project
"build_info.json",
// The mixins for each of the loaders
"DistantHorizons."+ p.name +".fabricLike.mixins.json"
// Properties for each of the loaders
"fabric.mod.json",
"quilt.mod.json",
"META-INF/mods.toml",
// The mixins for each of the loaders
"DistantHorizons."+ p.name +".fabricLike.mixins.json"
]
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
@@ -301,6 +359,17 @@ subprojects { p ->
// TODOI: Find something we can use so we can basically re-map only when the jar is shadowed and relocated
// println p.tasks.findByName('shadowJar')
// These "hasProperty"'s are so that they can be passed through the cli (ie in the CI)
try {
if (infoGitCommit == "null")
infoGitCommit = 'git rev-parse --verify HEAD'.execute().text.trim()
if (infoGitBranch == "null")
infoGitBranch = 'git symbolic-ref --short HEAD'.execute().text.trim()
} catch (Exception e) {
infoGitCommit = infoGitBranch = "Git not found"
println "Git or Git project not found"
}
def replaceProperties = [
version : mod_version,
@@ -316,7 +385,14 @@ subprojects { p ->
compatible_minecraft_versions: compatible_minecraft_versions,
compatible_forgemc_versions : compatible_forgemc_versions,
java_version : java_version,
quilt_contributors : "{"+quilt_contributors.join(", ")+"}"
quilt_contributors : "{"+quilt_contributors.join(", ")+"}",
info_git_commit : infoGitBranch,
info_git_branch : infoGitCommit,
info_build_source : infoBuildSource,
fabric_incompatibility_list : fabric_incompatibility_list,
fabric_recommend_list : fabric_recommend_list,
]
// The left side is what gets replaced in the mod info and the right side is where to get it from in the gradle.properties
@@ -337,6 +413,23 @@ subprojects { p ->
}
}
}
// ==================== Delete un-needed files ====================
exclude "DistantHorizons.fabricLike.mixins.json" // This isnt required atm, but we will be using it later
// exclude "*.distanthorizons.accesswidener"
//// include "${accessWidenerVersion}.distanthorizons.accesswidener"
// Jank solution to remove all unused accesswideners
// The line above would work..., except forge requires the original accesswidener file, meaning we require this jank solution to keep it
exclude { file ->
if (file.name.contains(".distanthorizons.accesswidener") && file.name != "${accessWidenerVersion}.distanthorizons.accesswidener") {
return true
}
return false
}
}
@@ -346,7 +439,7 @@ subprojects { p ->
manifest {
attributes 'Implementation-Title': rootProject.mod_name,
'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.lod.core.jar.JarMain' // When changing the main of the jar change this line
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain' // When changing the main of the jar change this line
}
}
@@ -383,12 +476,16 @@ allprojects { p ->
apply plugin: "java"
apply plugin: "maven-publish"
archivesBaseName = rootProject.mod_name
version = project.name + "-" + rootProject.versionStr
group = rootProject.maven_group
// this is the text that appears at the top of the overview (home) page
// and is used when bookmarking a page
javadoc.title = rootProject.mod_name + "-" + project.name
repositories {
// The central repo
mavenCentral()
@@ -462,10 +559,11 @@ allprojects { p ->
// Set the OS lwjgl is using to the current os
project.ext.lwjglNatives = "natives-" + os.toFamilyName()
dependencies { // All of these dependancies are in Vanilla Minecraft, but we need to depend on it as we arent importing Minecraft in the core
dependencies { // All of these dependencies are in Vanilla Minecraft, but we need to depend on it as we arent importing Minecraft in the core
// Imports most of lwjgl's libraries (well, only the ones that we need)
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}") // TODO: Use Minecraft's version for lwjgl_version (which changes each version) instead of a hard defined version for all versions
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}") // TODO: Use Minecraft's version for lwjgl_version (which changes in nearly every version) instead of a hard defined version for all versions
// REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use
implementation "org.lwjgl:lwjgl"
implementation "org.lwjgl:lwjgl-assimp"
implementation "org.lwjgl:lwjgl-glfw"
@@ -494,9 +592,10 @@ allprojects { p ->
task copyCommonLoaderResources(type: Copy) {
from project(":common").file("src/main/resources/${accessWidenerVersion}.lod.accesswidener")
from project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
into(file(p.file("build/resources/main")))
rename "${accessWidenerVersion}.lod.accesswidener", "lod.accesswidener"
rename "${accessWidenerVersion}.distanthorizons.accesswidener", "distanthorizons.accesswidener"
// Move the fabricLike mixin to its different places for each subproject
if (findProject(":fabricLike")) {
@@ -511,17 +610,6 @@ allprojects { p ->
into p.file("build/resources/main")
}
// TODO: This method doesnt seem to actually remove it from the jar, probably called at incorrect spots
task deleteDuplicatedCommonLoaderResources(type: Delete) {
// Delete the duplicated fabricLike.mixins.json
delete p.file("build/resources/main/DistantHorizons.fabricLike.mixins.json")
// Delete all the duplicated accesswideners
delete fileTree(p.file("build/resources/main")) {
include "*.lod.accesswidener"
}
}
tasks.withType(JavaCompile) {
if (isMinecraftSubProject) {
options.release = rootProject.java_version as Integer
+13 -6
View File
@@ -1,15 +1,22 @@
#!/bin/sh
echo "==================== Note: All build jars will be in the folder called 'buildAllJars' ===================="
mkdir -p buildAllJars | true
# Loop trough everything in the version properties folder
for d in versionProperties/*; do
# Get the name of the version that is going to be compiled
version=$(echo "$d" | sed "s/versionProperties\///" | sed "s/.properties//")
# Clean out the folders and build it
# Clean out the folders, build it, and merge it
# (We could use "./" to run gradlew, but as it is a shell script im going to be running it with the "sh" command)
echo "Cleaning workspace to build $version"
sh gradlew clean -PmcVer=$version --no-daemon | true
echo "Building $version"
sh gradlew build -PmcVer=$version --no-daemon | true
# The "| true" at the end of those 2 are just to make sure the script continues even if a build fails
echo "==================== Cleaning workspace to build $version ===================="
sh gradlew clean -PmcVer=$version --no-daemon || true
echo "====================Building $version ===================="
sh gradlew build -PmcVer=$version --no-daemon || true
echo "==================== Merging $version ===================="
sh gradlew mergeJars -PmcVer=$version --no-daemon || true
echo "==================== Moving jar ===================="
mv Merged/*.jar buildAllJars/ || true
# The "| true" at the end of those are just to make sure the script continues even if a build fails
done
+20 -20
View File
@@ -1,25 +1,25 @@
@echo off
echo Windows build all script needs to be rewritten
echo I dont use Windows so I cant really make this
echo So if someone does use Windows and knows how to script stuff then can you please port the "buildall" script I made for Unix
@echo off & setlocal enabledelayedexpansion
@rem Note for devs: If this script doesnt work, please look at the unix buildAll script
@rem This script was originally created on linux, so there may be some problems with this translation
echo ==================== Note: All build jars will be in the folder called 'buildAllJars' ====================
mkdir buildAllJars
@rem Loop trough everything in the version properties folder
for %%f in (versionProperties\*) do (
@rem Get the name of the version that is going to be compiled
set version=%%~nf
@REM Old BAT script if you need some help with this task
@rem Clean out the folders, build it, and merge it
echo ==================== Cleaning workspace to build !version! ====================
call .\gradlew.bat clean -PmcVer="!version!" --no-daemon
echo ==================== Building !version! ====================
call .\gradlew.bat build -PmcVer="!version!" --no-daemon
echo ==================== Merging !version! ====================
call .\gradlew.bat mergeJars -PmcVer="!version!" --no-daemon
echo ==================== Moving jar ====================
move Merged\*.jar buildAllJars\
)
@REM SETLOCAL
@REM CALL :buildVersion "1.19"
@REM CALL :buildVersion "1.18.2"
@REM CALL :buildVersion "1.18.1"
@REM CALL :buildVersion "1.17.1"
@REM CALL :buildVersion "1.16.5"
@REM EXIT /B %ERRORLEVEL%
@REM
@REM :buildVersion
@REM @echo on
@REM call ./gradlew.bat clean -PmcVer="%~1" --no-daemon
@REM call ./gradlew.bat build -PmcVer="%~1" --no-daemon
@REM @echo off
@REM EXIT /B 0
endlocal
+1 -1
View File
@@ -56,7 +56,7 @@ a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the team lead James Seibel through Discord at `@BackSun#4157`. All
reported by contacting the team lead James Seibel through Discord at `@backsun`. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
+1 -1
View File
@@ -3,7 +3,7 @@ plugins {
}
minecraft {
accessWideners(project(":common").file("src/main/resources/${accessWidenerVersion}.lod.accesswidener"))
accessWideners(project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener"))
version(rootProject.minecraft_version)
}
@@ -0,0 +1,64 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common;
import com.seibel.distanthorizons.common.forge.LodForgeMethodCaller;
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
/**
* This is the common main class
*
* @author Ran
*/
public class LodCommonMain
{
public static boolean forge = false;
public static LodForgeMethodCaller forgeMethodCaller;
public static void startup(LodForgeMethodCaller forgeMethodCaller)
{
if (forgeMethodCaller != null)
{
LodCommonMain.forge = true;
LodCommonMain.forgeMethodCaller = forgeMethodCaller;
}
DependencySetup.createSharedBindings();
SharedApi.init();
// if (!serverSided) {
// new NetworkReceiver().register_Client();
// } else {
// new NetworkReceiver().register_Server();
// }
}
public static void initConfig()
{
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, 2);
Config.completeDelayedSetup();
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,13 +16,13 @@
* 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.lod.common.forge;
import com.seibel.lod.common.wrappers.minecraft.MinecraftClientWrapper;
package com.seibel.distanthorizons.common.forge;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.core.Direction;
#if POST_MC_1_19
#if POST_MC_1_19_2
import net.minecraft.util.RandomSource;
#endif
import net.minecraft.world.level.ColorResolver;
@@ -36,14 +36,17 @@ import java.util.Random;
/**
* used for calling methods that forge modified
* (forge modifies vanilla methods for some reason)
*
* @author Ran
*/
public interface LodForgeMethodCaller {
#if PRE_MC_1_19
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, Random random); // FIXME: For 1.19
#else
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, RandomSource random); // FIXME: For 1.19
public interface LodForgeMethodCaller
{
#if PRE_MC_1_19_2
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, Random random); // FIXME: For 1.19
#else
List<BakedQuad> getQuads(MinecraftClientWrapper mc, Block block, BlockState blockState, Direction direction, RandomSource random); // FIXME: For 1.19
#endif
int colorResolverGetColor(ColorResolver resolver, Biome biome, double x, double z);
int colorResolverGetColor(ColorResolver resolver, Biome biome, double x, double z);
}
@@ -0,0 +1,96 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.rendering;
#if PRE_MC_1_19_4
import com.mojang.math.Matrix4f;
#else
import org.joml.Matrix4f;
#endif
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import org.lwjgl.opengl.GL15;
import java.nio.FloatBuffer;
public class SeamlessOverdraw
{
/**
* Proof-of-concept experimental option, not intended for normal use. <br>
* (Poorly) replaces Minecraft's far clip plane so it lines up with DH's near clip plane.
*/
public static float[] overwriteMinecraftNearFarClipPlanes(Matrix4f minecraftProjectionMatrix, float previousPartialTicks)
{
float[] matrixFloatArray;
#if PRE_MC_1_19_4
FloatBuffer matrixFloatBuffer = FloatBuffer.allocate(16);
minecraftProjectionMatrix.store(matrixFloatBuffer);
matrixFloatArray = matrixFloatBuffer.array();
#else
// Passing float buffers in caused native code crashes, so we are passing in a float array instead
matrixFloatArray = new float[16];
minecraftProjectionMatrix.get(matrixFloatArray);
#endif
return overwriteMinecraftNearFarClipPlanes(matrixFloatArray, previousPartialTicks);
}
public static float[] overwriteMinecraftNearFarClipPlanes(Mat4f minecraftProjectionMatrix, float previousPartialTicks)
{
return overwriteMinecraftNearFarClipPlanes(minecraftProjectionMatrix.getValuesAsArray(), previousPartialTicks);
}
private static float[] overwriteMinecraftNearFarClipPlanes(float[] projectionMatrixFloatArray, float previousPartialTicks)
{
float dhFarClipPlane = RenderUtil.getNearClipPlaneDistanceInBlocks(previousPartialTicks);
// works for fabric, bad not for forge for some reason :/
float farClip = dhFarClipPlane * 5.1f; // magic number found via trial and error, James has no idea what it represents, except that it makes the seam between DH and vanilla rendering pretty close
float nearClip = 0.5f; // this causes issues with some vanilla rendering, specifically the wireframe around selected blocks is slightly off. Unfortunately the ratio between the near and far clip plane can't be easily modified without completely screwing up the rendering.
// these may be the wrong index locations in any version of MC other than 1.18.2
projectionMatrixFloatArray[10] = -((farClip + nearClip) / (farClip - nearClip)); // near clip plane
projectionMatrixFloatArray[11] = -((2 * farClip * nearClip) / (farClip - nearClip)); // far clip plane
return projectionMatrixFloatArray;
}
//================//
// helper methods //
//================//
public static void applyLegacyProjectionMatrix(float[] projectionMatrixFloatArray)
{
int glMatrixMode = GL15.glGetInteger(GL15.GL_MATRIX_MODE);
GL15.glMatrixMode(GL15.GL_PROJECTION);
GL15.glLoadMatrixf(projectionMatrixFloatArray);
GL15.glMatrixMode(glMatrixMode);
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,20 +16,17 @@
* 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.lod.fabric.wrappers.modAccessor;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IStarlightAccessor;
package com.seibel.distanthorizons.common.util;
public class StarlightAccessor implements IStarlightAccessor {
@Override
public String getModName() {
return "Starlight-Fabric-1.18.X";
}
public StarlightAccessor() {
}
/**
* Added to MC's dynamic textures via mixins
* in order to denote whether a texture is a lightmap or not. <br><br>
*
* If not done any dynamic texture could be used as the lightmap
* which causes some weird rendering bugs.
*/
public interface ILightTextureMarker
{
void markLightTexture();
}
@@ -0,0 +1,47 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.util;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.LevelAccessor;
public class ProxyUtil
{
public static ILevelWrapper getLevelWrapper(LevelAccessor level)
{
ILevelWrapper levelWrapper;
if (level instanceof ServerLevel)
{
levelWrapper = ServerLevelWrapper.getWrapper((ServerLevel) level);
}
else
{
levelWrapper = ClientLevelWrapper.getWrapper((ClientLevel) level);
}
return levelWrapper;
}
}
@@ -0,0 +1,75 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers;
import com.seibel.distanthorizons.common.wrappers.gui.ClassicConfigGUI;
import com.seibel.distanthorizons.common.wrappers.gui.LangWrapper;
import com.seibel.distanthorizons.common.wrappers.level.KeyedClientLevelManager;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftDedicatedServerWrapper;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
/**
* Binds all necessary dependencies, so we
* can access them in Core. <br>
* This needs to be called before any Core classes
* are loaded.
*
* @author James Seibel
* @author Ran
* @version 12-1-2021
*/
public class DependencySetup
{
public static void createSharedBindings()
{
SingletonInjector.INSTANCE.bind(ILangWrapper.class, LangWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE);
SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
SingletonInjector.INSTANCE.bind(IKeyedClientLevelManager.class, KeyedClientLevelManager.INSTANCE);
DependencySetupDoneCheck.isDone = true;
}
//@Environment(EnvType.SERVER)
public static void createServerBindings()
{
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftDedicatedServerWrapper.INSTANCE);
}
//@Environment(EnvType.CLIENT)
public static void createClientBindings()
{
SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE);
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,14 +16,16 @@
* 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.lod.common.wrappers;
package com.seibel.distanthorizons.common.wrappers;
import java.util.function.Supplier;
public class DependencySetupDoneCheck
{
// TODO move to DependencySetup
public static boolean isDone = false;
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> {return false;});
// TODO why is this here and what is its purpose?
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; });
}
@@ -0,0 +1,174 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers;
import java.nio.FloatBuffer;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
#if PRE_MC_1_19_4
import com.mojang.math.Matrix4f;
#else
import org.joml.Matrix4f;
#endif
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.ChunkPos;
/**
* This class converts to and from Minecraft objects (Ex: Matrix4f)
* and objects we created (Ex: Mat4f).
*
* @author James Seibel
* @version 11-20-2021
*/
public class McObjectConverter
{
private static int bufferIndex(int x, int y)
{
return y * 4 + x;
}
/** Taken from Minecraft's com.mojang.math.Matrix4f class from 1.18.2 */
private static void storeMatrix(Matrix4f matrix, FloatBuffer buffer)
{
#if PRE_MC_1_19_4
matrix.store(buffer);
#else
// Mojang starts to use joml's Matrix4f libary in 1.19.3 so we copy their store method and use it here if its newer than 1.19.3
buffer.put(bufferIndex(0, 0), matrix.m00());
buffer.put(bufferIndex(0, 1), matrix.m01());
buffer.put(bufferIndex(0, 2), matrix.m02());
buffer.put(bufferIndex(0, 3), matrix.m03());
buffer.put(bufferIndex(1, 0), matrix.m10());
buffer.put(bufferIndex(1, 1), matrix.m11());
buffer.put(bufferIndex(1, 2), matrix.m12());
buffer.put(bufferIndex(1, 3), matrix.m13());
buffer.put(bufferIndex(2, 0), matrix.m20());
buffer.put(bufferIndex(2, 1), matrix.m21());
buffer.put(bufferIndex(2, 2), matrix.m22());
buffer.put(bufferIndex(2, 3), matrix.m23());
buffer.put(bufferIndex(3, 0), matrix.m30());
buffer.put(bufferIndex(3, 1), matrix.m31());
buffer.put(bufferIndex(3, 2), matrix.m32());
buffer.put(bufferIndex(3, 3), matrix.m33());
#endif
}
/** 4x4 float matrix converter */
public static Mat4f Convert(Matrix4f mcMatrix)
{
FloatBuffer buffer = FloatBuffer.allocate(16);
storeMatrix(mcMatrix, buffer);
Mat4f matrix = new Mat4f(buffer);
#if PRE_MC_1_19_4
matrix.transpose(); // In 1.19.3 and later, we no longer need to transpose it
#endif
return matrix;
}
static final Direction[] directions;
static final EDhDirection[] lodDirections;
static
{
EDhDirection[] lodDirs = EDhDirection.values();
directions = new Direction[lodDirs.length];
lodDirections = new EDhDirection[lodDirs.length];
for (EDhDirection lodDir : lodDirs)
{
Direction dir;
switch (lodDir.name().toUpperCase())
{
case "DOWN":
dir = Direction.DOWN;
break;
case "UP":
dir = Direction.UP;
break;
case "NORTH":
dir = Direction.NORTH;
break;
case "SOUTH":
dir = Direction.SOUTH;
break;
case "WEST":
dir = Direction.WEST;
break;
case "EAST":
dir = Direction.EAST;
break;
default:
dir = null;
break;
}
if (dir == null)
{
throw new IllegalArgumentException("Invalid direction on init mapping: " + lodDir);
}
directions[lodDir.ordinal()] = dir;
lodDirections[dir.ordinal()] = lodDir;
}
}
public static BlockPos Convert(DhBlockPos wrappedPos)
{
return new BlockPos(wrappedPos.x, wrappedPos.y, wrappedPos.z);
}
public static ChunkPos Convert(DhChunkPos wrappedPos)
{
return new ChunkPos(wrappedPos.x, wrappedPos.z);
}
public static Direction Convert(EDhDirection lodDirection)
{
return directions[lodDirection.ordinal()];
}
public static EDhDirection Convert(Direction direction)
{
return lodDirections[direction.ordinal()];
}
public static void DebugCheckAllPackers()
{
BiConsumer<Integer, Integer> func = (x, z) -> DhChunkPos._DebugCheckPacker(x, z, ChunkPos.asLong(x, z));
func.accept(0, 0);
func.accept(12345, 134);
func.accept(-12345, -134);
func.accept(-30000000 / 16, 30000000 / 16);
func.accept(30000000 / 16, -30000000 / 16);
func.accept(30000000 / 16, 30000000 / 16);
func.accept(-30000000 / 16, -30000000 / 16);
Consumer<BlockPos> func2 = (p) -> DhBlockPos._DebugCheckPacker(p.getX(), p.getY(), p.getZ(), p.asLong());
func2.accept(new BlockPos(0, 0, 0));
func2.accept(new BlockPos(12345, 134, 123));
func2.accept(new BlockPos(-12345, -134, -80));
func2.accept(new BlockPos(-30000000, 2047, 30000000));
func2.accept(new BlockPos(30000000, -2048, -30000000));
func2.accept(new BlockPos(30000000, 2047, 30000000));
func2.accept(new BlockPos(-30000000, -2048, -30000000));
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,10 +16,11 @@
* 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.lod.common.wrappers;
import com.seibel.lod.core.wrapperInterfaces.IVersionConstants;
package com.seibel.distanthorizons.common.wrappers;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
/**
@@ -42,21 +43,27 @@ public class VersionConstants implements IVersionConstants
{
return 0;
}
@Override
public int getWorldGenerationCountPerThread()
{
return 1;
}
@Override
public boolean isVanillaRenderedChunkSquare()
{
return false;
}
@Override
public String getMinecraftVersion() {
public String getMinecraftVersion()
{
#if PRE_MC_1_19_2
return Minecraft.getInstance().getGame().getVersion().getId();
#else
return SharedConstants.getCurrentVersion().getId();
#endif
}
}
@@ -0,0 +1,203 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
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.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.chunk.ChunkAccess;
import java.io.IOException;
import java.util.HashSet;
/**
* This handles creating abstract wrapper objects.
*
* @author James Seibel
* @version 2022-12-5
*/
public class WrapperFactory implements IWrapperFactory
{
public static final WrapperFactory INSTANCE = new WrapperFactory();
@Override
public AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel)
{
if (targetLevel instanceof IDhServerLevel)
{
return new BatchGenerationEnvironment((IDhServerLevel) targetLevel);
}
else
{
throw new IllegalArgumentException("The target level must be a server-side level.");
}
}
@Override
public IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BiomeWrapper.deserialize(str, levelWrapper); }
@Override
public IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BlockStateWrapper.deserialize(str, levelWrapper); }
@Override
public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; }
@Override
public HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); }
/**
* Note: when this is updated for different MC versions, make sure you also update the documentation in
* {@link IDhApiWorldGenerator#generateChunks} and the type list in {@link WrapperFactory#createChunkWrapperErrorMessage}. <br><br>
*
* For full method documentation please see: {@link IWrapperFactory#createChunkWrapper}
*
* @see IWrapperFactory#createChunkWrapper
*/
public IChunkWrapper createChunkWrapper(Object[] objectArray) throws ClassCastException
{
if (objectArray.length == 1 && objectArray[0] instanceof IChunkWrapper)
{
try
{
// this path should only happen when called by Distant Horizons code
// API implementors should never hit this path
return (IChunkWrapper) objectArray[0];
}
catch (Exception e)
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
}
// MC 1.16, 1.18, 1.19, 1.20
#if POST_MC_1_17_1 || MC_1_16_5
else if (objectArray.length == 2)
{
// correct number of parameters from the API
// chunk
if (!(objectArray[0] instanceof ChunkAccess))
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
ChunkAccess chunk = (ChunkAccess) objectArray[0];
// level / light source
if (!(objectArray[1] instanceof Level))
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
// the level is needed for the DH level wrapper...
Level level = (Level) objectArray[1];
// ...the LevelReader is needed for chunk lighting
LevelReader lightSource = level;
// level wrapper
ILevelWrapper levelWrapper;
if (level instanceof ServerLevel)
{
levelWrapper = ServerLevelWrapper.getWrapper((ServerLevel)level);
}
else if (level instanceof ClientLevel)
{
levelWrapper = ClientLevelWrapper.getWrapper((ClientLevel)level);
}
else
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
return new ChunkWrapper(chunk, lightSource, levelWrapper);
}
// incorrect number of parameters from the API
else
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
#else
// Intentional compiler error to bring attention to the missing wrapper function.
// If you need to work on an unimplemented version but don't have the ability to implement this yet
// you can comment it out, but please don't commit it. Someone will have to implement it .
// After implementing the new version please read this method's javadocs for instructions
// on what other locations also need to be updated, the DhAPI specifically needs to
// be updated to state which objects this method accepts.
not implemented for this version of Minecraft!
#endif
}
/**
* Note: when this is updated for different MC versions,
* make sure you also update the documentation in {@link IDhApiWorldGenerator#generateChunks}.
*/
private static String createChunkWrapperErrorMessage(Object[] objectArray)
{
StringBuilder message = new StringBuilder(
"Chunk wrapper creation failed. \n" +
"Expected parameters: \n");
// MC 1.16, 1.18, 1.19, 1.20
#if POST_MC_1_17_1 || MC_1_16_5
message.append("[" + ChunkAccess.class.getName() + "], \n");
message.append("[" + ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName() + "]. \n");
#else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif
if (objectArray.length != 0)
{
message.append("Given parameters: ");
for (Object obj : objectArray)
{
message.append("[").append(obj.getClass().getName()).append("], ");
}
}
else
{
message.append(" No parameters given.");
}
return message.toString();
}
}
@@ -0,0 +1,289 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block;
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.world.level.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import net.minecraft.client.Minecraft;
#if POST_MC_1_17
import net.minecraft.core.Holder;
import net.minecraft.resources.RegistryOps;
#endif
#if POST_MC_1_19_2
#endif
#if MC_1_16_5 || MC_1_17_1
import net.minecraft.core.Registry;
#elif MC_1_18_2 || MC_1_19_2
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.data.BuiltinRegistries;
#else
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
#endif
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
#if !PRE_MC_1_18_2
import net.minecraft.world.level.biome.Biomes;
#endif
/** This class wraps the minecraft BlockPos.Mutable (and BlockPos) class */
public class BiomeWrapper implements IBiomeWrapper
{
private static final Logger LOGGER = LogManager.getLogger();
#if PRE_MC_1_18_2
public static final ConcurrentMap<Biome, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>();
#else
public static final ConcurrentMap<Holder<Biome>, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>();
#endif
public static final String EMPTY_STRING = "EMPTY";
public static final BiomeWrapper EMPTY_WRAPPER = new BiomeWrapper(null, null);
/** keep track of broken biomes so we don't log every time */
private static final HashSet<String> BrokenResourceLocationStrings = new HashSet<>();
// properties //
#if PRE_MC_1_18_2
public final Biome biome;
#else
public final Holder<Biome> biome;
#endif
/** technically final, but since it requires a method call to generate it can't be marked as such */
private String serialString = null;
//==============//
// constructors //
//==============//
static public IBiomeWrapper getBiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
{
if (biome == null)
{
return EMPTY_WRAPPER;
}
if (WRAPPER_BY_BIOME.containsKey(biome))
{
return WRAPPER_BY_BIOME.get(biome);
}
else
{
BiomeWrapper newWrapper = new BiomeWrapper(biome, levelWrapper);
WRAPPER_BY_BIOME.put(biome, newWrapper);
return newWrapper;
}
}
private BiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
{
this.biome = biome;
this.serialString = this.serialize(levelWrapper);
LOGGER.trace("Created BiomeWrapper ["+this.serialString+"] for ["+biome+"]");
}
//=========//
// methods //
//=========//
@Override
public String getName()
{
if (this == EMPTY_WRAPPER)
{
return EMPTY_STRING;
}
#if PRE_MC_1_18_2
return biome.toString();
#else
return this.biome.unwrapKey().orElse(Biomes.THE_VOID).registry().toString();
#endif
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
else if (obj == null || this.getClass() != obj.getClass())
{
return false;
}
BiomeWrapper that = (BiomeWrapper) obj;
// the serialized value is used so we can test the contents instead of the references
return Objects.equals(this.getSerialString(), that.getSerialString());
}
@Override
public int hashCode() { return Objects.hash(this.getSerialString()); }
@Override
public String getSerialString() { return this.serialString; }
@Override
public Object getWrappedMcObject() { return this.biome; }
@Override
public String toString() { return this.getSerialString(); }
//=======================//
// serialization methods //
//=======================//
public String serialize(ILevelWrapper levelWrapper)
{
if (levelWrapper == null)
{
return EMPTY_STRING;
}
if (this.serialString == null)
{
net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess();
ResourceLocation resourceLocation;
#if MC_1_16_5 || MC_1_17_1
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
#elif MC_1_18_2 || MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
#else
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
#endif
if (resourceLocation == null)
{
String biomeName;
#if MC_1_16_5 || MC_1_17_1
biomeName = this.biome.toString();
#else
biomeName = this.biome.value().toString();
#endif
LOGGER.warn("unable to serialize: " + biomeName);
// shouldn't normally happen, but just in case
this.serialString = "";
}
else
{
this.serialString = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
}
}
return this.serialString;
}
public static IBiomeWrapper deserialize(String resourceLocationString, ILevelWrapper levelWrapper) throws IOException
{
if (resourceLocationString.equals(EMPTY_STRING))
{
LOGGER.warn("["+EMPTY_STRING+"] biome string deserialized. This may mean there was a file saving error or a biome saving error.");
return EMPTY_WRAPPER;
}
else if (resourceLocationString.trim().isEmpty() || resourceLocationString.equals(""))
{
LOGGER.warn("Null biome string deserialized.");
return EMPTY_WRAPPER;
}
// parse the resource location
int separatorIndex = resourceLocationString.indexOf(":");
if (separatorIndex == -1)
{
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
}
ResourceLocation resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
try
{
Level level = (Level)levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
boolean success;
#if MC_1_16_5 || MC_1_17_1
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (biome != null);
#elif MC_1_18_2 || MC_1_19_2
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#else
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#endif
if (!success)
{
if (!BrokenResourceLocationStrings.contains(resourceLocationString))
{
BrokenResourceLocationStrings.add(resourceLocationString);
LOGGER.warn("Unable to deserialize biome from string: [" + resourceLocationString + "]");
}
return EMPTY_WRAPPER;
}
return getBiomeWrapper(biome, levelWrapper);
}
catch (Exception e)
{
throw new IOException("Failed to deserialize the string [" + resourceLocationString + "] into a BiomeWrapper: " + e.getMessage(), e);
}
}
}
@@ -0,0 +1,460 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
#if MC_1_16_5 || MC_1_17_1
import net.minecraft.core.Registry;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.EmptyBlockGetter;
#elif MC_1_18_2 || MC_1_19_2
import net.minecraft.client.Minecraft;
import net.minecraft.world.level.Level;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.world.level.EmptyBlockGetter;
#else
import net.minecraft.client.Minecraft;
import net.minecraft.world.level.Level;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.EmptyBlockGetter;
#endif
public class BlockStateWrapper implements IBlockStateWrapper
{
/** example "minecraft:water" */
public static final String RESOURCE_LOCATION_SEPARATOR = ":";
/** example "minecraft:water_STATE_{level:0}" */
public static final String STATE_STRING_SEPARATOR = "_STATE_";
// must be defined before AIR, otherwise a null pointer will be thrown
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>();
public static final String AIR_STRING = "AIR";
public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null);
// TODO: Make this changeable through the config
public static final String[] RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS = { AIR_STRING, "minecraft:barrier", "minecraft:structure_void", "minecraft:light", "minecraft:tripwire" };
public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
/** keep track of broken blocks so we don't log every time */
private static final HashSet<ResourceLocation> BrokenResourceLocations = new HashSet<>();
// properties //
public final BlockState blockState;
/** technically final, but since it requires a method call to generate it can't be marked as such */
private String serialString;
/**
* Cached opacity value, -1 if not populated. <br>
* Should be between {@link IBlockStateWrapper#FULLY_OPAQUE} and {@link IBlockStateWrapper#FULLY_OPAQUE}
*/
private int opacity = -1;
//==============//
// constructors //
//==============//
public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper)
{
if (blockState == null || blockState.isAir())
{
return AIR;
}
if (WRAPPER_BY_BLOCK_STATE.containsKey(blockState))
{
return WRAPPER_BY_BLOCK_STATE.get(blockState);
}
else
{
BlockStateWrapper newWrapper = new BlockStateWrapper(blockState, levelWrapper);
WRAPPER_BY_BLOCK_STATE.put(blockState, newWrapper);
return newWrapper;
}
}
private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper)
{
this.blockState = blockState;
this.serialString = this.serialize(levelWrapper);
LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"]");
}
//================//
// helper methods //
//================//
/**
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
* This way the method won't accidentally be called before the deserialization can be completed.
*/
public static HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper)
{
// use the cached version if possible
if (rendererIgnoredBlocks != null)
{
return rendererIgnoredBlocks;
}
// deserialize each of the given resource locations
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
for (String blockResourceLocation : RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS)
{
try
{
BlockStateWrapper DefaultBlockStateToIgnore = (BlockStateWrapper) deserialize(blockResourceLocation, levelWrapper);
blockStateWrappers.add(DefaultBlockStateToIgnore);
if (DefaultBlockStateToIgnore == AIR)
{
continue;
}
// add all possible blockstates (to account for light blocks with different light values and such)
List<BlockState> blockStatesToIgnore = DefaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
for (BlockState blockState : blockStatesToIgnore)
{
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper);
blockStateWrappers.add(newBlockToIgnore);
}
}
catch (IOException e)
{
LOGGER.warn("Unable to deserialize rendererIgnoredBlock with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
}
}
rendererIgnoredBlocks = blockStateWrappers;
return rendererIgnoredBlocks;
}
//=================//
// wrapper methods //
//=================//
@Override
public int getOpacity()
{
// use the cached opacity value if possible
if (this.opacity != -1)
{
return this.opacity;
}
// this method isn't perfect, but works well enough for our use case
int opacity;
if (this.isAir())
{
opacity = FULLY_TRANSPARENT;
}
else if (this.isLiquid() && !this.blockState.canOcclude())
{
// probably not a waterlogged block (which should block light entirely)
// +1 to indicate that the block is translucent (in between transparent and opaque)
opacity = FULLY_TRANSPARENT + 1;
}
else if (this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO))
{
opacity = FULLY_TRANSPARENT;
}
else
{
// default for all other blocks
opacity = FULLY_OPAQUE;
}
this.opacity = opacity;
return this.opacity;
}
@Override
public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; }
@Override
public String getSerialString() { return this.serialString; }
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null || this.getClass() != obj.getClass())
{
return false;
}
BlockStateWrapper that = (BlockStateWrapper) obj;
// the serialized value is used so we can test the contents instead of the references
return Objects.equals(this.getSerialString(), that.getSerialString());
}
@Override
public int hashCode() { return Objects.hash(this.getSerialString()); }
@Override
public Object getWrappedMcObject() { return this.blockState; }
@Override
public boolean isAir() { return this.isAir(this.blockState); }
public boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); }
@Override
public boolean isSolid()
{
#if PRE_MC_1_20_1
return this.blockState.getMaterial().isSolid();
#else
return !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty();
#endif
}
@Override
public boolean isLiquid()
{
if (this.isAir())
{
return false;
}
#if PRE_MC_1_20_1
return this.blockState.getMaterial().isLiquid() || !this.blockState.getFluidState().isEmpty();
#else
return !this.blockState.getFluidState().isEmpty();
#endif
}
@Override
public String toString() { return this.getSerialString(); }
//=======================//
// serialization methods //
//=======================//
private String serialize(ILevelWrapper levelWrapper)
{
if (this.blockState == null)
{
return AIR_STRING;
}
// older versions of MC have a static registry
#if !(MC_1_16_5 || MC_1_17_1)
Level level = (Level)levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
#endif
ResourceLocation resourceLocation;
#if MC_1_16_5 || MC_1_17_1
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
#elif MC_1_18_2 || MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
#else
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
#endif
if (resourceLocation == null)
{
LOGGER.warn("No ResourceLocation found, unable to serialize: " + this.blockState);
return AIR_STRING;
}
this.serialString = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath()
+ STATE_STRING_SEPARATOR + serializeBlockStateProperties(this.blockState);
return this.serialString;
}
/** will only work if a level is currently loaded */
public static IBlockStateWrapper deserialize(String resourceStateString, ILevelWrapper levelWrapper) throws IOException
{
if (resourceStateString.equals(AIR_STRING) || resourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case
{
return AIR;
}
// try to parse out the BlockState
String blockStatePropertiesString = null; // will be null if no properties were included
int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR);
if (stateSeparatorIndex != -1)
{
// blockstate properties found
blockStatePropertiesString = resourceStateString.substring(stateSeparatorIndex + STATE_STRING_SEPARATOR.length());
resourceStateString = resourceStateString.substring(0, stateSeparatorIndex);
}
// parse the resource location
int resourceSeparatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR);
if (resourceSeparatorIndex == -1)
{
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
}
ResourceLocation resourceLocation = new ResourceLocation(resourceStateString.substring(0, resourceSeparatorIndex), resourceStateString.substring(resourceSeparatorIndex + 1));
// attempt to get the BlockState from all possible BlockStates
try
{
#if !(MC_1_16_5 || MC_1_17_1)
// use the given level if possible, otherwise try using the currently loaded one
Level level = (levelWrapper != null ? (Level)levelWrapper.getWrappedMcObject() : null);
level = (level == null ? Minecraft.getInstance().level : level);
#endif
Block block;
#if MC_1_16_5 || MC_1_17_1
block = Registry.BLOCK.get(resourceLocation);
#elif MC_1_18_2 || MC_1_19_2
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
#else
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
#endif
if (block == null)
{
// shouldn't normally happen, but here to make the compiler happy
if (!BrokenResourceLocations.contains(resourceLocation))
{
BrokenResourceLocations.add(resourceLocation);
LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
}
return AIR;
}
// attempt to find the blockstate from all possibilities
BlockState foundState = null;
if (blockStatePropertiesString != null)
{
List<BlockState> possibleStateList = block.getStateDefinition().getPossibleStates();
for (BlockState possibleState : possibleStateList)
{
String possibleStatePropertiesString = serializeBlockStateProperties(possibleState);
if (possibleStatePropertiesString.equals(blockStatePropertiesString))
{
foundState = possibleState;
break;
}
}
}
// use the default if no state was found or given
if (foundState == null)
{
if (blockStatePropertiesString != null)
{
// we should have found a blockstate, but didn't
if (!BrokenResourceLocations.contains(resourceLocation))
{
BrokenResourceLocations.add(resourceLocation);
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
}
}
foundState = block.defaultBlockState();
}
return new BlockStateWrapper(foundState, levelWrapper);
}
catch (Exception e)
{
throw new IOException("Failed to deserialize the string [" + resourceStateString + "] into a BlockStateWrapper: " + e.getMessage(), e);
}
}
/** used to compare and save BlockStates based on their properties */
private static String serializeBlockStateProperties(BlockState blockState)
{
// get the property list for this block (doesn't contain this block state's values, just the names and possible values)
java.util.Collection<net.minecraft.world.level.block.state.properties.Property<?>> blockPropertyCollection = blockState.getProperties();
// alphabetically sort the list so they are always in the same order
List<net.minecraft.world.level.block.state.properties.Property<?>> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection);
sortedBlockPropteryList.sort((a, b) -> a.getName().compareTo(b.getName()));
StringBuilder stringBuilder = new StringBuilder();
for (net.minecraft.world.level.block.state.properties.Property<?> property : sortedBlockPropteryList)
{
String propertyName = property.getName();
String value = "NULL";
if (blockState.hasProperty(property))
{
value = blockState.getValue(property).toString();
}
stringBuilder.append("{");
stringBuilder.append(propertyName).append(RESOURCE_LOCATION_SEPARATOR).append(value);
stringBuilder.append("}");
}
return stringBuilder.toString();
}
}
@@ -0,0 +1,65 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
/**
* For wrapping/utilizing around TextureAtlasSprite
*
* @author Ran
*/
public class TextureAtlasSpriteWrapper
{
/**
* This code is from Minecraft Forge
* Which is licensed under the terms of GNU Lesser General Public License
* as published by the Free Software Foundation version 2.1
* of the License.
*
* The code has been modified to use TextureAtlasSprite
*/
public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y)
{
#if PRE_MC_1_17_1
return sprite.mainImage[0].getPixelRGBA(
x + sprite.framesX[frameIndex] * sprite.getWidth(),
y + sprite.framesY[frameIndex] * sprite.getHeight());
#elif PRE_MC_1_19_4
if (sprite.animatedTexture != null)
{
x += sprite.animatedTexture.getFrameX(frameIndex) * sprite.width;
y += sprite.animatedTexture.getFrameY(frameIndex) * sprite.height;
}
return sprite.mainImage[0].getPixelRGBA(x, y);
#else
if (sprite.contents().animatedTexture != null)
{
x += sprite.contents().animatedTexture.getFrameX(frameIndex) * sprite.contents().width();
y += sprite.contents().animatedTexture.getFrameY(frameIndex) * sprite.contents().width();
}
return sprite.contents().originalImage.getPixelRGBA(x, y);
#endif
}
}
@@ -0,0 +1,243 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.common.LodCommonMain;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.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 TintGetterOverrideFast implements BlockAndTintGetter
{
LevelReader parent;
public TintGetterOverrideFast(LevelReader parent)
{
this.parent = parent;
}
private Biome _getBiome(BlockPos pos)
{
#if POST_MC_1_18_2
return parent.getBiome(pos).value();
#else
return parent.getBiome(pos);
#endif
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
if (LodCommonMain.forgeMethodCaller != null)
{
return LodCommonMain.forgeMethodCaller.colorResolverGetColor(colorResolver, _getBiome(blockPos),
blockPos.getX(), blockPos.getZ());
}
else
{
return colorResolver.getColor(_getBiome(blockPos), blockPos.getX(), blockPos.getZ());
}
}
@Override
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
@Override
public LevelLightEngine getLightEngine()
{
return parent.getLightEngine();
}
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos)
{
return parent.getBrightness(lightLayer, blockPos);
}
@Override
public int getRawBrightness(BlockPos blockPos, int i)
{
return parent.getRawBrightness(blockPos, i);
}
@Override
public boolean canSeeSky(BlockPos blockPos)
{
return parent.canSeeSky(blockPos);
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos blockPos)
{
return parent.getBlockEntity(blockPos);
}
@Override
public BlockState getBlockState(BlockPos blockPos)
{
return parent.getBlockState(blockPos);
}
@Override
public FluidState getFluidState(BlockPos blockPos)
{
return parent.getFluidState(blockPos);
}
@Override
public int getLightEmission(BlockPos blockPos)
{
return parent.getLightEmission(blockPos);
}
@Override
public int getMaxLightLevel()
{
return parent.getMaxLightLevel();
}
@Override
public Stream<BlockState> getBlockStates(AABB aABB)
{
return parent.getBlockStates(aABB);
}
@Override
public BlockHitResult clip(ClipContext clipContext)
{
return parent.clip(clipContext);
}
@Override
@Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
{
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier)
{
return parent.getBlockFloorHeight(voxelShape, supplier);
}
@Override
public double getBlockFloorHeight(BlockPos blockPos)
{
return parent.getBlockFloorHeight(blockPos);
}
@Override
public int getMaxBuildHeight()
{
return parent.getMaxBuildHeight();
}
#if POST_MC_1_17_1
@Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType)
{
return parent.getBlockEntity(blockPos, blockEntityType);
}
@Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext)
{
return parent.isBlockInLine(clipBlockStateContext);
}
@Override
public int getHeight()
{
return parent.getHeight();
}
@Override
public int getMinBuildHeight()
{
return parent.getMinBuildHeight();
}
@Override
public int getSectionsCount()
{
return parent.getSectionsCount();
}
@Override
public int getMinSection()
{
return parent.getMinSection();
}
@Override
public int getMaxSection()
{
return parent.getMaxSection();
}
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos)
{
return parent.isOutsideBuildHeight(blockPos);
}
@Override
public boolean isOutsideBuildHeight(int i)
{
return parent.isOutsideBuildHeight(i);
}
@Override
public int getSectionIndex(int i)
{
return parent.getSectionIndex(i);
}
@Override
public int getSectionIndexFromSectionY(int i)
{
return parent.getSectionIndexFromSectionY(i);
}
@Override
public int getSectionYFromSectionIndex(int i)
{
return parent.getSectionYFromSectionIndex(i);
}
#endif
}
@@ -0,0 +1,269 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.common.LodCommonMain;
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;
public TintGetterOverrideSmooth(LevelReader parent, int smoothingRange)
{
this.parent = parent;
this.smoothingRange = smoothingRange;
}
private Biome _getBiome(BlockPos pos)
{
#if POST_MC_1_18_2
return 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;
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 int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
return calculateBlockTint(blockPos, colorResolver);
}
@Override
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
@Override
public LevelLightEngine getLightEngine()
{
return parent.getLightEngine();
}
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos)
{
return parent.getBrightness(lightLayer, blockPos);
}
@Override
public int getRawBrightness(BlockPos blockPos, int i)
{
return parent.getRawBrightness(blockPos, i);
}
@Override
public boolean canSeeSky(BlockPos blockPos)
{
return parent.canSeeSky(blockPos);
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos blockPos)
{
return parent.getBlockEntity(blockPos);
}
@Override
public BlockState getBlockState(BlockPos blockPos)
{
return parent.getBlockState(blockPos);
}
@Override
public FluidState getFluidState(BlockPos blockPos)
{
return parent.getFluidState(blockPos);
}
@Override
public int getLightEmission(BlockPos blockPos)
{
return parent.getLightEmission(blockPos);
}
@Override
public int getMaxLightLevel()
{
return parent.getMaxLightLevel();
}
@Override
public Stream<BlockState> getBlockStates(AABB aABB)
{
return parent.getBlockStates(aABB);
}
@Override
public BlockHitResult clip(ClipContext clipContext)
{
return parent.clip(clipContext);
}
@Override
@Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
{
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier)
{
return parent.getBlockFloorHeight(voxelShape, supplier);
}
@Override
public double getBlockFloorHeight(BlockPos blockPos)
{
return parent.getBlockFloorHeight(blockPos);
}
@Override
public int getMaxBuildHeight()
{
return parent.getMaxBuildHeight();
}
#if POST_MC_1_17_1
@Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType)
{
return parent.getBlockEntity(blockPos, blockEntityType);
}
@Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext)
{
return parent.isBlockInLine(clipBlockStateContext);
}
@Override
public int getHeight()
{
return parent.getHeight();
}
@Override
public int getMinBuildHeight()
{
return parent.getMinBuildHeight();
}
@Override
public int getSectionsCount()
{
return parent.getSectionsCount();
}
@Override
public int getMinSection()
{
return parent.getMinSection();
}
@Override
public int getMaxSection()
{
return parent.getMaxSection();
}
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos)
{
return parent.isOutsideBuildHeight(blockPos);
}
@Override
public boolean isOutsideBuildHeight(int i)
{
return parent.isOutsideBuildHeight(i);
}
@Override
public int getSectionIndex(int i)
{
return parent.getSectionIndex(i);
}
@Override
public int getSectionIndexFromSectionY(int i)
{
return parent.getSectionIndexFromSectionY(i);
}
@Override
public int getSectionYFromSectionIndex(int i)
{
return parent.getSectionYFromSectionIndex(i);
}
#endif
}
@@ -0,0 +1,100 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.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 POST_MC_1_18_2
import net.minecraft.core.Holder;
#endif
public class TintWithoutLevelOverrider implements BlockAndTintGetter
{
final BiomeWrapper biome;
public TintWithoutLevelOverrider(BiomeWrapper biome)
{
this.biome = biome;
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ());
}
private Biome _unwrap(#if POST_MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{
#if POST_MC_1_18_2
return biome.value();
#else
return biome;
#endif
}
@Override
public float getShade(Direction direction, boolean shade)
{
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public LevelLightEngine getLightEngine()
{
throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public BlockState getBlockState(BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public FluidState getFluidState(BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
#if MC_1_17_1 || POST_MC_1_18_2
@Override
public int getHeight()
{
throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public int getMinBuildHeight()
{
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
#endif
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,14 +16,11 @@
* 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.lod.common.wrappers.block;
import com.seibel.lod.common.LodCommonMain;
package com.seibel.distanthorizons.common.wrappers.block;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Cursor3D;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
@@ -33,25 +30,33 @@ import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import org.jetbrains.annotations.Nullable;
public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter {
final BiomeWrapper biome;
public int smoothingRange;
#if POST_MC_1_18_2
import net.minecraft.core.Holder;
#endif
public TintWithoutLevelSmoothOverrider(BiomeWrapper biome, int smoothingRange) {
this.biome = biome;
this.smoothingRange = smoothingRange;
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) {
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ());
}
private Biome _unwrap(#if POST_MC_1_18_2 Holder<Biome> #else Biome #endif biome) {
public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
{
final BiomeWrapper biome;
public int smoothingRange;
public TintWithoutLevelSmoothOverrider(BiomeWrapper biome, int smoothingRange)
{
this.biome = biome;
this.smoothingRange = smoothingRange;
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ());
}
private Biome _unwrap(#if POST_MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{
#if POST_MC_1_18_2
return biome.value();
return biome.value();
#else
return biome;
return biome;
#endif
}
}
//
// public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
// {
@@ -81,35 +86,47 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter {
// }
// 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 TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public LevelLightEngine getLightEngine() {
throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public BlockState getBlockState(BlockPos pos) {
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public FluidState getFluidState(BlockPos pos) {
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public int getHeight() {
throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public int getMinBuildHeight() {
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public float getShade(Direction direction, boolean shade)
{
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public LevelLightEngine getLightEngine()
{
throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public BlockState getBlockState(BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public FluidState getFluidState(BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
#if MC_1_17_1 || POST_MC_1_18_2
@Override
public int getHeight()
{
throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public int getMinBuildHeight()
{
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
#endif
}
@@ -0,0 +1,48 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block.cache;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import net.minecraft.world.level.block.state.BlockState;
import java.util.concurrent.ConcurrentHashMap;
public class ClientBlockDetailMap
{
private final ConcurrentHashMap<BlockState, ClientBlockStateCache> blockCache = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ClientLevelWrapper level;
public ClientBlockDetailMap(ClientLevelWrapper level) { this.level = level; }
public ClientBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos)
{ //TODO: Allow a per pos unique setting
return blockCache.computeIfAbsent(state, (s) -> new ClientBlockStateCache(s, level, pos));
}
public void clear() { blockCache.clear(); }
public int getColor(BlockState state, BiomeWrapper biome, DhBlockPos pos)
{
return getBlockStateData(state, pos).getAndResolveFaceColor(biome, pos);
}
}
@@ -0,0 +1,307 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block.cache;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.TextureAtlasSpriteWrapper;
import com.seibel.distanthorizons.common.wrappers.block.TintWithoutLevelOverrider;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.*;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.RotatedPillarBlock;
#if POST_MC_1_19_2
import net.minecraft.util.RandomSource;
#else
import java.util.Random;
#endif
import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger;
import java.util.HashSet;
import java.util.List;
/**
* @version 2022-9-16
*/
public class ClientBlockStateCache
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
#if PRE_MC_1_19_2
public static final Random random = new Random(0);
#else
public static final RandomSource random = RandomSource.create();
#endif
public final BlockState blockState;
public final LevelReader level;
public final BlockPos pos;
public ClientBlockStateCache(BlockState blockState, IClientLevelWrapper samplingLevel, DhBlockPos samplingPos)
{
this.blockState = blockState;
level = (LevelReader) samplingLevel.getWrappedMcObject();
pos = McObjectConverter.Convert(samplingPos);
resolveColors();
//LOGGER.info("ClientBlocKCache created for {}", blockState);
}
boolean isColorResolved = false;
int baseColor = 0; //TODO: Impl per-face color
boolean needShade = true;
boolean needPostTinting = false;
int tintIndex = 0;
public static final int FLOWER_COLOR_SCALE = 5;
enum ColorMode
{
Default,
Flower,
Leaves;
static ColorMode getColorMode(Block b)
{
if (b instanceof LeavesBlock) return Leaves;
if (b instanceof FlowerBlock) return Flower;
return Default;
}
}
private static int getWidth(TextureAtlasSprite texture)
{
#if PRE_MC_1_19_4
return texture.getWidth();
#else
return texture.contents().width();
#endif
}
private static int getHeight(TextureAtlasSprite texture)
{
#if PRE_MC_1_19_4
return texture.getHeight();
#else
return texture.contents().height();
#endif
}
//TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
{
int count = 0;
double alpha = 0;
double red = 0;
double green = 0;
double blue = 0;
int tempColor;
{
// textures normally use u and v instead of x and y
for (int u = 0; u < getWidth(texture); u++)
{
for (int v = 0; v < getHeight(texture); v++)
{
//note: Minecraft color format is: 0xAA BB GG RR
//________ DH mod color format is: 0xAA RR GG BB
//OpenGL RGBA format native order: 0xRR GG BB AA
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
double r = ((tempColor & 0x000000FF)) / 255.;
double g = ((tempColor & 0x0000FF00) >>> 8) / 255.;
double b = ((tempColor & 0x00FF0000) >>> 16) / 255.;
double a = ((tempColor & 0xFF000000) >>> 24) / 255.;
int scale = 1;
if (colorMode == ColorMode.Leaves)
{
r *= a;
g *= a;
b *= a;
a = 1.;
}
else if (a == 0.)
{
continue;
}
else if (colorMode == ColorMode.Flower && (g + 0.1 < b || g + 0.1 < r))
{
scale = FLOWER_COLOR_SCALE;
}
count += scale;
alpha += a * a * scale;
red += r * r * scale;
green += g * g * scale;
blue += b * b * scale;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
else
{
// determine the average color
tempColor = ColorUtil.rgbToInt(
(int) (Math.sqrt(alpha / count) * 255.),
(int) (Math.sqrt(red / count) * 255.),
(int) (Math.sqrt(green / count) * 255.),
(int) (Math.sqrt(blue / count) * 255.));
}
return tempColor;
}
private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN};
private void resolveColors()
{
if (isColorResolved) return;
if (blockState.getFluidState().isEmpty())
{
List<BakedQuad> quads = null;
for (Direction direction : DIRECTION_ORDER)
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(blockState).getQuads(blockState, direction, random);
if (quads != null && !quads.isEmpty() &&
!(blockState.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
break;
} ;
if (quads == null || quads.isEmpty())
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(blockState).getQuads(blockState, null, random);
}
if (quads != null && !quads.isEmpty())
{
needPostTinting = quads.get(0).isTinted();
needShade = quads.get(0).isShade();
tintIndex = quads.get(0).getTintIndex();
baseColor = calculateColorFromTexture(
#if PRE_MC_1_17_1 quads.get(0).sprite,
#else quads.get(0).getSprite(), #endif
ColorMode.getColorMode(blockState.getBlock()));
}
else
{ // Backup method.
needPostTinting = false;
needShade = false;
tintIndex = 0;
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
ColorMode.getColorMode(blockState.getBlock()));
}
}
else
{ // Liquid Block
needPostTinting = true;
needShade = false;
tintIndex = 0;
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
ColorMode.getColorMode(blockState.getBlock()));
}
isColorResolved = true;
}
public int getAndResolveFaceColor(BiomeWrapper biome, DhBlockPos pos)
{
// FIXME: impl per-face colors
// only get the tint if the block needs to be tinted
if (!this.needPostTinting)
{
return this.baseColor;
}
// don't try tinting blocks that don't support our method of tint getting
if (BROKEN_BLOCK_STATES.contains(this.blockState))
{
return this.baseColor;
}
// attempt to get the tint
int tintColor = -1;
try
{
// try to use the fast tint getter logic first
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{
try
{
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState, new TintWithoutLevelOverrider(biome), McObjectConverter.Convert(pos), this.tintIndex);
}
catch (UnsupportedOperationException e)
{
// 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);
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
}
}
// use the level logic only if requested
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
// specifically oceans don't render correctly
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState, new TintGetterOverrideFast(this.level), McObjectConverter.Convert(pos), this.tintIndex);
}
}
catch (Exception e)
{
// only display the error once per block/biome type to reduce log spam
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);
BROKEN_BLOCK_STATES.add(this.blockState);
}
}
if (tintColor != -1)
{
return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
}
else
{
// unable to get the tinted color, use the base color instead
return this.baseColor;
}
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,13 +16,13 @@
* 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.lod.common.wrappers.block.cache;
package com.seibel.distanthorizons.common.wrappers.block.cache;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.lod.common.wrappers.world.ServerLevelWrapper;
import com.seibel.lod.core.pos.DhBlockPos;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import net.minecraft.world.level.block.state.BlockState;
@@ -32,10 +32,12 @@ public class ServerBlockDetailMap
//private final ConcurrentHashMap<#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ServerLevelWrapper level;
public ServerBlockDetailMap(ServerLevelWrapper level) { this.level = level; }
public ServerBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos) { //TODO: Allow a per pos unique setting
return blockCache.computeIfAbsent(state, (s) -> new ServerBlockStateCache(s, level, new DhBlockPos(0,0,0)));
public ServerBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos)
{ //TODO: Allow a per pos unique setting
return blockCache.computeIfAbsent(state, (s) -> new ServerBlockStateCache(s, level, new DhBlockPos(0, 0, 0)));
}
public void clear() { blockCache.clear(); }
}
@@ -0,0 +1,104 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block.cache;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
/**
* @version 2022-9-16
*/
public class ServerBlockStateCache
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public final BlockState state;
public final LevelReader level;
public final BlockPos pos;
public ServerBlockStateCache(BlockState blockState, ILevelWrapper samplingLevel, DhBlockPos samplingPos)
{
state = blockState;
level = (LevelReader) samplingLevel.getWrappedMcObject();
pos = McObjectConverter.Convert(samplingPos);
resolveShapes();
//LOGGER.info("ServerBlockState created for {}", blockState);
}
boolean noCollision = false;
boolean[] occludeFaces = null;
boolean[] fullFaces = null;
boolean isShapeResolved = false;
public void resolveShapes()
{
if (isShapeResolved) return;
if (state.getFluidState().isEmpty())
{
noCollision = state.getCollisionShape(level, pos).isEmpty();
occludeFaces = new boolean[6];
if (state.canOcclude())
{
for (Direction dir : Direction.values())
{
// Note: isEmpty() isn't quite correct... best would be a isFull() or something...
occludeFaces[McObjectConverter.Convert(dir).ordinal()]
= !state.getFaceOcclusionShape(level, pos, dir).isEmpty();
}
}
VoxelShape voxelShape = state.getShape(level, pos);
fullFaces = new boolean[6];
if (!voxelShape.isEmpty())
{
for (Direction dir : Direction.values())
{
VoxelShape faceShape = voxelShape.getFaceShape(dir);
AABB aabb = faceShape.bounds();
boolean xFull = aabb.minX <= 0.01 && aabb.maxX >= 0.99;
boolean yFull = aabb.minY <= 0.01 && aabb.maxY >= 0.99;
boolean zFull = aabb.minZ <= 0.01 && aabb.maxZ >= 0.99;
fullFaces[McObjectConverter.Convert(dir).ordinal()] =
(xFull || dir.getAxis().equals(Direction.Axis.X))
&& (yFull || dir.getAxis().equals(Direction.Axis.Y))
&& (zFull || dir.getAxis().equals(Direction.Axis.Z));
}
}
}
else
{ // Liquid Block. Treat as full block
occludeFaces = new boolean[6];
Arrays.fill(occludeFaces, true);
fullFaces = new boolean[6];
Arrays.fill(fullFaces, true);
}
}
}
@@ -0,0 +1,216 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.chunk;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Compact, efficient storage for light levels.
* all blocks only take up 4 bits in total,
* and if a 16x16x16 area is detected to have the same light level in all positions,
* then we store a single byte for that light level, instead of 2 kilobytes.
*
* @author Builderb0y
*/
public class ChunkLightStorage
{
/** the minimum Y level in the chunk which this storage is storing light levels for (inclusive). */
public int minY;
/** the maximum Y level in the chunk which this storage is storing light levels for (exclusive). */
public int maxY;
/** the data stored in this storage, split up into 16x16x16 areas. */
public LightSection[] lightSections;
public ChunkLightStorage(int minY, int maxY)
{
this.minY = minY;
this.maxY = maxY;
}
public int get(int x, int y, int z)
{
if (y < this.minY)
{
return 0;
}
if (y >= this.maxY)
{
return 15;
}
if (this.lightSections != null)
{
LightSection lightSection = this.lightSections[BitShiftUtil.divideByPowerOfTwo(y - this.minY, 4)];
if (lightSection != null)
{
return lightSection.get(x, y, z);
}
}
return 0;
}
public void set(int x, int y, int z, int lightLevel)
{
if (y < this.minY || y >= this.maxY)
{
return;
}
//populate array if it doesn't exist.
if (this.lightSections == null)
{
this.lightSections = new LightSection[BitShiftUtil.divideByPowerOfTwo(this.maxY - this.minY, 4)];
}
int index = (y - this.minY) >> 4;
LightSection lightSection = this.lightSections[index];
//populate lightSection in array if it doesn't exist.
if (lightSection == null)
{
lightSection = new LightSection(0);
this.lightSections[index] = lightSection;
}
lightSection.set(x, y, z, lightLevel);
}
//================//
// helper classes //
//================//
public static class LightSection
{
public byte constantValue;
public long[] data;
public short[] counts;
public LightSection(int initialValue)
{
this.constantValue = (byte) (initialValue);
this.counts = new short[16];
this.counts[initialValue] = 16 * 16 * 16;
}
public int get(int x, int y, int z)
{
if (this.constantValue >= 0)
{
return this.constantValue;
}
x &= 15;
y &= 15;
z &= 15;
long bits = this.data[(z << 4) | x];
return ((int) (bits >>> (y << 2))) & 15;
}
public void set(int x, int y, int z, int lightLevel)
{
int oldLightLevel = -1;
if (this.constantValue >= 0)
{
oldLightLevel = this.constantValue;
//if the light level didn't change, then there's nothing to do.
if (oldLightLevel == lightLevel) return;
//if we are a constant value and need to change something,
//then that means we need to convert to a non-constant value.
this.data = DataRecycler.get();
//repeat oldLightLevel 16 times as a bit pattern.
long payload = oldLightLevel;
payload |= payload << 4;
payload |= payload << 8;
payload |= payload << 16;
payload |= payload << 32;
//fill our data with our constant value.
Arrays.fill(this.data, payload);
//we are no longer a constant value.
this.constantValue = -1;
}
x &= 15;
y &= 15;
z &= 15;
int index = (z << 4) | x;
long bits = this.data[index];
//if we weren't a constant value before, now's the time to initialize oldLightLevel.
if (oldLightLevel < 0)
{
oldLightLevel = ((int) (bits >>> (y << 2))) & 15;
}
//clear the 4 bits that correspond to the light level at x, y, z...
bits &= ~(15L << (y << 2));
//...and then re-populate those bits with the new light level.
bits |= ((long) (lightLevel)) << (y << 2);
//store the updated bits in our data.
this.data[index] = bits;
//we have one less of the old light level...
this.counts[oldLightLevel]--;
//...and one more of the new level.
//if the number associated with the new level is now 4096 (AKA 16 ^ 3),
//then this implies every position in this section has the same light level,
//and therefore we can convert back to a constant value.
if (++this.counts[lightLevel] == 4096)
{
this.constantValue = (byte) (lightLevel);
DataRecycler.reclaim(this.data);
this.data = null;
}
}
}
static class DataRecycler
{
private static final ArrayList<long[]> recycled = new ArrayList<>(256);
static synchronized long[] get()
{
if (recycled.isEmpty())
{
return new long[256];
}
else
{
return recycled.remove(recycled.size() - 1);
}
}
static synchronized void reclaim(long[] data) { if (recycled.size() < 256) recycled.add(data); }
}
}
@@ -0,0 +1,570 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.chunk;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.client.multiplayer.ClientChunkCache;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.Heightmap;
import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
#if POST_MC_1_17_1
import net.minecraft.core.QuartPos;
#endif
#if MC_1_16_5
import net.minecraft.world.level.chunk.LevelChunkSection;
#endif
#if MC_1_17_1
import net.minecraft.world.level.chunk.LevelChunkSection;
#endif
#if MC_1_18_2
import net.minecraft.world.level.chunk.LevelChunkSection;
#endif
#if MC_1_19_2 || MC_1_19_4
import net.minecraft.world.level.chunk.LevelChunkSection;
#endif
#if POST_MC_1_20_1
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.core.SectionPos;
#endif
public class ChunkWrapper implements IChunkWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/** useful for debugging, but can slow down chunk operations quite a bit due to being called every time. */
private static final boolean RUN_RELATIVE_POS_INDEX_VALIDATION = ModInfo.IS_DEV_BUILD;
/** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos());
private final ChunkAccess chunk;
private final DhChunkPos chunkPos;
private final LevelReader lightSource;
private final ILevelWrapper wrappedLevel;
private boolean isDhLightCorrect = false;
/** only used when connected to a dedicated server */
private boolean isMcClientLightingCorrect = false;
private ChunkLightStorage blockLightStorage;
private ChunkLightStorage skyLightStorage;
private ArrayList<DhBlockPos> blockLightPosList = null;
private boolean useDhLighting;
/**
* Due to vanilla `isClientLightReady()` not being designed for use by a non-render thread, it may return 'true'
* before the light engine has ticked, (right after all light changes is marked by the engine to be processed).
* To fix this, on client-only mode, we mixin-redirect the `isClientLightReady()` so that after the call, it will
* trigger a synchronous update of this flag here on all chunks that are wrapped. <br><br>
*
* Note: Using a static weak hash map to store the chunks that need to be updated, as instance of chunk wrapper
* can be duplicated, with same chunk instance. And the data stored here are all temporary, and thus will not be
* visible when a chunk is re-wrapped later. <br>
* (Also, thread safety done via a reader writer lock)
*/
private static final ConcurrentLinkedQueue<ChunkWrapper> chunksNeedingClientLightUpdating = new ConcurrentLinkedQueue<>();
//=============//
// constructor //
//=============//
public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource, ILevelWrapper wrappedLevel)
{
this.chunk = chunk;
this.lightSource = lightSource;
this.wrappedLevel = wrappedLevel;
this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z);
// TODO is this the best way to differentiate between when we are generating chunks and when MC gave us a chunk?
boolean isDhGeneratedChunk = (this.lightSource.getClass() == DhLitWorldGenRegion.class);
// MC loaded chunks should get their lighting from MC, DH generated chunks should get their lighting from DH
this.useDhLighting = isDhGeneratedChunk;
// FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the relative position validator
chunksNeedingClientLightUpdating.add(this);
}
//=========//
// methods //
//=========//
@Override
public int getHeight()
{
#if PRE_MC_1_17_1
return 255;
#else
return this.chunk.getHeight();
#endif
}
@Override
public int getMinBuildHeight()
{
#if PRE_MC_1_17_1
return 0;
#else
return this.chunk.getMinBuildHeight();
#endif
}
@Override
public int getMaxBuildHeight() { return this.chunk.getMaxBuildHeight(); }
@Override
public int getMinFilledHeight()
{
LevelChunkSection[] sections = this.chunk.getSections();
for (int index = 0; index < sections.length; index++)
{
if (sections[index] == null)
{
continue;
}
#if MC_1_16_5
if (!sections[index].isEmpty())
{
// convert from an index to a block coordinate
return this.chunk.getSections()[index].bottomBlockY() * 16;
}
#elif MC_1_17_1
if (!sections[index].isEmpty())
{
// convert from an index to a block coordinate
return this.chunk.getSections()[index].bottomBlockY() * 16;
}
#else
if (!sections[index].hasOnlyAir())
{
// convert from an index to a block coordinate
return this.chunk.getSectionYFromSectionIndex(index) * 16;
}
#endif
}
return Integer.MAX_VALUE;
}
@Override
public int getSolidHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel); }
@Override
public int getLightBlockingHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel); }
@Override
public IBiomeWrapper getBiome(int relX, int relY, int relZ)
{
#if PRE_MC_1_17_1
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome(
relX >> 2, relY >> 2, relZ >> 2),
this.wrappedLevel);
#elif PRE_MC_1_18_2
return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome(
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
this.wrappedLevel);
#elif PRE_MC_1_18_2
return BiomeWrapper.getBiomeWrapper(this.chunk.getNoiseBiome(
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
this.wrappedLevel);
#else
//Now returns a Holder<Biome> instead of Biome
return BiomeWrapper.getBiomeWrapper(this.chunk.getNoiseBiome(
QuartPos.fromBlock(relX), QuartPos.fromBlock(relY), QuartPos.fromBlock(relZ)),
this.wrappedLevel);
#endif
}
@Override
public DhChunkPos getChunkPos() { return this.chunkPos; }
public ChunkAccess getChunk() { return this.chunk; }
@Override
public int getMaxBlockX() { return this.chunk.getPos().getMaxBlockX(); }
@Override
public int getMaxBlockZ() { return this.chunk.getPos().getMaxBlockZ(); }
@Override
public int getMinBlockX() { return this.chunk.getPos().getMinBlockX(); }
@Override
public int getMinBlockZ() { return this.chunk.getPos().getMinBlockZ(); }
@Override
public long getLongChunkPos() { return this.chunk.getPos().toLong(); }
@Override
public void setIsDhLightCorrect(boolean isDhLightCorrect) { this.isDhLightCorrect = isDhLightCorrect; }
@Override
public void setUseDhLighting(boolean useDhLighting) { this.useDhLighting = useDhLighting; }
@Override
public boolean isLightCorrect()
{
if (this.useDhLighting)
{
return this.isDhLightCorrect;
}
#if MC_1_16_5 || MC_1_17_1
return false; // MC's lighting engine doesn't work consistently enough to trust for 1.16 or 1.17
#else
if (this.chunk instanceof LevelChunk)
{
LevelChunk levelChunk = (LevelChunk) this.chunk;
if (levelChunk.getLevel() instanceof ClientLevel)
{
// connected to a server
return this.isMcClientLightingCorrect;
}
else
{
// in a single player world
return this.chunk.isLightCorrect() && levelChunk.loaded;
}
}
else
{
// called when in a single player world and the chunk is a proto chunk (in world gen, and not active)
return this.chunk.isLightCorrect();
}
#endif
}
@Override
public int getDhBlockLight(int relX, int y, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
return this.getBlockLightStorage().get(relX, y, relZ);
}
@Override
public void setDhBlockLight(int relX, int y, int relZ, int lightValue)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
this.getBlockLightStorage().set(relX, y, relZ, lightValue);
}
private ChunkLightStorage getBlockLightStorage()
{
if (this.blockLightStorage == null)
{
this.blockLightStorage = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight());
}
return this.blockLightStorage;
}
@Override
public int getDhSkyLight(int relX, int y, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
return this.getSkyLightStorage().get(relX, y, relZ);
}
@Override
public void setDhSkyLight(int relX, int y, int relZ, int lightValue)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
this.getSkyLightStorage().set(relX, y, relZ, lightValue);
}
private ChunkLightStorage getSkyLightStorage()
{
if (this.skyLightStorage == null)
{
this.skyLightStorage = new ChunkLightStorage(this.getMinBuildHeight(), this.getMaxBuildHeight());
}
return this.skyLightStorage;
}
@Override
public int getBlockLight(int relX, int y, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
// use the full lighting engine when the chunks are within render distance or the config requests it
if (this.useDhLighting)
{
// DH lighting method
return this.getBlockLightStorage().get(relX, y, relZ);
}
else
{
// note: this returns 0 if the chunk is unload
// MC lighting method
return this.lightSource.getBrightness(LightLayer.BLOCK, new BlockPos(relX + this.getMinBlockX(), y, relZ + this.getMinBlockZ()));
}
}
@Override
public int getSkyLight(int relX, int y, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
// use the full lighting engine when the chunks are within render distance or the config requests it
if (this.useDhLighting)
{
// DH lighting method
return this.getSkyLightStorage().get(relX, y, relZ);
}
else
{
// MC lighting method
return this.lightSource.getBrightness(LightLayer.SKY, new BlockPos(relX + this.getMinBlockX(), y, relZ + this.getMinBlockZ()));
}
}
@Override
public ArrayList<DhBlockPos> getBlockLightPosList()
{
// only populate the list once
if (this.blockLightPosList == null)
{
this.blockLightPosList = new ArrayList<>();
#if PRE_MC_1_20_1
this.chunk.getLights().forEach((blockPos) ->
{
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
});
#else
this.chunk.findBlockLightSources((blockPos, blockState) ->
{
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
});
#endif
}
return this.blockLightPosList;
}
@Override
public boolean doNearbyChunksExist()
{
if (this.lightSource instanceof DhLitWorldGenRegion)
{
return true;
}
for (int dx = -1; dx <= 1; dx++)
{
for (int dz = -1; dz <= 1; dz++)
{
if (dx == 0 && dz == 0)
{
continue;
}
else if (this.lightSource.getChunk(dx + this.chunk.getPos().x, dz + this.chunk.getPos().z, ChunkStatus.BIOMES, false) == null)
{
return false;
}
}
}
return true;
}
public LevelReader getColorResolver() { return this.lightSource; }
@Override
public String toString() { return this.chunk.getClass().getSimpleName() + this.chunk.getPos(); }
@Override
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get();
blockPos.setX(relX);
blockPos.setY(relY);
blockPos.setZ(relZ);
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
}
@Override
public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; }
public static void syncedUpdateClientLightStatus()
{
#if PRE_MC_1_18_2
// TODO: Check what to do in 1.18.1 and older
// since we don't currently handle this list,
// clear it to prevent memory leaks
chunksNeedingClientLightUpdating.clear();
#else
// update the chunks client lighting
ChunkWrapper chunkWrapper = chunksNeedingClientLightUpdating.poll();
while (chunkWrapper != null)
{
chunkWrapper.updateIsClientLightingCorrect();
chunkWrapper = chunksNeedingClientLightUpdating.poll();
}
#endif
}
/** Should be called after client light updates are triggered. */
private void updateIsClientLightingCorrect()
{
if (this.chunk instanceof LevelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel)
{
LevelChunk levelChunk = (LevelChunk) this.chunk;
ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource();
this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null &&
#if MC_1_16_5 || MC_1_17_1
levelChunk.isLightCorrect();
#elif PRE_MC_1_20_1
levelChunk.isClientLightReady();
#else
checkLightSectionsOnChunk(levelChunk, levelChunk.getLevel().getLightEngine());
#endif
}
}
#if POST_MC_1_20_1
private static boolean checkLightSectionsOnChunk(LevelChunk chunk, LevelLightEngine engine)
{
LevelChunkSection[] sections = chunk.getSections();
int minY = chunk.getMinSection();
int maxY = chunk.getMaxSection();
for (int y = minY; y < maxY; ++y)
{
LevelChunkSection section = sections[chunk.getSectionIndexFromSectionY(y)];
if (section.hasOnlyAir()) continue;
if (!engine.lightOnInSection(SectionPos.of(chunk.getPos(), y)))
{
return false;
}
}
return true;
}
#endif
//================//
// helper methods //
//================//
/** used to prevent accidentally attempting to get/set values outside this chunk's boundaries */
private void throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(int x, int y, int z) throws IndexOutOfBoundsException
{
if (!RUN_RELATIVE_POS_INDEX_VALIDATION)
{
return;
}
// FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the constructor
int minHeight = this.getMinBuildHeight();
int maxHeight = this.getMaxBuildHeight() + 1;
if (x < 0 || x >= LodUtil.CHUNK_WIDTH
|| z < 0 || z >= LodUtil.CHUNK_WIDTH
|| y < minHeight || y > maxHeight)
{
String errorMessage = "Relative position [" + x + "," + y + "," + z + "] out of bounds. \n" +
"X/Z must be between 0 and 15 (inclusive) \n" +
"Y must be between [" + minHeight + "] and [" + maxHeight + "] (inclusive).";
throw new IndexOutOfBoundsException(errorMessage);
}
}
/**
* Converts a 3D position into a 1D array index. <br><br>
*
* Source: <br>
* <a href="https://stackoverflow.com/questions/7367770/how-to-flatten-or-index-3d-array-in-1d-array">stackoverflow</a>
*/
public int relativeBlockPosToIndex(int xRel, int y, int zRel)
{
int yRel = y - this.getMinBuildHeight();
return (zRel * LodUtil.CHUNK_WIDTH * this.getHeight()) + (yRel * LodUtil.CHUNK_WIDTH) + xRel;
}
/**
* Converts a 3D position into a 1D array index. <br><br>
*
* Source: <br>
* <a href="https://stackoverflow.com/questions/7367770/how-to-flatten-or-index-3d-array-in-1d-array">stackoverflow</a>
*/
public DhBlockPos indexToRelativeBlockPos(int index)
{
final int zRel = index / (LodUtil.CHUNK_WIDTH * this.getHeight());
index -= (zRel * LodUtil.CHUNK_WIDTH * this.getHeight());
final int y = index / LodUtil.CHUNK_WIDTH;
final int yRel = y + this.getMinBuildHeight();
final int xRel = index % LodUtil.CHUNK_WIDTH;
return new DhBlockPos(xRel, yRel, zRel);
}
}
@@ -0,0 +1,675 @@
package com.seibel.distanthorizons.common.wrappers.gui;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
// Logger (for debug stuff)
import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui;
import com.seibel.distanthorizons.api.enums.config.EUpdateBranch;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.types.*;
import com.seibel.distanthorizons.common.wrappers.gui.updater.ChangelogScreen;
// Uses https://github.com/TheElectronWill/night-config for toml (only for Fabric since Forge already includes this)
// Gets info from our own mod
// Minecraft imports
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.util.AnnotationUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
#if PRE_MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.GuiComponent;
#else
import net.minecraft.client.gui.GuiGraphics;
#endif
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.client.resources.language.I18n; // translation
#if POST_MC_1_17_1
import net.minecraft.client.gui.narration.NarratableEntry;
#endif
import net.minecraft.resources.ResourceLocation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
/**
* Based upon TinyConfig but is highly modified
* https://github.com/Minenash/TinyConfig
*
* Credits to Motschen
*
* @author coolGi
* @version 5-21-2022
*/
// FLOATS DONT WORK WITH THIS
/** This file is going to be removed sometime soon, please dont hook onto anything within this file until the new UI is compleated */
@SuppressWarnings("unchecked")
public class ClassicConfigGUI
{
/*
This would be removed later on as it is going to be re-written in java swing
*/
private static final Logger LOGGER = LogManager.getLogger();
public static final ConfigCoreInterface CONFIG_CORE_INTERFACE = new ConfigCoreInterface();
//==============//
// Initializers //
//==============//
// Some regexes to check if an input is valid
private static final Pattern INTEGER_ONLY_REGEX = Pattern.compile("(-?[0-9]*)");
private static final Pattern DECIMAL_ONLY_REGEX = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)");
private static class ConfigScreenConfigs
{
// This contains all the configs for the configs
public static final int SpaceFromRightScreen = 10;
public static final int ButtonWidthSpacing = 5;
public static final int ResetButtonWidth = 40;
}
/**
* The terribly coded old stuff
*/
public static class EntryInfo
{
Object widget;
Map.Entry<EditBox, Component> error;
String tempValue;
int index;
}
/**
* creates a text field
*/
private static void textField(AbstractConfigType info, Function<String, Number> func, Pattern pattern, boolean cast)
{
boolean isNumber = pattern != null;
((EntryInfo) info.guiValue).widget = (BiFunction<EditBox, Button, Predicate<String>>) (editBox, button) -> stringValue ->
{
stringValue = stringValue.trim();
if (!(stringValue.isEmpty() || !isNumber || pattern.matcher(stringValue).matches()))
return false;
Number value = 0;
((EntryInfo) info.guiValue).error = null;
if (isNumber && !stringValue.isEmpty() && !stringValue.equals("-") && !stringValue.equals("."))
{
try
{
value = func.apply(stringValue);
}
catch (Exception e)
{
value = null;
}
byte isValid = ((ConfigEntry) info).isValid(value);
switch (isValid)
{
case 0:
((EntryInfo) info.guiValue).error = null; break;
case -1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry) info).getMin())); break;
case 1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry) info).getMax())); break;
case 2:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid")); break;
}
}
((EntryInfo) info.guiValue).tempValue = stringValue;
editBox.setTextColor(((ConfigEntry) info).isValid(value) == 0 ? 0xFFFFFFFF : 0xFFFF7777);
// button.active = entries.stream().allMatch(e -> e.inLimits);
if (((ConfigEntry) info).isValid(value) == 0 && info.getType() != List.class)
{
if (!cast)
((ConfigEntry) info).uiSetWithoutSaving(value);
else
((ConfigEntry) info).uiSetWithoutSaving(value.intValue());
}
// else if (((ConfigEntry) info).isValidMemoryAddress() == 0)
// {
// if (((List<String>) info.get()).size() == ((EntryInfo) info.guiValue).index)
// info.uiSet(((List<String>) info.get()).add(""));
// info.uiSet(((List<String>) info.get()).set(((EntryInfo) info.guiValue).index, Arrays.stream(((EntryInfo) info.guiValue).tempValue.replace("[", "").replace("]", "").split(", ")).collect(Collectors.toList()).get(0)));
// }
return true;
};
}
//==============//
// GUI handling //
//==============//
/**
* if you want to get this config gui's screen call this
*/
public static Screen getScreen(ConfigBase configBase, Screen parent, String category)
{
return new ConfigScreen(configBase, parent, category);
}
/**
* Pain
*/
private static class ConfigScreen extends DhScreen
{
protected ConfigScreen(ConfigBase configBase, Screen parent, String category)
{
super(Translatable(
I18n.exists(configBase.modID + ".config" + (category.isEmpty() ? "." + category : "") + ".title") ?
configBase.modID + ".config.title" :
configBase.modID + ".config" + (category.isEmpty() ? "" : "." + category) + ".title")
);
this.configBase = configBase;
this.parent = parent;
this.category = category;
this.translationPrefix = configBase.modID + ".config.";
}
private final ConfigBase configBase;
private final String translationPrefix;
private final Screen parent;
private final String category;
private ConfigListWidget list;
private boolean reload = false;
private Button doneButton;
// Real Time config update //
@Override
public void tick()
{
super.tick();
}
/**
* When you close it, it goes to the previous screen and saves
*/
@Override
public void onClose()
{
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(this.minecraft).setScreen(this.parent);
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
}
@Override
protected void init()
{
super.init();
if (!reload)
{
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile();
}
// Changelog button
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() && Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE)
{
this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen
this.width - 28, this.height - 28,
// Width and height of the button
20, 20,
// Offset
0, 0,
// Some textuary stuff
0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20,
// Create the button and tell it where to go
(buttonWidget) -> {
ChangelogScreen changelogScreen = new ChangelogScreen(this);
if (changelogScreen.usable)
Objects.requireNonNull(minecraft).setScreen(changelogScreen);
else
LOGGER.warn("Changelog was not able to open");
},
// Add a title to the button
Translatable(ModInfo.ID + ".updater.title")
));
}
addBtn(MakeBtn(Translatable("distanthorizons.general.cancel"), this.width / 2 - 154, this.height - 28, 150, 20, button -> {
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile();
Objects.requireNonNull(minecraft).setScreen(parent);
}));
doneButton = addBtn(MakeBtn(Translatable("distanthorizons.general.done"), this.width / 2 + 4, this.height - 28, 150, 20, (button) -> {
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(minecraft).setScreen(parent);
}));
this.list = new ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, this.height - 32, 25);
if (this.minecraft != null && this.minecraft.level != null)
this.list.setRenderBackground(false);
this.addWidget(this.list);
for (AbstractConfigType info : ConfigBase.INSTANCE.entries)
{
try
{
if (info.getCategory().matches(category) && info.getAppearance().showInGui)
addMenuItem(info);
}
catch (Exception e)
{
System.out.println("ERROR: Failed to show [" + info.getNameWCategory() + "]");
if (info.get() != null)
System.out.print(" with the value [" + info.get() + "] with type [" + info.getType() + "]");
e.printStackTrace();
}
}
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
}
private void addMenuItem(AbstractConfigType info)
{
initEntry(info, this.translationPrefix);
Component name = Translatable(translationPrefix + info.getNameWCategory());
if (ConfigEntry.class.isAssignableFrom(info.getClass()))
{
Button.OnPress btnAction = button -> {
((ConfigEntry) info).uiSetWithoutSaving(((ConfigEntry) info).getDefaultValue());
((EntryInfo) info.guiValue).index = 0;
this.reload = true;
Objects.requireNonNull(minecraft).setScreen(this);
};
int a = this.width - ConfigScreenConfigs.SpaceFromRightScreen - 150 - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth;
int b = 0;
int c = ConfigScreenConfigs.ResetButtonWidth;
int d = 20;
Button resetButton = MakeBtn(Translatable("distanthorizons.general.reset").withStyle(ChatFormatting.RED), a, b, c, d, btnAction);
if (((EntryInfo) info.guiValue).widget instanceof Map.Entry)
{
Map.Entry<Button.OnPress, Function<Object, Component>> widget = (Map.Entry<Button.OnPress, Function<Object, Component>>) ((EntryInfo) info.guiValue).widget;
if (info.getType().isEnum())
{
widget.setValue(value -> Translatable(translationPrefix + "enum." + info.getType().getSimpleName() + "." + info.get().toString()));
}
this.list.addButton(MakeBtn(widget.getValue().apply(info.get()), this.width - 150 - ConfigScreenConfigs.SpaceFromRightScreen, 0, 150, 20, widget.getKey()), resetButton, null, name);
return;
}
else if (((EntryInfo) info.guiValue).widget != null)
{
EditBox widget = new EditBox(font, this.width - 150 - ConfigScreenConfigs.SpaceFromRightScreen + 2, 0, 150 - 4, 20, null);
widget.setMaxLength(150);
widget.insertText(String.valueOf(info.get()));
Predicate<String> processor = ((BiFunction<EditBox, Button, Predicate<String>>) ((EntryInfo) info.guiValue).widget).apply(widget, doneButton);
widget.setFilter(processor);
this.list.addButton(widget, resetButton, null, name);
return;
}
}
if (ConfigCategory.class.isAssignableFrom(info.getClass()))
{
Button widget = MakeBtn(name, this.width / 2 - 100, this.height - 28, 100 * 2, 20, (button -> {
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(minecraft).setScreen(ClassicConfigGUI.getScreen(this.configBase, this, ((ConfigCategory) info).getDestination()));
}));
this.list.addButton(widget, null, null, null);
return;
}
if (ConfigUIButton.class.isAssignableFrom(info.getClass()))
{
Button widget = MakeBtn(name, this.width / 2 - 100, this.height - 28, 100 * 2, 20, (button -> {
((ConfigUIButton) info).runAction();
}));
this.list.addButton(widget, null, null, null);
return;
}
if (ConfigUIComment.class.isAssignableFrom(info.getClass()))
{
this.list.addButton(null, null, null, name);
return;
}
if (ConfigLinkedEntry.class.isAssignableFrom(info.getClass()))
{
this.addMenuItem(((ConfigLinkedEntry) info).get());
return;
}
LOGGER.warn("Config [" + info.getNameWCategory() + "] failed to show. Please try something like changing its type.");
}
@Override
#if PRE_MC_1_20_1
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
#else
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#endif
{
#if PRE_MC_1_20_2 // 1.20.2 now enables this by default in the `this.list.render` function
this.renderBackground(matrices); // Renders background
#else
super.render(matrices, mouseX, mouseY, delta);
#endif
this.list.render(matrices, mouseX, mouseY, delta); // Render buttons
DhDrawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title
if (this.configBase.modID.equals("distanthorizons"))
{
// Display version
DhDrawString(matrices, font, TextOrLiteral(ModInfo.VERSION), 2, height - 10, 0xAAAAAA);
// If the update is pending, display this message to inform the user that it will apply when the game restarts
if (SelfUpdater.deleteOldJarOnJvmShutdown)
DhDrawString(matrices, font, Translatable(configBase.modID + ".updater.waitingForClose"), 4, height - 38, 0xFFFFFF);
}
// Render the tooltip only if it can find a tooltip in the language file
for (AbstractConfigType info : ConfigBase.INSTANCE.entries)
{
if (info.getCategory().matches(category) && info.getAppearance().showInGui)
{
if (list.getHoveredButton(mouseX, mouseY).isPresent())
{
AbstractWidget buttonWidget = list.getHoveredButton(mouseX, mouseY).get();
Component text = ButtonEntry.buttonsWithText.get(buttonWidget);
if (text == null)
{
continue;
}
// A quick fix for tooltips on linked entries
AbstractConfigType newInfo = ConfigLinkedEntry.class.isAssignableFrom(info.getClass()) ?
((ConfigLinkedEntry) info).get() :
info;
Component name = Translatable(this.translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.getName());
String key = translationPrefix + (newInfo.category.isEmpty() ? "" : newInfo.category + ".") + newInfo.getName() + ".@tooltip";
if (((EntryInfo) newInfo.guiValue).error != null && text.equals(name))
DhRenderTooltip(matrices, font, ((EntryInfo) newInfo.guiValue).error.getValue(), mouseX, mouseY);
else if (I18n.exists(key) && (text != null && text.equals(name)))
{
List<Component> list = new ArrayList<>();
for (String str : I18n.get(key).split("\n"))
{
list.add(TextOrTranslatable(str));
}
DhRenderComponentTooltip(matrices, font, list, mouseX, mouseY);
}
}
}
}
#if PRE_MC_1_20_2
super.render(matrices, mouseX, mouseY, delta);
#endif
}
}
private static void initEntry(AbstractConfigType info, String translationPrefix)
{
info.guiValue = new EntryInfo();
Class<?> fieldClass = info.getType();
if (ConfigEntry.class.isAssignableFrom(info.getClass()))
{
if (fieldClass == Integer.class)
{
// For int
textField(info, Integer::parseInt, INTEGER_ONLY_REGEX, true);
}
else if (fieldClass == Double.class)
{
// For double
textField(info, Double::parseDouble, DECIMAL_ONLY_REGEX, false);
}
else if (fieldClass == String.class || fieldClass == List.class)
{
// For string or list
textField(info, String::length, null, true);
}
else if (fieldClass == Boolean.class)
{
// For boolean
Function<Object, Component> func = value -> Translatable("distanthorizons.general."+((Boolean) value ? "true" : "false")).withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
((EntryInfo) info.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
((ConfigEntry) info).uiSetWithoutSaving(!(Boolean) info.get());
button.setMessage(func.apply(info.get()));
}, func);
}
else if (fieldClass.isEnum())
{
// For enum
List<?> values = Arrays.asList(info.getType().getEnumConstants());
Function<Object, Component> func = value -> Translatable(translationPrefix + "enum." + fieldClass.getSimpleName() + "." + info.get().toString());
((EntryInfo) info.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
// get the currently selected enum and enum index
int startingIndex = values.indexOf(info.get());
Enum<?> enumValue = (Enum<?>) values.get(startingIndex);
// search for the next enum that is selectable
int index = startingIndex + 1;
index = (index >= values.size()) ? 0 : index;
while (index != startingIndex)
{
enumValue = (Enum<?>) values.get(index);
if (!AnnotationUtil.doesEnumHaveAnnotation(enumValue, DisallowSelectingViaConfigGui.class))
{
// this enum shouldn't be selectable via the UI,
// skip it
break;
}
index++;
index = (index >= values.size()) ? 0 : index;
}
if (index == startingIndex)
{
// none of the enums should be selectable, this is a programmer error
enumValue = (Enum<?>) values.get(startingIndex);
LOGGER.warn("Enum [" + enumValue.getClass() + "] doesn't contain any values that should be selectable via the UI, sticking to the currently selected value [" + enumValue + "].");
}
((ConfigEntry<Enum<?>>) info).uiSetWithoutSaving(enumValue);
button.setMessage(func.apply(info.get()));
}, func);
}
}
else if (ConfigCategory.class.isAssignableFrom(info.getClass()))
{
// if (!info.info.getName().equals(""))
// info.name = new TranslatableComponent(info.info.getName());
}
// return info;
}
public static class ConfigListWidget extends ContainerObjectSelectionList<ButtonEntry>
{
Font textRenderer;
public ConfigListWidget(Minecraft minecraftClient, int i, int j, int k, int l, int m)
{
super(minecraftClient, i, j, k, l, m);
this.centerListVertically = false;
textRenderer = minecraftClient.font;
}
public void addButton(AbstractWidget button, AbstractWidget resetButton, AbstractWidget indexButton, Component text)
{
this.addEntry(ButtonEntry.create(button, text, resetButton, indexButton));
}
@Override
public int getRowWidth()
{
return 10000;
}
public Optional<AbstractWidget> getHoveredButton(double mouseX, double mouseY)
{
for (ButtonEntry buttonEntry : this.children())
{
if (buttonEntry.button != null && buttonEntry.button.isMouseOver(mouseX, mouseY))
{
return Optional.of(buttonEntry.button);
}
}
return Optional.empty();
}
}
public static class ButtonEntry extends ContainerObjectSelectionList.Entry<ButtonEntry>
{
private static final Font textRenderer = Minecraft.getInstance().font;
public final AbstractWidget button;
private final AbstractWidget resetButton;
private final AbstractWidget indexButton;
private final Component text;
private final List<AbstractWidget> children = new ArrayList<>();
public static final Map<AbstractWidget, Component> buttonsWithText = new HashMap<>();
private ButtonEntry(AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton)
{
buttonsWithText.put(button, text);
this.button = button;
this.resetButton = resetButton;
this.text = text;
this.indexButton = indexButton;
if (button != null)
children.add(button);
if (resetButton != null)
children.add(resetButton);
if (indexButton != null)
children.add(indexButton);
}
public static ButtonEntry create(AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton)
{
return new ButtonEntry(button, text, resetButton, indexButton);
}
@Override
#if PRE_MC_1_20_1
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
#else
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
#endif
{
if (button != null)
{
SetY(button, y);
button.render(matrices, mouseX, mouseY, tickDelta);
}
if (resetButton != null)
{
SetY(resetButton, y);
resetButton.render(matrices, mouseX, mouseY, tickDelta);
}
if (indexButton != null)
{
SetY(indexButton, y);
indexButton.render(matrices, mouseX, mouseY, tickDelta);
}
if (text != null && (!text.getString().contains("spacer") || button != null))
#if PRE_MC_1_20_1
GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF);
#else
matrices.drawString(textRenderer, text, 12, y + 5, 0xFFFFFF);
#endif
}
@Override
public List<? extends GuiEventListener> children()
{
return children;
}
// Only for 1.17 and over
// Remove in 1.16 and below
#if POST_MC_1_17_1
@Override
public List<? extends NarratableEntry> narratables()
{
return children;
}
#endif
}
//================//
// event handling //
//================//
private static class ConfigCoreInterface implements IConfigGui
{
/**
* in the future it would be good to pass in the current page and other variables,
* but for now just knowing when the page is closed is good enough
*/
public final ArrayList<Runnable> onScreenChangeListenerList = new ArrayList<>();
@Override
public void addOnScreenChangeListener(Runnable newListener) { this.onScreenChangeListenerList.add(newListener); }
@Override
public void removeOnScreenChangeListener(Runnable oldListener) { this.onScreenChangeListenerList.remove(oldListener); }
}
}
@@ -0,0 +1,77 @@
package com.seibel.distanthorizons.common.wrappers.gui;
import net.minecraft.client.gui.Font;
#if PRE_MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
#else
import net.minecraft.client.gui.GuiGraphics;
#endif
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import java.util.List;
public class DhScreen extends Screen
{
protected DhScreen(Component $$0)
{
super($$0);
}
// addRenderableWidget in 1.17 and over
// addButton in 1.16 and below
protected Button addBtn(Button button)
{
#if PRE_MC_1_17_1
return this.addButton(button);
#else
return this.addRenderableWidget(button);
#endif
}
#if PRE_MC_1_20_1
protected void DhDrawCenteredString(PoseStack guiStack, Font font, Component text, int x, int y, int color)
{
drawCenteredString(guiStack, font, text, x, y, color);
}
protected void DhDrawString(PoseStack guiStack, Font font, Component text, int x, int y, int color)
{
drawString(guiStack, font, text, x, y, color);
}
protected void DhRenderTooltip(PoseStack guiStack, Font font, List<? extends net.minecraft.util.FormattedCharSequence> text, int x, int y)
{
renderTooltip(guiStack, text, x, y);
}
protected void DhRenderComponentTooltip(PoseStack guiStack, Font font, List<Component> comp, int x, int y)
{
renderComponentTooltip(guiStack, comp, x, y);
}
protected void DhRenderTooltip(PoseStack guiStack, Font font, Component comp, int x, int y)
{
renderTooltip(guiStack, comp, x, y);
}
#else
protected void DhDrawCenteredString(GuiGraphics guiStack, Font font, Component text, int x, int y, int color)
{
guiStack.drawCenteredString(font, text, x, y, color);
}
protected void DhDrawString(GuiGraphics guiStack, Font font, Component text, int x, int y, int color)
{
guiStack.drawString(font, text, x, y, color);
}
protected void DhRenderTooltip(GuiGraphics guiStack, Font font, List<? extends net.minecraft.util.FormattedCharSequence> text, int x, int y)
{
guiStack.renderTooltip(font, text, x, y);
}
protected void DhRenderComponentTooltip(GuiGraphics guiStack, Font font, List<Component> comp, int x, int y)
{
guiStack.renderComponentTooltip(font, comp, x, y);
}
protected void DhRenderTooltip(GuiGraphics guiStack, Font font, Component text, int x, int y)
{
guiStack.renderTooltip(font, text, x, y);
}
#endif
}
@@ -0,0 +1,43 @@
package com.seibel.distanthorizons.common.wrappers.gui;
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.OpenGLConfigScreen;
import net.minecraft.client.gui.screens.Screen;
public class GetConfigScreen
{
public static type useScreen = type.Classic;
public enum type
{
Classic,
@Deprecated
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)
{
// Generate the language
// This shouldn't be here, but I need a way to test it after Minecraft inits its assets
//System.out.println(ConfigBase.INSTANCE.generateLang(false, true));
switch (useScreen)
{
case Classic:
return ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client");
case OpenGL:
MinecraftScreen.getScreen(parent, new OpenGLConfigScreen(), ModInfo.ID + ".title");
return null;
// case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title");
case JavaFX:
return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
default:
return null;
}
}
}
@@ -0,0 +1,72 @@
package com.seibel.distanthorizons.common.wrappers.gui;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
#if PRE_MC_1_19_2
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
#endif
public class GuiHelper
{
/**
* Helper static methods for versional compat
*/
public static Button MakeBtn(Component base, int a, int b, int c, int d, Button.OnPress action)
{
#if PRE_MC_1_19_4
return new Button(a, b, c, d, base, action);
#else
return Button.builder(base, action).bounds(a, b, c, d).build();
#endif
}
public static MutableComponent TextOrLiteral(String text)
{
#if PRE_MC_1_19_2
return new TextComponent(text);
#else
return Component.literal(text);
#endif
}
public static MutableComponent TextOrTranslatable(String text)
{
#if PRE_MC_1_19_2
return new TextComponent(text);
#else
return Component.translatable(text);
#endif
}
public static MutableComponent Translatable(String text, Object... args)
{
#if PRE_MC_1_19_2
return new TranslatableComponent(text, args);
#else
return Component.translatable(text, args);
#endif
}
public static void SetX(AbstractWidget w, int x)
{
#if PRE_MC_1_19_4
w.x = x;
#else
w.setX(x);
#endif
}
public static void SetY(AbstractWidget w, int y)
{
#if PRE_MC_1_19_4
w.y = y;
#else
w.setY(y);
#endif
}
}
@@ -0,0 +1,21 @@
package com.seibel.distanthorizons.common.wrappers.gui;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
import net.minecraft.client.resources.language.I18n;
public class LangWrapper implements ILangWrapper
{
public static final LangWrapper INSTANCE = new LangWrapper();
@Override
public boolean langExists(String str)
{
return I18n.exists(str);
}
@Override
public String getLang(String str)
{
return I18n.get(str);
}
}
@@ -0,0 +1,142 @@
package com.seibel.distanthorizons.common.wrappers.gui;
import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.distanthorizons.core.config.gui.AbstractScreen;
import net.minecraft.client.Minecraft;
#if POST_MC_1_20_1
import net.minecraft.client.gui.GuiGraphics;
#endif
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.screens.Screen;
import org.jetbrains.annotations.NotNull;
import java.nio.file.Path;
import java.util.*;
public class MinecraftScreen
{
public static Screen getScreen(Screen parent, AbstractScreen screen, String translationName)
{
return new ConfigScreenRenderer(parent, screen, translationName);
}
private static class ConfigScreenRenderer extends DhScreen
{
private final Screen parent;
private ConfigListWidget list;
private AbstractScreen screen;
#if PRE_MC_1_19_2
public static net.minecraft.network.chat.TranslatableComponent translate(String str, Object... args)
{
return new net.minecraft.network.chat.TranslatableComponent(str, args);
}
#else
public static net.minecraft.network.chat.MutableComponent translate(String str, Object... args)
{
return net.minecraft.network.chat.Component.translatable(str, args);
}
#endif
protected ConfigScreenRenderer(Screen parent, AbstractScreen screen, String translationName)
{
super(translate(translationName));
screen.minecraftWindow = Minecraft.getInstance().getWindow().getWindow();
this.parent = parent;
this.screen = screen;
}
@Override
protected void init()
{
super.init(); // Init Minecraft's screen
Window mcWindow = this.minecraft.getWindow();
screen.width = mcWindow.getWidth();
screen.height = mcWindow.getHeight();
screen.scaledWidth = this.width;
screen.scaledHeight = this.height;
screen.init(); // Init our own config screen
this.list = new ConfigListWidget(this.minecraft, this.width, this.height, 0, this.height, 25); // Select the area to tint
if (this.minecraft != null && this.minecraft.level != null) // Check if in game
this.list.setRenderBackground(false); // Disable from rendering
this.addWidget(this.list); // Add the tint to the things to be rendered
}
@Override
#if PRE_MC_1_20_1
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
#else
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#endif
{
#if MC_1_20_2
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#else
this.renderBackground(matrices); // Render background
#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;
screen.mouseY = mouseY;
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)
}
@Override
public void resize(Minecraft mc, int width, int height)
{
super.resize(mc, width, height); // Resize Minecraft's screen
Window mcWindow = this.minecraft.getWindow();
screen.width = mcWindow.getWidth();
screen.height = mcWindow.getHeight();
screen.scaledWidth = this.width;
screen.scaledHeight = this.height;
screen.onResize(); // Resize our screen
}
@Override
public void tick()
{
super.tick(); // Tick Minecraft's screen
screen.tick(); // Tick our screen
if (screen.close) // If we decide to close the screen, then actually close the screen
onClose();
}
@Override
public void onClose()
{
screen.onClose(); // Close our screen
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
}
@Override
public void onFilesDrop(@NotNull List<Path> files)
{
screen.onFilesDrop(files);
}
// For checking if it should close when you press the escape key
@Override
public boolean shouldCloseOnEsc()
{
return screen.shouldCloseOnEsc;
}
}
public static class ConfigListWidget extends ContainerObjectSelectionList
{
public ConfigListWidget(Minecraft minecraftClient, int i, int j, int k, int l, int m)
{
super(minecraftClient, i, j, k, l, m);
this.centerListVertically = false;
}
}
}
@@ -0,0 +1,177 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.gui;
/**
* Creates a button with a texture on it (and a background) that works with all mc versions
*
* @author coolGi
* @version 2023-10-03
*/
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
#if POST_MC_1_17_1
import net.minecraft.client.renderer.GameRenderer;
#endif
#if PRE_MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
#else
import net.minecraft.client.gui.GuiGraphics;
#endif
#if PRE_MC_1_20_2
public class TexturedButtonWidget extends ImageButton
#else
public class TexturedButtonWidget extends Button
#endif
{
public final boolean renderBackground;
#if POST_MC_1_20_2
private final int u;
private final int v;
private final int hoveredVOffset;
private final ResourceLocation texture;
private final int textureWidth;
private final int textureHeight;
#endif
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, Component text) {
this(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text, true);
}
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground)
{
#if PRE_MC_1_20_2
super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text);
#else
// We don't pass on the text option as otherwise it will render (we normally pass it for narration)
// TODO: Find a fix for it
super(x, y, width, height, Component.empty(), pressAction, DEFAULT_NARRATION);
this.u = u;
this.v = v;
this.hoveredVOffset = hoveredVOffset;
this.texture = texture;
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
#endif
this.renderBackground = renderBackground;
}
#if PRE_MC_1_20_2
#if PRE_MC_1_19_4
@Override
public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta) {
if (this.renderBackground) // Renders the background of the button
{
#if PRE_MC_1_17_1
Minecraft.getInstance().getTextureManager().bind(WIDGETS_LOCATION);
RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
#else
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderTexture(0, WIDGETS_LOCATION);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
#endif
int i = this.getYImage(this.isHovered);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableDepthTest();
#if PRE_MC_1_19_4
this.blit(matrices, this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height);
this.blit(matrices, this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
#else
this.blit(matrices, this.getX(), this.getY(), 0, 46 + i * 20, this.getWidth() / 2, this.getHeight());
this.blit(matrices, this.getX() + this.getWidth() / 2, this.getY(), 200 - this.width / 2, 46 + i * 20, this.getWidth() / 2, this.getHeight());
#endif
}
super.renderButton(matrices, mouseX, mouseY, delta);
}
#else
#if PRE_MC_1_20_1
@Override
public void renderWidget(PoseStack matrices, int mouseX, int mouseY, float delta)
{
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderTexture(0, WIDGETS_LOCATION);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
#else
@Override
public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta)
{
#endif
if (this.renderBackground) // Renders the background of the button
{
int i = 1;
if (!this.active) i = 0;
else if (this.isHovered) i = 2;
#if PRE_MC_1_20_1
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableDepthTest();
this.blit(matrices, this.getX(), this.getY(), 0, 46 + i * 20, this.getWidth() / 2, this.getHeight());
this.blit(matrices, this.getX() + this.getWidth() / 2, this.getY(), 200 - this.width / 2, 46 + i * 20, this.getWidth() / 2, this.getHeight());
#else
matrices.blit(WIDGETS_LOCATION, this.getX(), this.getY(), 0, 46 + i * 20, this.getWidth() / 2, this.getHeight());
matrices.blit(WIDGETS_LOCATION, this.getX() + this.getWidth() / 2, this.getY(), 200 - this.width / 2, 46 + i * 20, this.getWidth() / 2, this.getHeight());
#endif
}
super.renderWidget(matrices, mouseX, mouseY, delta);
}
#endif
#else
@Override
public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta)
{
if (this.renderBackground)
{
//RenderSystem.enableBlend();
//RenderSystem.enableDepthTest();
matrices.blitSprite(SPRITES.get(this.active, this.isHoveredOrFocused()), this.getX(), this.getY(), this.getWidth(), this.getHeight());
}
// Renders the sprite
int i = 0;
if (!this.active) i = 2;
else if (this.isHovered) i = 1;
matrices.blit(this.texture, this.getX(), this.getY(), this.u, this.v + (this.hoveredVOffset * i), this.width, this.height, this.textureWidth, this.textureHeight);
}
#endif
}
@@ -0,0 +1,252 @@
package com.seibel.distanthorizons.common.wrappers.gui.updater;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.jar.installer.MarkdownFormatter;
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
#if POST_MC_1_17_1
import net.minecraft.client.gui.narration.NarratableEntry;
#endif
#if PRE_MC_1_20_1
import net.minecraft.client.gui.GuiComponent;
#else
import net.minecraft.client.gui.GuiGraphics;
#endif
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
import java.util.*;
/**
* The screen that pops up if the mod has an update.
*
* @author coolGi
*/
// TODO: After finishing the config, rewrite this in openGL as well
// TODO: Make this
public class ChangelogScreen extends DhScreen
{
private Screen parent;
private String versionID;
private List<String> changelog;
private TextArea changelogArea;
public boolean usable = false;
public ChangelogScreen(Screen parent)
{
this(parent, null);
if (!ModrinthGetter.initted) // Make sure the modrinth stuff is initted
ModrinthGetter.init();
if (!ModrinthGetter.initted) // If its not initted, then this isnt usable
return;
if (!ModrinthGetter.mcVersions.contains(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()))
return;
String versionID = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
if (versionID == null)
return;
try
{
setupChangelog(versionID);
usable = true;
}
catch (Exception e)
{
e.printStackTrace();
}
}
public ChangelogScreen(Screen parent, String versionID)
{
super(Translatable(ModInfo.ID + ".updater.title"));
this.parent = parent;
this.versionID = versionID;
if (versionID == null)
return;
try
{
setupChangelog(versionID);
usable = true;
}
catch (Exception e)
{
e.printStackTrace();
}
}
private void setupChangelog(String versionID)
{
this.changelog = new ArrayList<>();
// Put the new version name at the very top of the change log
this.changelog.add("§lChangelog for " + ModrinthGetter.releaseNames.get(versionID) + "§r");
this.changelog.add("");
this.changelog.add("");
// Get the release changelog and split it by the new lines
String[] unwrappedChangelog = // Arrays.asList could be used if a list object is desired here vs List.of which is only available for Java 9+
new MarkdownFormatter.MinecraftFormat().convertTo( // This formats markdown to minecraft's "§" characters
ModrinthGetter.changeLogs.get(versionID)
).split("\\n");
// Makes the words wrap around to not go off the screen
for (String str : unwrappedChangelog)
{
this.changelog.addAll(
MarkdownFormatter.splitString(str, 75)
);
}
// Debugging
// System.out.println(this.changelog);
}
@Override
protected void init()
{
super.init();
if (!usable)
return;
this.addBtn( // Close
MakeBtn(Translatable(ModInfo.ID + ".general.back"), 5, this.height - 25, 100, 20, (btn) -> {
this.onClose();
})
);
this.changelogArea = new TextArea(this.minecraft, this.width * 2, this.height, 32, this.height - 32, 10);
for (int i = 0; i < changelog.size(); i++)
{
this.changelogArea.addButton(TextOrLiteral(changelog.get(i)));
// drawString(matrices, this.font, changelog.get(i), this.width / 2 - 175, this.height / 2 - 100 + i*10, 0xFFFFFF);
}
}
@Override
#if PRE_MC_1_20_1
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
#else
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#endif
{
#if MC_1_20_2
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#else
this.renderBackground(matrices); // Render background
#endif
if (!usable)
return;
// Set the scroll position to the mouse height relative to the screen
// This is a bit of a hack as we cannot scroll on this area
double scrollAmount = ((double) mouseY) / ((double) this.height) * 1.1 * this.changelogArea.getMaxScroll();
#if MC_1_16_5 || MC_1_17_1
this.changelogArea.setScrollAmount(scrollAmount);
#else
this.changelogArea.scrollAmount = scrollAmount;
#endif
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
DhDrawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title
}
@Override
public void onClose()
{
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
}
public static class TextArea extends ContainerObjectSelectionList<ButtonEntry>
{
Font textRenderer;
public TextArea(Minecraft minecraftClient, int i, int j, int k, int l, int m)
{
super(minecraftClient, i, j, k, l, m);
this.centerListVertically = false;
textRenderer = minecraftClient.font;
}
public void addButton(Component text)
{
this.addEntry(ButtonEntry.create(text));
}
@Override
public int getRowWidth()
{
return 10000;
}
}
public static class ButtonEntry extends ContainerObjectSelectionList.Entry<ButtonEntry>
{
private static final Font textRenderer = Minecraft.getInstance().font;
private final Component text;
private final List<AbstractWidget> children = new ArrayList<>();
private ButtonEntry(Component text)
{
this.text = text;
}
public static ButtonEntry create(Component text)
{
return new ButtonEntry(text);
}
#if PRE_MC_1_20_1
@Override
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
{
GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF);
}
#else
@Override
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, text, 12, y + 5, 0xFFFFFF);
}
#endif
@Override
public List<? extends GuiEventListener> children()
{
return children;
}
#if POST_MC_1_17_1
@Override
public List<? extends NarratableEntry> narratables()
{
return children;
}
#endif
}
}
@@ -0,0 +1,180 @@
package com.seibel.distanthorizons.common.wrappers.gui.updater;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.api.enums.config.EUpdateBranch;
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
import com.seibel.distanthorizons.core.jar.ModJarInfo;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.jar.JarUtils;
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import net.minecraft.client.Minecraft;
#if POST_MC_1_20_1
import net.minecraft.client.gui.GuiGraphics;
#else
import com.mojang.blaze3d.vertex.PoseStack;
#endif
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.resources.ResourceLocation;
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
import java.util.*;
/**
* The screen that pops up if the mod has an update.
*
* @author coolGi
*/
// TODO: After finishing the config, rewrite this in Java Swing as well
// and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192
public class UpdateModScreen extends DhScreen
{
private Screen parent;
private String newVersionID;
private String currentVer;
private String nextVer;
public UpdateModScreen(Screen parent, String newVersionID)
{
super(Translatable(ModInfo.ID + ".updater.title"));
this.parent = parent;
this.newVersionID = newVersionID;
switch (Config.Client.Advanced.AutoUpdater.updateBranch.get()) {
case STABLE:
currentVer = ModInfo.VERSION;
nextVer = ModrinthGetter.releaseNames.get(this.newVersionID);
break;
case NIGHTLY:
currentVer = ModJarInfo.Git_Commit.substring(0,7);
nextVer = this.newVersionID.substring(0,7);
break;
}
}
@Override
protected void init()
{
super.init();
try
{
// We cannot get assets from the root of the mod so we use this hack
// TODO: Load the icon.png and logo.png in the mod initialise rather than here
ResourceLocation logoLocation = new ResourceLocation(ModInfo.ID, "logo.png");
Minecraft.getInstance().getTextureManager().register(
logoLocation,
new DynamicTexture(NativeImage.read(JarUtils.accessFile("logo.png")))
);
// Logo image
this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen
this.width / 2 - 65, this.height / 2 - 110,
// Width and height of the button
130, 65,
// Offset
0, 0,
// Some textuary stuff
0, logoLocation, 130, 65,
// Create the button and tell it where to go
// For now it goes to the client option by default
(buttonWidget) -> System.out.println("Nice, you found an easter egg :)"), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
// Add a title to the button
Translatable(ModInfo.ID + ".updater.title"),
// Dont render the background of the button
false
));
}
catch (Exception e)
{
e.printStackTrace();
}
if (Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE)
{
this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen
this.width / 2 - 97, this.height / 2 + 8,
// Width and height of the button
20, 20,
// Offset
0, 0,
// Some textuary stuff
0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20,
// Create the button and tell it where to go
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
// Add a title to the button
Translatable(ModInfo.ID + ".updater.title")
));
}
this.addBtn( // Update
MakeBtn(Translatable(ModInfo.ID + ".updater.update"), this.width / 2 - 75, this.height / 2 + 8, 150, 20, (btn) -> {
SelfUpdater.updateMod();
this.onClose();
})
);
this.addBtn( // Silent update
MakeBtn(Translatable(ModInfo.ID + ".updater.silent"), this.width / 2 - 75, this.height / 2 + 30, 150, 20, (btn) -> {
Config.Client.Advanced.AutoUpdater.enableSilentUpdates.set(true);
SelfUpdater.updateMod();
this.onClose();
})
);
this.addBtn( // Later (not now)
MakeBtn(Translatable(ModInfo.ID + ".updater.later"), this.width / 2 + 2, this.height / 2 + 70, 100, 20, (btn) -> {
this.onClose();
})
);
this.addBtn( // Never
MakeBtn(Translatable(ModInfo.ID + ".updater.never"), this.width / 2 - 102, this.height / 2 + 70, 100, 20, (btn) -> {
Config.Client.Advanced.AutoUpdater.enableAutoUpdater.set(false);
this.onClose();
})
);
}
@Override
#if PRE_MC_1_20_1
public void render(PoseStack matrices, int mouseX, int mouseY, float delta)
#else
public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta)
#endif
{
#if MC_1_20_2
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#else
this.renderBackground(matrices); // Render background
#endif
// Render the text's
DhDrawCenteredString(matrices, this.font, Translatable(ModInfo.ID + ".updater.text1"), this.width / 2, this.height / 2 - 35, 0xFFFFFF);
DhDrawCenteredString(matrices, this.font,
Translatable(ModInfo.ID + ".updater.text2", currentVer, nextVer),
this.width / 2, this.height / 2 - 20, 0x52FD52);
// TODO: add the tooltips for the buttons
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
// TODO: Add tooltips
}
@Override
public void onClose()
{
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
}
}
@@ -0,0 +1,54 @@
package com.seibel.distanthorizons.common.wrappers.level;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.client.multiplayer.ClientLevel;
import java.util.Objects;
public class KeyedClientLevelManager implements IKeyedClientLevelManager
{
public static final KeyedClientLevelManager INSTANCE = new KeyedClientLevelManager();
/** This is set and managed by the ClientApi for servers with support for DH. */
private IServerKeyedClientLevel overrideWrapper = null;
private boolean useOverrideWrapper = false;
//=============//
// constructor //
//=============//
private KeyedClientLevelManager() { }
//======================//
// level override logic //
//======================//
@Override
public void setServerKeyedLevel(IServerKeyedClientLevel clientLevel) { this.overrideWrapper = clientLevel; }
@Override
public IServerKeyedClientLevel getOverrideWrapper() { return this.overrideWrapper; }
@Override
public IServerKeyedClientLevel getServerKeyedLevel(ILevelWrapper level, String serverLevelKey)
{
Objects.requireNonNull(level);
Objects.requireNonNull(serverLevelKey);
return new ServerKeyedClientLevel((ClientLevel) level.getWrappedMcObject(), serverLevelKey);
}
@Override
public void setUseOverrideWrapper(boolean useOverrideWrapper) { this.useOverrideWrapper = useOverrideWrapper; }
@Override
public boolean getUseOverrideWrapper() { return this.useOverrideWrapper; }
}
@@ -0,0 +1,21 @@
package com.seibel.distanthorizons.common.wrappers.level;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import net.minecraft.client.multiplayer.ClientLevel;
public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServerKeyedClientLevel
{
/** A unique identifier (generally the level's name) for differentiating multiverse levels */
private final String serverLevelKey;
public ServerKeyedClientLevel(ClientLevel level, String serverLevelKey)
{
super(level);
this.serverLevelKey = serverLevelKey;
}
@Override
public String getServerLevelKey() { return this.serverLevelKey; }
}
@@ -0,0 +1,309 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.minecraft;
import java.io.File;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.UUID;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.api.enums.config.ELodShading;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import net.minecraft.CrashReport;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
#if PRE_MC_1_19_2
import net.minecraft.network.chat.TextComponent;
#endif
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
/**
* A singleton that wraps the Minecraft object.
*
* @author James Seibel
* @version 3-5-2022
*/
//@Environment(EnvType.CLIENT)
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
public final Minecraft mc = Minecraft.getInstance();
/**
* The lightmap for the current:
* Time, dimension, brightness setting, etc.
*/
private NativeImage lightMap = null;
private ProfilerWrapper profilerWrapper;
private MinecraftClientWrapper()
{
}
//================//
// 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()
{
lightMap = null;
}
//=================//
// method wrappers //
//=================//
@Override
public float getShade(EDhDirection lodDirection)
{
ELodShading lodShading = Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading.get();
switch (lodShading)
{
default:
case MINECRAFT:
if (this.mc.level != null)
{
Direction mcDir = McObjectConverter.Convert(lodDirection);
return this.mc.level.getShade(mcDir, true);
}
else
{
return 0.0f;
}
case OLD_LIGHTING:
switch (lodDirection)
{
case DOWN:
return 0.5F;
default:
case UP:
return 1.0F;
case NORTH:
case SOUTH:
return 0.8F;
case WEST:
case EAST:
return 0.6F;
}
case NONE:
return 1.0F;
}
}
@Override
public boolean hasSinglePlayerServer() { return mc.hasSingleplayerServer(); }
@Override
public boolean clientConnectedToDedicatedServer() { return mc.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
@Override
public String getCurrentServerName() { return mc.getCurrentServer().name; }
@Override
public String getCurrentServerIp() { return mc.getCurrentServer().ip; }
@Override
public String getCurrentServerVersion()
{
return mc.getCurrentServer().version.getString();
}
//=============//
// Simple gets //
//=============//
public LocalPlayer getPlayer()
{
return mc.player;
}
@Override
public boolean playerExists()
{
return mc.player != null;
}
@Override
public UUID getPlayerUUID()
{
return getPlayer().getUUID();
}
@Override
public DhBlockPos getPlayerBlockPos()
{
BlockPos playerPos = getPlayer().blockPosition();
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
}
@Override
public DhChunkPos getPlayerChunkPos()
{
#if PRE_MC_1_17_1
ChunkPos playerPos = new ChunkPos(getPlayer().blockPosition());
#else
ChunkPos playerPos = getPlayer().chunkPosition();
#endif
return new DhChunkPos(playerPos.x, playerPos.z);
}
public ModelManager getModelManager()
{
return mc.getModelManager();
}
@Nullable
@Override
public IClientLevelWrapper getWrappedClientLevel()
{
if (this.mc.level == null)
{
return null;
}
return ClientLevelWrapper.getWrapperIgnoringOverride(this.mc.level);
}
/** Please move over to getInstallationDirectory() */
@Deprecated
@Override
public File getGameDirectory()
{
return getInstallationDirectory();
}
@Override
public IProfilerWrapper getProfiler()
{
if (profilerWrapper == null)
profilerWrapper = new ProfilerWrapper(mc.getProfiler());
else if (mc.getProfiler() != profilerWrapper.profiler)
profilerWrapper.profiler = mc.getProfiler();
return profilerWrapper;
}
/** Returns all worlds available to the server */
@Override
public ArrayList<ILevelWrapper> getAllServerWorlds()
{
ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>();
Iterable<ServerLevel> serverWorlds = mc.getSingleplayerServer().getAllLevels();
for (ServerLevel world : serverWorlds)
{
worlds.add(ServerLevelWrapper.getWrapper(world));
}
return worlds;
}
@Override
public void sendChatMessage(String string)
{
LocalPlayer p = getPlayer();
if (p == null) return;
#if PRE_MC_1_19_2
p.sendMessage(new TextComponent(string), getPlayer().getUUID());
#else
p.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
#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
public void crashMinecraft(String errorMessage, Throwable exception)
{
LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception);
CrashReport report = new CrashReport(errorMessage, exception);
Minecraft.crash(report);
}
@Override
public Object getOptionsObject()
{
return mc.options;
}
@Override
public boolean isDedicatedServer()
{
return false;
}
@Override
public File getInstallationDirectory()
{
return mc.gameDirectory;
}
@Override
public void executeOnRenderThread(Runnable runnable) { this.mc.execute(runnable); }
}
@@ -0,0 +1,27 @@
package com.seibel.distanthorizons.common.wrappers.minecraft;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import net.minecraft.server.dedicated.DedicatedServer;
import java.io.File;
//@Environment(EnvType.SERVER)
public class MinecraftDedicatedServerWrapper implements IMinecraftSharedWrapper
{
public static final MinecraftDedicatedServerWrapper INSTANCE = new MinecraftDedicatedServerWrapper();
private MinecraftDedicatedServerWrapper() { }
public DedicatedServer dedicatedServer = null;
@Override
public boolean isDedicatedServer()
{
return true;
}
@Override
public File getInstallationDirectory()
{
if (dedicatedServer == null)
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server complete initialization!");
return dedicatedServer.getServerDirectory();
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,40 +16,50 @@
* 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.lod.common.wrappers.minecraft;
package com.seibel.distanthorizons.common.wrappers.minecraft;
import java.awt.Color;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.stream.Collectors;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.core.dependencyInjection.SingletonInjector;
import com.seibel.lod.core.pos.DhChunkPos;
import com.seibel.lod.common.wrappers.misc.LightMapWrapper;
import com.seibel.lod.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.WrapperFactory;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.render.glObject.GLProxy;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
#if PRE_MC_1_19_4
import com.mojang.math.Vector3f;
import com.seibel.lod.core.util.math.Mat4f;
import com.seibel.lod.core.util.math.Vec3d;
import com.seibel.lod.core.util.math.Vec3f;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IBCLibAccessor;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.lod.common.wrappers.McObjectConverter;
import com.seibel.lod.common.wrappers.WrapperFactory;
import com.seibel.lod.core.pos.DhBlockPos;
#else
import org.joml.Vector3f;
#endif
#if MC_1_20_2
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
#endif
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
@@ -57,10 +67,9 @@ import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
#if PRE_MC_1_17_1
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.material.FluidState;
import org.lwjgl.opengl.GL15;
#else
@@ -69,7 +78,6 @@ import net.minecraft.world.level.material.FogType;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
/**
@@ -87,11 +95,21 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
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);
public LightMapWrapper lightmap = null;
/**
* In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps. <br>
* Requiring the use of multiple {@link LightMapWrapper}.
*/
public HashMap<IDimensionTypeWrapper, LightMapWrapper> lightmapByDimensionType = new HashMap<>();
/**
* Holds the render buffer that should be used when displaying levels to the screen.
* This is used for Optifine shader support so we can render directly to Optifine's level frame buffer.
*/
public int finalLevelFrameBufferId = -1;
@Override
@@ -111,9 +129,14 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
}
@Override
public boolean playerHasBlindnessEffect()
/** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */
public boolean playerHasBlindingEffect()
{
return MC.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null;
return MC.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null
#if POST_AND_MC_1_19_2
|| MC.player.getActiveEffectsMap().get(MobEffects.DARKNESS) != null // Deep dark effect
#endif
;
}
@Override
@@ -138,7 +161,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public double getGamma()
{
#if PRE_MC_1_19
#if PRE_MC_1_19_2
return MC.options.gamma;
#else
return MC.options.gamma().get();
@@ -146,10 +169,8 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
}
@Override
public Color getFogColor(float partialTicks) {
if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("bclib"))
return ModAccessorInjector.INSTANCE.get(IBCLibAccessor.class).getFogColor(); // BCLib uses a different fog method so we need to use that instead if they are loaded
public Color getFogColor(float partialTicks)
{
#if PRE_MC_1_17_1
float[] colorValues = new float[4];
GL15.glGetFloatv(GL15.GL_FOG_COLOR, colorValues);
@@ -157,20 +178,28 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
FogRenderer.setupColor(MC.gameRenderer.getMainCamera(), partialTicks, MC.level, 1, MC.gameRenderer.getDarkenWorldAmount(partialTicks));
float[] colorValues = RenderSystem.getShaderFogColor();
#endif
return new Color(colorValues[0], colorValues[1], colorValues[2], colorValues[3]);
return new Color(
Math.max(0f, Math.min(colorValues[0], 1f)),
Math.max(0f, Math.min(colorValues[1], 1f)),
Math.max(0f, Math.min(colorValues[2], 1f)),
Math.max(0f, Math.min(colorValues[3], 1f))
);
}
// getSpecialFogColor() is the same as getFogColor()
@Override
public Color getSkyColor() {
if (MC.level.dimensionType().hasSkyLight()) {
public Color getSkyColor()
{
if (MC.level.dimensionType().hasSkyLight())
{
#if PRE_MC_1_17_1
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), MC.getFrameTime());
#else
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), MC.getFrameTime());
#endif
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
} else
}
else
return new Color(0, 0, 0);
}
@@ -184,7 +213,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public int getRenderDistance()
{
#if PRE_MC_1_18_1
#if PRE_MC_1_18_2
//FIXME: How to resolve this?
return MC.options.renderDistance;
#else
@@ -227,53 +256,86 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return height;
}
private RenderTarget getRenderTarget() {
RenderTarget r = null; //MC.levelRenderer.getCloudsTarget();
return r!=null ? r : MC.getMainRenderTarget();
}
@Override
public int getTargetFrameBuffer() {
return getRenderTarget().frameBufferId;
}
@Override
public int getTargetFrameBufferViewportWidth() {
return getRenderTarget().viewWidth;
}
@Override
public int getTargetFrameBufferViewportHeight() {
return getRenderTarget().viewHeight;
}
private RenderTarget getRenderTarget() { return MC.getMainRenderTarget(); }
@Override
public int getTargetFrameBuffer()
{
int frameBufferOverrideId = DhApiRenderProxy.INSTANCE.targetFrameBufferOverride;
if (frameBufferOverrideId != -1)
{
return frameBufferOverrideId;
}
// used so we can access the framebuffer shaders end up rendering to
if (AbstractOptifineAccessor.optifinePresent())
{
return this.finalLevelFrameBufferId;
}
return this.getRenderTarget().frameBufferId;
}
@Override
public void clearTargetFrameBuffer() { this.finalLevelFrameBufferId = -1; }
@Override
public int getDepthTextureId() { return this.getRenderTarget().getDepthTextureId(); }
@Override
public int getTargetFrameBufferViewportWidth()
{
return getRenderTarget().viewWidth;
}
@Override
public int getTargetFrameBufferViewportHeight()
{
return getRenderTarget().viewHeight;
}
/**
* This method returns the ChunkPos of all chunks that Minecraft
* is going to render this frame. <br><br>
* <p>
*/
public boolean usingBackupGetVanillaRenderedChunks = false;
public boolean usingBackupGetVanillaRenderedChunks = false;
@Override
public HashSet<DhChunkPos> getVanillaRenderedChunks() {
public HashSet<DhChunkPos> getVanillaRenderedChunks()
{
ISodiumAccessor sodium = ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class);
if (sodium != null) {
if (sodium != null)
{
return sodium.getNormalRenderedChunks();
}
IOptifineAccessor optifine = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
if (optifine != null) {
if (optifine != null)
{
HashSet<DhChunkPos> pos = optifine.getNormalRenderedChunks();
if (pos == null)
pos = getMaximumRenderedChunks();
return pos;
}
if (!usingBackupGetVanillaRenderedChunks) {
try {
if (!usingBackupGetVanillaRenderedChunks)
{
try
{
#if MC_1_20_2
LevelRenderer levelRenderer = MC.levelRenderer;
Collection<SectionRenderDispatcher.RenderSection> chunks = levelRenderer.visibleSections;
return (chunks.stream().map((chunk) -> {
AABB chunkBoundingBox = chunk.getBoundingBox();
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
}).collect(Collectors.toCollection(HashSet::new)));
#else
LevelRenderer levelRenderer = MC.levelRenderer;
Collection<LevelRenderer.RenderChunkInfo> chunks =
#if PRE_MC_1_18_1 levelRenderer.renderChunks;
#if PRE_MC_1_18_2 levelRenderer.renderChunks;
#else levelRenderer.renderChunkStorage.get().renderChunks; #endif
return (chunks.stream().map((chunk) -> {
AABB chunkBoundingBox =
#if PRE_MC_1_18_2 chunk.chunk.bb;
@@ -281,14 +343,20 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
}).collect(Collectors.toCollection(HashSet::new)));
} catch (LinkageError e) {
try {
#endif
}
catch (LinkageError e)
{
try
{
MinecraftClientWrapper.INSTANCE.sendChatMessage(
"\u00A7e\u00A7l\u00A7uWARNING: Distant Horizons: getVanillaRenderedChunks method failed."
+ " Using Backup Method.");
MinecraftClientWrapper.INSTANCE.sendChatMessage(
"\u00A7eOverdraw prevention will be worse than normal.");
} catch (Exception e2) {
}
catch (Exception e2)
{
}
LOGGER.error("getVanillaRenderedChunks Error: ", e);
usingBackupGetVanillaRenderedChunks = true;
@@ -296,38 +364,39 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
}
return getMaximumRenderedChunks();
}
@Override
public ILightMapWrapper getLightmapWrapper() {
return lightmap;
}
@Override
public boolean isFogStateSpecial() {
public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
@Override
public boolean isFogStateSpecial()
{
#if PRE_MC_1_17_1
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
FluidState fluidState = camera.getFluidInCamera();
Entity entity = camera.getEntity();
boolean isUnderWater = (entity instanceof LivingEntity) && ((LivingEntity)entity).hasEffect(MobEffects.BLINDNESS);
isUnderWater |= fluidState.is(FluidTags.WATER);
isUnderWater |= fluidState.is(FluidTags.LAVA);
return isUnderWater;
boolean isBlind = this.playerHasBlindingEffect();
isBlind |= fluidState.is(FluidTags.WATER);
isBlind |= fluidState.is(FluidTags.LAVA);
return isBlind;
#else
Entity entity = MC.gameRenderer.getMainCamera().getEntity();
boolean isBlind = (entity instanceof LivingEntity) && ((LivingEntity)entity).hasEffect(MobEffects.BLINDNESS);
boolean isBlind = this.playerHasBlindingEffect();
return MC.gameRenderer.getMainCamera().getFluidInCamera() != FogType.NONE || isBlind;
#endif
}
@Override
public boolean tryDisableVanillaFog() {
return true; // Handled via MixinFogRenderer in both forge and fabric
}
public void updateLightmap(NativeImage lightPixels) {
if (lightmap== null) {
lightmap = new LightMapWrapper();
public void updateLightmap(NativeImage lightPixels, IClientLevelWrapper level)
{
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
// object for the same MC level and/or the same hash,
// so this will have to do for now
IDimensionTypeWrapper dimensionType = level.getDimensionType();
if (!this.lightmapByDimensionType.containsKey(dimensionType))
{
this.lightmapByDimensionType.put(dimensionType, new LightMapWrapper());
}
lightmap.uploadLightmap(lightPixels);
}
this.lightmapByDimensionType.get(dimensionType).uploadLightmap(lightPixels);
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,10 +16,10 @@
* 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.lod.common.wrappers.minecraft;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
package com.seibel.distanthorizons.common.wrappers.minecraft;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import net.minecraft.util.profiling.ProfilerFiller;
@@ -29,32 +29,33 @@ import net.minecraft.util.profiling.ProfilerFiller;
*/
public class ProfilerWrapper implements IProfilerWrapper
{
public ProfilerFiller profiler;
public ProfilerWrapper(ProfilerFiller newProfiler)
{
profiler = newProfiler;
}
/** starts a new section inside the currently running section */
@Override
public void push(String newSection)
{
profiler.push(newSection);
}
/** ends the currently running section and starts a new one */
@Override
public void popPush(String newSection)
{
profiler.popPush(newSection);
}
/** ends the currently running section */
@Override
public void pop()
{
profiler.pop();
}
public ProfilerFiller profiler;
public ProfilerWrapper(ProfilerFiller newProfiler)
{
profiler = newProfiler;
}
/** starts a new section inside the currently running section */
@Override
public void push(String newSection)
{
profiler.push(newSection);
}
/** ends the currently running section and starts a new one */
@Override
public void popPush(String newSection)
{
profiler.popPush(newSection);
}
/** ends the currently running section */
@Override
public void pop()
{
profiler.pop();
}
}
@@ -0,0 +1,74 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.misc;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer;
/**
* @author James Seibel
* @version 11-21-2021
*/
public class LightMapWrapper implements ILightMapWrapper
{
private int textureId = 0;
public LightMapWrapper()
{
}
private void createLightmap(NativeImage image)
{
textureId = GL32.glGenTextures();
GL32.glBindTexture(GL32.GL_TEXTURE_2D, textureId);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
}
public void uploadLightmap(NativeImage image)
{
int currentBind = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, textureId);
if (textureId == 0)
{
createLightmap(image);
}
// NativeImage::upload(int levelOfDetail, int xOffset, int yOffset, bool shouldCleanup?)
image.upload(0, 0, 0, false);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, currentBind);
}
@Override
public void bind()
{
GL32.glActiveTexture(GL32.GL_TEXTURE0);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
}
@Override
public void unbind()
{
GL32.glBindTexture(GL32.GL_TEXTURE_2D, 0);
}
}
@@ -0,0 +1,54 @@
package com.seibel.distanthorizons.common.wrappers.misc;
import com.google.common.collect.MapMaker;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.server.level.ServerPlayer;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
public class ServerPlayerWrapper implements IServerPlayerWrapper
{
private static final ConcurrentMap<ServerPlayer, ServerPlayerWrapper>
serverPlayerWrapperMap = new MapMaker().weakKeys().makeMap();
private final ServerPlayer serverPlayer;
public static ServerPlayerWrapper getWrapper(ServerPlayer serverPlayer)
{
return serverPlayerWrapperMap.computeIfAbsent(serverPlayer, ServerPlayerWrapper::new);
}
private ServerPlayerWrapper(ServerPlayer serverPlayer)
{
this.serverPlayer = serverPlayer;
}
public UUID getUUID()
{
return serverPlayer.getUUID();
}
public IServerLevelWrapper getLevel()
{
#if PRE_MC_1_20_1
return ServerLevelWrapper.getWrapper(this.serverPlayer.getLevel());
#else
return ServerLevelWrapper.getWrapper(this.serverPlayer.serverLevel());
#endif
}
public Object getWrappedMcObject()
{
return serverPlayer;
}
@Override
public String toString()
{
return "Wrapped{" + serverPlayer.toString() + "}";
}
}
@@ -0,0 +1,196 @@
package com.seibel.distanthorizons.common.wrappers.world;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.cache.ClientBlockDetailMap;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.ConcurrentHashMap;
public class ClientLevelWrapper implements IClientLevelWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName());
private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>();
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
private final ClientLevel level;
private final ClientBlockDetailMap blockMap = new ClientBlockDetailMap(this);
//=============//
// constructor //
//=============//
protected ClientLevelWrapper(ClientLevel level) { this.level = level; }
//===============//
// wrapper logic //
//===============//
@Nullable
public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level)
{
if (level == null)
{
return null;
}
// used if the client is connected to a server that defines the currently loaded level
if (KEYED_CLIENT_LEVEL_MANAGER.getUseOverrideWrapper())
{
return KEYED_CLIENT_LEVEL_MANAGER.getOverrideWrapper();
}
return getWrapperIgnoringOverride(level);
}
public static IClientLevelWrapper getWrapperIgnoringOverride(@NotNull ClientLevel level) { return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new); }
@Nullable
@Override
public IServerLevelWrapper tryGetServerSideWrapper()
{
try
{
Iterable<ServerLevel> serverLevels = MinecraftClientWrapper.INSTANCE.mc.getSingleplayerServer().getAllLevels();
// attempt to find the server level with the same dimension type
// TODO this assumes only one level per dimension type, the SubDimensionLevelMatcher will need to be added for supporting multiple levels per dimension
ServerLevelWrapper foundLevelWrapper = null;
// TODO: Surely there is a more efficient way to write this code
for (ServerLevel serverLevel : serverLevels)
{
if (serverLevel.dimension() == this.level.dimension())
{
foundLevelWrapper = ServerLevelWrapper.getWrapper(serverLevel);
break;
}
}
return foundLevelWrapper;
}
catch (Exception e)
{
LOGGER.error("Failed to get server side wrapper for client level: " + level);
return null;
}
}
//====================//
// base level methods //
//====================//
@Override
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState)
{
return this.blockMap.getColor(((BlockStateWrapper) blockState).blockState, (BiomeWrapper) biome, pos);
}
@Override
public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
@Override
public EDhApiLevelType getLevelType() { return EDhApiLevelType.CLIENT_LEVEL; }
public ClientLevel getLevel() { return this.level; }
@Override
public boolean hasCeiling() { return this.level.dimensionType().hasCeiling(); }
@Override
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
@Override
public int getHeight() { return this.level.getHeight(); }
@Override
public int getMinHeight()
{
#if PRE_MC_1_17_1
return 0;
#else
return this.level.getMinBuildHeight();
#endif
}
@Override
public IChunkWrapper tryGetChunk(DhChunkPos pos)
{
if (!this.level.hasChunk(pos.x, pos.z))
{
return null;
}
ChunkAccess chunk = this.level.getChunk(pos.x, pos.z, ChunkStatus.EMPTY, false);
if (chunk == null)
{
return null;
}
return new ChunkWrapper(chunk, this.level, 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
public ClientLevel getWrappedMcObject() { return this.level; }
@Override
public void onUnload() { LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level); }
@Override
public String toString()
{
if (this.level == null)
{
return "Wrapped{null}";
}
return "Wrapped{" + this.level.toString() + "@" + this.getDimensionType().getDimensionName() + "}";
}
}
@@ -0,0 +1,105 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.world;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import net.minecraft.world.level.dimension.DimensionType;
/**
* @author James Seibel
* @version 2022-9-16
*/
public class DimensionTypeWrapper implements IDimensionTypeWrapper
{
private static final ConcurrentMap<DimensionType, DimensionTypeWrapper> dimensionTypeWrapperMap = new ConcurrentHashMap<>();
private final DimensionType dimensionType;
public DimensionTypeWrapper(DimensionType dimensionType)
{
this.dimensionType = dimensionType;
}
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
{
//first we check if the biome has already been wrapped
if (dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
return dimensionTypeWrapperMap.get(dimensionType);
//if it hasn't been created yet, we create it and save it in the map
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper);
//we return the newly created wrapper
return dimensionTypeWrapper;
}
public static void clearMap()
{
dimensionTypeWrapperMap.clear();
}
@Override
public String getDimensionName()
{
return dimensionType.effectsLocation().getPath();
}
@Override
public boolean hasCeiling()
{
return dimensionType.hasCeiling();
}
@Override
public boolean hasSkyLight()
{
return dimensionType.hasSkyLight();
}
@Override
public Object getWrappedMcObject()
{
return this.dimensionType;
}
@Override
public boolean equals(Object obj)
{
if (obj.getClass() != DimensionTypeWrapper.class)
{
return false;
}
else
{
DimensionTypeWrapper other = (DimensionTypeWrapper) obj;
return other.getDimensionName().equals(this.getDimensionName());
}
}
}
@@ -0,0 +1,183 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.world;
import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.cache.ServerBlockDetailMap;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
/**
* @version 2022-9-16
*/
public class ServerLevelWrapper implements IServerLevelWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final ConcurrentHashMap<ServerLevel, ServerLevelWrapper> LEVEL_WRAPPER_BY_SERVER_LEVEL = new ConcurrentHashMap<>();
final ServerLevel level;
ServerBlockDetailMap blockMap = new ServerBlockDetailMap(this);
//==============//
// constructors //
//==============//
public static ServerLevelWrapper getWrapper(ServerLevel level) { return LEVEL_WRAPPER_BY_SERVER_LEVEL.computeIfAbsent(level, ServerLevelWrapper::new); }
public ServerLevelWrapper(ServerLevel level)
{
this.level = level;
}
//=========//
// methods //
//=========//
@Nullable
@Override
public IClientLevelWrapper tryGetClientLevelWrapper()
{
MinecraftClientWrapper client = MinecraftClientWrapper.INSTANCE;
if (client.mc.level == null)
{
return null;
}
return ClientLevelWrapper.getWrapper(client.mc.level);
}
@Override
public File getSaveFolder()
{
return level.getChunkSource().getDataStorage().dataFolder;
}
@Override
public DimensionTypeWrapper getDimensionType()
{
return DimensionTypeWrapper.getDimensionTypeWrapper(level.dimensionType());
}
@Override
public EDhApiLevelType getLevelType() { return EDhApiLevelType.SERVER_LEVEL; }
public ServerLevel getLevel()
{
return level;
}
@Override
public boolean hasCeiling()
{
return level.dimensionType().hasCeiling();
}
@Override
public boolean hasSkyLight()
{
return level.dimensionType().hasSkyLight();
}
@Override
public int getHeight()
{
return level.getHeight();
}
@Override
public int getMinHeight()
{
#if PRE_MC_1_17_1
return 0;
#else
return level.getMinBuildHeight();
#endif
}
@Override
public IChunkWrapper tryGetChunk(DhChunkPos pos)
{
if (!level.hasChunk(pos.x, pos.z)) return null;
ChunkAccess chunk = level.getChunk(pos.x, pos.z, ChunkStatus.FULL, false);
if (chunk == null) return null;
return new ChunkWrapper(chunk, level, 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 = level.getChunkSource();
return source.hasChunk(chunkX, chunkZ);
}
@Override
public IBlockStateWrapper getBlockState(DhBlockPos pos)
{
return BlockStateWrapper.fromBlockState(level.getBlockState(McObjectConverter.Convert(pos)), this);
}
@Override
public IBiomeWrapper getBiome(DhBlockPos pos)
{
return BiomeWrapper.getBiomeWrapper(level.getBiome(McObjectConverter.Convert(pos)), this);
}
@Override
public ServerLevel getWrappedMcObject()
{
return level;
}
@Override
public void onUnload() { LEVEL_WRAPPER_BY_SERVER_LEVEL.remove(this.level); }
@Override
public String toString()
{
return "Wrapped{" + level.toString() + "@" + getDimensionType().getDimensionName() + "}";
}
}
@@ -0,0 +1,740 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2021 Tom Lee (TomTheFurry)
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import com.google.common.collect.ImmutableMap;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.*;
import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRenderDataTransformer;
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.objects.EventTimer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepBiomes;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepFeatures;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepNoise;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepStructureReference;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepStructureStart;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepSurface;
#if POST_MC_1_19_4
import net.minecraft.core.registries.Registries;
#else
import net.minecraft.core.Registry;
#endif
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
import net.minecraft.world.level.levelgen.DebugLevelSource;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.nbt.CompoundTag;
import org.apache.logging.log4j.LogManager;
/*
Total: 3.135214124s
=====================================
Empty Chunks: 0.000558328s
StructureStart Step: 0.025177207s
StructureReference Step: 0.00189559s
Biome Step: 0.13789155s
Noise Step: 1.570347555s
Surface Step: 0.741238194s
Carver Step: 0.000009923s
Feature Step: 0.389072425s
Lod Generation: 0.269023348s
*/
public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnvironmentWrapper
{
public static final ConfigBasedSpamLogger PREF_LOGGER =
new ConfigBasedSpamLogger(LogManager.getLogger("LodWorldGen"),
() -> Config.Client.Advanced.Logging.logWorldGenPerformance.get(), 1);
public static final ConfigBasedLogger EVENT_LOGGER =
new ConfigBasedLogger(LogManager.getLogger("LodWorldGen"),
() -> Config.Client.Advanced.Logging.logWorldGenEvent.get());
public static final ConfigBasedLogger LOAD_LOGGER =
new ConfigBasedLogger(LogManager.getLogger("LodWorldGen"),
() -> Config.Client.Advanced.Logging.logWorldGenLoadEvent.get());
//TODO: Make actual proper support for StarLight
public static class PerfCalculator
{
private static final String[] TIME_NAMES = {
"total",
"setup",
"structStart",
"structRef",
"biome",
"noise",
"surface",
"carver",
"feature",
"light",
"cleanup",
//"lodCreation" (No longer used)
};
public static final int SIZE = 50;
ArrayList<Rolling> times = new ArrayList<>();
public PerfCalculator()
{
for (int i = 0; i < 11; i++)
{
times.add(new Rolling(SIZE));
}
}
public void recordEvent(EventTimer event)
{
for (EventTimer.Event e : event.events)
{
String name = e.name;
int index = Arrays.asList(TIME_NAMES).indexOf(name);
if (index == -1) continue;
times.get(index).add(e.timeNs);
}
times.get(0).add(event.getTotalTimeNs());
}
public String toString()
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < times.size(); i++)
{
if (times.get(i).getAverage() == 0) continue;
sb.append(TIME_NAMES[i]).append(": ").append(times.get(i).getAverage()).append("\n");
}
return sb.toString();
}
}
private final IDhServerLevel serverlevel;
//=================Generation Step===================
public final LinkedBlockingQueue<GenerationEvent> generationEventList = new LinkedBlockingQueue<>();
public final GlobalParameters params;
public final StepStructureStart stepStructureStart = new StepStructureStart(this);
public final StepStructureReference stepStructureReference = new StepStructureReference(this);
public final StepBiomes stepBiomes = new StepBiomes(this);
public final StepNoise stepNoise = new StepNoise(this);
public final StepSurface stepSurface = new StepSurface(this);
public final StepFeatures stepFeatures = new StepFeatures(this);
public boolean unsafeThreadingRecorded = false;
public static final long EXCEPTION_TIMER_RESET_TIME = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
public static final int EXCEPTION_COUNTER_TRIGGER = 20;
public static final int RANGE_TO_RANGE_EMPTY_EXTENSION = 1;
public int unknownExceptionCount = 0;
public long lastExceptionTriggerTime = 0;
private AtomicReference<RegionFileStorageExternalCache> regionFileStorageCacheRef = new AtomicReference<>();
public RegionFileStorageExternalCache getOrCreateRegionFileCache(RegionFileStorage storage)
{
RegionFileStorageExternalCache cache = regionFileStorageCacheRef.get();
if (cache == null)
{
cache = new RegionFileStorageExternalCache(storage);
if (!regionFileStorageCacheRef.compareAndSet(null, cache))
{
cache = regionFileStorageCacheRef.get();
}
}
return cache;
}
public static ThreadLocal<Boolean> isDistantGeneratorThread = new ThreadLocal<>();
public static ThreadLocal<Object> onDistantGenerationMixinData = new ThreadLocal<>();
public static boolean isCurrentThreadDistantGeneratorThread() { return (isDistantGeneratorThread.get() != null); }
public static void putDistantGenerationMixinData(Object data)
{
LodUtil.assertTrue(isCurrentThreadDistantGeneratorThread());
onDistantGenerationMixinData.set(data);
}
public static Object getDistantGenerationMixinData()
{
LodUtil.assertTrue(isCurrentThreadDistantGeneratorThread());
return onDistantGenerationMixinData.get();
}
public static void clearDistantGenerationMixinData()
{
LodUtil.assertTrue(isCurrentThreadDistantGeneratorThread());
onDistantGenerationMixinData.remove();
}
//==============//
// constructors //
//==============//
public static ImmutableMap<EDhApiWorldGenerationStep, Integer> BorderNeeded;
public static int MaxBorderNeeded;
static
{
DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread = BatchGenerationEnvironment::isCurrentThreadDistantGeneratorThread;
boolean isTerraFirmaCraft = false;
try
{
Class.forName("net.dries007.tfc.world.TFCChunkGenerator");
isTerraFirmaCraft = true;
}
catch (ClassNotFoundException e)
{
//Ignore
}
EVENT_LOGGER.info("DH TerraFirmaCraft detection: " + isTerraFirmaCraft);
ImmutableMap.Builder<EDhApiWorldGenerationStep, Integer> builder = ImmutableMap.builder();
builder.put(EDhApiWorldGenerationStep.EMPTY, 1);
builder.put(EDhApiWorldGenerationStep.STRUCTURE_START, 0);
builder.put(EDhApiWorldGenerationStep.STRUCTURE_REFERENCE, 0);
builder.put(EDhApiWorldGenerationStep.BIOMES, isTerraFirmaCraft ? 1 : 0);
builder.put(EDhApiWorldGenerationStep.NOISE, isTerraFirmaCraft ? 1 : 0);
builder.put(EDhApiWorldGenerationStep.SURFACE, 0);
builder.put(EDhApiWorldGenerationStep.CARVERS, 0);
builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0);
builder.put(EDhApiWorldGenerationStep.FEATURES, 0);
builder.put(EDhApiWorldGenerationStep.LIGHT, 0);
BorderNeeded = builder.build();
MaxBorderNeeded = BorderNeeded.values().stream().mapToInt(Integer::intValue).max().getAsInt();
}
public BatchGenerationEnvironment(IDhServerLevel serverlevel)
{
super(serverlevel);
this.serverlevel = serverlevel;
EVENT_LOGGER.info("================WORLD_GEN_STEP_INITING=============");
serverlevel.getServerLevelWrapper().getDimensionType();
ChunkGenerator generator = ((ServerLevelWrapper) (serverlevel.getServerLevelWrapper())).getLevel().getChunkSource().getGenerator();
if (!(generator instanceof NoiseBasedChunkGenerator ||
generator instanceof DebugLevelSource ||
generator instanceof FlatLevelSource))
{
if (generator.getClass().toString().equals("class com.terraforged.mod.chunk.TFChunkGenerator"))
{
EVENT_LOGGER.info("TerraForge Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it.");
EVENT_LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
}
else if (generator.getClass().toString().equals("class net.dries007.tfc.world.TFCChunkGenerator"))
{
EVENT_LOGGER.info("TerraFirmaCraft Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it.");
EVENT_LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
}
else
{
EVENT_LOGGER.warn("Unknown Chunk Generator detected: [" + generator.getClass() + "], Distant Generation May Fail!");
EVENT_LOGGER.warn("If it does crash, disable Distant Generation or set the Generation Mode to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "].");
}
}
this.params = new GlobalParameters(serverlevel);
}
public <T> T joinSync(CompletableFuture<T> future)
{
if (!unsafeThreadingRecorded && !future.isDone())
{
EVENT_LOGGER.error("Unsafe MultiThreading in Chunk Generator: ", new RuntimeException("Concurrent future"));
EVENT_LOGGER.error("To increase stability, it is recommended to set world generation threads count to 1.");
unsafeThreadingRecorded = true;
}
return future.join();
}
public void updateAllFutures()
{
if (this.unknownExceptionCount > 0)
{
if (System.nanoTime() - this.lastExceptionTriggerTime >= EXCEPTION_TIMER_RESET_TIME)
{
this.unknownExceptionCount = 0;
}
}
// Update all current out standing jobs
Iterator<GenerationEvent> iter = this.generationEventList.iterator();
while (iter.hasNext())
{
GenerationEvent event = iter.next();
if (event.future.isDone())
{
if (event.future.isCompletedExceptionally() && !event.future.isCancelled())
{
try
{
event.future.get(); // Should throw exception
LodUtil.assertNotReach();
}
catch (Exception e)
{
this.unknownExceptionCount++;
this.lastExceptionTriggerTime = System.nanoTime();
EVENT_LOGGER.error("Batching World Generator event ["+event+"] threw an exception: "+e.getMessage(), e);
}
}
iter.remove();
}
else if (event.hasTimeout(Config.Client.Advanced.WorldGenerator.worldGenerationTimeoutLengthInSeconds.get(), TimeUnit.SECONDS))
{
EVENT_LOGGER.error("Batching World Generator: " + event + " timed out and terminated!");
EVENT_LOGGER.info("Dump PrefEvent: " + event.timer);
try
{
if (!event.terminate())
{
EVENT_LOGGER.error("Failed to terminate the stuck generation event!");
}
}
finally
{
iter.remove();
}
}
}
if (this.unknownExceptionCount > EXCEPTION_COUNTER_TRIGGER)
{
EVENT_LOGGER.error("Too many exceptions in Batching World Generator! Disabling the generator.");
this.unknownExceptionCount = 0;
Config.Client.Advanced.WorldGenerator.enableDistantGeneration.set(false);
}
}
private static ProtoChunk EmptyChunk(ServerLevel level, ChunkPos chunkPos)
{
return new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if POST_MC_1_17_1 , level #endif
#if POST_MC_1_18_2 , level.registryAccess().registryOrThrow(
#if PRE_MC_1_19_4
Registry.BIOME_REGISTRY
#else
Registries.BIOME
#endif
), null #endif
);
}
public ChunkAccess loadOrMakeChunk(ChunkPos chunkPos)
{
ServerLevel level = this.params.level;
CompoundTag chunkData = null;
try
{
// Warning: if multiple threads attempt to access this method at the same time,
// it can throw EOFExceptions that are caught and logged by Minecraft
//chunkData = level.getChunkSource().chunkMap.readChunk(chunkPos);
RegionFileStorage storage = this.params.level.getChunkSource().chunkMap.worker.storage;
RegionFileStorageExternalCache cache = this.getOrCreateRegionFileCache(storage);
chunkData = cache.read(chunkPos);
}
catch (Exception e)
{
LOAD_LOGGER.error("DistantHorizons: Couldn't load or make chunk " + chunkPos + ". Error: " + e.getMessage(), e);
}
if (chunkData == null)
{
return EmptyChunk(level, chunkPos);
}
else
{
try
{
LOAD_LOGGER.info("DistantHorizons: Loading chunk " + chunkPos + " from disk.");
return ChunkLoader.read(level, chunkPos, chunkData);
}
catch (Exception e)
{
LOAD_LOGGER.error("DistantHorizons: Couldn't load or make chunk " + chunkPos + ". Returning an empty chunk. Error: " + e.getMessage(), e);
return EmptyChunk(level, chunkPos);
}
}
}
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border)
{
return new ArrayGridList<>(total, border, total.gridSize - border);
}
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step)
{
return GetCutoutFrom(total, MaxBorderNeeded - BorderNeeded.get(step));
}
public void generateLodFromList(GenerationEvent genEvent) throws InterruptedException
{
EVENT_LOGGER.debug("Lod Generate Event: " + genEvent.minPos);
ArrayGridList<ChunkWrapper> chunkWrapperList;
DhLitWorldGenRegion region;
DummyLightEngine lightEngine;
LightGetterAdaptor adaptor;
int borderSize = MaxBorderNeeded;
int refSize = genEvent.size + borderSize * 2;
int refPosX = genEvent.minPos.x - borderSize;
int refPosZ = genEvent.minPos.z - borderSize;
try
{
ArrayGridList<ChunkAccess> totalChunks;
adaptor = new LightGetterAdaptor(this.params.level);
lightEngine = new DummyLightEngine(adaptor);
EmptyChunkGenerator generator = (int x, int z) ->
{
ChunkPos chunkPos = new ChunkPos(x, z);
ChunkAccess target = null;
try
{
target = this.loadOrMakeChunk(chunkPos);
}
catch (RuntimeException e2)
{
// Continue...
}
if (target == null)
{
target = new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if POST_MC_1_17_1 , params.level #endif
#if POST_MC_1_18_2 , params.biomes, null #endif
);
}
return target;
};
totalChunks = new ArrayGridList<>(refSize, (x, z) -> generator.generate(x + refPosX, z + refPosZ));
genEvent.refreshTimeout();
region = new DhLitWorldGenRegion(params.level, lightEngine, totalChunks,
ChunkStatus.STRUCTURE_STARTS, refSize / 2, generator);
adaptor.setRegion(region);
genEvent.threadedParam.makeStructFeat(region, params);
chunkWrapperList = new ArrayGridList<>(totalChunks.gridSize);
totalChunks.forEachPos((x, z) ->
{
ChunkAccess chunk = totalChunks.get(x, z);
if (chunk != null)
{
chunkWrapperList.set(x, z, new ChunkWrapper(chunk, region, serverlevel.getLevelWrapper()));
}
});
this.generateDirect(genEvent, chunkWrapperList, borderSize, genEvent.targetGenerationStep, region);
genEvent.timer.nextEvent("cleanup");
}
catch (StepStructureStart.StructStartCorruptedException f)
{
genEvent.threadedParam.markAsInvalid();
throw (RuntimeException) f.getCause();
}
ArrayGridList<ChunkWrapper> finalGenChunks = GetCutoutFrom(chunkWrapperList, borderSize);
for (int offsetY = 0; offsetY < finalGenChunks.gridSize; offsetY++)
{
for (int offsetX = 0; offsetX < finalGenChunks.gridSize; offsetX++)
{
ChunkWrapper wrappedChunk = finalGenChunks.get(offsetX, offsetY);
ChunkAccess target = wrappedChunk.getChunk();
if (target instanceof LevelChunk)
{
#if MC_1_16_5 || MC_1_17_1
((LevelChunk) target).setLoaded(true);
#else
((LevelChunk) target).loaded = true;
#endif
}
if (!wrappedChunk.isLightCorrect())
{
throw new RuntimeException("The generated chunk somehow has isLightCorrect() returning false");
}
boolean isFull = target.getStatus() == ChunkStatus.FULL || target instanceof LevelChunk;
#if POST_MC_1_18_2
boolean isPartial = target.isOldNoiseGeneration();
#endif
if (isFull)
{
LOAD_LOGGER.info("Detected full existing chunk at {}", target.getPos());
genEvent.resultConsumer.accept(wrappedChunk);
}
#if POST_MC_1_18_2
else if (isPartial)
{
LOAD_LOGGER.info("Detected old existing chunk at {}", target.getPos());
genEvent.resultConsumer.accept(wrappedChunk);
}
#endif
else if (target.getStatus() == ChunkStatus.EMPTY)
{
genEvent.resultConsumer.accept(wrappedChunk);
}
else
{
genEvent.resultConsumer.accept(wrappedChunk);
}
}
}
genEvent.timer.complete();
genEvent.refreshTimeout();
if (PREF_LOGGER.canMaybeLog())
{
genEvent.threadedParam.perf.recordEvent(genEvent.timer);
PREF_LOGGER.infoInc("{}", genEvent.timer);
}
}
public void generateDirect(
GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunksToGenerate, int border,
EDhApiWorldGenerationStep step, DhLitWorldGenRegion region) throws InterruptedException
{
if (Thread.interrupted())
{
return;
}
try
{
chunksToGenerate.forEach((chunkWrapper) ->
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk instanceof ProtoChunk)
{
ProtoChunk protoChunk = ((ProtoChunk) chunk);
protoChunk.setLightEngine(region.getLightEngine());
}
});
if (step == EDhApiWorldGenerationStep.EMPTY)
{
return;
}
genEvent.timer.nextEvent("structStart");
throwIfThreadInterrupted();
this.stepStructureStart.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunksToGenerate, EDhApiWorldGenerationStep.STRUCTURE_START));
genEvent.refreshTimeout();
if (step == EDhApiWorldGenerationStep.STRUCTURE_START)
{
return;
}
genEvent.timer.nextEvent("structRef");
throwIfThreadInterrupted();
this.stepStructureReference.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunksToGenerate, EDhApiWorldGenerationStep.STRUCTURE_REFERENCE));
genEvent.refreshTimeout();
if (step == EDhApiWorldGenerationStep.STRUCTURE_REFERENCE)
{
return;
}
genEvent.timer.nextEvent("biome");
throwIfThreadInterrupted();
this.stepBiomes.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunksToGenerate, EDhApiWorldGenerationStep.BIOMES));
genEvent.refreshTimeout();
if (step == EDhApiWorldGenerationStep.BIOMES)
{
return;
}
genEvent.timer.nextEvent("noise");
throwIfThreadInterrupted();
this.stepNoise.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunksToGenerate, EDhApiWorldGenerationStep.NOISE));
genEvent.refreshTimeout();
if (step == EDhApiWorldGenerationStep.NOISE)
{
return;
}
genEvent.timer.nextEvent("surface");
throwIfThreadInterrupted();
this.stepSurface.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunksToGenerate, EDhApiWorldGenerationStep.SURFACE));
genEvent.refreshTimeout();
if (step == EDhApiWorldGenerationStep.SURFACE)
{
return;
}
genEvent.timer.nextEvent("carver");
throwIfThreadInterrupted();
// caves can generally be ignored since they aren't generally visible from far away
if (step == EDhApiWorldGenerationStep.CARVERS)
{
return;
}
genEvent.timer.nextEvent("feature");
throwIfThreadInterrupted();
this.stepFeatures.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunksToGenerate, EDhApiWorldGenerationStep.FEATURES));
genEvent.refreshTimeout();
}
finally
{
genEvent.timer.nextEvent("light");
// generate lighting using DH's lighting engine
int maxSkyLight = this.serverlevel.getServerLevelWrapper().hasSkyLight() ? 15 : 0;
ArrayList<IChunkWrapper> iChunkWrapperList = new ArrayList<>(chunksToGenerate);
for (int i = 0; i < iChunkWrapperList.size(); i++)
{
IChunkWrapper centerChunk = iChunkWrapperList.get(i);
if (centerChunk == null)
{
continue;
}
throwIfThreadInterrupted();
// make sure the height maps are all properly generated
// if this isn't done everything else afterward may fail
Heightmap.primeHeightmaps(((ChunkWrapper)centerChunk).getChunk(), ChunkStatus.FEATURES.heightmapsAfter());
// populate the lighting
DhLightingEngine.INSTANCE.lightChunk(centerChunk, iChunkWrapperList, maxSkyLight);
}
genEvent.refreshTimeout();
}
}
public interface EmptyChunkGenerator
{
ChunkAccess generate(int x, int z);
}
@Override
public int getEventCount() { return this.generationEventList.size(); }
@Override
public void stop()
{
EVENT_LOGGER.info(BatchGenerationEnvironment.class.getSimpleName() + " shutting down...");
EVENT_LOGGER.info("Canceling in progress generation event futures...");
Iterator<GenerationEvent> iter = this.generationEventList.iterator();
while (iter.hasNext())
{
GenerationEvent event = iter.next();
event.future.cancel(true);
iter.remove();
}
// clear the chunk cache
RegionFileStorageExternalCache regionStorage = this.regionFileStorageCacheRef.get();
if (regionStorage != null)
{
try
{
regionStorage.close();
}
catch (IOException e)
{
EVENT_LOGGER.error("Failed to close region file storage cache!", e);
}
}
EVENT_LOGGER.info(BatchGenerationEnvironment.class.getSimpleName() + " shutdown complete.");
}
@Override
public CompletableFuture<Void> generateChunks(
int minX, int minZ, int genSize, EDhApiWorldGenerationStep targetStep,
ExecutorService worldGeneratorThreadPool, Consumer<IChunkWrapper> resultConsumer)
{
//System.out.println("GenerationEvent: "+genSize+"@"+minX+","+minZ+" "+targetStep);
// TODO: Check event overlap via e.tooClose()
GenerationEvent genEvent = GenerationEvent.startEvent(new DhChunkPos(minX, minZ), genSize, this, targetStep, resultConsumer, worldGeneratorThreadPool);
this.generationEventList.add(genEvent);
return genEvent.future;
}
/**
* Called before code that may run for an extended period of time. <br>
* This is necessary to allow canceling world gen since waiting
* for some world gen requests to finish can take a while.
*/
public static void throwIfThreadInterrupted() throws InterruptedException
{
if (Thread.interrupted())
{
throw new InterruptedException(FullDataToRenderDataTransformer.class.getSimpleName() + " task interrupted.");
}
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -17,21 +17,23 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.common.wrappers.worldGeneration;
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import com.seibel.lod.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.lod.core.util.objects.UncheckedInterruptedException;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.api.enums.config.ELightGenerationMode;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.pos.DhChunkPos;
import com.seibel.lod.core.util.objects.EventTimer;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.distanthorizons.core.generation.WorldGenerationQueue;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.objects.EventTimer;
import com.seibel.distanthorizons.core.util.threading.ThreadPools;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import org.apache.logging.log4j.Logger;
@@ -40,23 +42,22 @@ public final class GenerationEvent
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private static int generationFutureDebugIDs = 0;
final int id;
final ThreadedParameters threadedParam;
final DhChunkPos minPos;
final int size;
final EDhApiWorldGenerationStep targetGenerationStep;
final ELightGenerationMode lightMode;
final double runTimeRatio;
EventTimer timer = null;
long inQueueTime;
long timeoutTime = -1;
public final int id;
public final ThreadedParameters threadedParam;
public final DhChunkPos minPos;
public final int size;
public final EDhApiWorldGenerationStep targetGenerationStep;
public EventTimer timer = null;
public long inQueueTime;
public long timeoutTime = -1;
public CompletableFuture<Void> future = null;
final Consumer<IChunkWrapper> resultConsumer;
public final Consumer<IChunkWrapper> resultConsumer;
public GenerationEvent(DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
EDhApiWorldGenerationStep targetGenerationStep, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer)
public GenerationEvent(
DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
{
this.inQueueTime = System.nanoTime();
this.id = generationFutureDebugIDs++;
@@ -64,51 +65,44 @@ public final class GenerationEvent
this.size = size;
this.targetGenerationStep = targetGenerationStep;
this.threadedParam = ThreadedParameters.getOrMake(generationGroup.params);
this.lightMode = Config.Client.WorldGenerator.lightGenerationMode.get();
this.runTimeRatio = runTimeRatio;
this.resultConsumer = resultConsumer;
}
public static GenerationEvent startEvent(DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
EDhApiWorldGenerationStep target, double runTimeRatio, Consumer<IChunkWrapper> resultConsumer)
public static GenerationEvent startEvent(
DhChunkPos minPos, int size, BatchGenerationEnvironment genEnvironment,
EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
ExecutorService worldGeneratorThreadPool)
{
if (size % 2 == 0)
{
size += 1; // size must be odd for vanilla world gen regions to work
}
GenerationEvent generationEvent = new GenerationEvent(minPos, size, generationGroup, target, runTimeRatio, resultConsumer);
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer);
generationEvent.future = CompletableFuture.runAsync(() ->
{
long runStartTime = System.nanoTime();
generationEvent.timeoutTime = runStartTime;
generationEvent.inQueueTime = runStartTime - generationEvent.inQueueTime;
generationEvent.timer = new EventTimer("setup");
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
try
{
//LOGGER.info("generating [{}]", event.minPos);
generationGroup.generateLodFromList(generationEvent);
genEnvironment.generateLodFromList(generationEvent);
}
catch (InterruptedException ignored)
{
}
finally
{
BatchGenerationEnvironment.isDistantGeneratorThread.remove();
if (!Thread.interrupted() && runTimeRatio < 1.0)
{
long endTime = System.nanoTime();
try
{
long deltaMs = TimeUnit.NANOSECONDS.toMillis(endTime - runStartTime);
Thread.sleep((long) (deltaMs / runTimeRatio - deltaMs));
}
catch (InterruptedException ignored)
{
}
}
}
}, generationGroup.executors);
}, worldGeneratorThreadPool);
return generationEvent;
}
@@ -129,7 +123,7 @@ public final class GenerationEvent
public boolean terminate()
{
LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
BatchGenerationEnvironment.threadFactory.dumpAllThreadStacks();
ThreadPools.WORLD_GEN_THREAD_FACTORY.dumpAllThreadStacks();
this.future.cancel(true);
return this.future.isCancelled();
}
@@ -156,6 +150,6 @@ public final class GenerationEvent
}
@Override
public String toString() { return this.id+":"+this.size+"@"+this.minPos+"("+this.targetGenerationStep +")"; }
public String toString() { return this.id + ":" + this.size + "@" + this.minPos + "(" + this.targetGenerationStep + ")"; }
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,12 +16,12 @@
* 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.lod.common.wrappers.worldGeneration;
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import com.mojang.datafixers.DataFixer;
import com.seibel.lod.common.wrappers.world.ServerLevelWrapper;
import com.seibel.lod.core.level.IDhServerLevel;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
@@ -31,36 +31,43 @@ import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.chunk.ChunkGenerator;
#if POST_MC_1_18_1
#if POST_MC_1_18_2
import net.minecraft.world.level.chunk.storage.ChunkScanAccess;
#endif
import net.minecraft.world.level.levelgen.WorldGenSettings;
#if PRE_MC_1_19
#if PRE_MC_1_19_2
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
#else
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.levelgen.RandomState;
#if POST_MC_1_19_4
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.core.registries.Registries;
#endif
#endif
import net.minecraft.world.level.storage.WorldData;
public final class GlobalParameters
{
public final ChunkGenerator generator;
#if PRE_MC_1_19
#if PRE_MC_1_19_2
public final StructureManager structures;
#else
public final StructureTemplateManager structures;
public final RandomState randomState;
#endif
#if PRE_MC_1_19_4
public final WorldGenSettings worldGenSettings;
public final ThreadedLevelLightEngine lightEngine;
#else
public final WorldOptions worldOptions;
#endif
public final IDhServerLevel lodLevel;
public final ServerLevel level;
public final Registry<Biome> biomes;
public final RegistryAccess registry;
public final long worldSeed;
public final DataFixer fixerUpper;
#if POST_MC_1_18_1
#if POST_MC_1_18_2
public final BiomeManager biomeManager;
public final ChunkScanAccess chunkScanner; // FIXME: Figure out if this is actually needed
#endif
@@ -68,24 +75,31 @@ public final class GlobalParameters
public GlobalParameters(IDhServerLevel lodLevel)
{
this.lodLevel = lodLevel;
level = ((ServerLevelWrapper)lodLevel.getServerLevelWrapper()).getWrappedMcObject_UNSAFE();
lightEngine = (ThreadedLevelLightEngine) level.getLightEngine();
level = ((ServerLevelWrapper) lodLevel.getServerLevelWrapper()).getWrappedMcObject();
MinecraftServer server = level.getServer();
WorldData worldData = server.getWorldData();
worldGenSettings = worldData.worldGenSettings();
registry = server.registryAccess();
#if PRE_MC_1_19_4
worldGenSettings = worldData.worldGenSettings();
biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY);
worldSeed = worldGenSettings.seed();
#if POST_MC_1_18_1
#else
worldOptions = worldData.worldGenOptions();
biomes = registry.registryOrThrow(Registries.BIOME);
worldSeed = worldOptions.seed();
#endif
#if POST_MC_1_18_2
biomeManager = new BiomeManager(level, BiomeManager.obfuscateSeed(worldSeed));
chunkScanner = level.getChunkSource().chunkScanner();
#endif
structures = server.getStructureManager();
generator = level.getChunkSource().getGenerator();
fixerUpper = server.getFixerUpper();
#if POST_MC_1_19
#if POST_MC_1_19_2
randomState = level.getChunkSource().randomState();
#endif
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,9 +16,9 @@
* 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.lod.common.wrappers.worldGeneration;
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
//FIXME: Move this outside the WorldGenerationStep thingy
public class Rolling
@@ -34,7 +34,9 @@ public class Rolling
this.size = size;
samples = new double[size];
for (int i = 0; i < size; i++)
{
samples[i] = 0d;
}
}
public void add(double x)
@@ -48,6 +50,7 @@ public class Rolling
public double getAverage()
{
return size==0 ? 0 : total / size;
return size == 0 ? 0 : total / size;
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,16 +16,16 @@
* 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.lod.common.wrappers.worldGeneration;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.PerfCalculator;
import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.WorldGenStructFeatManager;
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 POST_MC_1_18_1
#if POST_MC_1_18_2
import net.minecraft.world.level.levelgen.structure.StructureCheck;
#endif
@@ -35,7 +35,7 @@ public final class ThreadedParameters
final ServerLevel level;
public WorldGenStructFeatManager structFeat = null;
#if POST_MC_1_18_1
#if POST_MC_1_18_2
public StructureCheck structCheck;
#endif
boolean isValid = true;
@@ -63,9 +63,9 @@ public final class ThreadedParameters
previousGlobalParameters = param;
this.level = param.level;
#if PRE_MC_1_18_1
#if PRE_MC_1_18_2
this.structFeat = new WorldGenStructFeatManager(param.worldGenSettings, level);
#elif PRE_MC_1_19
#elif PRE_MC_1_19_2
this.structCheck = this.createStructureCheck(param);
#else
this.structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
@@ -80,11 +80,15 @@ public final class ThreadedParameters
public void makeStructFeat(WorldGenLevel genLevel, GlobalParameters param)
{
structFeat = new WorldGenStructFeatManager(param.worldGenSettings, genLevel #if POST_MC_1_18_1, structCheck #endif);
#if PRE_MC_1_19_4
structFeat = new WorldGenStructFeatManager(param.worldGenSettings, genLevel #if POST_MC_1_18_2 , structCheck #endif );
#else
structFeat = new WorldGenStructFeatManager(param.worldOptions, genLevel, structCheck);
#endif
}
#if PRE_MC_1_19
#if POST_MC_1_18_2 && PRE_MC_1_19_2
public void recreateStructureCheck()
{
if (previousGlobalParameters != null)
@@ -1,38 +1,50 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
* This file is part of the Distant Horizons mod
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* 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 Lesser General Public License for more details.
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* 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.lod.common.wrappers.worldGeneration.mimicObject;
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.google.common.collect.Maps;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Dynamic;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
#if POST_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.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
@@ -41,24 +53,37 @@ 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.*;
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
import net.minecraft.world.level.levelgen.Heightmap;
#if POST_MC_1_18_1
#if POST_MC_1_18_2
import net.minecraft.world.level.levelgen.blending.BlendingData;
#if PRE_MC_1_19_2
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext;
import net.minecraft.world.ticks.LevelChunkTicks;
#endif
#if POST_MC_1_18_2
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
#if PRE_MC_1_19_2
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
#endif
#endif
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.Fluid;
#if MC_1_16_5 || MC_1_17_1
import net.minecraft.world.level.material.Fluids;
#endif
import net.minecraft.world.level.material.Fluid;
public class ChunkLoader
{
#if POST_MC_1_19
#if POST_MC_1_19_2
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#elif POST_MC_1_18_1
#elif POST_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";
@@ -67,14 +92,14 @@ public class ChunkLoader
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;
#if POST_MC_1_18_1
#if POST_MC_1_18_2
private static BlendingData readBlendingData(CompoundTag chunkData)
{
BlendingData blendingData = null;
if (chunkData.contains("blending_data", 10))
{
@SuppressWarnings({ "unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null);
}
@@ -82,43 +107,48 @@ public class ChunkLoader
}
#endif
private static LevelChunkSection[] readSections(LevelAccessor level, LevelLightEngine lightEngine, ChunkPos chunkPos, CompoundTag chunkData)
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if POST_MC_1_18_1
#if POST_MC_1_18_2
#if PRE_MC_1_19_4
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#else
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#endif
#if PRE_MC_1_18_2
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#elif PRE_MC_1_19
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#elif PRE_MC_1_19_2
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#endif
#endif
int i = #if PRE_MC_1_17_1 16; #else level.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[i];
boolean isLightOn = chunkData.getBoolean("isLightOn");
boolean hasSkyLight = level.dimensionType().hasSkyLight();
ListTag tagSections = chunkData.getList("Sections", 10);
if (tagSections.isEmpty()) tagSections = chunkData.getList("sections", 10);
for (int j = 0; j < tagSections.size(); ++j)
{
CompoundTag tagSection = tagSections.getCompound(j);
int sectionYPos = tagSection.getByte("Y");
#if PRE_MC_1_18_1
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12)) {
#if PRE_MC_1_18_2
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12))
{
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
levelChunkSection.getStates().read(tagSection.getList("Palette", 10),
tagSection.getLongArray("BlockStates"));
levelChunkSection.recalcBlockCounts();
if (!levelChunkSection.isEmpty())
chunkSections[#if PRE_MC_1_17_1 sectionYPos #else level.getSectionIndexFromSectionY(sectionYPos) #endif]
= levelChunkSection;
chunkSections[#if PRE_MC_1_17_1 sectionYPos #else level.getSectionIndexFromSectionY(sectionYPos) #endif ]
= levelChunkSection;
}
#else
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
@@ -130,7 +160,7 @@ public class ChunkLoader
#else
PalettedContainer<Holder<Biome>> biomeContainer;
#endif
blockStateContainer = tagSection.contains("block_states", 10)
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
@@ -144,17 +174,14 @@ public class ChunkLoader
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, i, (String) string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#endif
#if PRE_MC_1_20_1
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
#else
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
#endif
}
#endif
if (!isLightOn) continue;
if (tagSection.contains("BlockLight", 7))
lightEngine.queueSectionData(LightLayer.BLOCK, SectionPos.of(chunkPos, sectionYPos),
new DataLayer(tagSection.getByteArray("BlockLight")), true);
if (hasSkyLight && tagSection.contains("SkyLight", 7))
lightEngine.queueSectionData(LightLayer.SKY, SectionPos.of(chunkPos, sectionYPos),
new DataLayer(tagSection.getByteArray("SkyLight")), true);
}
return chunkSections;
}
@@ -170,7 +197,7 @@ public class ChunkLoader
}
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
}
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
{
ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
@@ -187,81 +214,90 @@ public class ChunkLoader
public static ChunkStatus.ChunkType readChunkType(CompoundTag tagLevel)
{
ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status"));
if (chunkStatus != null) {
if (chunkStatus != null)
{
return chunkStatus.getChunkType();
}
return ChunkStatus.ChunkType.PROTOCHUNK;
}
public static LevelChunk read(WorldGenLevel level, LevelLightEngine lightEngine, ChunkPos chunkPos, CompoundTag chunkData)
public static LevelChunk read(WorldGenLevel level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if PRE_MC_1_18_1
#if PRE_MC_1_18_2
CompoundTag tagLevel = chunkData.getCompound("Level");
#else
CompoundTag tagLevel = chunkData;
#endif
ChunkPos actualPos = new ChunkPos(tagLevel.getInt("xPos"), tagLevel.getInt("zPos"));
if (!Objects.equals(chunkPos, actualPos)) {
if (!Objects.equals(chunkPos, actualPos))
{
LOGGER.error("Chunk file at {} is in the wrong location; Ignoring. (Expected {}, got {})", chunkPos, chunkPos, actualPos);
return null;
}
ChunkStatus.ChunkType chunkType = readChunkType(tagLevel);
#if PRE_MC_1_18_1
#if PRE_MC_1_18_2
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
return null;
#else
BlendingData blendingData = readBlendingData(tagLevel);
#if PRE_MC_1_19
#if PRE_MC_1_19_2
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
return null;
#else
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || level.getChunk(chunkPos.getMiddleBlockX(),chunkPos.getMiddleBlockZ()).isOldNoiseGeneration()))
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && blendingData == null)
return null;
#endif
#endif
long inhabitedTime = tagLevel.getLong("InhabitedTime");
//================== Read params for making the LevelChunk ==================
UpgradeData upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
? new UpgradeData(tagLevel.getCompound(TAG_UPGRADE_DATA)#if POST_MC_1_17_1, level #endif)
? new UpgradeData(tagLevel.getCompound(TAG_UPGRADE_DATA)#if POST_MC_1_17_1 , level #endif )
: UpgradeData.EMPTY;
boolean isLightOn = tagLevel.getBoolean("isLightOn");
if (isLightOn) lightEngine.retainData(chunkPos, true);
#if PRE_MC_1_18_1
#if PRE_MC_1_18_2
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)#if POST_MC_1_17_1, level #endif,
level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)#if POST_MC_1_17_1 , level #endif ,
chunkPos, level.getLevel().getChunkSource().getGenerator().getBiomeSource(),
tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null);
TickList<Block> blockTicks = tagLevel.contains(BLOCK_TICKS_TAG_PRE18, 9)
? ChunkTickList.create(tagLevel.getList(BLOCK_TICKS_TAG_PRE18, 10), Registry.BLOCK::getKey, Registry.BLOCK::get)
: new ProtoTickList<Block>(block -> (block == null || block.defaultBlockState().isAir()), chunkPos,
tagLevel.getList("ToBeTicked", 9)#if POST_MC_1_17_1, level #endif);
tagLevel.getList("ToBeTicked", 9)#if POST_MC_1_17_1 , level #endif );
TickList<Fluid> fluidTicks = tagLevel.contains(FLUID_TICKS_TAG_PRE18, 9)
? ChunkTickList.create(tagLevel.getList(FLUID_TICKS_TAG_PRE18, 10), Registry.FLUID::getKey, Registry.FLUID::get)
: new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos,
tagLevel.getList("LiquidsToBeTicked", 9)#if POST_MC_1_17_1, level #endif);
tagLevel.getList("LiquidsToBeTicked", 9)#if POST_MC_1_17_1 , level #endif );
#else
#if PRE_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);
#else
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);
#endif
LevelChunkSection[] levelChunkSections = readSections(level, lightEngine, chunkPos, tagLevel);
#endif
LevelChunkSection[] levelChunkSections = readSections(level, chunkPos, tagLevel);
// ====================== Make the chunk =========================
#if PRE_MC_1_18_1
#if PRE_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);
fluidTicks, inhabitedTime, levelChunkSections, null, blendingData);
#endif
// Set some states after object creation
chunk.setLightCorrect(isLightOn);
@@ -274,5 +310,6 @@ public class ChunkLoader
{
LOGGER.error("Distant Horizons: Recoverable errors when loading section [" + chunkPos.x + ", " + i + ", " + chunkPos.z + "]: " + string);
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,21 +16,20 @@
* 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.lod.common.wrappers.worldGeneration.mimicObject;
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.SpawnerBlock;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.EmptyChunkGenerator;
import com.seibel.lod.api.enums.config.ELightGenerationMode;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
@@ -54,93 +53,108 @@ import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.lighting.LevelLightEngine;
public class LightedWorldGenRegion extends WorldGenRegion
public class DhLitWorldGenRegion extends WorldGenRegion
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
public final WorldGenLevelLightEngine light;
public final ELightGenerationMode lightMode;
public final EmptyChunkGenerator generator;
public final DummyLightEngine lightEngine;
public final BatchGenerationEnvironment.EmptyChunkGenerator generator;
public final int writeRadius;
public final int size;
private final ChunkPos firstPos;
private final List<ChunkAccess> cache;
Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>();
#if PRE_MC_1_18_1
/**
* Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe,
* specifically: "it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap.getAndMoveToFirst()"
*/
ReentrantLock getChunkLock = new ReentrantLock();
#if PRE_MC_1_18_2
private ChunkPos overrideCenterPos = null;
public void setOverrideCenter(ChunkPos pos) {overrideCenterPos = pos;}
#if PRE_MC_1_17_1
@Override
public int getCenterX() {
return overrideCenterPos==null ? super.getCenterX() : overrideCenterPos.x;
}
@Override
public int getCenterZ() {
return overrideCenterPos==null ? super.getCenterX() : overrideCenterPos.z;
}
#else
@Override
public ChunkPos getCenter() {
return overrideCenterPos==null ? super.getCenter() : overrideCenterPos;
}
#endif
#endif
public LightedWorldGenRegion(ServerLevel serverLevel, WorldGenLevelLightEngine lightEngine,
List<ChunkAccess> list, ChunkStatus chunkStatus, int i,
ELightGenerationMode lightMode, EmptyChunkGenerator generator)
public void setOverrideCenter(ChunkPos pos) { overrideCenterPos = pos; }
#if PRE_MC_1_17_1
@Override
public int getCenterX()
{
super(serverLevel, list #if POST_MC_1_17_1, chunkStatus, i #endif);
this.lightMode = lightMode;
this.firstPos = list.get(0).getPos();
this.generator = generator;
light = lightEngine;
writeRadius = i;
cache = list;
size = Mth.floor(Math.sqrt(list.size()));
return overrideCenterPos==null ? super.getCenterX() : overrideCenterPos.x;
}
@Override
public int getCenterZ()
{
return overrideCenterPos==null ? super.getCenterX() : overrideCenterPos.z;
}
#else
@Override
public ChunkPos getCenter()
{
return overrideCenterPos == null ? super.getCenter() : overrideCenterPos;
}
#endif
#endif
public DhLitWorldGenRegion(
ServerLevel serverLevel, DummyLightEngine lightEngine,
List<ChunkAccess> chunkList, ChunkStatus chunkStatus, int writeRadius,
BatchGenerationEnvironment.EmptyChunkGenerator generator)
{
super(serverLevel, chunkList #if POST_MC_1_17_1 , chunkStatus, writeRadius #endif );
this.firstPos = chunkList.get(0).getPos();
this.generator = generator;
this.lightEngine = lightEngine;
this.writeRadius = writeRadius;
this.cache = chunkList;
this.size = Mth.floor(Math.sqrt(chunkList.size()));
}
#if POST_MC_1_17_1
// Bypass BCLib mixin overrides.
@Override
public boolean ensureCanWrite(BlockPos blockPos)
@Override
public boolean ensureCanWrite(BlockPos blockPos)
{
int i = SectionPos.blockToSectionCoord(blockPos.getX());
int j = SectionPos.blockToSectionCoord(blockPos.getZ());
ChunkPos chunkPos = this.getCenter();
ChunkAccess center = this.getChunk(chunkPos.x, chunkPos.z);
int k = Math.abs(chunkPos.x - i);
int l = Math.abs(chunkPos.z - j);
if (k > this.writeRadius || l > this.writeRadius) {
return false;
}
#if POST_MC_1_18_1
if (center.isUpgrading()) {
LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration();
if (blockPos.getY() < levelHeightAccessor.getMinBuildHeight() || blockPos.getY() >= levelHeightAccessor.getMaxBuildHeight()) {
return false;
}
}
int i = SectionPos.blockToSectionCoord(blockPos.getX());
int j = SectionPos.blockToSectionCoord(blockPos.getZ());
ChunkPos chunkPos = this.getCenter();
ChunkAccess center = this.getChunk(chunkPos.x, chunkPos.z);
int k = Math.abs(chunkPos.x - i);
int l = Math.abs(chunkPos.z - j);
if (k > this.writeRadius || l > this.writeRadius)
{
return false;
}
#if POST_MC_1_18_2
if (center.isUpgrading())
{
LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration();
if (blockPos.getY() < levelHeightAccessor.getMinBuildHeight() || blockPos.getY() >= levelHeightAccessor.getMaxBuildHeight())
{
return false;
}
}
#endif
return true;
}
return true;
}
#endif
// TODO Check this
// @Override
// public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos,
// StructureFeature<?> structureFeature) {
// return structFeat.startsForFeature(sectionPos, structureFeature);
// }
// Skip updating the related tile entities
@Override
public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j) {
public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j)
{
ChunkAccess chunkAccess = this.getChunk(blockPos);
if (chunkAccess instanceof LevelChunk)
return true;
@@ -151,142 +165,162 @@ public class LightedWorldGenRegion extends WorldGenRegion
// this.getChunk(blockPos).markPosForPostprocessing(blockPos);
return true;
}
// Skip Dropping the item on destroy
@Override
public boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) {
public boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i)
{
BlockState blockState = this.getBlockState(blockPos);
if (blockState.isAir()) {
if (blockState.isAir())
{
return false;
}
return this.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 3, i);
}
// Skip BlockEntity stuff. It aren't really needed
@Override
public BlockEntity getBlockEntity(BlockPos blockPos) {
public BlockEntity getBlockEntity(BlockPos blockPos)
{
BlockState blockState = this.getBlockState(blockPos);
// This is a bypass for the spawner block since MC complains about not having it
#if POST_MC_1_17_1
if (blockState.getBlock() instanceof SpawnerBlock) {
if (blockState.getBlock() instanceof SpawnerBlock)
{
return ((EntityBlock) blockState.getBlock()).newBlockEntity(blockPos, blockState);
} else return null;
}
else return null;
#else
if (blockState.getBlock() instanceof SpawnerBlock) {
return ((EntityBlock) blockState.getBlock()).newBlockEntity(this);
} else return null;
#endif
}
// Skip BlockEntity stuff. It aren't really needed
@Override
public boolean addFreshEntity(Entity entity) {
public boolean addFreshEntity(Entity entity)
{
return true;
}
// Allays have empty chunks even if it's outside the worldGenRegion
// @Override
// public boolean hasChunk(int i, int j) {
// return true;
// }
// Override to ensure no other mod mixins cause skipping the overrided
// getChunk(...)
@Override
public ChunkAccess getChunk(int i, int j) {
return this.getChunk(i, j, ChunkStatus.EMPTY);
public ChunkAccess getChunk(int i, int j)
{
try
{
// lock is to prevent issues with underlying MC code that doesn't support multithreading
this.getChunkLock.lock();
return this.getChunk(i, j, ChunkStatus.EMPTY);
}
finally
{
this.getChunkLock.unlock();
}
}
// Override to ensure no other mod mixins cause skipping the overrided
// getChunk(...)
@Override
public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus) {
return this.getChunk(i, j, chunkStatus, true);
public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus)
{
try
{
// lock is to prevent issues with underlying MC code that doesn't support multithreading
this.getChunkLock.lock();
return this.getChunk(i, j, chunkStatus, true);
}
finally
{
this.getChunkLock.unlock();
}
}
// Use this instead of super.getChunk() to bypass C2ME concurrency checks
private ChunkAccess superGetChunk(int x, int z, ChunkStatus cs) {
private ChunkAccess superGetChunk(int x, int z, ChunkStatus cs)
{
int k = x - firstPos.x;
int l = z - firstPos.z;
return cache.get(k + l * size);
}
// Use this instead of super.hasChunk() to bypass C2ME concurrency checks
private boolean superHasChunk(int x, int z) {
public boolean superHasChunk(int x, int z)
{
int k = x - firstPos.x;
int l = z - firstPos.z;
return l >= 0 && l < size && k >= 0 && k < size;
}
// Allow creating empty chunks even if it's outside the worldGenRegion
@Override
@Nullable
public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl) {
public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl)
{
ChunkAccess chunk = getChunkAccess(i, j, chunkStatus, bl);
if (chunk instanceof LevelChunk) {
chunk = new ImposterProtoChunk((LevelChunk) chunk #if POST_MC_1_18_1, true #endif);
if (chunk instanceof LevelChunk)
{
chunk = new ImposterProtoChunk((LevelChunk) chunk #if POST_MC_1_18_2 , true #endif );
}
return chunk;
}
private static ChunkStatus debugTriggeredForStatus = null;
private ChunkAccess getChunkAccess(int i, int j, ChunkStatus chunkStatus, boolean bl) {
private ChunkAccess getChunkAccess(int i, int j, ChunkStatus chunkStatus, boolean bl)
{
ChunkAccess chunk = superHasChunk(i, j) ? superGetChunk(i, j, ChunkStatus.EMPTY) : null;
if (chunk != null && chunk.getStatus().isOrAfter(chunkStatus)) {
if (chunk != null && chunk.getStatus().isOrAfter(chunkStatus))
{
return chunk;
}
if (!bl)
return null;
if (chunk == null) {
if (chunk == null)
{
chunk = chunkMap.get(ChunkPos.asLong(i, j));
if (chunk == null) {
if (chunk == null)
{
chunk = generator.generate(i, j);
if (chunk == null)
throw new NullPointerException("The provided generator should not return null!");
chunkMap.put(ChunkPos.asLong(i, j), chunk);
}
}
if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus) {
if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus)
{
LOGGER.info("WorldGen requiring " + chunkStatus
+ " outside expected range detected. Force passing EMPTY chunk and seeing if it works.");
debugTriggeredForStatus = chunkStatus;
}
return chunk;
}
// Override force use of my own light engine
/** Overriding allows us to use our own lighting engine */
@Override
public LevelLightEngine getLightEngine() {
return light;
}
// Override force use of my own light engine
public LevelLightEngine getLightEngine() { return this.lightEngine; }
/** Overriding allows us to use our own lighting engine */
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) {
if (lightMode != ELightGenerationMode.FAST) {
return light.getLayerListener(lightLayer).getLightValue(blockPos);
}
if (lightLayer == LightLayer.BLOCK)
return 0;
BlockPos p = super.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, blockPos);
return (p.getY() <= blockPos.getY()) ? getMaxLightLevel() : 0;
}
// Override force use of my own light engine
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return 0; }
/** Overriding allows us to use our own lighting engine */
@Override
public int getRawBrightness(BlockPos blockPos, int i) {
if (lightMode != ELightGenerationMode.FAST) {
return light.getRawBrightness(blockPos, i);
}
BlockPos p = super.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, blockPos);
return (p.getY() <= blockPos.getY()) ? getMaxLightLevel() : 0;
}
// Override force use of my own light engine
public int getRawBrightness(BlockPos blockPos, int i) { return 0; }
/** Overriding allows us to use our own lighting engine */
@Override
public boolean canSeeSky(BlockPos blockPos) {
public boolean canSeeSky(BlockPos blockPos)
{
return (getBrightness(LightLayer.SKY, blockPos) >= getMaxLightLevel());
}
@@ -294,8 +328,9 @@ public class LightedWorldGenRegion extends WorldGenRegion
{
return calculateBlockTint(blockPos, colorResolver);
}
private Biome _getBiome(BlockPos pos) {
private Biome _getBiome(BlockPos pos)
{
#if POST_MC_1_18_2
return getBiome(pos).value();
#else
@@ -305,7 +340,7 @@ public class LightedWorldGenRegion extends WorldGenRegion
public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
#if PRE_MC_1_19
#if PRE_MC_1_19_2
int i = (Minecraft.getInstance()).options.biomeBlendRadius;
#else
int i = (Minecraft.getInstance()).options.biomeBlendRadius().get();
@@ -328,5 +363,5 @@ public class LightedWorldGenRegion extends WorldGenRegion
}
return (k / j & 0xFF) << 16 | (l / j & 0xFF) << 8 | m / j & 0xFF;
}
}
@@ -0,0 +1,99 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import net.minecraft.world.level.lighting.*;
import org.jetbrains.annotations.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.DataLayer;
public class DummyLightEngine extends LevelLightEngine
{
public DummyLightEngine(LightGetterAdaptor genRegion)
{
super(genRegion, false, false);
}
#if PRE_MC_1_20_1
@Override
public void onBlockEmissionIncrease(BlockPos blockPos, int i) { }
@Override
public int runUpdates(int i, boolean bl, boolean bl2) { return 0; }
@Override
public void enableLightSources(ChunkPos chunkPos, boolean bl) { }
#else
@Override
public int runLightUpdates() { return 0; }
@Override
public void setLightEnabled(ChunkPos $$0, boolean $$1) { }
@Override
public void propagateLightSources(ChunkPos arg) { }
public boolean lightOnInSection(SectionPos $$0) { return false; }
#endif
@Override
public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer #if PRE_MC_1_20_1 , boolean bl #endif ) { }
@Override
public void checkBlock(BlockPos blockPos) { }
@Override
public boolean hasLightWork() { return false; }
@Override
public void updateSectionStatus(SectionPos sectionPos, boolean bl) { }
@Override
public LayerLightEventListener getLayerListener(LightLayer lightLayer) { return LayerLightEventListener.DummyLightLayerEventListener.INSTANCE; }
@Override
public int getRawBrightness(BlockPos blockPos, int i) { return 0; }
public void lightChunk(ChunkAccess chunkAccess, boolean needLightBlockUpdate) { }
@Override
public String getDebugData(LightLayer lightLayer, SectionPos sectionPos) { throw new UnsupportedOperationException("This should never be used!"); }
@Override
public void retainData(ChunkPos chunkPos, boolean bl) { }
#if POST_MC_1_17_1
@Override
public int getLightSectionCount() { throw new UnsupportedOperationException("This should never be used!"); }
@Override
public int getMinLightSection() { throw new UnsupportedOperationException("This should never be used!"); }
@Override
public int getMaxLightSection() { throw new UnsupportedOperationException("This should never be used!"); }
#endif
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,11 +16,11 @@
* 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.lod.common.wrappers.worldGeneration.mimicObject;
import com.seibel.lod.core.dependencyInjection.ModAccessorInjector;
import com.seibel.lod.core.wrapperInterfaces.modAccessor.IStarlightAccessor;
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IStarlightAccessor;
import net.minecraft.world.level.BlockGetter;
#if POST_MC_1_17_1
@@ -28,36 +28,45 @@ import net.minecraft.world.level.LevelHeightAccessor;
#endif
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LightChunkGetter;
#if POST_MC_1_20_1
import net.minecraft.world.level.chunk.LightChunk;
#endif
public class LightGetterAdaptor implements LightChunkGetter {
public class LightGetterAdaptor implements LightChunkGetter
{
private final BlockGetter heightGetter;
public LightedWorldGenRegion genRegion = null;
public DhLitWorldGenRegion genRegion = null;
final boolean shouldReturnNull;
public LightGetterAdaptor(BlockGetter heightAccessor) {
public LightGetterAdaptor(BlockGetter heightAccessor)
{
this.heightGetter = heightAccessor;
shouldReturnNull = ModAccessorInjector.INSTANCE.get(IStarlightAccessor.class) != null;
}
public void setRegion(LightedWorldGenRegion region) {
public void setRegion(DhLitWorldGenRegion region)
{
genRegion = region;
}
@Override
public BlockGetter getChunkForLighting(int chunkX, int chunkZ) {
public #if PRE_MC_1_20_1 BlockGetter #else LightChunk #endif getChunkForLighting(int chunkX, int chunkZ)
{
if (genRegion == null)
throw new IllegalStateException("World Gen region has not been set!");
// May be null
return genRegion.getChunk(chunkX, chunkZ, ChunkStatus.EMPTY, false);
}
@Override
public BlockGetter getLevel() {
public BlockGetter getLevel()
{
return shouldReturnNull ? null : (genRegion != null ? genRegion : heightGetter);
}
#if POST_MC_1_17_1
public LevelHeightAccessor getLevelHeightAccessor() {
public LevelHeightAccessor getLevelHeightAccessor()
{
return heightGetter;
}
#endif
@@ -0,0 +1,194 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFile;
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
import javax.annotation.Nullable;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantLock;
public class RegionFileStorageExternalCache implements AutoCloseable
{
public final RegionFileStorage storage;
public static final int MAX_CACHE_SIZE = 16;
/**
* Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe,
* specifically: "it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap.getAndMoveToFirst()"
*/
ReentrantLock getRegionFileLock = new ReentrantLock();
static class RegionFileCache
{
public final long pos;
public final RegionFile file;
public RegionFileCache(long pos, RegionFile file)
{
this.pos = pos;
this.file = file;
}
}
public ConcurrentLinkedQueue<RegionFileCache> regionFileCache = new ConcurrentLinkedQueue<>();
public RegionFileStorageExternalCache(RegionFileStorage storage) { this.storage = storage; }
@Nullable
public RegionFile getRegionFile(ChunkPos pos) throws IOException
{
long posLong = ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ());
RegionFile rFile = null;
// Check vanilla cache
int retryCount = 0;
int maxRetryCount = 8;
while (retryCount < maxRetryCount)
{
retryCount++;
try
{
this.getRegionFileLock.lock();
#if MC_1_16_5 || MC_1_17_1
rFile = this.storage.getRegionFile(pos);
// keeping the region cache size low helps prevent concurrency issues
if (this.storage.regionCache.size() > 150) // max 256
{
RegionFile removedFile = this.storage.regionCache.removeLast();
if (removedFile != null)
{
removedFile.close();
}
}
#else
rFile = this.storage.regionCache.getOrDefault(posLong, null);
#endif
break;
}
catch (ArrayIndexOutOfBoundsException e)
{
#if MC_1_16_5 || MC_1_17_1
// the file just wasn't cached
break;
#else
// potential concurrency issue, wait a second and try to get the file again
try
{
Thread.sleep(250);
}
catch (InterruptedException ignored)
{
}
#endif
}
finally
{
this.getRegionFileLock.unlock();
}
}
if (retryCount >= maxRetryCount)
{
BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at " + pos + ".");
}
if (rFile != null)
{
return rFile;
}
// Then check our custom cache
for (RegionFileCache cache : this.regionFileCache)
{
if (cache.pos == posLong)
{
return cache.file;
}
}
// Otherwise, check if file exist, and if so, add it to the cache
Path storageFolderPath;
#if MC_1_16_5 || MC_1_17_1
storageFolderPath = this.storage.folder.toPath();
#else
storageFolderPath = this.storage.folder;
#endif
if (!Files.exists(storageFolderPath))
{
return null;
}
Path regionFilePath = storageFolderPath.resolve("r." + pos.getRegionX() + "." + pos.getRegionZ() + ".mca");
#if MC_1_16_5 || MC_1_17_1
rFile = new RegionFile(regionFilePath.toFile(), storageFolderPath.toFile(), false);
#else
rFile = new RegionFile(regionFilePath, storageFolderPath, false);
#endif
this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ()), rFile));
while (this.regionFileCache.size() > MAX_CACHE_SIZE)
{
this.regionFileCache.poll().file.close();
}
return rFile;
}
@Nullable
public CompoundTag read(ChunkPos pos) throws IOException
{
RegionFile file = this.getRegionFile(pos);
if (file == null)
{
return null;
}
try (DataInputStream stream = file.getChunkDataInputStream(pos))
{
if (stream == null)
{
return null;
}
return NbtIo.read(stream);
}
catch (Throwable e)
{
return null;
}
}
@Override
public void close() throws IOException
{
RegionFileCache cache;
while ((cache = this.regionFileCache.poll()) != null)
{
cache.file.close();
}
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,82 +16,113 @@
* 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.lod.common.wrappers.worldGeneration.mimicObject;
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.levelgen.WorldGenSettings;
#if PRE_MC_1_19
#if PRE_MC_1_19_2
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
import net.minecraft.world.level.StructureFeatureManager;
#else
#if POST_MC_1_19_4
import net.minecraft.world.level.levelgen.WorldOptions;
#endif
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.StructureManager;
#endif
#if POST_MC_1_18_1
#if POST_MC_1_18_2
import net.minecraft.world.level.levelgen.structure.StructureCheck;
#endif
import net.minecraft.world.level.levelgen.structure.StructureStart;
#if PRE_MC_1_19
public class WorldGenStructFeatManager extends StructureFeatureManager {
#else
public class WorldGenStructFeatManager extends StructureManager {
#endif
#if PRE_MC_1_18_2
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
public class WorldGenStructFeatManager extends #if PRE_MC_1_19_2 StructureFeatureManager #else StructureManager #endif
{
final WorldGenLevel genLevel;
#if PRE_MC_1_19_4
WorldGenSettings worldGenSettings;
#if POST_MC_1_18_1
StructureCheck structureCheck;
#else
WorldOptions worldOptions;
#endif
public WorldGenStructFeatManager(WorldGenSettings worldGenSettings,
WorldGenLevel genLevel #if POST_MC_1_18_1 , StructureCheck structureCheck #endif ) {
super(genLevel, worldGenSettings #if POST_MC_1_18_1 , structureCheck #endif );
#if POST_MC_1_18_2
StructureCheck structureCheck;
#endif
#if PRE_MC_1_19_4
public WorldGenStructFeatManager(
WorldGenSettings worldGenSettings,
WorldGenLevel genLevel #if POST_MC_1_18_2 , StructureCheck structureCheck #endif )
{
super(genLevel, worldGenSettings #if POST_MC_1_18_2 , structureCheck #endif );
this.genLevel = genLevel;
this.worldGenSettings = worldGenSettings;
}
#else
public WorldGenStructFeatManager(
WorldOptions worldOptions,
WorldGenLevel genLevel, StructureCheck structureCheck)
{
super(genLevel, worldOptions, structureCheck);
this.genLevel = genLevel;
this.worldOptions = worldOptions;
}
#endif
@Override
public WorldGenStructFeatManager forWorldGenRegion(WorldGenRegion worldGenRegion) {
public WorldGenStructFeatManager forWorldGenRegion(WorldGenRegion worldGenRegion)
{
if (worldGenRegion == genLevel)
return this;
return new WorldGenStructFeatManager(worldGenSettings, worldGenRegion #if POST_MC_1_18_1 , structureCheck #endif );
#if PRE_MC_1_19_4
return new WorldGenStructFeatManager(worldGenSettings, worldGenRegion #if POST_MC_1_18_2 , structureCheck #endif );
#else
return new WorldGenStructFeatManager(worldOptions, worldGenRegion, structureCheck);
#endif
}
private ChunkAccess _getChunk(int x, int z, ChunkStatus status) {
private ChunkAccess _getChunk(int x, int z, ChunkStatus status)
{
if (genLevel == null) return null;
return genLevel.getChunk(x, z, status, false);
}
#if PRE_MC_1_18_1
#if PRE_MC_1_18_2
@Override
public Stream<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos2,
StructureFeature<?> structureFeature) {
public Stream<? extends StructureStart<?>> startsForFeature(
SectionPos sectionPos2,
StructureFeature<?> structureFeature)
{
ChunkAccess chunk = _getChunk(sectionPos2.x(), sectionPos2.z(), ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return Stream.empty();
// FIXME getReferencesForFeature can throw ConcurrentModificationException's
return chunk.getReferencesForFeature(structureFeature).stream().map(pos -> {
SectionPos sectPos = SectionPos.of(ChunkPos.getX(pos), 0, ChunkPos.getZ(pos));
ChunkAccess startChunk = _getChunk(sectPos.x(), sectPos.z(), ChunkStatus.STRUCTURE_STARTS);
@@ -101,13 +132,14 @@ public class WorldGenStructFeatManager extends StructureManager {
}
#else
@Override
public boolean hasAnyStructureAt(BlockPos blockPos) {
public boolean hasAnyStructureAt(BlockPos blockPos)
{
SectionPos sectionPos = SectionPos.of(blockPos);
ChunkAccess chunk = _getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return false;
return chunk.hasAnyStructureReferences();
}
#if MC_1_18_1
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
@@ -133,36 +165,40 @@ public class WorldGenStructFeatManager extends StructureManager {
return builder.build();
}
#else
#if PRE_MC_1_19
#if PRE_MC_1_19_2
@Override
public List<StructureStart> startsForFeature(SectionPos sectionPos, Predicate<ConfiguredStructureFeature<?, ?>> predicate) {
public List<StructureStart> startsForFeature(SectionPos sectionPos, Predicate<ConfiguredStructureFeature<?, ?>> predicate)
{
ChunkAccess chunk = _getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return List.of();
// Copied from StructureFeatureManager::startsForFeature(...)
Map<ConfiguredStructureFeature<?, ?>, LongSet> map = chunk.getAllReferences();
ImmutableList.Builder<StructureStart> builder = ImmutableList.builder();
Iterator<Map.Entry<ConfiguredStructureFeature<?, ?>, LongSet>> var5 = map.entrySet().iterator();
while(var5.hasNext()) {
while (var5.hasNext())
{
Map.Entry<ConfiguredStructureFeature<?, ?>, LongSet> entry = var5.next();
ConfiguredStructureFeature<?, ?> configuredStructureFeature = entry.getKey();
if (predicate.test(configuredStructureFeature)) {
LongSet var10002 = (LongSet)entry.getValue();
if (predicate.test(configuredStructureFeature))
{
LongSet var10002 = (LongSet) entry.getValue();
Objects.requireNonNull(builder);
this.fillStartsForFeature(configuredStructureFeature, var10002, builder::add);
}
}
return builder.build();
}
@Override
public List<StructureStart> startsForFeature(SectionPos sectionPos, ConfiguredStructureFeature<?, ?> configuredStructureFeature) {
public List<StructureStart> startsForFeature(SectionPos sectionPos, ConfiguredStructureFeature<?, ?> configuredStructureFeature)
{
ChunkAccess chunk = _getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return (List<StructureStart>) Stream.empty();
// Copied from StructureFeatureManager::startsForFeature(...)
LongSet longSet = chunk.getReferencesForFeature(configuredStructureFeature);
ImmutableList.Builder<StructureStart> builder = ImmutableList.builder();
@@ -170,44 +206,49 @@ public class WorldGenStructFeatManager extends StructureManager {
this.fillStartsForFeature(configuredStructureFeature, longSet, builder::add);
return builder.build();
}
@Override
public Map<ConfiguredStructureFeature<?, ?>, LongSet> getAllStructuresAt(BlockPos blockPos) {
public Map<ConfiguredStructureFeature<?, ?>, LongSet> getAllStructuresAt(BlockPos blockPos)
{
SectionPos sectionPos = SectionPos.of(blockPos);
ChunkAccess chunk = _getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return (Map<ConfiguredStructureFeature<?, ?>, LongSet>) Stream.empty();
return chunk.getAllReferences();
return chunk.getAllReferences();
}
#else
@Override
public List<StructureStart> startsForStructure(ChunkPos sectionPos, Predicate<Structure> predicate) {
public List<StructureStart> startsForStructure(ChunkPos sectionPos, Predicate<Structure> predicate)
{
ChunkAccess chunk = _getChunk(sectionPos.x, sectionPos.z, ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return List.of();
// Copied from StructureFeatureManager::startsForFeature(...)
Map<Structure, LongSet> map = chunk.getAllReferences();
ImmutableList.Builder<StructureStart> builder = ImmutableList.builder();
Iterator<Map.Entry<Structure, LongSet>> var5 = map.entrySet().iterator();
while (var5.hasNext()) {
while (var5.hasNext())
{
Map.Entry<Structure, LongSet> entry = var5.next();
Structure configuredStructureFeature = entry.getKey();
if (predicate.test(configuredStructureFeature)) {
if (predicate.test(configuredStructureFeature))
{
LongSet var10002 = (LongSet) entry.getValue();
Objects.requireNonNull(builder);
this.fillStartsForStructure(configuredStructureFeature, var10002, builder::add);
}
}
return builder.build();
}
@Override
public List<StructureStart> startsForStructure(SectionPos sectionPos, Structure structure) {
public List<StructureStart> startsForStructure(SectionPos sectionPos, Structure structure)
{
ChunkAccess chunk = _getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return (List<StructureStart>) Stream.empty();
// Copied from StructureFeatureManager::startsForFeature(...)
LongSet longSet = chunk.getReferencesForStructure(structure);
ImmutableList.Builder<StructureStart> builder = ImmutableList.builder();
@@ -215,9 +256,10 @@ public class WorldGenStructFeatManager extends StructureManager {
this.fillStartsForStructure(structure, longSet, builder::add);
return builder.build();
}
@Override
public Map<Structure, LongSet> getAllStructuresAt(BlockPos blockPos) {
public Map<Structure, LongSet> getAllStructuresAt(BlockPos blockPos)
{
SectionPos sectionPos = SectionPos.of(blockPos);
ChunkAccess chunk = _getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES);
if (chunk == null) return (Map<Structure, LongSet>) Stream.empty();
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,70 +16,68 @@
* 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.lod.common.wrappers.worldGeneration.step;
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList;
import java.util.List;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import net.minecraft.core.Registry;
import net.minecraft.server.level.WorldGenRegion;
#if PRE_MC_1_19
import net.minecraft.world.level.StructureFeatureManager;
#if PRE_MC_1_19_2
#endif
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.DebugLevelSource;
import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
#if POST_MC_1_18_1
#if POST_MC_1_18_2
import net.minecraft.world.level.levelgen.blending.Blender;
#endif
public final class StepBiomes {
/**
*
*/
private final BatchGenerationEnvironment environment;
/**
* @param batchGenerationEnvironment
*/
public StepBiomes(BatchGenerationEnvironment batchGenerationEnvironment)
{
environment = batchGenerationEnvironment;
}
public final ChunkStatus STATUS = ChunkStatus.BIOMES;
public final class StepBiomes
{
public static final ChunkStatus STATUS = ChunkStatus.BIOMES;
public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkAccess> chunks) {
private final BatchGenerationEnvironment environment;
public StepBiomes(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
public void generateGroup(
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
for (ChunkAccess chunk : chunks) {
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue;
((ProtoChunk) chunk).setStatus(STATUS);
chunksToDo.add(chunk);
}
for (ChunkAccess chunk : chunksToDo) {
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepBiomes: "+chunk.getPos());
#if PRE_MC_1_18_1
#if PRE_MC_1_18_2
environment.params.generator.createBiomes(environment.params.biomes, chunk);
#elif PRE_MC_1_19
#elif PRE_MC_1_19_2
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else
#elif PRE_MC_1_19_4
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else
chunk = environment.joinSync(environment.params.generator.createBiomes(Runnable::run, environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#endif
}
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,68 +16,73 @@
* 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.lod.common.wrappers.worldGeneration.step;
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.LightedWorldGenRegion;
import com.seibel.lod.core.util.gridList.ArrayGridList;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.ReportedException;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap;
#if POST_MC_1_18_1
import net.minecraft.world.level.levelgen.blending.Blender;
#if POST_MC_1_18_2
#endif
public final class StepFeatures {
/**
*
*/
public final class StepFeatures
{
public static final ChunkStatus STATUS = ChunkStatus.FEATURES;
private final BatchGenerationEnvironment environment;
/**
* @param batchGenerationEnvironment
*/
public StepFeatures(BatchGenerationEnvironment batchGenerationEnvironment)
public StepFeatures(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
public void generateGroup(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers)
{
environment = batchGenerationEnvironment;
}
public final ChunkStatus STATUS = ChunkStatus.FEATURES;
public void generateGroup(ThreadedParameters tParams, LightedWorldGenRegion worldGenRegion,
ArrayGridList<ChunkAccess> chunks) {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
for (ChunkAccess chunk : chunks) {
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue;
((ProtoChunk) chunk).setStatus(STATUS);
chunksToDo.add(chunk);
}
for (ChunkAccess chunk : chunksToDo) {
try {
#if PRE_MC_1_18_1
for (ChunkAccess chunk : chunksToDo)
{
try
{
#if PRE_MC_1_18_2
worldGenRegion.setOverrideCenter(chunk.getPos());
Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter());
environment.params.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeat);
#else
Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter());
environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk,
tParams.structFeat.forWorldGenRegion(worldGenRegion));
#endif
} catch (ReportedException e) {
Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter());
BatchGenerationEnvironment.clearDistantGenerationMixinData();
}
catch (ReportedException e)
{
e.printStackTrace();
// FIXME: Features concurrent modification issue. Something about cocobeans might just
// error out. For now just retry.
}
}
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,63 +16,65 @@
* 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.lod.common.wrappers.worldGeneration.step;
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList;
import java.util.List;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.lod.core.util.objects.UncheckedInterruptedException;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import net.minecraft.server.level.WorldGenRegion;
#if POST_MC_1_17_1
#endif
#if PRE_MC_1_19
#if PRE_MC_1_19_2
#endif
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
#if POST_MC_1_18_1
#if POST_MC_1_18_2
import net.minecraft.world.level.levelgen.blending.Blender;
#endif
public final class StepNoise {
/**
*
*/
private final BatchGenerationEnvironment environment;
/**
* @param batchGenerationEnvironment
*/
public StepNoise(BatchGenerationEnvironment batchGenerationEnvironment)
{
environment = batchGenerationEnvironment;
}
public final class StepNoise
{
private static final ChunkStatus STATUS = ChunkStatus.NOISE;
public final ChunkStatus STATUS = ChunkStatus.NOISE;
public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkAccess> chunks) {
private final BatchGenerationEnvironment environment;
public StepNoise(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
public void generateGroup(
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
for (ChunkAccess chunk : chunks) {
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue;
((ProtoChunk) chunk).setStatus(STATUS);
chunksToDo.add(chunk);
}
for (ChunkAccess chunk : chunksToDo) {
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepNoise: "+chunk.getPos());
#if PRE_MC_1_17_1
environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk);
#elif PRE_MC_1_18_1
#elif PRE_MC_1_18_2
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif PRE_MC_1_19
#elif PRE_MC_1_19_2
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else
@@ -82,4 +84,5 @@ public final class StepNoise {
UncheckedInterruptedException.throwIfInterrupted();
}
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,60 +16,55 @@
* 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.lod.common.wrappers.worldGeneration.step;
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList;
import java.util.List;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.ChunkPos;
#if PRE_MC_1_19
import net.minecraft.world.level.StructureFeatureManager;
#if PRE_MC_1_19_2
#endif
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.structure.StructureStart;
public final class StepStructureReference {
/**
*
*/
public final class StepStructureReference
{
private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES;
private final BatchGenerationEnvironment environment;
/**
* @param batchGenerationEnvironment
*/
public StepStructureReference(BatchGenerationEnvironment batchGenerationEnvironment)
public StepStructureReference(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
public void generateGroup(
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers)
{
environment = batchGenerationEnvironment;
}
public final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES;
public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkAccess> chunks) {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
for (ChunkAccess chunk : chunks) {
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue;
((ProtoChunk) chunk).setStatus(STATUS);
chunksToDo.add(chunk);
}
for (ChunkAccess chunk : chunksToDo) {
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepStructureReference: "+chunk.getPos());
environment.params.generator.createReferences(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
this.environment.params.generator.createReferences(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
}
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,17 +16,18 @@
* 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.lod.common.wrappers.worldGeneration.step;
import java.lang.invoke.MethodHandles;
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
@@ -36,36 +37,39 @@ import org.apache.logging.log4j.Logger;
public final class StepStructureStart
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS;
private static final ReentrantLock STRUCTURE_PLACEMENT_LOCK = new ReentrantLock();
private final BatchGenerationEnvironment environment;
/**
* @param batchGenerationEnvironment
*/
public StepStructureStart(BatchGenerationEnvironment batchGenerationEnvironment)
{
environment = batchGenerationEnvironment;
}
public final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS;
public static class StructStartCorruptedException extends RuntimeException {
public StepStructureStart(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
public static class StructStartCorruptedException extends RuntimeException
{
private static final long serialVersionUID = -8987434342051563358L;
public StructStartCorruptedException(ArrayIndexOutOfBoundsException e) {
public StructStartCorruptedException(ArrayIndexOutOfBoundsException e)
{
super("StructStartCorruptedException");
super.initCause(e);
fillInStackTrace();
}
}
public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkAccess> chunks)
public void generateGroup(
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers) throws InterruptedException
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkAccess chunk : chunks)
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (!chunk.getStatus().isOrAfter(STATUS))
{
((ProtoChunk) chunk).setStatus(STATUS);
@@ -73,22 +77,40 @@ public final class StepStructureStart
}
}
#if PRE_MC_1_19
if (environment.params.worldGenSettings.generateFeatures()) {
#elif POST_MC_1_19
#if PRE_MC_1_19_2
if (environment.params.worldGenSettings.generateFeatures())
{
#elif PRE_MC_1_19_4
if (environment.params.worldGenSettings.generateStructures()) {
#else
if (environment.params.worldOptions.generateStructures())
{
#endif
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepStructureStart: "+chunk.getPos());
#if PRE_MC_1_19
// 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 PRE_MC_1_19_2
environment.params.generator.createStructures(environment.params.registry, tParams.structFeat, chunk, environment.params.structures,
environment.params.worldSeed);
#elif POST_MC_1_19
#elif PRE_MC_1_19_4
environment.params.generator.createStructures(environment.params.registry, environment.params.randomState, tParams.structFeat, chunk, environment.params.structures,
environment.params.worldSeed);
#else
environment.params.generator.createStructures(environment.params.registry,
environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, environment.params.structures);
#endif
#if POST_MC_1_18_1
#if POST_MC_1_18_2
try
{
tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
@@ -110,13 +132,17 @@ public final class StepStructureStart
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
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();
}
}
}
}
@@ -1,8 +1,8 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
@@ -16,55 +16,59 @@
* 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.lod.common.wrappers.worldGeneration.step;
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList;
import java.util.List;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap;
public final class StepSurface {
/**
*
*/
public final class StepSurface
{
private static final ChunkStatus STATUS = ChunkStatus.SURFACE;
private final BatchGenerationEnvironment environment;
/**
* @param batchGenerationEnvironment
*/
public StepSurface(BatchGenerationEnvironment batchGenerationEnvironment)
public StepSurface(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
public void generateGroup(
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers)
{
environment = batchGenerationEnvironment;
}
public final ChunkStatus STATUS = ChunkStatus.SURFACE;
public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkAccess> chunks) {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkAccess chunk : chunks) {
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue;
((ProtoChunk) chunk).setStatus(STATUS);
chunksToDo.add(chunk);
}
for (ChunkAccess chunk : chunksToDo) {
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepSurface: "+chunk.getPos());
#if PRE_MC_1_18_1
#if PRE_MC_1_18_2
environment.params.generator.buildSurfaceAndBedrock(worldGenRegion, chunk);
#elif PRE_MC_1_19
#elif PRE_MC_1_19_2
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
#else
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), environment.params.randomState, chunk);
#endif
}
}
}
@@ -1,54 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common;
import com.seibel.lod.common.forge.LodForgeMethodCaller;
import com.seibel.lod.common.wrappers.DependencySetup;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.api.internal.SharedApi;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.config.ConfigBase;
/**
* This is the common main class
* @author Ran
*/
public class LodCommonMain {
public static boolean forge = false;
public static LodForgeMethodCaller forgeMethodCaller;
public static void startup(LodForgeMethodCaller caller) {
if (caller != null) {
LodCommonMain.forge = true;
forgeMethodCaller = caller;
}
DependencySetup.createSharedBindings();
SharedApi.init();
// if (!serverSided) {
// new NetworkReceiver().register_Client();
// } else {
// new NetworkReceiver().register_Server();
// }
}
public static void initConfig() {
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class,1);
}
}
@@ -1,43 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.networking;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.player.Player;
/**
* This is packet handler for our mod
* It basically handles the packets sent from the server & client
*
* @author Ran
*/
// TODO: Server sided stuff here
public class NetworkHandler {
public static void receivePacketClient(Minecraft client, FriendlyByteBuf buf, Player player) {
// This just exists here for testing purposes, it'll be removed in the future
System.out.println("Received int " + buf.readInt());
}
public static void receivePacketServer(FriendlyByteBuf buf, Player player) {
}
}
@@ -1,61 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.networking;
/*
#if MC_1_16_5
import me.shedaniel.architectury.networking.NetworkManager;
#else
import dev.architectury.networking.NetworkManager;
#endif
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
*/
/**
* @author Ran
*/
// Comment: What does the 'server' side mean? Dedicated server? Or does it include the internal server?
// (I removed the hookup that calls the register method, since I'm not sure what it is doing yet)
public class NetworkReceiver {
/*
public void register_Client() {
NetworkManager.registerReceiver(NetworkManager.serverToClient(), Networking.RESOURCE_LOCATION, new ClientReceiver());
}
public void register_Server() {
NetworkManager.registerReceiver(NetworkManager.clientToServer(), Networking.RESOURCE_LOCATION, new ServerReceiver());
}
static class ServerReceiver implements NetworkManager.NetworkReceiver {
@Override
public void receive(FriendlyByteBuf buf, NetworkManager.PacketContext context) {
com.seibel.lod.common.networking.NetworkHandler.receivePacketServer(buf, context.getPlayer());
}
}
static class ClientReceiver implements NetworkManager.NetworkReceiver {
@Override
public void receive(FriendlyByteBuf buf, NetworkManager.PacketContext context) {
com.seibel.lod.common.networking.NetworkHandler.receivePacketClient(Minecraft.getInstance(), buf, context.getPlayer());
}
}
*/
}
@@ -1,73 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.networking;
import com.seibel.lod.core.ModInfo;
//#if MC_1_16_5
//import me.shedaniel.architectury.networking.NetworkManager;
//#else
//import dev.architectury.networking.NetworkManager;
//#endif
import io.netty.buffer.Unpooled;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import java.util.Objects;
/**
* This class holds most of the networking code for the mod.
* @author Ran
*/
public class Networking {
public static final ResourceLocation RESOURCE_LOCATION = new ResourceLocation("lod", "data");
public static FriendlyByteBuf createNew() {
// TODO: Probably replace the Unpooled.buffer()
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeInt(ModInfo.PROTOCOL_VERSION);
return buf;
}
/**
* Sends a packet to a player.
*
* @param player the player to send the packet to
* @param buf the payload of the packet.
*/
public static void send(ServerPlayer player, FriendlyByteBuf buf) {
// NetworkManager.sendToPlayer(player, RESOURCE_LOCATION, buf);
}
/**
* Sends a packet to the connected server.
*
* @param buf the payload of the packet
* @throws IllegalStateException if the client is not connected to a server
*/
public static void send(FriendlyByteBuf buf) throws IllegalStateException {
// NetworkManager.sendToServer(RESOURCE_LOCATION, buf);
}
}
@@ -1,67 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers;
import com.seibel.lod.common.wrappers.gui.LangWrapper;
import com.seibel.lod.common.wrappers.minecraft.MinecraftDedicatedServerWrapper;
import com.seibel.lod.core.wrapperInterfaces.config.ILangWrapper;
import com.seibel.lod.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.lod.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.lod.core.IReflectionHandler;
import com.seibel.lod.core.ReflectionHandler;
import com.seibel.lod.core.dependencyInjection.SingletonInjector;
import com.seibel.lod.core.wrapperInterfaces.IVersionConstants;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
/**
* Binds all necessary dependencies, so we
* can access them in Core. <br>
* This needs to be called before any Core classes
* are loaded.
*
* @author James Seibel
* @author Ran
* @version 12-1-2021
*/
public class DependencySetup {
public static void createSharedBindings()
{
SingletonInjector.INSTANCE.bind(ILangWrapper.class, LangWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE);
SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
DependencySetupDoneCheck.isDone = true;
}
//@Environment(EnvType.SERVER)
public static void createServerBindings() {
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftDedicatedServerWrapper.INSTANCE);
}
//@Environment(EnvType.CLIENT)
public static void createClientBindings() {
SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IReflectionHandler.class, ReflectionHandler.INSTANCE);
}
}
@@ -1,132 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers;
import java.nio.FloatBuffer;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import com.mojang.math.Matrix4f;
import com.seibel.lod.core.enums.ELodDirection;
import com.seibel.lod.core.pos.DhBlockPos;
import com.seibel.lod.core.pos.DhChunkPos;
import com.seibel.lod.core.util.math.Mat4f;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.ChunkPos;
/**
* This class converts to and from Minecraft objects (Ex: Matrix4f)
* and objects we created (Ex: Mat4f).
*
* @author James Seibel
* @version 11-20-2021
*/
public class McObjectConverter
{
/** 4x4 float matrix converter */
public static Mat4f Convert(Matrix4f mcMatrix)
{
FloatBuffer buffer = FloatBuffer.allocate(16);
mcMatrix.store(buffer);
Mat4f matrix = new Mat4f(buffer);
matrix.transpose();
return matrix;
}
static final Direction[] directions;
static final ELodDirection[] lodDirections;
static {
ELodDirection[] lodDirs = ELodDirection.values();
directions = new Direction[lodDirs.length];
lodDirections = new ELodDirection[lodDirs.length];
for (ELodDirection lodDir : lodDirs)
{
Direction dir;
switch (lodDir.name().toUpperCase())
{
case "DOWN":
dir = Direction.DOWN;
break;
case "UP":
dir = Direction.UP;
break;
case "NORTH":
dir = Direction.NORTH;
break;
case "SOUTH":
dir = Direction.SOUTH;
break;
case "WEST":
dir = Direction.WEST;
break;
case "EAST":
dir = Direction.EAST;
break;
default:
dir = null;
break;
}
if (dir == null)
{
throw new IllegalArgumentException("Invalid direction on init mapping: " + lodDir);
}
directions[lodDir.ordinal()] = dir;
lodDirections[dir.ordinal()] = lodDir;
}
}
public static BlockPos Convert(DhBlockPos wrappedPos) {
return new BlockPos(wrappedPos.x, wrappedPos.y, wrappedPos.z);
}
public static ChunkPos Convert(DhChunkPos wrappedPos) {
return new ChunkPos(wrappedPos.x, wrappedPos.z);
}
public static Direction Convert(ELodDirection lodDirection)
{
return directions[lodDirection.ordinal()];
}
public static ELodDirection Convert(Direction direction)
{
return lodDirections[direction.ordinal()];
}
public static void DebugCheckAllPackers() {
BiConsumer<Integer, Integer> func = (x, z) -> DhChunkPos._DebugCheckPacker(x,z,ChunkPos.asLong(x,z));
func.accept(0,0);
func.accept(12345,134);
func.accept(-12345,-134);
func.accept(-30000000/16,30000000/16);
func.accept(30000000/16,-30000000/16);
func.accept(30000000/16,30000000/16);
func.accept(-30000000/16,-30000000/16);
Consumer<BlockPos> func2 = (p) -> DhBlockPos._DebugCheckPacker(p.getX(),p.getY(),p.getZ(),p.asLong());
func2.accept(new BlockPos(0,0,0));
func2.accept(new BlockPos(12345,134,123));
func2.accept(new BlockPos(-12345,-134,-80));
func2.accept(new BlockPos(-30000000, 2047, 30000000));
func2.accept(new BlockPos(30000000, -2048, -30000000));
func2.accept(new BlockPos(30000000, 2047, 30000000));
func2.accept(new BlockPos(-30000000, -2048, -30000000));
}
}
@@ -1,148 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers;
import com.seibel.lod.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
import com.seibel.lod.common.wrappers.block.BlockStateWrapper;
import com.seibel.lod.common.wrappers.block.BiomeWrapper;
import com.seibel.lod.common.wrappers.chunk.ChunkWrapper;
import com.seibel.lod.core.level.IDhLevel;
import com.seibel.lod.core.level.IDhServerLevel;
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.chunk.ChunkAccess;
import java.io.IOException;
/**
* This handles creating abstract wrapper objects.
* @author James Seibel
* @version 2022-12-5
*/
public class WrapperFactory implements IWrapperFactory
{
public static final WrapperFactory INSTANCE = new WrapperFactory();
@Override
public AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel)
{
if (targetLevel instanceof IDhServerLevel)
{
return new BatchGenerationEnvironment((IDhServerLevel) targetLevel);
}
else
{
throw new IllegalArgumentException("The target level must be a server-side level.");
}
}
@Override
public IBiomeWrapper deserializeBiomeWrapper(String str) throws IOException { return BiomeWrapper.deserialize(str); }
@Override
public IBlockStateWrapper deserializeBlockStateWrapper(String str) throws IOException { return BlockStateWrapper.deserialize(str); }
@Override
public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; }
/**
* Note: when this is updated for different MC versions, make sure you also update the documentation in
* {@link IDhApiWorldGenerator#generateChunks} and the type list in {@link WrapperFactory#createChunkWrapperErrorMessage}. <br><br>
*
* For full method documentation please see: {@link IWrapperFactory#createChunkWrapper}
* @see IWrapperFactory#createChunkWrapper
*/
public IChunkWrapper createChunkWrapper(Object[] objectArray) throws ClassCastException
{
if (objectArray.length == 1 && objectArray[0] instanceof IChunkWrapper) // alternate option if "instanceof" can't be compiled down to older JRE versions "IChunkWrapper.class.isInstance(objectArray[0])" Feel free to delete this comment if we've compiled the mod for 1.16 or earlier - James
{
try
{
// this path should only happen when called by Distant Horizons code
// API implementors should never hit this path
return (IChunkWrapper) objectArray[0];
}
catch (Exception e)
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
}
// correct number of parameters from the API
else if (objectArray.length == 2)
{
// chunk
if (!(objectArray[0] instanceof ChunkAccess))
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
ChunkAccess chunk = (ChunkAccess) objectArray[0];
// light source
if (!(objectArray[1] instanceof LevelReader))
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
LevelReader lightSource = (LevelReader) objectArray[1];
return new ChunkWrapper(chunk, lightSource, /*A DH wrapped level isn't necessary*/null);
}
// incorrect number of parameters from the API
else
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
}
/** Note: when this is updated for different MC versions, make sure you also update the documentation in {@link IDhApiWorldGenerator#generateChunks}. */
private static String createChunkWrapperErrorMessage(Object[] objectArray)
{
StringBuilder message = new StringBuilder(
"Chunk wrapper creation failed. \n" +
"Expected parameters: " +
"[" + ChunkAccess.class.getName() + "], " +
"[" + LevelReader.class.getName() + "]. \n");
if (objectArray.length != 0)
{
message.append("Given parameters: ");
for (Object obj : objectArray)
{
message.append("[").append(obj.getClass().getName()).append("], ");
}
}
else
{
message.append(" No parameters given.");
}
return message.toString();
}
}
@@ -1,34 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers;
import net.minecraft.world.level.levelgen.Heightmap;
/**
* Stores any variables or code that
* may be shared between wrapper objects.
*
* @author James Seibel
* @version 11-20-2021
*/
public class WrapperUtil {
/** If we ever need to use a heightmap for any reason, use this one. */
public static final Heightmap.Types DEFAULT_HEIGHTMAP = Heightmap.Types.WORLD_SURFACE_WG;
}
@@ -1,118 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers.block;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import com.google.common.collect.ImmutableBiMap;
import com.google.gson.JsonParser;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.Lifecycle;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.core.Holder;
#if POST_MC_1_19
import net.minecraft.data.worldgen.biome.EndBiomes;
import net.minecraft.data.worldgen.biome.NetherBiomes;
#endif
import net.minecraft.resources.RegistryFixedCodec;
import net.minecraft.resources.RegistryOps;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
/** This class wraps the minecraft BlockPos.Mutable (and BlockPos) class */
public class BiomeWrapper implements IBiomeWrapper
{
#if PRE_MC_1_18_2
public static final ConcurrentMap<Biome, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>();
public final Biome biome;
#else
public static final ConcurrentMap<Holder<Biome>, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>();
public final Holder<Biome> biome;
#endif
static public IBiomeWrapper getBiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif biome)
{
return biomeWrapperMap.computeIfAbsent(biome, BiomeWrapper::new);
}
private BiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif biome)
{
this.biome = biome;
}
@Override
public String getName()
{
#if PRE_MC_1_18_2
return biome.toString();
#else
return biome.unwrapKey().orElse(Biomes.THE_VOID).registry().toString();
#endif
}
@Override
public String serialize() {
//FIXME: Pass in a level obj
String data = Biome.CODEC.encodeStart(RegistryOps.create(JsonOps.INSTANCE, Minecraft.getInstance().level.registryAccess()),
biome).get().orThrow().toString();
return data;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BiomeWrapper that = (BiomeWrapper) o;
return Objects.equals(biome, that.biome);
}
@Override
public int hashCode() {
return Objects.hash(biome);
}
public static IBiomeWrapper deserialize(String str) throws IOException
{
try
{
#if PRE_MC_1_18_2 Biome #else
Holder<Biome> #endif
biome = Biome.CODEC.decode(RegistryOps.create(JsonOps.INSTANCE, Minecraft.getInstance().level.registryAccess()),
JsonParser.parseString(str)).get().orThrow().getFirst();
return getBiomeWrapper(biome);
}
catch (Exception e)
{
throw new IOException("Failed to deserialize biome wrapper", e);
}
}
@Override
public Object getWrappedMcObject_UNSAFE() { return this.biome; }
}
@@ -1,90 +0,0 @@
package com.seibel.lod.common.wrappers.block;
import com.google.gson.JsonParser;
import com.mojang.serialization.JsonOps;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
public class BlockStateWrapper implements IBlockStateWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final BlockStateWrapper AIR = new BlockStateWrapper(null);
public static ConcurrentHashMap<BlockState, BlockStateWrapper> cache = new ConcurrentHashMap<>();
public static BlockStateWrapper fromBlockState(BlockState blockState)
{
if (blockState == null || blockState.isAir())
return AIR;
if (blockState.getFluidState().isEmpty())
return cache.computeIfAbsent(blockState, BlockStateWrapper::new);
else
return cache.computeIfAbsent(blockState.getFluidState().createLegacyBlock(), BlockStateWrapper::new);
}
public final BlockState blockState;
BlockStateWrapper(BlockState blockState) {
this.blockState = blockState;
//LOGGER.info("Created BlockStateWrapper for {}", blockState);
}
@Override
public String serialize()
{
if (this.blockState == null)
{
return "AIR";
}
return BlockState.CODEC.encodeStart(JsonOps.COMPRESSED, this.blockState).get().orThrow().toString();
}
public static BlockStateWrapper deserialize(String str) throws IOException {
if (str.equals("AIR")) {
return AIR;
}
try {
return new BlockStateWrapper(
BlockState.CODEC.decode(JsonOps.COMPRESSED, JsonParser.parseString(str)).get().orThrow().getFirst()
);
} catch (Exception e) {
throw new IOException("Failed to deserialize BlockStateWrapper", e);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BlockStateWrapper that = (BlockStateWrapper) o;
return Objects.equals(blockState, that.blockState);
}
@Override
public int hashCode() {
return Objects.hash(blockState);
}
@Override
public Object getWrappedMcObject_UNSAFE() { return this.blockState; }
@Override
public boolean isAir() { return this.isAir(this.blockState); }
public boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); }
}
@@ -1,52 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers.block;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
/**
* For wrapping/utilizing around TextureAtlasSprite
* @author Ran
*/
public class TextureAtlasSpriteWrapper {
/**
* This code is from Minecraft Forge
* Which is licensed under the terms of GNU Lesser General Public License
* as published by the Free Software Foundation version 2.1
* of the License.
*
* The code has been modified to use TextureAtlasSprite
*/
public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y) {
#if PRE_MC_1_17_1
return sprite.mainImage[0].getPixelRGBA(
x + sprite.framesX[frameIndex] * sprite.getWidth(),
y + sprite.framesY[frameIndex] * sprite.getHeight());
#else
if (sprite.animatedTexture != null) {
x += sprite.animatedTexture.getFrameX(frameIndex) * sprite.width;
y += sprite.animatedTexture.getFrameY(frameIndex) * sprite.height;
}
return sprite.mainImage[0].getPixelRGBA(x, y);
#endif
}
}
@@ -1,217 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers.block;
import com.seibel.lod.common.LodCommonMain;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.Util;
import net.minecraft.client.color.block.BlockTintCache;
import net.minecraft.client.renderer.BiomeColors;
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.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class TintGetterOverrideFast implements BlockAndTintGetter {
LevelReader parent;
public TintGetterOverrideFast(LevelReader parent) {
this.parent = parent;
}
private Biome _getBiome(BlockPos pos) {
#if POST_MC_1_18_2
return parent.getBiome(pos).value();
#else
return parent.getBiome(pos);
#endif
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) {
if (LodCommonMain.forgeMethodCaller != null) {
return LodCommonMain.forgeMethodCaller.colorResolverGetColor(colorResolver, _getBiome(blockPos),
blockPos.getX(), blockPos.getZ());
} else {
return colorResolver.getColor(_getBiome(blockPos), blockPos.getX(), blockPos.getZ());
}
}
@Override
public float getShade(Direction direction, boolean bl) {
return parent.getShade(direction, bl);
}
@Override
public LevelLightEngine getLightEngine() {
return parent.getLightEngine();
}
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) {
return parent.getBrightness(lightLayer, blockPos);
}
@Override
public int getRawBrightness(BlockPos blockPos, int i) {
return parent.getRawBrightness(blockPos, i);
}
@Override
public boolean canSeeSky(BlockPos blockPos) {
return parent.canSeeSky(blockPos);
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos blockPos) {
return parent.getBlockEntity(blockPos);
}
@Override
public BlockState getBlockState(BlockPos blockPos) {
return parent.getBlockState(blockPos);
}
@Override
public FluidState getFluidState(BlockPos blockPos) {
return parent.getFluidState(blockPos);
}
@Override
public int getLightEmission(BlockPos blockPos) {
return parent.getLightEmission(blockPos);
}
@Override
public int getMaxLightLevel() {
return parent.getMaxLightLevel();
}
@Override
public Stream<BlockState> getBlockStates(AABB aABB) {
return parent.getBlockStates(aABB);
}
@Override
public BlockHitResult clip(ClipContext clipContext) {
return parent.clip(clipContext);
}
@Override
@Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState) {
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) {
return parent.getBlockFloorHeight(voxelShape, supplier);
}
@Override
public double getBlockFloorHeight(BlockPos blockPos) {
return parent.getBlockFloorHeight(blockPos);
}
@Override
public int getMaxBuildHeight() {
return parent.getMaxBuildHeight();
}
#if POST_MC_1_17_1
@Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType) {
return parent.getBlockEntity(blockPos, blockEntityType);
}
@Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext) {
return parent.isBlockInLine(clipBlockStateContext);
}
@Override
public int getHeight() {
return parent.getHeight();
}
@Override
public int getMinBuildHeight() {
return parent.getMinBuildHeight();
}
@Override
public int getSectionsCount() {
return parent.getSectionsCount();
}
@Override
public int getMinSection() {
return parent.getMinSection();
}
@Override
public int getMaxSection() {
return parent.getMaxSection();
}
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos) {
return parent.isOutsideBuildHeight(blockPos);
}
@Override
public boolean isOutsideBuildHeight(int i) {
return parent.isOutsideBuildHeight(i);
}
@Override
public int getSectionIndex(int i) {
return parent.getSectionIndex(i);
}
@Override
public int getSectionIndexFromSectionY(int i) {
return parent.getSectionIndexFromSectionY(i);
}
@Override
public int getSectionYFromSectionIndex(int i) {
return parent.getSectionYFromSectionIndex(i);
}
#endif
}
@@ -1,241 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers.block;
import com.seibel.lod.common.LodCommonMain;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import net.minecraft.Util;
import net.minecraft.client.color.block.BlockTintCache;
import net.minecraft.client.renderer.BiomeColors;
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;
public TintGetterOverrideSmooth(LevelReader parent, int smoothingRange) {
this.parent = parent;
this.smoothingRange = smoothingRange;
}
private Biome _getBiome(BlockPos pos) {
#if POST_MC_1_18_2
return 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;
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 int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) {
return calculateBlockTint(blockPos, colorResolver);
}
@Override
public float getShade(Direction direction, boolean bl) {
return parent.getShade(direction, bl);
}
@Override
public LevelLightEngine getLightEngine() {
return parent.getLightEngine();
}
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) {
return parent.getBrightness(lightLayer, blockPos);
}
@Override
public int getRawBrightness(BlockPos blockPos, int i) {
return parent.getRawBrightness(blockPos, i);
}
@Override
public boolean canSeeSky(BlockPos blockPos) {
return parent.canSeeSky(blockPos);
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos blockPos) {
return parent.getBlockEntity(blockPos);
}
@Override
public BlockState getBlockState(BlockPos blockPos) {
return parent.getBlockState(blockPos);
}
@Override
public FluidState getFluidState(BlockPos blockPos) {
return parent.getFluidState(blockPos);
}
@Override
public int getLightEmission(BlockPos blockPos) {
return parent.getLightEmission(blockPos);
}
@Override
public int getMaxLightLevel() {
return parent.getMaxLightLevel();
}
@Override
public Stream<BlockState> getBlockStates(AABB aABB) {
return parent.getBlockStates(aABB);
}
@Override
public BlockHitResult clip(ClipContext clipContext) {
return parent.clip(clipContext);
}
@Override
@Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState) {
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) {
return parent.getBlockFloorHeight(voxelShape, supplier);
}
@Override
public double getBlockFloorHeight(BlockPos blockPos) {
return parent.getBlockFloorHeight(blockPos);
}
@Override
public int getMaxBuildHeight() {
return parent.getMaxBuildHeight();
}
#if POST_MC_1_17_1
@Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType) {
return parent.getBlockEntity(blockPos, blockEntityType);
}
@Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext) {
return parent.isBlockInLine(clipBlockStateContext);
}
@Override
public int getHeight() {
return parent.getHeight();
}
@Override
public int getMinBuildHeight() {
return parent.getMinBuildHeight();
}
@Override
public int getSectionsCount() {
return parent.getSectionsCount();
}
@Override
public int getMinSection() {
return parent.getMinSection();
}
@Override
public int getMaxSection() {
return parent.getMaxSection();
}
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos) {
return parent.isOutsideBuildHeight(blockPos);
}
@Override
public boolean isOutsideBuildHeight(int i) {
return parent.isOutsideBuildHeight(i);
}
@Override
public int getSectionIndex(int i) {
return parent.getSectionIndex(i);
}
@Override
public int getSectionIndexFromSectionY(int i) {
return parent.getSectionIndexFromSectionY(i);
}
@Override
public int getSectionYFromSectionIndex(int i) {
return parent.getSectionYFromSectionIndex(i);
}
#endif
}
@@ -1,91 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers.block;
import com.seibel.lod.common.LodCommonMain;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
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 TintWithoutLevelOverrider implements BlockAndTintGetter {
final BiomeWrapper biome;
public TintWithoutLevelOverrider(BiomeWrapper biome) {
this.biome = biome;
}
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) {
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ());
}
private Biome _unwrap(#if POST_MC_1_18_2 Holder<Biome> #else Biome #endif biome) {
#if POST_MC_1_18_2
return biome.value();
#else
return biome;
#endif
}
@Override
public float getShade(Direction direction, boolean shade) {
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public LevelLightEngine getLightEngine() {
throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public BlockState getBlockState(BlockPos pos) {
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public FluidState getFluidState(BlockPos pos) {
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public int getHeight() {
throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public int getMinBuildHeight() {
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
}
@@ -1,25 +0,0 @@
package com.seibel.lod.common.wrappers.block.cache;
import com.seibel.lod.common.wrappers.block.BiomeWrapper;
import com.seibel.lod.common.wrappers.world.ClientLevelWrapper;
import com.seibel.lod.core.pos.DhBlockPos;
import net.minecraft.world.level.block.state.BlockState;
import java.util.concurrent.ConcurrentHashMap;
public class ClientBlockDetailMap {
private final ConcurrentHashMap<BlockState, ClientBlockStateCache> blockCache = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ClientLevelWrapper level;
public ClientBlockDetailMap(ClientLevelWrapper level) { this.level = level; }
public ClientBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos) { //TODO: Allow a per pos unique setting
return blockCache.computeIfAbsent(state, (s) -> new ClientBlockStateCache(s, level, new DhBlockPos(0,0,0)));
}
public void clear() { blockCache.clear(); }
public int getColor(BlockState state, BiomeWrapper biome, DhBlockPos pos) {
return getBlockStateData(state, pos).getAndResolveFaceColor(biome);
}
}
@@ -1,186 +0,0 @@
package com.seibel.lod.common.wrappers.block.cache;
import com.seibel.lod.common.wrappers.McObjectConverter;
import com.seibel.lod.common.wrappers.block.*;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.pos.DhBlockPos;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.RotatedPillarBlock;
#if POST_MC_1_19
import net.minecraft.util.RandomSource;
#endif
import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.Random;
/**
*
* @version 2022-9-16
*/
public class ClientBlockStateCache
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
#if PRE_MC_1_19
public static final Random random = new Random(0);
#else
public static final RandomSource random = RandomSource.create();
#endif
public final BlockState state;
public final LevelReader level;
public final BlockPos pos;
public ClientBlockStateCache(BlockState blockState, IClientLevelWrapper samplingLevel, DhBlockPos samplingPos) {
state = blockState;
level = (LevelReader) samplingLevel.getWrappedMcObject_UNSAFE();
pos = McObjectConverter.Convert(samplingPos);
resolveColors();
//LOGGER.info("ClientBlocKCache created for {}", blockState);
}
boolean isColorResolved = false;
int baseColor = 0; //TODO: Impl per-face color
boolean needShade = true;
boolean needPostTinting = false;
int tintIndex = 0;
public static final int FLOWER_COLOR_SCALE = 5;
enum ColorMode {
Default,
Flower,
Leaves;
static ColorMode getColorMode(Block b) {
if (b instanceof LeavesBlock) return Leaves;
if (b instanceof FlowerBlock) return Flower;
return Default;
}
}
//TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode) {
int count = 0;
double alpha = 0;
double red = 0;
double green = 0;
double blue = 0;
int tempColor;
{
// textures normally use u and v instead of x and y
for (int u = 0; u < texture.getWidth(); u++)
{
for (int v = 0; v < texture.getHeight(); v++)
{
//note: Minecraft color format is: 0xAA BB GG RR
//________ DH mod color format is: 0xAA RR GG BB
//OpenGL RGBA format native order: 0xRR GG BB AA
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
double r = ((tempColor & 0x000000FF) )/255.;
double g = ((tempColor & 0x0000FF00) >>> 8)/255.;
double b = ((tempColor & 0x00FF0000) >>> 16)/255.;
double a = ((tempColor & 0xFF000000) >>> 24)/255.;
int scale = 1;
if (colorMode == ColorMode.Leaves) {
r *= a;
g *= a;
b *= a;
a = 1.;
} else if (a==0.) {
continue;
} else if (colorMode == ColorMode.Flower && (g+0.1<b || g+0.1<r)) {
scale = FLOWER_COLOR_SCALE;
}
count += scale;
alpha += a*a*scale;
red += r*r*scale;
green += g*g*scale;
blue += b*b*scale;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = ColorUtil.rgbToInt(0,255,255,255);
else
{
// determine the average color
tempColor = ColorUtil.rgbToInt(
(int) (Math.sqrt(alpha/count)*255.),
(int) (Math.sqrt(red / count)*255.),
(int) (Math.sqrt(green / count)*255.),
(int) (Math.sqrt(blue / count)*255.));
}
return tempColor;
}
private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN};
private void resolveColors() {
if (isColorResolved) return;
if (state.getFluidState().isEmpty()) {
List<BakedQuad> quads = null;
for (Direction direction : DIRECTION_ORDER)
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(state).getQuads(state, direction, random);
if (quads != null && !quads.isEmpty() &&
!(state.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
break;
};
if (quads == null || quads.isEmpty()) {
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(state).getQuads(state, null, random);
}
if (quads != null && !quads.isEmpty()) {
needPostTinting = quads.get(0).isTinted();
needShade = quads.get(0).isShade();
tintIndex = quads.get(0).getTintIndex();
baseColor = calculateColorFromTexture(
#if PRE_MC_1_17_1 quads.get(0).sprite,
#else quads.get(0).getSprite(), #endif
ColorMode.getColorMode(state.getBlock()));
} else { // Backup method.
needPostTinting = false;
needShade = false;
tintIndex = 0;
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(state),
ColorMode.getColorMode(state.getBlock()));
}
} else { // Liquid Block
needPostTinting = true;
needShade = false;
tintIndex = 0;
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(state),
ColorMode.getColorMode(state.getBlock()));
}
isColorResolved = true;
}
public int getAndResolveFaceColor(BiomeWrapper biome)
{
// FIXME: impl per-face colors
if (!needPostTinting) return baseColor;
int tintColor = Minecraft.getInstance().getBlockColors()
.getColor(state, new TintWithoutLevelOverrider(biome), pos, tintIndex);
if (tintColor == -1) return baseColor;
return ColorUtil.multiplyARGBwithRGB(baseColor, tintColor);
}
}
@@ -1,77 +0,0 @@
package com.seibel.lod.common.wrappers.block.cache;
import com.seibel.lod.common.wrappers.McObjectConverter;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.pos.DhBlockPos;
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
/**
*
* @version 2022-9-16
*/
public class ServerBlockStateCache
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public final BlockState state;
public final LevelReader level;
public final BlockPos pos;
public ServerBlockStateCache(BlockState blockState, ILevelWrapper samplingLevel, DhBlockPos samplingPos) {
state = blockState;
level = (LevelReader) samplingLevel.getWrappedMcObject_UNSAFE();
pos = McObjectConverter.Convert(samplingPos);
resolveShapes();
//LOGGER.info("ServerBlockState created for {}", blockState);
}
boolean noCollision = false;
boolean[] occludeFaces = null;
boolean[] fullFaces = null;
boolean isShapeResolved = false;
public void resolveShapes() {
if (isShapeResolved) return;
if (state.getFluidState().isEmpty()) {
noCollision = state.getCollisionShape(level, pos).isEmpty();
occludeFaces = new boolean[6];
if (state.canOcclude()) {
for (Direction dir : Direction.values()) {
// Note: isEmpty() isn't quite correct... best would be a isFull() or something...
occludeFaces[McObjectConverter.Convert(dir).ordinal()]
= !state.getFaceOcclusionShape(level, pos, dir).isEmpty();
}
}
VoxelShape voxelShape = state.getShape(level, pos);
fullFaces = new boolean[6];
if (!voxelShape.isEmpty()) {
for (Direction dir : Direction.values()) {
VoxelShape faceShape = voxelShape.getFaceShape(dir);
AABB aabb = faceShape.bounds();
boolean xFull = aabb.minX <= 0.01 && aabb.maxX >= 0.99;
boolean yFull = aabb.minY <= 0.01 && aabb.maxY >= 0.99;
boolean zFull = aabb.minZ <= 0.01 && aabb.maxZ >= 0.99;
fullFaces[McObjectConverter.Convert(dir).ordinal()] =
(xFull || dir.getAxis().equals(Direction.Axis.X))
&& (yFull || dir.getAxis().equals(Direction.Axis.Y))
&& (zFull || dir.getAxis().equals(Direction.Axis.Z));
}
}
} else { // Liquid Block. Treat as full block
occludeFaces = new boolean[6];
Arrays.fill(occludeFaces, true);
fullFaces = new boolean[6];
Arrays.fill(fullFaces, true);
}
}
}
@@ -1,217 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers.chunk;
import com.seibel.lod.common.wrappers.block.BlockStateWrapper;
import com.seibel.lod.core.pos.DhChunkPos;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.lod.common.wrappers.WrapperUtil;
import com.seibel.lod.common.wrappers.block.BiomeWrapper;
import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.LightedWorldGenRegion;
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.core.BlockPos;
#if POST_MC_1_17_1
import net.minecraft.core.QuartPos;
#endif
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.Heightmap;
// Which nullable should be used???
import org.jetbrains.annotations.Nullable;
//import javax.annotation.Nullable;
/**
*
* @author James Seibel
* @version 3-5-2022
*/
public class ChunkWrapper implements IChunkWrapper
{
private final ChunkAccess chunk;
private final DhChunkPos chunkPos;
private final LevelReader lightSource;
private final ILevelWrapper wrappedLevel;
public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource, @Nullable ILevelWrapper wrappedLevel)
{
this.chunk = chunk;
this.lightSource = lightSource;
this.wrappedLevel = wrappedLevel;
chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z);
}
@Override
public int getHeight(){
#if PRE_MC_1_17_1
return 255;
#else
return chunk.getHeight();
#endif
}
@Override
public int getMinBuildHeight()
{
#if PRE_MC_1_17_1
return 0;
#else
return chunk.getMinBuildHeight();
#endif
}
@Override
public int getMaxBuildHeight()
{
return chunk.getMaxBuildHeight();
}
@Override
public int getHeightMapValue(int xRel, int zRel)
{
return chunk.getOrCreateHeightmapUnprimed(WrapperUtil.DEFAULT_HEIGHTMAP).getFirstAvailable(xRel, zRel);
}
@Override
public IBiomeWrapper getBiome(int x, int y, int z)
{
//if (wrappedLevel != null) return wrappedLevel.getBiome(new DhBlockPos(x + getMinX(), y, z + getMinZ()));
#if PRE_MC_1_17_1
return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome(
x >> 2, y >> 2, z >> 2));
#elif PRE_MC_1_18_1
return BiomeWrapper.getBiomeWrapper(chunk.getBiomes().getNoiseBiome(
QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z)));
#elif PRE_MC_1_18_2
return BiomeWrapper.getBiomeWrapper(chunk.getNoiseBiome(
QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z)));
#else //Now returns a Holder<Biome> instead of Biome
return BiomeWrapper.getBiomeWrapper(chunk.getNoiseBiome(
QuartPos.fromBlock(x), QuartPos.fromBlock(y), QuartPos.fromBlock(z)));
#endif
}
@Override
public DhChunkPos getChunkPos() {
return chunkPos;
}
public ChunkAccess getChunk() {
return chunk;
}
@Override
public int getMaxY(int x, int z) {
return chunk.getHeight(Heightmap.Types.WORLD_SURFACE, Math.floorMod(x, 16), Math.floorMod(z, 16));
}
@Override
public int getMaxX(){
return chunk.getPos().getMaxBlockX();
}
@Override
public int getMaxZ(){
return chunk.getPos().getMaxBlockZ();
}
@Override
public int getMinX(){
return chunk.getPos().getMinBlockX();
}
@Override
public int getMinZ() {
return chunk.getPos().getMinBlockZ();
}
@Override
public long getLongChunkPos() {
return chunk.getPos().toLong();
}
@Override
public boolean isLightCorrect(){
#if PRE_MC_1_18_1
return true;
#else
if (chunk instanceof LevelChunk)
{
// called when connected to a server (and sometimes when in a singleplayer world)
return ((LevelChunk) chunk).isClientLightReady() || chunk.isLightCorrect();
}
else
{
// called when in a single player world
return chunk.isLightCorrect();
}
#endif
}
@Override
public int getBlockLight(int x, int y, int z) {
if (lightSource == null) return -1;
return lightSource.getBrightness(LightLayer.BLOCK, new BlockPos(x + getMinX(),y,z + getMinZ()));
}
@Override
public int getSkyLight(int x, int y, int z) {
if (lightSource == null) return -1;
return lightSource.getBrightness(LightLayer.SKY, new BlockPos(x + getMinX(),y,z + getMinZ()));
}
@Override
public boolean doesNearbyChunksExist() {
if (lightSource instanceof LightedWorldGenRegion) return true;
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
if (dx==0 && dz==0) continue;
if (lightSource.getChunk(dx+chunk.getPos().x, dz+chunk.getPos().z, ChunkStatus.BIOMES, false) == null) return false;
}
}
return true;
}
public LevelReader getColorResolver()
{
return lightSource;
}
@Override
public String toString() {
return chunk.getClass().getSimpleName() + chunk.getPos();
}
@Override
public IBlockStateWrapper getBlockState(int x, int y, int z) {
//if (wrappedLevel != null) return wrappedLevel.getBlockState(new DhBlockPos(x + getMinX(), y, z + getMinZ()));
return BlockStateWrapper.fromBlockState(chunk.getBlockState(new BlockPos(x,y,z)));
}
@Override
public boolean isStillValid() {
return wrappedLevel == null || wrappedLevel.tryGetChunk(chunkPos) == this;
}
}
@@ -1,460 +0,0 @@
package com.seibel.lod.common.wrappers.gui;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
// Logger (for debug stuff)
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.config.file.ConfigFileHandling;
import com.seibel.lod.core.config.types.AbstractConfigType;
import com.seibel.lod.core.config.types.ConfigCategory;
import com.seibel.lod.core.config.types.ConfigEntry;
// Uses https://github.com/TheElectronWill/night-config for toml (only for Fabric since Forge already includes this)
// Gets info from our own mod
import com.seibel.lod.core.config.*;
// Minecraft imports
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.lod.core.jar.updater.SelfUpdater;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.client.resources.language.I18n; // translation
#if POST_MC_1_17_1
import net.minecraft.client.gui.narration.NarratableEntry;
#endif
#if PRE_MC_1_19
import net.minecraft.network.chat.TextComponent;
import net.minecraft.network.chat.TranslatableComponent;
#endif
/**
* Based upon TinyConfig but is highly modified
* https://github.com/Minenash/TinyConfig
*
* Credits to Motschen
*
* @author coolGi
* @version 4-28-2022
*/
// FLOATS DONT WORK WITH THIS
@SuppressWarnings("unchecked")
public abstract class ClassicConfigGUI {
/*
This would be removed later on
*/
//==============//
// Initializers //
//==============//
// Some regexes to check if an input is valid
private static final Pattern INTEGER_ONLY_REGEX = Pattern.compile("(-?[0-9]*)");
private static final Pattern DECIMAL_ONLY_REGEX = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)");
private static class ConfigScreenConfigs {
// This contains all the configs for the configs
public static final int SpaceFromRightScreen = 10;
public static final int ButtonWidthSpacing = 5;
public static final int ResetButtonWidth = 40;
}
/**
* The terribly coded old stuff
*/
public static class EntryInfo {
Object widget;
Map.Entry<EditBox, Component> error;
String tempValue;
int index;
}
/**
* creates a text field
*/
private static void textField(AbstractConfigType info, Function<String, Number> func, Pattern pattern, boolean cast) {
boolean isNumber = pattern != null;
((EntryInfo) info.guiValue).widget = (BiFunction<EditBox, Button, Predicate<String>>) (editBox, button) -> stringValue ->
{
stringValue = stringValue.trim();
if (!(stringValue.isEmpty() || !isNumber || pattern.matcher(stringValue).matches()))
return false;
Number value = 0;
((EntryInfo) info.guiValue).error = null;
if (isNumber && !stringValue.isEmpty() && !stringValue.equals("-") && !stringValue.equals(".")) {
value = func.apply(stringValue);
#if PRE_MC_1_19
((EntryInfo) info.guiValue).error = ((ConfigEntry) info).isValid(value) == 0 ? null : new AbstractMap.SimpleEntry<>(editBox, new TextComponent(((ConfigEntry) info).isValid(value) == -1 ?
#else
((EntryInfo) info.guiValue).error = ((ConfigEntry) info).isValidMemoryAddress(value) == 0 ? null : new AbstractMap.SimpleEntry<>(editBox, Component.translatable(((ConfigEntry) info).isValidMemoryAddress(value) == -1 ?
#endif
"§cMinimum " + "length" + (cast ? " is " + (int) ((ConfigEntry) info).getMin() : " is " + ((ConfigEntry) info).getMin()) :
"§cMaximum " + "length" + (cast ? " is " + (int) ((ConfigEntry) info).getMax() : " is " + ((ConfigEntry) info).getMax())));
}
((EntryInfo) info.guiValue).tempValue = stringValue;
editBox.setTextColor(((ConfigEntry) info).isValid(value) == 0 ? 0xFFFFFFFF : 0xFFFF7777);
// button.active = entries.stream().allMatch(e -> e.inLimits);
if (((ConfigEntry) info).isValid(value) == 0 && info.getType() != List.class) {
if (!cast)
((ConfigEntry) info).setWithoutSaving(value);
else
((ConfigEntry) info).setWithoutSaving(value.intValue());
}
// else if (((ConfigEntry) info).isValidMemoryAddress() == 0)
// {
// if (((List<String>) info.get()).size() == ((EntryInfo) info.guiValue).index)
// info.set(((List<String>) info.get()).add(""));
// info.set(((List<String>) info.get()).set(((EntryInfo) info.guiValue).index, Arrays.stream(((EntryInfo) info.guiValue).tempValue.replace("[", "").replace("]", "").split(", ")).collect(Collectors.toList()).get(0)));
// }
return true;
};
}
//==============//
// GUI handling //
//==============//
/**
* if you want to get this config gui's screen call this
*/
public static Screen getScreen(ConfigBase configBase, Screen parent, String category) {
return new ConfigScreen(configBase, parent, category);
}
/**
* Pain
*/
private static class ConfigScreen extends Screen {
protected ConfigScreen(ConfigBase configBase, Screen parent, String category) {
#if PRE_MC_1_19
super(new TranslatableComponent(
#else
super(Component.translatable(
#endif
I18n.exists(configBase.modID + ".config" + (category.isEmpty() ? "." + category : "") + ".title") ?
configBase.modID + ".config.title" :
configBase.modID + ".config" + (category.isEmpty() ? "" : "." + category) + ".title")
);
this.configBase = configBase;
this.parent = parent;
this.category = category;
this.translationPrefix = configBase.modID + ".config.";
}
private final ConfigBase configBase;
private final String translationPrefix;
private final Screen parent;
private final String category;
private ConfigListWidget list;
private boolean reload = false;
// Real Time config update //
@Override
public void tick() {
super.tick();
}
/**
* When you close it, it goes to the previous screen and saves
*/
@Override
public void onClose() {
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(minecraft).setScreen(this.parent);
}
// addRenderableWidget in 1.17 and over
// addButton in 1.16 and below
private Button addBtn(Button button) {
#if PRE_MC_1_17_1
this.addButton(button);
#else
this.addRenderableWidget(button);
#endif
return button;
}
@Override
protected void init() {
super.init();
if (!reload)
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile();
addBtn(new Button(this.width / 2 - 154, this.height - 28, 150, 20, CommonComponents.GUI_CANCEL, button -> {
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile();
Objects.requireNonNull(minecraft).setScreen(parent);
}));
Button done = addBtn(new Button(this.width / 2 + 4, this.height - 28, 150, 20, CommonComponents.GUI_DONE, (button) -> {
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(minecraft).setScreen(parent);
}));
this.list = new ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, this.height - 32, 25);
if (this.minecraft != null && this.minecraft.level != null)
this.list.setRenderBackground(false);
this.addWidget(this.list);
for (AbstractConfigType info : ConfigBase.INSTANCE.entries) {
if (info.getCategory().matches(category) && info.getAppearance().showInGui) {
initEntry(info, this.translationPrefix);
#if PRE_MC_1_19
TranslatableComponent name = new TranslatableComponent(translationPrefix + info.getNameWCategory());
#else
Component name = Component.translatable(translationPrefix + info.getNameWCategory());
#endif
if (ConfigEntry.class.isAssignableFrom(info.getClass())) {
#if PRE_MC_1_19
Button resetButton = new Button(this.width - ConfigScreenConfigs.SpaceFromRightScreen - 150 - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth, 0, ConfigScreenConfigs.ResetButtonWidth, 20, new TextComponent("Reset").withStyle(ChatFormatting.RED), (button -> {
#else
Button resetButton = new Button(this.width - ConfigScreenConfigs.SpaceFromRightScreen - 150 - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth, 0, ConfigScreenConfigs.ResetButtonWidth, 20, Component.translatable("Reset").withStyle(ChatFormatting.RED), (button -> {
#endif
((ConfigEntry) info).setWithoutSaving(((ConfigEntry) info).getDefaultValue());
((EntryInfo) info.guiValue).index = 0;
this.reload = true;
Objects.requireNonNull(minecraft).setScreen(this);
}));
if (((EntryInfo) info.guiValue).widget instanceof Map.Entry) {
Map.Entry<Button.OnPress, Function<Object, Component>> widget = (Map.Entry<Button.OnPress, Function<Object, Component>>) ((EntryInfo) info.guiValue).widget;
if (info.getType().isEnum())
#if PRE_MC_1_19
widget.setValue(value -> new TranslatableComponent(translationPrefix + "enum." + info.getType().getSimpleName() + "." + info.get().toString()));
#else
widget.setValue(value -> Component.translatable(translationPrefix + "enum." + info.getType().getSimpleName() + "." + info.get().toString()));
#endif
this.list.addButton(new Button(this.width - 150 - ConfigScreenConfigs.SpaceFromRightScreen, 0, 150, 20, widget.getValue().apply(info.get()), widget.getKey()), resetButton, null, name);
} else if (((EntryInfo) info.guiValue).widget != null) {
EditBox widget = new EditBox(font, this.width - 150 - ConfigScreenConfigs.SpaceFromRightScreen + 2, 0, 150 - 4, 20, null);
widget.setMaxLength(150);
widget.insertText(String.valueOf(info.get()));
Predicate<String> processor = ((BiFunction<EditBox, Button, Predicate<String>>) ((EntryInfo) info.guiValue).widget).apply(widget, done);
widget.setFilter(processor);
this.list.addButton(widget, resetButton, null, name);
}
} else if (ConfigCategory.class.isAssignableFrom(info.getClass())) {
Button widget = new Button(this.width / 2 - 100, this.height - 28, 100 * 2, 20, name, (button -> {
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(minecraft).setScreen(ClassicConfigGUI.getScreen(this.configBase, this, ((ConfigCategory) info).getDestination()));
}));
this.list.addButton(widget, null, null, null);
}
}
}
}
@Override
public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices); // Renders background
this.list.render(matrices, mouseX, mouseY, delta); // Render buttons
drawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title
if (SelfUpdater.deleteOldOnClose)
drawString(matrices, font, new TranslatableComponent("lod.updater.waitingForClose"), 4, height-38, 0xFFFFFF);
// Render the tooltip only if it can find a tooltip in the language file
for (AbstractConfigType info : ConfigBase.INSTANCE.entries) {
if (info.getCategory().matches(category) && info.getAppearance().showInGui) {
if (list.getHoveredButton(mouseX, mouseY).isPresent()) {
AbstractWidget buttonWidget = list.getHoveredButton(mouseX, mouseY).get();
Component text = ButtonEntry.buttonsWithText.get(buttonWidget);
#if PRE_MC_1_19
TranslatableComponent name = new TranslatableComponent(this.translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.getName());
#else
Component name = Component.translatable(this.translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.getName());
#endif
String key = translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.getName() + ".@tooltip";
if (((EntryInfo) info.guiValue).error != null && text.equals(name))
renderTooltip(matrices, (Component) ((EntryInfo) info.guiValue).error.getValue(), mouseX, mouseY);
else if (I18n.exists(key) && (text != null && text.equals(name))) {
List<Component> list = new ArrayList<>();
for (String str : I18n.get(key).split("\n"))
#if PRE_MC_1_19
list.add(new TextComponent(str));
#else
list.add(Component.translatable(str));
#endif
renderComponentTooltip(matrices, list, mouseX, mouseY);
}
}
}
}
super.render(matrices, mouseX, mouseY, delta);
}
}
private static void initEntry(AbstractConfigType info, String translationPrefix) {
info.guiValue = new EntryInfo();
Class<?> fieldClass = info.getType();
if (ConfigEntry.class.isAssignableFrom(info.getClass())) {
if (fieldClass == Integer.class) {
// For int
textField(info, Integer::parseInt, INTEGER_ONLY_REGEX, true);
} else if (fieldClass == Double.class) {
// For double
textField(info, Double::parseDouble, DECIMAL_ONLY_REGEX, false);
} else if (fieldClass == String.class || fieldClass == List.class) {
// For string or list
textField(info, String::length, null, true);
} else if (fieldClass == Boolean.class) {
// For boolean
#if PRE_MC_1_19
Function<Object, Component> func = value -> new TextComponent((Boolean) value ? "True" : "False").withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
#else
Function<Object, Component> func = value -> Component.translatable((Boolean) value ? "True" : "False").withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
#endif
((EntryInfo) info.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
((ConfigEntry) info).setWithoutSaving(!(Boolean) info.get());
button.setMessage(func.apply(info.get()));
}, func);
}
else if (fieldClass.isEnum())
{
// For enum
List<?> values = Arrays.asList(info.getType().getEnumConstants());
#if PRE_MC_1_19
Function<Object, Component> func = value -> new TranslatableComponent(translationPrefix + "enum." + fieldClass.getSimpleName() + "." + info.get().toString());
#else
Function<Object, Component> func = value -> Component.translatable(translationPrefix + "enum." + fieldClass.getSimpleName() + "." + info.get().toString());
#endif
((EntryInfo) info.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
int index = values.indexOf(info.get()) + 1;
info.set(values.get(index >= values.size() ? 0 : index));
button.setMessage(func.apply(info.get()));
}, func);
}
} else if (ConfigCategory.class.isAssignableFrom(info.getClass())) {
// if (!info.info.getName().equals(""))
// info.name = new TranslatableComponent(info.info.getName());
}
// return info;
}
public static class ConfigListWidget extends ContainerObjectSelectionList<ButtonEntry> {
Font textRenderer;
public ConfigListWidget(Minecraft minecraftClient, int i, int j, int k, int l, int m) {
super(minecraftClient, i, j, k, l, m);
this.centerListVertically = false;
textRenderer = minecraftClient.font;
}
public void addButton(AbstractWidget button, AbstractWidget resetButton, AbstractWidget indexButton, Component text) {
this.addEntry(ButtonEntry.create(button, text, resetButton, indexButton));
}
@Override
public int getRowWidth() {
return 10000;
}
public Optional<AbstractWidget> getHoveredButton(double mouseX, double mouseY) {
for (ButtonEntry buttonEntry : this.children()) {
if (buttonEntry.button != null && buttonEntry.button.isMouseOver(mouseX, mouseY)) {
return Optional.of(buttonEntry.button);
}
}
return Optional.empty();
}
}
public static class ButtonEntry extends ContainerObjectSelectionList.Entry<ButtonEntry> {
private static final Font textRenderer = Minecraft.getInstance().font;
public final AbstractWidget button;
private final AbstractWidget resetButton;
private final AbstractWidget indexButton;
private final Component text;
private final List<AbstractWidget> children = new ArrayList<>();
public static final Map<AbstractWidget, Component> buttonsWithText = new HashMap<>();
private ButtonEntry(AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton) {
buttonsWithText.put(button, text);
this.button = button;
this.resetButton = resetButton;
this.text = text;
this.indexButton = indexButton;
if (button != null)
children.add(button);
if (resetButton != null)
children.add(resetButton);
if (indexButton != null)
children.add(indexButton);
}
public static ButtonEntry create(AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton) {
return new ButtonEntry(button, text, resetButton, indexButton);
}
@Override
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
if (button != null) {
button.y = y;
button.render(matrices, mouseX, mouseY, tickDelta);
}
if (resetButton != null) {
resetButton.y = y;
resetButton.render(matrices, mouseX, mouseY, tickDelta);
}
if (indexButton != null) {
indexButton.y = y;
indexButton.render(matrices, mouseX, mouseY, tickDelta);
}
if (text != null && (!text.getString().contains("spacer") || button != null))
GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF);
}
@Override
public List<? extends GuiEventListener> children() {
return children;
}
// Only for 1.17 and over
// Remove in 1.16 and below
#if POST_MC_1_17_1
@Override
public List<? extends NarratableEntry> narratables() {
return children;
}
#endif
}
}
@@ -1,28 +0,0 @@
package com.seibel.lod.common.wrappers.gui;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.config.ConfigBase;
import com.seibel.lod.core.config.gui.ConfigScreen;
import com.seibel.lod.core.config.gui.JavaScreenHandlerScreen;
import com.seibel.lod.core.config.gui.OpenGLConfigScreen;
import net.minecraft.client.gui.screens.Screen;
public class GetConfigScreen {
public static type useScreen = type.Classic;
public static enum type {
Classic,
@Deprecated
OpenGL, // This was jsut 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) {
return switch (useScreen) {
case Classic -> ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client");
case OpenGL -> MinecraftScreen.getScreen(parent, new OpenGLConfigScreen(), ModInfo.ID + ".title");
// case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title");
case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
default -> null;
};
}
}
@@ -1,17 +0,0 @@
package com.seibel.lod.common.wrappers.gui;
import com.seibel.lod.core.wrapperInterfaces.config.ILangWrapper;
import net.minecraft.client.resources.language.I18n;
public class LangWrapper implements ILangWrapper {
public static final LangWrapper INSTANCE = new LangWrapper();
@Override
public boolean langExists(String str) {
return I18n.exists(str);
}
@Override
public String getLang(String str) {
return I18n.get(str);
}
}
@@ -1,114 +0,0 @@
package com.seibel.lod.common.wrappers.gui;
import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.lod.core.config.gui.AbstractScreen;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.screens.Screen;
import org.jetbrains.annotations.NotNull;
import java.nio.file.Path;
import java.util.*;
public class MinecraftScreen {
public static Screen getScreen(Screen parent, AbstractScreen screen, String translationName) {
return new ConfigScreenRenderer(parent, screen, translationName);
}
private static class ConfigScreenRenderer extends Screen {
private final Screen parent;
private ConfigListWidget list;
private AbstractScreen screen;
#if PRE_MC_1_19
public static net.minecraft.network.chat.TranslatableComponent translate (String str, Object... args) {
return new net.minecraft.network.chat.TranslatableComponent(str, args);
}
#else
public static net.minecraft.network.chat.MutableComponent translate (String str, Object... args) {
return net.minecraft.network.chat.Component.translatable(str, args);
}
#endif
protected ConfigScreenRenderer(Screen parent, AbstractScreen screen, String translationName) {
super(translate(translationName));
screen.minecraftWindow = Minecraft.getInstance().getWindow().getWindow();
this.parent = parent;
this.screen = screen;
}
@Override
protected void init() {
super.init(); // Init Minecraft's screen
Window mcWindow = this.minecraft.getWindow();
screen.width = mcWindow.getWidth();
screen.height = mcWindow.getHeight();
screen.scaledWidth = this.width;
screen.scaledHeight = this.height;
screen.init(); // Init our own config screen
this.list = new ConfigListWidget(this.minecraft, this.width, this.height, 0, this.height, 25); // Select the area to tint
if (this.minecraft != null && this.minecraft.level != null) // Check if in game
this.list.setRenderBackground(false); // Disable from rendering
this.addWidget(this.list); // Add the tint to the things to be rendered
}
@Override
public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices); // Render background
this.list.render(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
screen.mouseX = mouseX;
screen.mouseY = mouseY;
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)
}
@Override
public void resize(Minecraft mc, int width, int height) {
super.resize(mc, width, height); // Resize Minecraft's screen
Window mcWindow = this.minecraft.getWindow();
screen.width = mcWindow.getWidth();
screen.height = mcWindow.getHeight();
screen.scaledWidth = this.width;
screen.scaledHeight = this.height;
screen.onResize(); // Resize our screen
}
@Override
public void tick() {
super.tick(); // Tick Minecraft's screen
screen.tick(); // Tick our screen
if (screen.close) // If we decide to close the screen, then actually close the screen
onClose();
}
@Override
public void onClose() {
screen.onClose(); // Close our screen
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
}
@Override
public void onFilesDrop(@NotNull List<Path> files) {
screen.onFilesDrop(files);
}
// For checking if it should close when you press the escape key
@Override
public boolean shouldCloseOnEsc() {
return screen.shouldCloseOnEsc;
}
}
public static class ConfigListWidget extends ContainerObjectSelectionList {
public ConfigListWidget(Minecraft minecraftClient, int i, int j, int k, int l, int m) {
super(minecraftClient, i, j, k, l, m);
this.centerListVertically = false;
}
}
}
@@ -1,71 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
/**
* Creates a button with a texture on it
*/
public class TexturedButtonWidget extends ImageButton {
#if POST_MC_1_17_1
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, ResourceLocation texture, OnPress pressAction) {
super(x, y, width, height, u, v, texture, pressAction);
}
#endif
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction) {
super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction);
}
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, Component text) {
super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text);
}
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, OnTooltip tooltipSupplier, Component text) {
super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, tooltipSupplier, text);
}
@Override
public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta) {
#if PRE_MC_1_17_1
Minecraft.getInstance().getTextureManager().bind(WIDGETS_LOCATION);
RenderSystem.color4f(1.0F, 1.0F, 1.0F, this.alpha);
#else
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderTexture(0, WIDGETS_LOCATION);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, this.alpha);
#endif
int i = this.getYImage(this.isHovered);
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.enableDepthTest();
this.blit(matrices, this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height);
this.blit(matrices, this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height);
super.renderButton(matrices, mouseX, mouseY, delta);
}
}
@@ -1,188 +0,0 @@
package com.seibel.lod.common.wrappers.gui.updater;
import com.google.common.collect.Lists;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.lod.common.wrappers.gui.ClassicConfigGUI;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.jar.JarUtils;
import com.seibel.lod.core.jar.installer.MarkdownFormatter;
import com.seibel.lod.core.jar.installer.ModrinthGetter;
import com.seibel.lod.core.jar.updater.SelfUpdater;
import net.minecraft.client.Minecraft;
import net.minecraft.client.StringSplitter;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.FormattedCharSequence;
import java.util.*;
/**
* The screen that pops up if the mod has an update.
*
* @author coolGi
*/
// TODO: After finishing the config, rewrite this in openGL as well
// TODO: Make this
public class ChangelogScreen extends Screen {
private Screen parent;
private String versionID;
private List<String> changelog;
private TextArea changelogArea;
public ChangelogScreen(Screen parent, String versionID) {
super(translate(ModInfo.ID + ".updater.title"));
this.parent = parent;
this.versionID = versionID;
this.changelog = new ArrayList<>();
// Get the release changelog and split it by the new lines
List<String> unwrappedChangelog =
List.of(new MarkdownFormatter.MinecraftFormat().convertTo( // This formats markdown to minecraft's "§" characters
ModrinthGetter.changeLogs.get(versionID)
).split("\\n"));
// Makes the words wrap around to not go off the screen
for (String str: unwrappedChangelog) {
this.changelog.addAll(
MarkdownFormatter.splitString(str, 75)
);
}
// Debugging
// System.out.println(this.changelog);
}
@Override
protected void init() {
super.init();
this.addBtn( // Close
new Button(5, this.height - 25, 100, 20, translate(ModInfo.ID + ".general.back"), (btn) -> {
this.onClose();
})
);
this.changelogArea = new TextArea(this.minecraft, this.width*2, this.height, 32, this.height - 32, 10);
for (int i = 0; i < changelog.size(); i++) {
this.changelogArea.addButton(new TextComponent(changelog.get(i)));
// drawString(matrices, this.font, changelog.get(i), this.width / 2 - 175, this.height / 2 - 100 + i*10, 0xFFFFFF);
}
}
@Override
public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices); // Render background
// Set the scroll position to the mouse height relative to the screen
this.changelogArea.setScrollAmount(
((double) mouseY)/((double) this.height) * this.changelogArea.getMaxScroll()
);
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
drawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title
}
@Override
public void onClose() {
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
}
// addRenderableWidget in 1.17 and over
// addButton in 1.16 and below
private void addBtn(Button button) {
#if PRE_MC_1_17_1
this.addButton(button);
#else
this.addRenderableWidget(button);
#endif
}
#if PRE_MC_1_19
public static net.minecraft.network.chat.TranslatableComponent translate (String str, Object... args) {
return new net.minecraft.network.chat.TranslatableComponent(str, args);
}
#else
public static net.minecraft.network.chat.MutableComponent translate (String str, Object... args) {
return net.minecraft.network.chat.Component.translatable(str, args);
}
#endif
public static class TextArea extends ContainerObjectSelectionList<ButtonEntry> {
Font textRenderer;
public TextArea(Minecraft minecraftClient, int i, int j, int k, int l, int m) {
super(minecraftClient, i, j, k, l, m);
this.centerListVertically = false;
textRenderer = minecraftClient.font;
}
public void addButton(Component text) {
this.addEntry(ButtonEntry.create(text));
}
@Override
public int getRowWidth() {
return 10000;
}
}
public static class ButtonEntry extends ContainerObjectSelectionList.Entry<ButtonEntry> {
private static final Font textRenderer = Minecraft.getInstance().font;
private final Component text;
private final List<AbstractWidget> children = new ArrayList<>();
private ButtonEntry(Component text) {
this.text = text;
}
public static ButtonEntry create(Component text) {
return new ButtonEntry(text);
}
@Override
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);
}
@Override
public List<? extends GuiEventListener> children() {
return children;
}
#if POST_MC_1_17_1
@Override
public List<? extends NarratableEntry> narratables() {
return children;
}
#endif
}
}
@@ -1,159 +0,0 @@
package com.seibel.lod.common.wrappers.gui.updater;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.lod.common.wrappers.gui.TexturedButtonWidget;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.jar.JarUtils;
import com.seibel.lod.core.jar.installer.ModrinthGetter;
import com.seibel.lod.core.jar.updater.SelfUpdater;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.resources.ResourceLocation;
import java.util.*;
/**
* The screen that pops up if the mod has an update.
*
* @author coolGi
*/
// TODO: After finishing the config, rewrite this in openGL as well
// and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192
public class UpdateModScreen extends Screen {
private Screen parent;
private String newVersionID;
public UpdateModScreen(Screen parent, String newVersionID) {
super(translate(ModInfo.ID + ".updater.title"));
this.parent = parent;
this.newVersionID = newVersionID;
}
@Override
protected void init() {
super.init();
try {
// We cannot get assets from the root of the mod so we use this hack
// TODO: Load the icon.png and logo.png in the mod initialise rather than here
ResourceLocation logoLocation = new ResourceLocation(ModInfo.ID, "logo.png");
Minecraft.getInstance().getTextureManager().register(
logoLocation,
new DynamicTexture(NativeImage.read(JarUtils.accessFile("logo.png")))
);
// Logo image
this.addBtn(new ImageButton(
// Where the button is on the screen
this.width / 2 - 65, this.height / 2 - 110,
// Width and height of the button
130, 65,
// Offset
0, 0,
// Some textuary stuff
0, logoLocation, 130, 65,
// Create the button and tell it where to go
// For now it goes to the client option by default
(buttonWidget) -> System.out.println("Nice, you found an easter egg :)"), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
// Add a title to the button
translate(ModInfo.ID + ".updater.title")
));
} catch (Exception e) { e.printStackTrace(); }
this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen
this.width / 2 - 97, this.height / 2 + 8,
// Width and height of the button
20, 20,
// Offset
0, 0,
// Some textuary stuff
0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20,
// Create the button and tell it where to go
// For now it goes to the client option by default
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
// Add a title to the button
translate(ModInfo.ID + ".updater.title")
));
this.addBtn( // Update
new Button(this.width / 2 - 75, this.height / 2 + 8, 150, 20, translate(ModInfo.ID + ".updater.update"), (btn) -> {
SelfUpdater.deleteOldOnClose = true;
SelfUpdater.updateMod();
this.onClose();
})
);
this.addBtn( // Silent update
new Button(this.width / 2 - 75, this.height / 2 + 30, 150, 20, translate(ModInfo.ID + ".updater.silent"), (btn) -> {
Config.Client.AutoUpdater.promptForUpdate.set(false);
SelfUpdater.updateMod();
this.onClose();
})
);
this.addBtn( // Later (not now)
new Button(this.width / 2 + 2, this.height / 2 + 70, 100, 20, translate(ModInfo.ID + ".updater.later"), (btn) -> {
this.onClose();
})
);
this.addBtn( // Never
new Button(this.width / 2 - 102, this.height / 2 + 70, 100, 20, translate(ModInfo.ID + ".updater.never"), (btn) -> {
Config.Client.AutoUpdater.enableAutoUpdater.set(false);
this.onClose();
})
);
}
@Override
public void render(PoseStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices); // Render background
// Render the text's
drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text1"), this.width / 2, this.height / 2 - 35, 0xFFFFFF);
drawCenteredString(matrices, this.font, translate(ModInfo.ID + ".updater.text2", ModInfo.VERSION, ModrinthGetter.releaseNames.get(this.newVersionID)), this.width / 2, this.height / 2 -20, 0x52FD52);
// TODO: add the tooltips for the buttons
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
// TODO: Add tooltips
}
@Override
public void onClose() {
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
}
// addRenderableWidget in 1.17 and over
// addButton in 1.16 and below
private void addBtn(Button button) {
#if PRE_MC_1_17_1
this.addButton(button);
#else
this.addRenderableWidget(button);
#endif
}
#if PRE_MC_1_19
public static net.minecraft.network.chat.TranslatableComponent translate (String str, Object... args) {
return new net.minecraft.network.chat.TranslatableComponent(str, args);
}
#else
public static net.minecraft.network.chat.MutableComponent translate (String str, Object... args) {
return net.minecraft.network.chat.Component.translatable(str, args);
}
#endif
}
@@ -1,262 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers.minecraft;
import java.io.File;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.lod.common.wrappers.world.ClientLevelWrapper;
import com.seibel.lod.common.wrappers.world.ServerLevelWrapper;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.enums.ELodDirection;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.lod.common.wrappers.McObjectConverter;
import com.seibel.lod.core.pos.DhBlockPos;
import com.seibel.lod.core.pos.DhChunkPos;
import net.minecraft.CrashReport;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
#if PRE_MC_1_19
import net.minecraft.network.chat.TextComponent;
#endif
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
/**
* A singleton that wraps the Minecraft object.
*
* @author James Seibel
* @version 3-5-2022
*/
//@Environment(EnvType.CLIENT)
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
public final Minecraft mc = Minecraft.getInstance();
/**
* The lightmap for the current:
* Time, dimension, brightness setting, etc.
*/
private NativeImage lightMap = null;
private ProfilerWrapper profilerWrapper;
private MinecraftClientWrapper()
{
}
//================//
// 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()
{
lightMap = null;
}
//=================//
// method wrappers //
//=================//
@Override
public float getShade(ELodDirection lodDirection) {
if (mc.level != null)
{
Direction mcDir = McObjectConverter.Convert(lodDirection);
return mc.level.getShade(mcDir, true);
}
else return 0.0f;
}
@Override
public boolean hasSinglePlayerServer() { return mc.hasSingleplayerServer(); }
@Override
public boolean clientConnectedToDedicatedServer() { return mc.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
@Override
public String getCurrentServerName() { return mc.getCurrentServer().name; }
@Override
public String getCurrentServerIp() { return mc.getCurrentServer().ip; }
@Override
public String getCurrentServerVersion()
{
return mc.getCurrentServer().version.getString();
}
//=============//
// Simple gets //
//=============//
public LocalPlayer getPlayer()
{
return mc.player;
}
@Override
public boolean playerExists()
{
return mc.player != null;
}
@Override
public DhBlockPos getPlayerBlockPos()
{
BlockPos playerPos = getPlayer().blockPosition();
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
}
@Override
public DhChunkPos getPlayerChunkPos()
{
#if PRE_MC_1_17_1
ChunkPos playerPos = new ChunkPos(getPlayer().blockPosition());
#else
ChunkPos playerPos = getPlayer().chunkPosition();
#endif
return new DhChunkPos(playerPos.x, playerPos.z);
}
public ModelManager getModelManager()
{
return mc.getModelManager();
}
@Nullable
@Override
public ILevelWrapper getWrappedClientWorld()
{
if (mc.level == null)
{
return null;
}
return ClientLevelWrapper.getWrapper(mc.level);
}
/** Please move over to getInstallationDirectory() */
@Deprecated
@Override
public File getGameDirectory()
{
return getInstallationDirectory();
}
@Override
public IProfilerWrapper getProfiler()
{
if (profilerWrapper == null)
profilerWrapper = new ProfilerWrapper(mc.getProfiler());
else if (mc.getProfiler() != profilerWrapper.profiler)
profilerWrapper.profiler = mc.getProfiler();
return profilerWrapper;
}
/** Returns all worlds available to the server */
@Override
public ArrayList<ILevelWrapper> getAllServerWorlds()
{
ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>();
Iterable<ServerLevel> serverWorlds = mc.getSingleplayerServer().getAllLevels();
for (ServerLevel world : serverWorlds)
{
worlds.add(ServerLevelWrapper.getWrapper(world));
}
return worlds;
}
@Override
public void sendChatMessage(String string)
{
#if PRE_MC_1_19
getPlayer().sendMessage(new TextComponent(string), getPlayer().getUUID());
#else
getPlayer().sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
#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
public void crashMinecraft(String errorMessage, Throwable exception)
{
LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...");
CrashReport report = new CrashReport(errorMessage, exception);
Minecraft.crash(report);
}
@Override
public Object getOptionsObject()
{
return mc.options;
}
@Override
public boolean isDedicatedServer() {
return false;
}
@Override
public File getInstallationDirectory() {
return mc.gameDirectory;
}
}
@@ -1,23 +0,0 @@
package com.seibel.lod.common.wrappers.minecraft;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import net.minecraft.server.dedicated.DedicatedServer;
import java.io.File;
//@Environment(EnvType.SERVER)
public class MinecraftDedicatedServerWrapper implements IMinecraftSharedWrapper {
public static final MinecraftDedicatedServerWrapper INSTANCE = new MinecraftDedicatedServerWrapper();
private MinecraftDedicatedServerWrapper() {}
public DedicatedServer dedicatedServer = null;
@Override
public boolean isDedicatedServer() {
return true;
}
@Override
public File getInstallationDirectory() {
if (dedicatedServer == null)
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server complete initialization!");
return dedicatedServer.getServerDirectory();
}
}
@@ -1,71 +0,0 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 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.lod.common.wrappers.misc;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
import net.minecraft.client.renderer.LightTexture;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer;
/**
* @author James Seibel
* @version 11-21-2021
*/
public class LightMapWrapper implements ILightMapWrapper
{
private int textureId = 0;
public LightMapWrapper()
{
}
private void createLightmap(NativeImage image)
{
textureId = GL32.glGenTextures();
GL32.glBindTexture(GL32.GL_TEXTURE_2D, textureId);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
}
public void uploadLightmap(NativeImage image)
{
int currentBind = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, textureId);
if (textureId == 0) {
createLightmap(image);
}
// NativeImage::upload(int levelOfDetail, int xOffset, int yOffset, bool shouldCleanup?)
image.upload(0, 0, 0, false);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, currentBind);
}
@Override
public void bind() {
GL32.glBindTexture(GL32.GL_TEXTURE_2D, textureId);
}
@Override
public void unbind() {
GL32.glBindTexture(GL32.GL_TEXTURE_2D, 0);
}
}

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