Compare commits

..

250 Commits

Author SHA1 Message Date
James Seibel b6b108a287 Merge branch 'stable' of gitlab.com:jeseibel/distant-horizons into stable 2024-09-04 07:08:38 -05:00
James Seibel ab4ef429db Up version 2.2.1-dev -> 2.2.1 and API 3.0.0 -> 3.0.1 2024-09-04 06:58:28 -05:00
James Seibel 86473e022e Fix referencing unavailable GLFW methods for MC 1.18.2 and lower 2024-09-03 07:03:30 -05:00
James Seibel fd89f569d0 Fix MC 1.17.1 not compiling 2024-09-01 21:16:14 -05:00
James Seibel eefc765823 Fix LODs flashing while moving 2024-09-01 21:10:32 -05:00
James Seibel ebccb2516b Cull beacons based on X/Z distance instead of 3D distance 2024-09-01 17:28:13 -05:00
James Seibel 8c62a40da9 Disable instanced rendering on Mac when Sodium is present
Closes !793 (Generic Rendering crashes with Sodium on M1 Mac)
2024-09-01 17:02:49 -05:00
James Seibel d56af5c38f Fix some beacon rendering/updating issues 2024-09-01 16:36:41 -05:00
James Seibel 39b1ec61ba Fix glass panes not affecting beacon colors 2024-09-01 15:04:27 -05:00
James Seibel cb613cf7df add disableUnchangedChunkCheck config 2024-08-31 22:11:29 -05:00
James Seibel 28e33b4c36 Fix MC 1.16+ compiling 2024-08-31 22:11:14 -05:00
James Seibel 855e6b8180 Update pos getters and remove (hopefully) unneeded imports 2024-08-31 21:57:53 -05:00
James Seibel d62161f529 Fix cloud color not matching MC 2024-08-31 21:56:32 -05:00
James Seibel 71d48411f1 Add DhBlockPosMutable and make the original immutable 2024-08-30 07:36:04 -05:00
James Seibel 731842e09c Fix DH beacon detection logic breaking the lighting engine 2024-08-29 19:54:44 -05:00
James Seibel 61169f87c0 Fix LODs not updating underground 2024-08-29 07:33:21 -05:00
James Seibel 9fb3b196d2 Add a quick DH cloud UI config 2024-08-28 07:16:33 -05:00
James Seibel 867b875cf9 Fix Lithium breaking world gen for MC 1.20.1 and older 2024-08-27 19:19:09 -05:00
James Seibel 3875c8c4ce Up version number 2.2.0 -> 2.2.1-dev 2024-08-20 19:16:28 -05:00
James Seibel 89b959d3f5 Up version number 2.1.3-dev -> 2.2.0 2024-08-20 17:45:42 -05:00
James Seibel d62e50d6f4 Fix Legacy GL causing fog to smear 2024-08-20 17:45:31 -05:00
James Seibel 16836a2b49 Move MixinChunkMap shared code to common 2024-08-18 14:46:49 -05:00
James Seibel f5651f26a5 Merge !65 (fix neo/forge chunk update events) 2024-08-18 14:31:24 -05:00
James Seibel 82ff59c857 Fix fog and SSAO being broken by some mods 2024-08-17 22:29:42 -05:00
James Seibel 8af61041f0 Remove 1.21 from CI build script
1.21.1 also covers 1.21
2024-08-16 17:28:36 -05:00
James Seibel 2a9136b56f Merge 1.21.1 and 1.21 2024-08-15 07:21:19 -05:00
James Seibel 64da6c811d revert temporary 1.17.1 breakage 2024-08-13 17:24:18 -05:00
James Seibel e6b93e0d92 forgot to close test preprocessor 2024-08-13 07:46:49 -05:00
James Seibel f874219a64 Wrong 1.17.1 perpreocessor used 2024-08-13 07:33:39 -05:00
James Seibel b4822740f4 temporarily break 1.17 compiling to test gitlab bages 2024-08-13 07:33:04 -05:00
James Seibel af205a50b4 Deprecate IDhApiWorldGenerator.isBusy(), task queuing is now handled internally 2024-08-12 22:20:14 -05:00
James Seibel 2f6eaf79bd Add optional DhApiChunk validation for world gen 2024-08-12 21:47:58 -05:00
James Seibel 625f1e700f Fix MC 1.21 / 1.21.1 2024-08-12 21:05:15 -05:00
James Seibel 897d5b0b11 Change MC 1.21 -> 1.21.1 in CI version 2024-08-12 19:53:48 -05:00
James Seibel 95641e2f4e Allow adding empty lists to DhApiChunk 2024-08-11 22:01:29 -05:00
James Seibel cd856b86c7 Fix DhApiChunk setDataPoints failing for empty lists 2024-08-11 21:55:19 -05:00
James Seibel c00aa6d627 Add MC 1.21.1 2024-08-11 20:05:24 -05:00
James Seibel 398a3fb0bc Add alex's cave warning message 2024-08-11 09:55:12 -05:00
James Seibel e0fa638ad9 Fix beacons not enabling/disabling correctly 2024-08-09 07:26:20 -05:00
James Seibel 4e42cbd4ce Fix frustum culling when the screen is warped 2024-08-07 18:55:09 -05:00
James Seibel b6c6be77cd Fix beacons not being updated 2024-08-07 07:47:30 -05:00
James Seibel 0964293a72 Fix direct memory leak and remove config for GpuUpload 2024-08-07 07:30:01 -05:00
James Seibel c8b6141ce0 Improve LOD detail level detection and hole filling 2024-08-04 08:31:03 -05:00
James Seibel 948540369f Attempt to improve LOD building speed and reduce broken lighting on servers 2024-08-03 17:11:22 -05:00
James Seibel 363df0ad6f Fix MC 1.16/1.17 compiling 2024-08-03 11:38:02 -05:00
James Seibel a37e105434 Add (disabled) test API world generator 2024-08-03 09:52:06 -05:00
James Seibel aeea0c00c3 Allow DhApiChunk to accept top down or bottom up data point orders 2024-08-03 09:33:05 -05:00
James Seibel 137352674e Fix off by 1 error in Render data transformer 2024-08-02 18:30:59 -05:00
James Seibel 4734552954 Fix MC 1.16 compiling 2024-08-02 18:21:47 -05:00
James Seibel 879c2f1ec4 Fix out of bounds exception in Full Data Transformer 2024-08-02 17:56:33 -05:00
James Seibel 7dc9d2a352 Clean up faster world gen and fix even offset gen events 2024-08-02 08:25:32 -05:00
James Seibel cabc470ebd Temporary Test removing world gen boarder chunks 2024-08-01 07:44:46 -05:00
James Seibel 0bf1f493aa Change some world gen info logs to debug 2024-08-01 07:06:47 -05:00
James Seibel 705bd14ee4 Fix cave culling affecting floating islands and add LOD reload to some configs 2024-07-31 19:06:47 -05:00
James Seibel 155955e49b Mark Iris 1.7.4 and lower as incompatible (as recommended by IMS) 2024-07-30 17:13:54 -05:00
James Seibel c76a793b18 Remove deprecated methods and move method to StringUtil 2024-07-30 17:07:16 -05:00
James Seibel 50cc8501a0 Remove unused sodium and McRenderWrapper methods
Removed methods were originally used to cull LODs if MC had loaded chunks, however this turned out to be more trouble than it was worth and caused more problems than it solved.
2024-07-30 17:01:09 -05:00
James Seibel 209279e3e4 Merge branch 'distant-horizons-m2' 2024-07-30 16:06:39 -05:00
James Seibel 41239572a5 Fix presets only using "custom" after any value was changed 2024-07-30 15:47:52 -05:00
James Seibel 106ab47c3d Fix default logging debug to file 2024-07-29 20:40:54 -05:00
James Seibel a84f9b60e5 Fix rapidly changing dimensions causing the game to crash 2024-07-29 07:29:56 -05:00
James Seibel 4481e8634a Fix incorrect DhApiChunk create constructor parameter order (again) 2024-07-28 20:18:31 -05:00
James Seibel 3e432682fb fix incorrect positions being fed into biome color code 2024-07-28 09:34:15 -05:00
James Seibel 05569c03a4 Revert and Deprecate DhApiChunk and DhApiTerrainDataPoint constructors 2024-07-28 08:56:26 -05:00
James Seibel 2a37bec8ff Merge tag '2.1.2a' into stable 2024-07-28 08:07:35 -05:00
James Seibel 2d567b84be Fix holes in LODs boarding different detail levels 2024-07-27 21:06:55 -05:00
James Seibel e2a378250f Fix LOD upload warning 2024-07-27 20:25:58 -05:00
James Seibel e2083a1836 Fix LODs flashing twice when changing configs 2024-07-27 20:11:49 -05:00
James Seibel 334946ab59 Potentially fix thread warnings in ClientBlockStateColorCache 2024-07-27 19:15:00 -05:00
James Seibel 8c9bb98125 Update IDhApiRenderProxy.clearRenderDataCache() to also clear cached block colors 2024-07-27 17:36:57 -05:00
James Seibel 726f0f3d3c Remove unused ServerBlockStateCache 2024-07-27 16:51:14 -05:00
James Seibel 50e5898692 Rename ClientBlockStateCache -> ClientBlockStateColorCache
And do some additional cleanup
2024-07-27 16:44:47 -05:00
James Seibel de05a5f674 Refactor and cleanup ClientBlockStateCache 2024-07-27 16:25:27 -05:00
James Seibel 31b57fae50 fix 1.16.5 compiling 2024-07-27 16:24:31 -05:00
James Seibel 2f686057f3 Fix ice/water vertical LOD lighting 2024-07-27 09:30:51 -05:00
James Seibel 132251341f Fix replay mod not showing LODs 2024-07-21 20:06:40 -05:00
James Seibel 2bac5f933a remove unused clientLevelWrapper.getGameDirectory() 2024-07-21 19:29:01 -05:00
James Seibel 2e565aa83a Improve cave culling and add config for ignored/cave blocks 2024-07-21 17:27:26 -05:00
James Seibel 4e9d0f4861 Fix ConfigEntry String value saving 2024-07-21 16:13:55 -05:00
James Seibel 7216b193e8 Fix API chunk world gen 2024-07-20 17:58:39 -05:00
James Seibel c33a5bf814 Add IDhApiWrapperFactory resourceLocation string methods for block/biomes 2024-07-20 11:21:01 -05:00
James Seibel 97756a5196 Add AbstractDhApiChunkWorldGenerator.generateApiChunk() 2024-07-20 10:45:55 -05:00
James Seibel 377f7d23e3 Remove render param from DhApiAfterRenderEvent 2024-07-14 09:31:03 -05:00
James Seibel 7005202384 Add a optional memory cache to the IDhApiTerrainDataRepo 2024-07-14 08:41:22 -05:00
James Seibel 99e8f57bac add missing genericRendering to IDhApiGraphicsConfig 2024-07-14 07:25:49 -05:00
James Seibel afddf4168e Change some chunk deserialization errors to warnings 2024-07-13 12:59:43 -05:00
James Seibel fbffdc0c9f Fix fog for Mac and remove near fog limitation 2024-07-13 12:17:30 -05:00
James Seibel e6d3647490 Increase default fog start distance 2024-07-13 08:14:33 -05:00
James Seibel 13363ff363 make clouds smaller and thinner 2024-07-12 21:47:16 -05:00
James Seibel 7f98e4b1eb Fix potential chunkWrapper null pointer 2024-07-12 21:31:12 -05:00
James Seibel 408460b0ae Fix missing imports for MC 1.19 and below 2024-07-12 20:31:36 -05:00
James Seibel b69ef5835d Fix repo connections not getting closed 2024-07-12 20:22:02 -05:00
James Seibel 0428fa0912 Clone API event parameters to reduce listener contamination 2024-07-12 19:22:25 -05:00
James Seibel 9f3124fa56 Add renderEventParam to generic rendering shader binding by IMS request 2024-07-12 17:27:32 -05:00
James Seibel fbbdab73c6 Attempt to fix lag spikes when right clicking blocks 2024-07-12 17:24:45 -05:00
James Seibel ee9441c521 Fix world gen not skipping already complete stages 2024-07-12 07:41:18 -05:00
James Seibel a9e0fd5d9b Add generic object setup/cleanup events 2024-07-12 07:16:06 -05:00
James Seibel 98464889ca Fix material typo 2 2024-07-11 22:51:47 -05:00
James Seibel eed5fd60c6 Fix material typo 2024-07-11 22:07:15 -05:00
James Seibel ac43cd5496 Add generic object materials 2024-07-11 18:13:07 -05:00
James Seibel 1f16a7c808 Fix generic rendering and add EDhApiBlockMaterial 2024-07-11 17:58:05 -05:00
James Seibel 39e4c70754 Add api for generic rendering config 2024-07-11 17:39:01 -05:00
James Seibel 82eb27af4c Add DhApiBeforeGenericObjectRenderEvent 2024-07-11 17:32:26 -05:00
IMS212 3aaab94b39 Support both Sodium 0.5 and 0.6 with reflection 2024-07-10 21:02:53 -07:00
James Seibel 07a0779ca4 Fix potential light map crashing and memory leak 2024-07-10 18:57:09 -05:00
James Seibel 2adba02a38 Add "IP Only" to multiplayer tooltip 2024-07-10 07:45:12 -05:00
James Seibel 9dd76db3fc Fix generic rendering at extreme distances 2024-07-10 07:37:18 -05:00
James Seibel 97dacf2429 Add toggleable logging for GL Buffer garbage collection
Will need to be tested by someone who is experiencing issue #718, so far I've been unable to reproduce anything meaningful.
2024-07-09 17:40:27 -05:00
James Seibel 1c189e162a fix sub MC 1.20.1 compiling 2024-07-09 16:39:29 -05:00
James Seibel f7a0fff869 Move IBlockStateWrapper constants into LodUtil 2024-07-09 16:39:04 -05:00
James Seibel 2f985d0926 Add beacon colors 2024-07-09 07:33:30 -05:00
James Seibel 2a3c544fba Increase cloud rendering performance 2024-07-08 19:56:29 -05:00
James Seibel 09d133b994 Add generic rendering localization 2024-07-08 07:45:03 -05:00
James Seibel 26a4223ecf Fix double unloading beacons 2024-07-07 19:54:25 -05:00
James Seibel e2943fdcaf Fix beacons un-rendering when unloading LODs 2024-07-07 19:45:47 -05:00
James Seibel f1053251b4 Add missing generic rendering config options 2024-07-07 18:13:58 -05:00
James Seibel be1dcaf43c Add cloud rendering 2024-07-07 18:03:11 -05:00
James Seibel a899d988fc Fix concurrent modification for GenericObjectRenderer 2024-07-04 21:43:15 -05:00
James Seibel 06b5b2c514 Fix potential null pointer in auto updater 2024-07-04 17:37:30 -05:00
James Seibel 864a19b79f Remove useless IServerLevelWrapper.tryGetClientLevelWrapper() 2024-07-04 16:31:04 -05:00
James Seibel 8974323406 Fix Api client level not containing the generic renderer 2024-07-04 16:15:51 -05:00
James Seibel 46c9e0103a Improve world gen timeout warning message 2024-07-04 16:01:33 -05:00
James Seibel 02203466ed Move generic rendering to the level API 2024-07-03 22:38:14 -05:00
James Seibel 87b22ea1cc Add a config to use pre-existing lighting 2024-07-03 20:30:56 -05:00
James Seibel d26327a930 fix max chunk Y position for empty chunks 2024-07-03 19:14:47 -05:00
James Seibel 469d2bdcb7 Add improved beacon logic 2024-07-02 17:51:26 -05:00
James Seibel 5516603a0c Add temporary proof-of-concept beacon rendering 2024-06-30 18:08:55 -05:00
James Seibel b737adc3da Up API version 2.1.0 -> 3.0.0 2024-06-30 16:36:49 -05:00
James Seibel f3a8afeee3 Up version 2.1.2 -> 2.1.3-dev 2024-06-25 19:25:35 -05:00
James Seibel a4501f86e9 Update coreSubProjects 2024-06-25 19:24:13 -05:00
James Seibel 095fff96ff Up version 2.1.1-dev -> 2.1.2 2024-06-24 20:53:45 -05:00
James Seibel a23211d061 Fix NeoForge not running 2024-06-24 20:52:14 -05:00
James Seibel b57ea41686 neoforge build script cleanup 2024-06-23 08:52:55 -05:00
James Seibel 62fb5ffb73 Add DB file lock checking 2024-06-23 08:36:48 -05:00
James Seibel 99c713967b Temporary spongepowered.vanillagradle fix/workaround 2024-06-22 16:21:19 -05:00
James Seibel 9f3de07bd8 Increase default world gen timeout to 3 minutes (from 60 sec) 2024-06-18 07:12:01 -05:00
James Seibel cd74117de3 Fix file handler tooltip 2024-06-17 07:42:35 -05:00
James Seibel e7d7033548 Improve F3 menu logic and visuals 2024-06-15 19:20:25 -05:00
James Seibel 34db7c9dac Lower the default CPU presets 2024-06-15 11:26:05 -05:00
James Seibel 272841aae9 Add a startup low memory warning 2024-06-15 11:05:10 -05:00
James Seibel 389b09a5cd Prevent creating LODs for already processed chunks 2024-06-15 09:42:49 -05:00
James Seibel 84bd876c71 Refactor ChunkWrapper 2024-06-15 08:11:26 -05:00
James Seibel 7e45051ffd Fix more MC version compiles 2024-06-14 22:21:52 -05:00
James Seibel 5570f3a313 Fix some compiling issues 2024-06-14 19:31:21 -05:00
James Seibel f4e71f7012 Add NeoForge 1.21 2024-06-14 19:05:45 -05:00
James Seibel 601d4e6e3a Fix CI not picking up 1.21 2024-06-14 07:40:29 -05:00
James Seibel a12092c1a1 Add fabric 1.21 support 2024-06-14 07:36:25 -05:00
James Seibel 94ad118c5d Minor memory optimization thanks to littlewolf 2024-06-13 07:30:42 -05:00
James Seibel 48e2978438 Fixes #713 (Forge/Neo level unload events not being called) 2024-06-13 07:15:11 -05:00
James Seibel 96b4c1a9e8 Use existing lighting for pre-generated chunks 2024-06-11 20:22:13 -05:00
James Seibel cc4a69c10c Move shared ChunkWrapper code form Main to Core 2024-06-11 18:35:02 -05:00
James Seibel 7293677ddb Batch Generation Environment refactoring 2024-06-10 21:32:14 -05:00
James Seibel 0f2ff20375 Re-arange ChunkLoader 2024-06-10 21:17:57 -05:00
James Seibel 7706240acb Remove OpenGL multithreading 2024-06-08 12:49:17 -05:00
James Seibel 4cf48fd997 Try changing LZMA preset from 4 -> 3 (faster, less compressed)
won't require any lod regeneration since the decompressor is the same
2024-06-08 11:06:42 -05:00
James Seibel 2708c1ee11 Improve config comment spacing 2024-06-08 08:33:41 -05:00
James Seibel ebb0f6ebad Up the manifold version 2023.1.17 -> 2024.1.15 2024-06-08 08:12:03 -05:00
James Seibel 2c263a2549 Up the API version 2.0.0 -> 2.1.0 2024-06-08 08:11:48 -05:00
James Seibel 955524c632 Remove blendium from the list of suggested fabric mods 2024-06-08 08:11:34 -05:00
James Seibel 564e0d3263 Add update branch config "auto" 2024-06-08 08:11:26 -05:00
James Seibel c533b2e8ea Fix config screen blur on 1.20.6 2024-06-08 07:19:50 -05:00
James Seibel 6073d8122a Up the version number 2.1.0-a -> 2.1.1-a-dev 2024-06-07 17:42:46 -05:00
James Seibel 71ca26bba9 Up the version number 2.0.4-a-dev -> 2.1.0-a 2024-05-30 20:14:04 -05:00
James Seibel 75a51be28c remove unused lightmapBindingIndex in DhApiRenderParam 2024-05-27 17:57:58 -05:00
James Seibel a66e4ba157 Potentially fix memory leaks when rendering is disabled 2024-05-27 17:38:00 -05:00
James Seibel f2b9e428d3 Re-add a missing import to fix compiling 2024-05-21 18:24:30 -05:00
James Seibel 5b2497b9d4 Minor MixinMinecraft reformatting 2024-05-21 17:16:36 -05:00
Yeshi0 e78424def4 typo 2024-05-21 20:58:44 +02:00
Yeshi0 e2c94de6e6 fix blurry text on auto update screen 2024-05-21 20:57:12 +02:00
Yeshi0 daa3caf684 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-05-21 15:12:52 +02:00
James Seibel 5991aa42d9 Revert "Add JVM Downgrader (DH now uses Java version Ω)"
This reverts commit af6dca6e5e.
2024-05-21 07:45:33 -05:00
James Seibel ff6a5aae69 Revert "Set the core to use Java version Ω"
This reverts commit c4a9e7a2a7.
2024-05-21 07:45:25 -05:00
James Seibel 80d9b4540b Fix LZ4 in retail MC 2024-05-21 07:43:45 -05:00
James Seibel 4998991ebe Fix option button in 1.20.6 being on the wrong side 2024-05-21 07:12:04 -05:00
James Seibel 14343569fe Fix neoforge config button position 2024-05-21 06:56:27 -05:00
James Seibel be6cc5ff4e Fix some old MC version compiling 2024-05-20 22:19:04 -05:00
James Seibel 0ad3391bea Put config button hide option only in file 2024-05-20 22:15:45 -05:00
James Seibel 582d998e2e Fix GuiHelper rename for MC 1.19.4 and below 2024-05-20 22:12:09 -05:00
James Seibel c00ee26075 Properly shade libraries when using Java version Ω 2024-05-20 22:10:48 -05:00
James Seibel 4c9f70a52f Merge branch 'main' of gitlab.com:jeseibel/distant-horizons 2024-05-20 22:06:59 -05:00
James Seibel 29481bc123 Fix 1.20.6 config page and config button 2024-05-20 22:06:25 -05:00
Cutiepie e274c9e004 Properly shade libraries when using Java version Ω 2024-05-21 11:53:00 +10:00
Yeshi 73988f0308 Merge branch distant-horizons:main into main 2024-05-20 20:06:23 +00:00
Cutiepie c4a9e7a2a7 Set the core to use Java version Ω 2024-05-21 01:56:33 +10:00
Cutiepie af6dca6e5e Add JVM Downgrader (DH now uses Java version Ω) 2024-05-21 01:26:03 +10:00
Yeshi0 a49720a221 fix gradle.properties typos (it was bothering me) 2024-05-20 17:04:10 +02:00
Yeshi0 12a66e70c9 remove unnecessary references to zstd 2024-05-20 17:01:45 +02:00
James Seibel 00d8aa356b minor ClassicConfigGUI reformat 2024-05-20 07:52:46 -05:00
James Seibel d40d94a565 Add probably broken AT OptionsScreen code
Will probably break 1.20.2 and 1.20.4
2024-05-20 07:52:38 -05:00
James Seibel c1f798793e roll back manifold version to fix mystery compiler issues 2024.1.15 -> 2023.1.17 2024-05-19 21:25:26 -05:00
James Seibel 8fe4ad454c update CI JDK 17 -> 21 2024-05-19 20:31:51 -05:00
James Seibel 17022f2df2 Document GuiHelper args 2024-05-19 14:26:27 -05:00
James Seibel 7fa4bc35f6 remove forge from 1.20.6 to fix CI/CD 2024-05-19 14:16:40 -05:00
James Seibel 85df9c5ef4 Add 1.20.6 to the CI build script 2024-05-18 20:32:33 -05:00
James Seibel 45d4f390a9 Fix a bunch of compiler errors 2024-05-18 20:31:15 -05:00
James Seibel e7b60b7562 Fix neoforge 1.20.6 compiling 2024-05-18 11:14:13 -05:00
James Seibel 2615177907 Fix fabric 1.20.6 compiling 2024-05-18 08:07:48 -05:00
James Seibel a83d7e2a26 Replace DhSectionPos with long's for GC performance
What could possibly go wrong?
2024-05-16 22:15:43 -05:00
James Seibel 8a2182e238 Merge branch 'main' of gitlab.com:jeseibel/distant-horizons 2024-05-15 20:40:11 -05:00
James Seibel d45455092c Replace QuadTree iterator linked list with ArrayDeque
Thanks JustALittleWolf!
2024-05-15 07:36:42 -04:00
James Seibel da18469fd4 Fix resource locations in biome/block wrappers 2024-05-15 07:24:08 -04:00
James Seibel 6b5bae9bee Cache block and biome wrapper deserialization values 2024-05-13 20:26:47 -04:00
James Seibel e29a7786e4 Potentially fix LODs not loading in 2024-05-11 16:23:50 -05:00
James Seibel 55a837ca5e Attempt to prevent thread starvation due to world gen 2024-05-10 22:27:29 -05:00
James Seibel 94cba6cf67 Fix compiling 2024-05-10 17:25:52 -05:00
James Seibel 294685df00 Remove indium recommended dependency
A lot of people were reading fabric's warning as a required dependency
2024-05-10 07:05:40 -05:00
James Seibel 2642b7a9a4 disable sql timeout 2024-05-09 23:22:50 -05:00
James Seibel 45594e4e47 Handle missing/corrupted block/biome ID's in the full data 2024-05-09 19:46:33 -05:00
James Seibel 54cd1a2e48 Fix monoliths due to duplicate IDs 2024-05-09 19:45:50 -05:00
James Seibel a20fb982ec Potential fix for NaN multiverse similarity 2024-05-09 07:35:00 -05:00
James Seibel 184d61e637 Up the API version 1.1.0 -> 2.0.0
There were several breaking changes and I forgot to up the major version number appropriately.
2024-05-04 18:17:10 -05:00
James Seibel 06ea56767f Up manifold version 2024.1.12 -> 2024.1.13 2024-05-04 15:37:20 -05:00
James Seibel 1f6e137759 Fix F3 levels not closing with multiverse 2024-05-04 15:36:51 -05:00
James Seibel c7cf7885ae Fix #670 Remove outdated world gen options from tooltip 2024-05-04 09:48:40 -05:00
James Seibel 8e98444887 Update coreSubProjects 2024-05-04 09:23:02 -05:00
James Seibel 9bfe2e8233 Up 1.20 fabric loader versions 0.14.24/0.15.1 -> 0.15.6 2024-05-04 09:22:44 -05:00
James Seibel 21f4adc769 Minor 1.20.6 preprocessor updates 2024-05-02 17:28:30 -05:00
James Seibel 3b10ca5809 Update arch loom 1.5-snapshot -> 1.6-snapshot 2024-05-02 17:27:04 -05:00
James Seibel 6cc8284747 Start adding 1.20.6 2024-05-01 07:45:23 -05:00
James Seibel 6254f7156f Improve nightly build and migration messages 2024-04-30 21:59:17 -05:00
James Seibel 0fa03701a4 Fix debug wireframes rendering on top of LODs 2024-04-30 21:24:08 -05:00
James Seibel 49125cae47 Remove ZStd compression option
Any ZStd data will be automatically deleted and re-generated
2024-04-30 21:17:54 -05:00
James Seibel 3298857d0c Remove references to FastUtil 8.5.13 2024-04-30 20:30:51 -05:00
James Seibel d939cbeb96 remove unused MixinWorldupgrader files 2024-04-30 19:44:44 -05:00
James Seibel 54d254be73 Fix optifine 1.16 support 2024-04-30 19:44:23 -05:00
James Seibel d433fdea62 Fix white grass/water if the biome is null 2024-04-28 17:35:10 -05:00
James Seibel ffa1c54ff3 Fix warning about BiomeWrapper null level on startup 2024-04-28 16:08:08 -05:00
James Seibel 019ac6dec3 Add corrupt data read handling 2024-04-28 15:52:11 -05:00
James Seibel 08d3da47f4 Fix fastutil relocation issues with world gen 2024-04-27 16:46:22 -05:00
James Seibel 348ac2b734 Closes #638 (optifine not rendering on 1.16 + 1.17) 2024-04-27 13:20:18 -05:00
James Seibel fe014b4985 revert b1c6a5c1 2024-04-27 12:56:20 -05:00
James Seibel b7f6f3b900 Remove (hopefully) unused MixinThreadingDetector 2024-04-27 11:55:27 -05:00
James Seibel 3c76ed71d8 Fix some lib shading issues 2024-04-27 11:35:16 -05:00
James Seibel 5de1998913 up the version number 2.0.3 -> 2.0.4 2024-04-26 07:33:48 -05:00
James Seibel 05c0f030cb Fix issues with compressors not appearing at runtime 2024-04-26 07:33:26 -05:00
James Seibel bd85329589 Merge Data_source_rewrite into main 2024-04-26 07:22:03 -05:00
James Seibel f0f864d824 update architectury.loom 1.1 -> 1.3 and gradle 7.6 -> 8.1
gradle update was required by architectury
2024-01-07 21:58:09 -06:00
James Seibel 5abbff42d8 update manifold 2023.1.29 -> 23.1.31 2024-01-07 21:57:13 -06:00
James Seibel 53e2c4f806 update 1.20.4 forge 49.0.3 -> 49.0.14 2024-01-07 21:56:54 -06:00
Rózsa Péter cb4c32e0a9 Fix clone URL
(cherry picked from commit 39b77c783b)
2023-12-25 18:37:02 +10:30
coolGi c620086ed1 Revert "Fixed 1.20.4 forge remap"
This reverts commit 0d565895e2.
2023-12-24 19:03:38 +10:30
coolGi 513cecf318 Revert "Hopefully fixed forge stable on 1.20.4"
This reverts commit 749921b436.
2023-12-24 19:03:31 +10:30
coolGi 749921b436 Hopefully fixed forge stable on 1.20.4 2023-12-23 15:39:20 +10:30
coolGi 0d565895e2 Fixed 1.20.4 forge remap
(cherry picked from commit 28d4cc86a9)
2023-12-23 15:35:12 +10:30
coolGi 2c6849a0fd Updated to new git url
(cherry picked from commit 0d7b0f9fe4)
2023-12-23 15:34:16 +10:30
James Seibel 4d53ec5b64 Fix forge 1.20.4 crashing due to incorrect version number order
(cherry picked from commit d597634ac6)
2023-12-23 15:33:59 +10:30
coolGi 4467af4798 Updated ci to include 1.20.4 2023-12-09 04:17:50 +00:00
coolGi d3f953686a Fixed up screens looking incorrect on 1.20.4 mc versions
(cherry picked from commit 04ddd83519)
2023-12-09 14:41:35 +10:30
coolGi 67901d82c5 Updated to 1.20.3/4
(cherry picked from commit 5b81ca2716)
2023-12-09 14:13:50 +10:30
James Seibel 013fbf638a Merge branch 'main' into 'stable'
merge 2.0.1a

See merge request jeseibel/distant-horizons!45
2023-12-04 01:11:27 +00:00
100 changed files with 3722 additions and 3234 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
# use Eclipse's JDK # use Eclipse's JDK
# The ci should always use a unix(-like) OS to work # The ci should always use a unix(-like) OS to work
image: eclipse-temurin:17 image: eclipse-temurin:21
# all stages need to be defined here # all stages need to be defined here
# TODO: Make stages depend on what is in versionProperties # TODO: Make stages depend on what is in versionProperties
@@ -30,7 +30,7 @@ build:
stage: build stage: build
parallel: parallel:
matrix: matrix:
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4"] - MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21.1"]
script: script:
# this both runs the unit tests and assembles the code # this both runs the unit tests and assembles the code
- ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/; - ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
+52 -18
View File
@@ -2,16 +2,16 @@ plugins {
id "java" id "java"
// Plugin to put dependencies inside our final jar // Plugin to put dependencies inside our final jar
id "com.github.johnrengelman.shadow" version '7.1.2' apply false id "com.github.johnrengelman.shadow" version '8.1.1' apply false
// Plugin to create merged jars // Plugin to create merged jars
id "io.github.pacifistmc.forgix" version "1.2.6" id "io.github.pacifistmc.forgix" version "1.2.9"
// Manifold preprocessor // Manifold preprocessor
id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha" id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha"
// Architectury is used here only as a replacement for forge's own loom // Architectury is used here only as a replacement for forge's own loom
id "dev.architectury.loom" version "1.5-SNAPSHOT" apply false id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false
} }
@@ -106,11 +106,12 @@ subprojects { p ->
apply plugin: "systems.manifold.manifold-gradle-plugin" apply plugin: "systems.manifold.manifold-gradle-plugin"
// Apply forge's loom // Apply forge's loom
if ( if ((findProject(":forge") && p == project(":forge")) ||
(findProject(":forge") && p == project(":forge")) ||
(findProject(":neoforge") && p == project(":neoforge")) (findProject(":neoforge") && p == project(":neoforge"))
) )
{
apply plugin: "dev.architectury.loom" apply plugin: "dev.architectury.loom"
}
// Set the manifold version (may not be required tough) // Set the manifold version (may not be required tough)
@@ -196,17 +197,29 @@ subprojects { p ->
implementation("org.junit.jupiter:junit-jupiter-engine:5.8.2") implementation("org.junit.jupiter:junit-jupiter-engine:5.8.2")
implementation("junit:junit:4.13") implementation("junit:junit:4.13")
// FastUtil
// Note: MC 1.16 uses 8.2.1, and versions after use 8.5.12
// We cannot relocate this library since we call some MC classes that reference it
implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
// Compression
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
forgeShadowMe("org.tukaani:xz:${rootProject.xz_version}") // LZMA
// Sqlite Database
forgeShadowMe("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}")
// NightConfig (includes Toml & Json) // NightConfig (includes Toml & Json)
// needs to be here and in core to prevent compiler errors
forgeShadowMe("com.electronwill.night-config:toml:${rootProject.nightconfig_version}") forgeShadowMe("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
forgeShadowMe("com.electronwill.night-config:json:${rootProject.nightconfig_version}") forgeShadowMe("com.electronwill.night-config:json:${rootProject.nightconfig_version}")
// Compression // SVG (not needed atm)
// needs to be here and in core to prevent compiler errors // forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}")
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
// Netty
// Breaks 1.16.5
//forgeShadowMe("io.netty:netty-all:${rootProject.netty_version}")
// Remember, for lwjgl dependencies that arent included in Minecraft, you need to also need to add it to the ShadowJar thing // 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}") { forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") {
@@ -270,16 +283,32 @@ subprojects { p ->
relocate "com.seibel.distanthorizons.fabriclike", "loaderCommon.${p.name}.com.seibel.distanthorizons.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" def librariesLocation = "DistantHorizons.libraries"
// SVG (not needed atm) // LWJGL
// relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg" // 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 (LZ4) // Compression (LZ4)
relocate "net.jpountz", "${librariesLocation}.jpountz" relocate "net.jpountz", "${librariesLocation}.jpountz"
// Sqlite Database
//At the moment, there is a bug in this library which doesnt allow it to be relocated
// relocate "org.sqlite", "${librariesLocation}.sqlite"
// JOML
if (project.hasProperty("embed_joml") && embed_joml == "true")
relocate "org.joml", "${librariesLocation}.joml"
// NightConfig (includes Toml & Json)
relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig" relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig"
// SVG (not needed atm)
// relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg"
// Netty
relocate "io.netty", "${librariesLocation}.netty"
mergeServiceFiles() mergeServiceFiles()
} }
// Using jar.finalizedBy(shadowJar) causes issues so we do this scuffed bypass // Using jar.finalizedBy(shadowJar) causes issues so we do this scuffed bypass
@@ -297,6 +326,7 @@ subprojects { p ->
"fabric.mod.json", "fabric.mod.json",
"quilt.mod.json", "quilt.mod.json",
"META-INF/mods.toml", "META-INF/mods.toml",
"META-INF/neoforge.mods.toml",
// The mixins for each of the loaders // The mixins for each of the loaders
"DistantHorizons."+ p.name +".fabricLike.mixins.json" "DistantHorizons."+ p.name +".fabricLike.mixins.json"
@@ -401,9 +431,11 @@ subprojects { p ->
jar { jar {
from "LICENSE.txt" from "LICENSE.txt"
manifest { manifest {
attributes 'Implementation-Title': rootProject.mod_name, attributes(
'Implementation-Title': rootProject.mod_name,
'Implementation-Version': rootProject.mod_version, 'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.distanthorizons.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
)
} }
} }
@@ -492,14 +524,16 @@ allprojects { p ->
} }
} }
// Required for ModMenu // VanillaGradle and Mixins in common
maven { url "https://maven.terraformersmc.com/" }
// Required for Mixins & VanillaGradle
maven { url "https://repo.spongepowered.org/maven/" } maven { url "https://repo.spongepowered.org/maven/" }
// Required for Canvas (mod) // Canvas mod
maven { url "https://maven.vram.io/" } maven { url "https://maven.vram.io/" }
// ModMenu mod
maven { url "https://maven.terraformersmc.com/" }
// neoforge
maven { url "https://maven.neoforged.net/releases/" }
// These 3 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource // These 3 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource
flatDir { flatDir {
+9
View File
@@ -1,4 +1,13 @@
// temporary fix for broken spongepowered version
buildscript {
configurations.configureEach {
resolutionStrategy {
force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82'
}
}
}
plugins { plugins {
id "org.spongepowered.gradle.vanilla" version "0.2.1-SNAPSHOT" id "org.spongepowered.gradle.vanilla" version "0.2.1-SNAPSHOT"
} }
@@ -0,0 +1,85 @@
package com.seibel.distanthorizons.common.commonMixins;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class MixinChunkMapCommon
{
public static void onChunkSave(ServerLevel level, ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{
// is this position already being updated?
if (SharedApi.isChunkAtChunkPosAlreadyUpdating(chunk.getPos().x, chunk.getPos().z))
{
return;
}
// is this chunk being saved to disk?
boolean savingChunkToDisk = ci.getReturnValue();
// true means a chunk was saved to disk
if (!savingChunkToDisk)
{
return;
}
// TODO are the following validations necessary since we are checking above if
// the callback return value should state if the chunk was actually saved or not?
// Do we trust it to always be correct?
// corrupt/incomplete chunk validation //
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
// this logic should prevent that from happening
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
{
return;
}
#else
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
{
return;
}
#endif
// biome validation //
// some chunks may be missing their biomes, which cause issues when attempting to save them
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.getBiomes() == null)
{
return;
}
#else
try
{
// this will throw an exception if the biomes aren't set up
chunk.getNoiseBiome(0,0,0);
}
catch (Exception e)
{
return;
}
#endif
// submit the update event
ServerApi.INSTANCE.serverChunkSaveEvent(
new ChunkWrapper(chunk, level, ServerLevelWrapper.getWrapper(level)),
ServerLevelWrapper.getWrapper(level)
);
}
}
@@ -20,13 +20,11 @@
package com.seibel.distanthorizons.common.wrappers; package com.seibel.distanthorizons.common.wrappers;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Mat4f;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@@ -137,41 +135,10 @@ public class McObjectConverter
} }
} }
public static BlockPos Convert(DhBlockPos wrappedPos) public static BlockPos Convert(DhBlockPos wrappedPos) { return new BlockPos(wrappedPos.getX(), wrappedPos.getY(), wrappedPos.getZ()); }
{ public static ChunkPos Convert(DhChunkPos wrappedPos) { return new ChunkPos(wrappedPos.getX(), wrappedPos.getZ()); }
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) public static Direction Convert(EDhDirection lodDirection) { return directions[lodDirection.ordinal()]; }
{ public static EDhDirection Convert(Direction direction) { return lodDirections[direction.ordinal()]; }
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));
}
} }
@@ -32,6 +32,7 @@ import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.level.IDhServerLevel; import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
@@ -54,9 +55,6 @@ import java.util.HashSet;
/** /**
* This handles creating abstract wrapper objects. * This handles creating abstract wrapper objects.
*
* @author James Seibel
* @version 2022-12-5
*/ */
public class WrapperFactory implements IWrapperFactory public class WrapperFactory implements IWrapperFactory
{ {
@@ -81,17 +79,56 @@ public class WrapperFactory implements IWrapperFactory
} }
} }
@Override
public IDhApiBiomeWrapper getBiomeWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException
{
if (!(levelWrapper instanceof ILevelWrapper))
{
throw new ClassCastException("levelWrapper must be returned by DH and of type ["+ILevelWrapper.class.getName()+"].");
}
return BiomeWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper);
}
@Override
public IDhApiBlockStateWrapper getDefaultBlockStateWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException
{
if (!(levelWrapper instanceof ILevelWrapper))
{
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
}
return BlockStateWrapper.deserialize(resourceLocationString, (ILevelWrapper)levelWrapper);
}
@Override @Override
public IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BiomeWrapper.deserialize(str, levelWrapper); } public IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BiomeWrapper.deserialize(str, levelWrapper); }
@Override
public IBiomeWrapper getPlainsBiomeWrapper(ILevelWrapper levelWrapper) // TODO is there a way we could get this without the levelWrapper? it isn't necessary but would clean up the code a bit
{
try
{
return BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, levelWrapper);
}
catch (IOException e)
{
throw new LodUtil.AssertFailureException("Unable to parse plains resource string ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"], error:\n " + e.getMessage());
}
}
@Override @Override
public IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BlockStateWrapper.deserialize(str, levelWrapper); } public IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BlockStateWrapper.deserialize(str, levelWrapper); }
@Override @Override
public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; } public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; }
@Override @Override
public HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); } public HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); }
@Override
public HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); }
@Override
public void resetRendererIgnoredCaveBlocks() { BlockStateWrapper.clearRendererIgnoredCaveBlocks(); }
@Override
public void resetRendererIgnoredBlocksSet() { BlockStateWrapper.clearRendererIgnoredBlocks(); }
/** /**
@@ -118,7 +155,7 @@ public class WrapperFactory implements IWrapperFactory
} }
} }
#if MC_VER <= MC_1_20_4 //#if MC_VER <= MC_1_XX_X
else if (objectArray.length == 2) else if (objectArray.length == 2)
{ {
// correct number of parameters from the API // correct number of parameters from the API
@@ -164,16 +201,7 @@ public class WrapperFactory implements IWrapperFactory
{ {
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray)); throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
} }
#else //#endif
// 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, * Note: when this is updated for different MC versions,
@@ -183,16 +211,13 @@ public class WrapperFactory implements IWrapperFactory
{ {
String[] expectedClassNames; String[] expectedClassNames;
#if MC_VER <= MC_1_20_4 //#if MC_VER <= MC_1_XX_X
expectedClassNames = new String[] expectedClassNames = new String[]
{ {
ChunkAccess.class.getName(), ChunkAccess.class.getName(),
ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName() ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName()
}; };
#else //#endif
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif
return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray); return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray);
} }
@@ -210,7 +235,7 @@ public class WrapperFactory implements IWrapperFactory
// confirm the API level wrapper is also a Core wrapper // confirm the API level wrapper is also a Core wrapper
if (!(levelWrapper instanceof ILevelWrapper)) if (!(levelWrapper instanceof ILevelWrapper))
{ {
throw new ClassCastException("Unable to cast... only DH provided IDhApiLevelWrapper's can be used."); // TODO throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
} }
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper; ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
@@ -231,7 +256,7 @@ public class WrapperFactory implements IWrapperFactory
Biome biome = (Biome) objectArray[0]; Biome biome = (Biome) objectArray[0];
return BiomeWrapper.getBiomeWrapper(biome, coreLevelWrapper); return BiomeWrapper.getBiomeWrapper(biome, coreLevelWrapper);
#elif MC_VER <= MC_1_20_4 #else
if (!(objectArray[0] instanceof Holder) || !(((Holder<?>) objectArray[0]).value() instanceof Biome)) if (!(objectArray[0] instanceof Holder) || !(((Holder<?>) objectArray[0]).value() instanceof Biome))
{ {
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray)); throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
@@ -239,9 +264,6 @@ public class WrapperFactory implements IWrapperFactory
Holder<Biome> biomeHolder = (Holder<Biome>) objectArray[0]; Holder<Biome> biomeHolder = (Holder<Biome>) objectArray[0];
return BiomeWrapper.getBiomeWrapper(biomeHolder, coreLevelWrapper); return BiomeWrapper.getBiomeWrapper(biomeHolder, coreLevelWrapper);
#else
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
not implemented for this version of Minecraft!
#endif #endif
} }
/** /**
@@ -254,11 +276,8 @@ public class WrapperFactory implements IWrapperFactory
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
expectedClassNames = new String[] { Biome.class.getName() }; expectedClassNames = new String[] { Biome.class.getName() };
#elif MC_VER <= MC_1_20_4
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
#else #else
// See preprocessor comment in createChunkWrapper() for full documentation expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
not implemented for this version of Minecraft!
#endif #endif
return createWrapperErrorMessage("Biome wrapper", expectedClassNames, objectArray); return createWrapperErrorMessage("Biome wrapper", expectedClassNames, objectArray);
@@ -269,13 +288,13 @@ public class WrapperFactory implements IWrapperFactory
// confirm the API level wrapper is also a Core wrapper // confirm the API level wrapper is also a Core wrapper
if (!(levelWrapper instanceof ILevelWrapper)) if (!(levelWrapper instanceof ILevelWrapper))
{ {
throw new ClassCastException("Unable to cast... only DH provided IDhApiLevelWrapper's can be used."); // TODO throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
} }
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper; ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
#if MC_VER <= MC_1_20_4 //#if MC_VER <= MC_1_XX_X
if (objectArray.length != 1) if (objectArray.length != 1)
{ {
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray)); throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
@@ -287,10 +306,7 @@ public class WrapperFactory implements IWrapperFactory
BlockState blockState = (BlockState) objectArray[0]; BlockState blockState = (BlockState) objectArray[0];
return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper); return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper);
#else //#endif
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
not implemented for this version of Minecraft!
#endif
} }
/** /**
* Note: when this is updated for different MC versions, * Note: when this is updated for different MC versions,
@@ -302,11 +318,8 @@ public class WrapperFactory implements IWrapperFactory
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
expectedClassNames = new String[] { Biome.class.getName() }; expectedClassNames = new String[] { Biome.class.getName() };
#elif MC_VER <= MC_1_20_4
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
#else #else
// See preprocessor comment in createChunkWrapper() for full documentation expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
not implemented for this version of Minecraft!
#endif #endif
return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray); return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray);
@@ -32,8 +32,6 @@ import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import net.minecraft.client.Minecraft;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2 #elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
@@ -66,9 +64,13 @@ public class BiomeWrapper implements IBiomeWrapper
public static final ConcurrentMap<Holder<Biome>, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>(); public static final ConcurrentMap<Holder<Biome>, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>();
#endif #endif
public static final String EMPTY_STRING = "EMPTY"; public static final ConcurrentHashMap<String, BiomeWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
public static final String EMPTY_BIOME_STRING = "EMPTY";
public static final BiomeWrapper EMPTY_WRAPPER = new BiomeWrapper(null, null); public static final BiomeWrapper EMPTY_WRAPPER = new BiomeWrapper(null, null);
public static final String PLAINS_RESOURCE_LOCATION_STRING = "minecraft:plains";
/** keep track of broken biomes so we don't log every time */ /** keep track of broken biomes so we don't log every time */
private static final HashSet<String> brokenResourceLocationStrings = new HashSet<>(); private static final HashSet<String> brokenResourceLocationStrings = new HashSet<>();
@@ -131,7 +133,7 @@ public class BiomeWrapper implements IBiomeWrapper
private BiomeWrapper() private BiomeWrapper()
{ {
this.biome = null; this.biome = null;
this.serialString = EMPTY_STRING; this.serialString = EMPTY_BIOME_STRING;
this.hashCode = Objects.hash(this.serialString); this.hashCode = Objects.hash(this.serialString);
} }
@@ -146,7 +148,7 @@ public class BiomeWrapper implements IBiomeWrapper
{ {
if (this == EMPTY_WRAPPER) if (this == EMPTY_WRAPPER)
{ {
return EMPTY_STRING; return EMPTY_BIOME_STRING;
} }
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
@@ -193,22 +195,23 @@ public class BiomeWrapper implements IBiomeWrapper
public String serialize(ILevelWrapper levelWrapper) public String serialize(ILevelWrapper levelWrapper)
{ {
if (this.serialString != null) if (this.biome == null)
{ {
return this.serialString; return EMPTY_BIOME_STRING;
} }
// we can't generate a serial string if the level is null // we can't generate a serial string if the level is null
if (levelWrapper == null) if (levelWrapper == null)
{ {
if (!emptyLevelSerializeFailLogged) if (!emptyLevelSerializeFailLogged)
{ {
emptyLevelSerializeFailLogged = true; emptyLevelSerializeFailLogged = true;
LOGGER.warn("Unable to serialize biome: ["+this.biome+"] because the passed in level wrapper is null. Future errors won't be logged."); LOGGER.warn("Unable to serialize biome: [" + this.biome + "] because the passed in level wrapper is null. Future errors of this type won't be logged.");
} }
return EMPTY_STRING; return EMPTY_BIOME_STRING;
} }
@@ -248,14 +251,18 @@ public class BiomeWrapper implements IBiomeWrapper
return this.serialString; return this.serialString;
} }
// TODO would it be worth while to cache these objects in a ConcurrentHashMap<string, IBiomeWrapper>?
public static IBiomeWrapper deserialize(String resourceLocationString, ILevelWrapper levelWrapper) throws IOException public static IBiomeWrapper deserialize(String resourceLocationString, ILevelWrapper levelWrapper) throws IOException
{ {
if (resourceLocationString.equals(EMPTY_STRING)) // we need the final string for the concurrent hash map later
final String finalResourceStateString = resourceLocationString;
if (resourceLocationString.equals(EMPTY_BIOME_STRING))
{ {
if (!emptyStringWarningLogged) if (!emptyStringWarningLogged)
{ {
emptyStringWarningLogged = true; emptyStringWarningLogged = true;
LOGGER.warn("[" + EMPTY_STRING + "] biome string deserialized. This may mean the level was null when a save was attempted, a file saving error, or a biome saving error. Future errors will not be logged."); LOGGER.warn("[" + EMPTY_BIOME_STRING + "] biome string deserialized. This may mean the level was null when a save was attempted, a file saving error, or a biome saving error. Future errors will not be logged.");
} }
return EMPTY_WRAPPER; return EMPTY_WRAPPER;
} }
@@ -265,15 +272,37 @@ public class BiomeWrapper implements IBiomeWrapper
return EMPTY_WRAPPER; return EMPTY_WRAPPER;
} }
if (WRAPPER_BY_RESOURCE_LOCATION.containsKey(finalResourceStateString))
{
return WRAPPER_BY_RESOURCE_LOCATION.get(finalResourceStateString);
}
// if no wrapper is found, default to the empty wrapper
BiomeWrapper foundWrapper = EMPTY_WRAPPER;
try
{
// parse the resource location // parse the resource location
int separatorIndex = resourceLocationString.indexOf(":"); int separatorIndex = resourceLocationString.indexOf(":");
if (separatorIndex == -1) if (separatorIndex == -1)
{ {
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "]."); throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
} }
ResourceLocation resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
ResourceLocation resourceLocation;
try
{
#if MC_VER < MC_1_21_1
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
#else
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
#endif
}
catch (Exception e)
{
throw new IOException("No Resource Location found for the string: [" + resourceLocationString + "] Error: [" + e.getMessage() + "].");
}
try try
@@ -307,11 +336,18 @@ public class BiomeWrapper implements IBiomeWrapper
return EMPTY_WRAPPER; return EMPTY_WRAPPER;
} }
return getBiomeWrapper(biome, levelWrapper);
foundWrapper = (BiomeWrapper) getBiomeWrapper(biome, levelWrapper);
return foundWrapper;
} }
catch (Exception e) catch (Exception e)
{ {
throw new IOException("Failed to deserialize the string [" + resourceLocationString + "] into a BiomeWrapper: " + e.getMessage(), e); throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BiomeWrapper: " + e.getMessage(), e);
}
}
finally
{
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
} }
} }
@@ -19,20 +19,28 @@
package com.seibel.distanthorizons.common.wrappers.block; package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags; import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.BeaconBeamBlock;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.awt.*;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
@@ -65,15 +73,15 @@ public class BlockStateWrapper implements IBlockStateWrapper
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>(); public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
public static final String AIR_STRING = "AIR"; public static final String AIR_STRING = "AIR";
public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null); public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null);
public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt"; public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt";
// 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; public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
/** keep track of broken blocks so we don't log every time */ /** keep track of broken blocks so we don't log every time */
private static final HashSet<ResourceLocation> BrokenResourceLocations = new HashSet<>(); private static final HashSet<ResourceLocation> BrokenResourceLocations = new HashSet<>();
@@ -88,11 +96,17 @@ public class BlockStateWrapper implements IBlockStateWrapper
private final int hashCode; private final int hashCode;
/** /**
* Cached opacity value, -1 if not populated. <br> * Cached opacity value, -1 if not populated. <br>
* Should be between {@link IBlockStateWrapper#FULLY_OPAQUE} and {@link IBlockStateWrapper#FULLY_OPAQUE} * Should be between {@link LodUtil#BLOCK_FULLY_OPAQUE} and {@link LodUtil#BLOCK_FULLY_OPAQUE}
*/ */
private int opacity = -1; private int opacity = -1;
/** used by the Iris shader mod to determine how each LOD should be rendered */ /** used by the Iris shader mod to determine how each LOD should be rendered */
private byte irisBlockMaterialId = 0; private byte blockMaterialId = 0;
private final boolean isBeaconBlock;
private final boolean isBeaconBaseBlock;
/** null if this block can't tint beacons */
private final Color beaconTintColor;
private final Color mapColor;
@@ -125,16 +139,68 @@ public class BlockStateWrapper implements IBlockStateWrapper
this.blockState = blockState; this.blockState = blockState;
this.serialString = this.serialize(levelWrapper); this.serialString = this.serialize(levelWrapper);
this.hashCode = Objects.hash(this.serialString); this.hashCode = Objects.hash(this.serialString);
this.irisBlockMaterialId = this.calculateIrisBlockMaterialId(); this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index;
//LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"] with material ID ["+this.irisBlockMaterialId+"]"); // beacon blocks
String lowercaseSerial = this.serialString.toLowerCase();
boolean isBeaconBaseBlock = false;
for (int i = 0; i < LodUtil.BEACON_BASE_BLOCK_NAME_LIST.size(); i++)
{
String baseBlockName = LodUtil.BEACON_BASE_BLOCK_NAME_LIST.get(i);
if (lowercaseSerial.contains(baseBlockName))
{
isBeaconBaseBlock = true;
break;
}
}
this.isBeaconBaseBlock = isBeaconBaseBlock;
this.isBeaconBlock = lowercaseSerial.contains("minecraft:beacon");
// beacon tint color
Color beaconTintColor = null;
if (this.blockState != null
// beacon blocks also show up here, but since they block the beacon beam we don't want their color
&& !this.isBeaconBlock)
{
Block block = this.blockState.getBlock();
if (block instanceof BeaconBeamBlock)
{
int colorInt;
#if MC_VER <= MC_1_19_4
colorInt = ((BeaconBeamBlock) block).getColor().getMaterialColor().col;
#else
colorInt = ((BeaconBeamBlock) block).getColor().getMapColor().col;
#endif
beaconTintColor = ColorUtil.toColorObjRGB(colorInt);
}
}
this.beaconTintColor = beaconTintColor;
int mcColor = 0;
if (this.blockState != null)
{
#if MC_VER < MC_1_20_1
mcColor = this.blockState.getMaterial().getColor().col;
#else
mcColor = this.blockState.getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).col;
#endif
this.mapColor = ColorUtil.toColorObjRGB(mcColor);
}
else
{
this.mapColor = new Color(0,0,0,0);
}
//LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"] with material ID ["+this.EDhApiBlockMaterialId+"]");
} }
//================// //====================//
// helper methods // // LodBuilder methods //
//================// //====================//
/** /**
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one. * Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
@@ -148,37 +214,104 @@ public class BlockStateWrapper implements IBlockStateWrapper
return rendererIgnoredBlocks; return rendererIgnoredBlocks;
} }
HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredBlocks = getBlockWrappers(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredBlocks;
}
/**
* Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one.
* This way the method won't accidentally be called before the deserialization can be completed.
*/
public static HashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper)
{
// use the cached version if possible
if (rendererIgnoredCaveBlocks != null)
{
return rendererIgnoredCaveBlocks;
}
HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredCaveBlocks = getBlockWrappers(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredCaveBlocks;
}
public static void clearRendererIgnoredBlocks() { rendererIgnoredBlocks = null; }
public static void clearRendererIgnoredCaveBlocks() { rendererIgnoredCaveBlocks = null; }
// lod builder helpers //
private static HashSet<IBlockStateWrapper> getBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
{
// get the base blocks
HashSet<String> blockStringList = new HashSet<>();
if (baseResourceLocations != null)
{
blockStringList.addAll(baseResourceLocations);
}
// get the config blocks
String ignoreBlockCsv = config.get();
if (ignoreBlockCsv != null)
{
blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(",")));
}
return getBlockWrappers(blockStringList, levelWrapper);
}
private static HashSet<IBlockStateWrapper> getBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
{
// deserialize each of the given resource locations // deserialize each of the given resource locations
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>(); HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
for (String blockResourceLocation : RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS) for (String blockResourceLocation : blockResourceLocationSet)
{ {
try try
{ {
BlockStateWrapper DefaultBlockStateToIgnore = (BlockStateWrapper) deserialize(blockResourceLocation, levelWrapper); if (blockResourceLocation == null)
blockStateWrappers.add(DefaultBlockStateToIgnore); {
// shouldn't happen, but just in case
if (DefaultBlockStateToIgnore == AIR) continue;
}
String cleanedResourceLocation = blockResourceLocation.trim();
if (cleanedResourceLocation.length() == 0)
{ {
continue; continue;
} }
BlockStateWrapper defaultBlockStateToIgnore = (BlockStateWrapper) deserialize(cleanedResourceLocation, levelWrapper);
blockStateWrappers.add(defaultBlockStateToIgnore);
if (defaultBlockStateToIgnore != AIR)
{
// add all possible blockstates (to account for light blocks with different light values and such) // add all possible blockstates (to account for light blocks with different light values and such)
List<BlockState> blockStatesToIgnore = DefaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates(); List<BlockState> blockStatesToIgnore = defaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
for (BlockState blockState : blockStatesToIgnore) for (BlockState blockState : blockStatesToIgnore)
{ {
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper); BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper);
blockStateWrappers.add(newBlockToIgnore); blockStateWrappers.add(newBlockToIgnore);
} }
} }
else
{
// air is a special case so it must be handled separately
blockStateWrappers.add(AIR);
}
}
catch (IOException e) catch (IOException e)
{ {
LOGGER.warn("Unable to deserialize rendererIgnoredBlock with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e); LOGGER.warn("Unable to deserialize block with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
}
catch (Exception e)
{
LOGGER.warn("Unexpected error deserializing block with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e);
} }
} }
rendererIgnoredBlocks = blockStateWrappers; return blockStateWrappers;
return rendererIgnoredBlocks;
} }
@@ -201,23 +334,23 @@ public class BlockStateWrapper implements IBlockStateWrapper
int opacity; int opacity;
if (this.isAir()) if (this.isAir())
{ {
opacity = FULLY_TRANSPARENT; opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
} }
else if (this.isLiquid() && !this.blockState.canOcclude()) else if (this.isLiquid() && !this.blockState.canOcclude())
{ {
// probably not a waterlogged block (which should block light entirely) // probably not a waterlogged block (which should block light entirely)
// +1 to indicate that the block is translucent (in between transparent and opaque) // +1 to indicate that the block is translucent (in between transparent and opaque)
opacity = FULLY_TRANSPARENT + 1; opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1;
} }
else if (this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) else if (this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO))
{ {
opacity = FULLY_TRANSPARENT; opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
} }
else else
{ {
// default for all other blocks // default for all other blocks
opacity = FULLY_OPAQUE; opacity = LodUtil.BLOCK_FULLY_OPAQUE;
} }
@@ -286,7 +419,19 @@ public class BlockStateWrapper implements IBlockStateWrapper
} }
@Override @Override
public byte getIrisBlockMaterialId() { return this.irisBlockMaterialId; } public boolean isBeaconBlock() { return this.isBeaconBlock; }
@Override
public boolean isBeaconBaseBlock() { return this.isBeaconBaseBlock; }
@Override
public boolean isBeaconTintBlock() { return this.beaconTintColor != null; }
@Override
public Color getMapColor() { return this.mapColor; }
@Override
public Color getBeaconTintColor() { return this.beaconTintColor; }
@Override
public byte getMaterialId() { return this.blockMaterialId; }
@Override @Override
public String toString() { return this.getSerialString(); } public String toString() { return this.getSerialString(); }
@@ -339,13 +484,26 @@ public class BlockStateWrapper implements IBlockStateWrapper
/** will only work if a level is currently loaded */ /** will only work if a level is currently loaded */
public static IBlockStateWrapper deserialize(String resourceStateString, ILevelWrapper levelWrapper) throws IOException public static IBlockStateWrapper deserialize(String resourceStateString, ILevelWrapper levelWrapper) throws IOException
{ {
if (resourceStateString.equals(AIR_STRING) || resourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case // we need the final string for the concurrent hash map later
final String finalResourceStateString = resourceStateString;
if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case
{ {
return AIR; return AIR;
} }
// attempt to use the existing wrapper
if (WRAPPER_BY_RESOURCE_LOCATION.containsKey(finalResourceStateString))
{
return WRAPPER_BY_RESOURCE_LOCATION.get(finalResourceStateString);
}
// if no wrapper is found, default to air
BlockStateWrapper foundWrapper = AIR;
try
{
// try to parse out the BlockState // try to parse out the BlockState
String blockStatePropertiesString = null; // will be null if no properties were included String blockStatePropertiesString = null; // will be null if no properties were included
int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR); int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR);
@@ -357,12 +515,25 @@ public class BlockStateWrapper implements IBlockStateWrapper
} }
// parse the resource location // parse the resource location
int resourceSeparatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR); int separatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR);
if (resourceSeparatorIndex == -1) if (separatorIndex == -1)
{ {
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "]."); throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
} }
ResourceLocation resourceLocation = new ResourceLocation(resourceStateString.substring(0, resourceSeparatorIndex), resourceStateString.substring(resourceSeparatorIndex + 1));
ResourceLocation resourceLocation;
try
{
#if MC_VER < MC_1_21_1
resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
#else
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
#endif
}
catch (Exception e)
{
throw new IOException("No Resource Location found for the string: [" + resourceStateString + "] Error: [" + e.getMessage() + "].");
}
@@ -396,6 +567,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
BrokenResourceLocations.add(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."); 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; return AIR;
} }
@@ -431,11 +603,20 @@ public class BlockStateWrapper implements IBlockStateWrapper
foundState = block.defaultBlockState(); foundState = block.defaultBlockState();
} }
return new BlockStateWrapper(foundState, levelWrapper);
foundWrapper = new BlockStateWrapper(foundState, levelWrapper);
return foundWrapper;
} }
catch (Exception e) catch (Exception e)
{ {
throw new IOException("Failed to deserialize the string [" + resourceStateString + "] into a BlockStateWrapper: " + e.getMessage(), e); throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BlockStateWrapper: " + e.getMessage(), e);
}
}
finally
{
// put if absent in case two threads deserialize at the same time
// unfortunately we can't put everything in a computeIfAbsent() since we also throw exceptions
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
} }
} }
@@ -475,11 +656,11 @@ public class BlockStateWrapper implements IBlockStateWrapper
// Iris methods // // Iris methods //
//==============// //==============//
private byte calculateIrisBlockMaterialId() private EDhApiBlockMaterial calculateEDhApiBlockMaterialId()
{ {
if (this.blockState == null) if (this.blockState == null)
{ {
return IrisBlockMaterial.AIR; return EDhApiBlockMaterial.AIR;
} }
@@ -492,15 +673,15 @@ public class BlockStateWrapper implements IBlockStateWrapper
|| serialString.contains("mushroom") || serialString.contains("mushroom")
) )
{ {
return IrisBlockMaterial.LEAVES; return EDhApiBlockMaterial.LEAVES;
} }
else if (this.blockState.is(Blocks.LAVA)) else if (this.blockState.is(Blocks.LAVA))
{ {
return IrisBlockMaterial.LAVA; return EDhApiBlockMaterial.LAVA;
} }
else if (this.isLiquid() || this.blockState.is(Blocks.WATER)) else if (this.isLiquid() || this.blockState.is(Blocks.WATER))
{ {
return IrisBlockMaterial.WATER; return EDhApiBlockMaterial.WATER;
} }
else if (this.blockState.getSoundType() == SoundType.WOOD else if (this.blockState.getSoundType() == SoundType.WOOD
|| serialString.contains("root") || serialString.contains("root")
@@ -509,7 +690,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
#endif #endif
) )
{ {
return IrisBlockMaterial.WOOD; return EDhApiBlockMaterial.WOOD;
} }
else if (this.blockState.getSoundType() == SoundType.METAL else if (this.blockState.getSoundType() == SoundType.METAL
#if MC_VER >= MC_1_19_2 #if MC_VER >= MC_1_19_2
@@ -521,11 +702,11 @@ public class BlockStateWrapper implements IBlockStateWrapper
#endif #endif
) )
{ {
return IrisBlockMaterial.METAL; return EDhApiBlockMaterial.METAL;
} }
else if (serialString.contains("grass_block")) else if (serialString.contains("grass_block"))
{ {
return IrisBlockMaterial.GRASS; return EDhApiBlockMaterial.GRASS;
} }
else if ( else if (
serialString.contains("dirt") serialString.contains("dirt")
@@ -535,7 +716,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
|| serialString.contains("mycelium") || serialString.contains("mycelium")
) )
{ {
return IrisBlockMaterial.DIRT; return EDhApiBlockMaterial.DIRT;
} }
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
else if (this.blockState.getSoundType() == SoundType.DEEPSLATE else if (this.blockState.getSoundType() == SoundType.DEEPSLATE
@@ -544,37 +725,37 @@ public class BlockStateWrapper implements IBlockStateWrapper
|| this.blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE || this.blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE
|| serialString.contains("deepslate") ) || serialString.contains("deepslate") )
{ {
return IrisBlockMaterial.DEEPSLATE; return EDhApiBlockMaterial.DEEPSLATE;
} }
#endif #endif
else if (this.serialString.contains("snow")) else if (this.serialString.contains("snow"))
{ {
return IrisBlockMaterial.SNOW; return EDhApiBlockMaterial.SNOW;
} }
else if (serialString.contains("sand")) else if (serialString.contains("sand"))
{ {
return IrisBlockMaterial.SAND; return EDhApiBlockMaterial.SAND;
} }
else if (serialString.contains("terracotta")) else if (serialString.contains("terracotta"))
{ {
return IrisBlockMaterial.TERRACOTTA; return EDhApiBlockMaterial.TERRACOTTA;
} }
else if (this.blockState.is(BlockTags.BASE_STONE_NETHER)) else if (this.blockState.is(BlockTags.BASE_STONE_NETHER))
{ {
return IrisBlockMaterial.NETHER_STONE; return EDhApiBlockMaterial.NETHER_STONE;
} }
else if (serialString.contains("stone") else if (serialString.contains("stone")
|| serialString.contains("ore")) || serialString.contains("ore"))
{ {
return IrisBlockMaterial.STONE; return EDhApiBlockMaterial.STONE;
} }
else if (this.blockState.getLightEmission() > 0) else if (this.blockState.getLightEmission() > 0)
{ {
return IrisBlockMaterial.ILLUMINATED; return EDhApiBlockMaterial.ILLUMINATED;
} }
else else
{ {
return IrisBlockMaterial.UNKOWN; return EDhApiBlockMaterial.UNKNOWN;
} }
} }
@@ -0,0 +1,485 @@
/*
* 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.wrappers.McObjectConverter;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.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.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 MC_VER >= 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;
import java.util.concurrent.locks.ReentrantLock;
/**
* This stores and calculates the colors
* the given {@link BlockState} should have based
* on the given {@link IClientLevelWrapper}.
*
* @see ColorUtil
*/
public class ClientBlockStateColorCache
{
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<>();
/**
* Methods using MC's "RandomSource" object aren't thread safe <br>
* so we need to put locks around that logic. <br>
* specifically:
* <code>
* getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM)
* </code>
*/
private static final ReentrantLock RESOLVE_LOCK = new ReentrantLock();
/** This is the order each direction on a block is processed when attempting to get the texture/color */
private static final Direction[] COLOR_RESOLUTION_DIRECTION_ORDER = { Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN };
private static final int FLOWER_COLOR_SCALE = 5;
#if MC_VER < MC_1_19_2
private static final Random RANDOM = new Random(0);
#else
/** Note: this object isn't thread safe and must be put in a lock */
private static final RandomSource RANDOM = RandomSource.create();
#endif
private final IClientLevelWrapper levelWrapper;
private final BlockState blockState;
private final LevelReader level;
private boolean isColorResolved = false;
private int baseColor = 0;
private boolean needShade = true;
private boolean needPostTinting = false;
private int tintIndex = 0;
//===========//
// constants //
//===========//
private static final int MIN_SRGB_BITS = 0x39000000; // 2^(-13)
private static final int MAX_SRGB_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
private static final float MIN_SRGB_BOUND = Float.intBitsToFloat(MIN_SRGB_BITS);
private static final float MAX_SRGB_BOUND = Float.intBitsToFloat(MAX_SRGB_BITS);
private static final int[] linearToSrgbTable = new int[]
{
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
};
private static final float[] srgbToLinearTable = new float[]
{
0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f,
0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f,
0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
};
//=============//
// constructor //
//=============//
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel)
{
this.blockState = blockState;
this.levelWrapper = samplingLevel;
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
this.resolveColors();
}
//===================//
// color calculation //
//===================//
private void resolveColors()
{
if (this.isColorResolved)
{
return;
}
try
{
// getQuads() isn't thread safe so we need to put this logic in a lock
RESOLVE_LOCK.lock();
if (this.blockState.getFluidState().isEmpty())
{
// look for the first non-empty direction
List<BakedQuad> quads = null;
for (Direction direction : COLOR_RESOLUTION_DIRECTION_ORDER)
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM);
if (quads != null && !quads.isEmpty()
&& !(
this.blockState.getBlock() instanceof RotatedPillarBlock
&& direction == Direction.UP
)
)
{
break;
}
}
if (quads == null || quads.isEmpty())
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(this.blockState).getQuads(this.blockState, null, RANDOM);
}
if (quads != null && !quads.isEmpty())
{
this.needPostTinting = quads.get(0).isTinted();
this.needShade = quads.get(0).isShade();
this.tintIndex = quads.get(0).getTintIndex();
this.baseColor = calculateColorFromTexture(
#if MC_VER < MC_1_17_1 quads.get(0).sprite,
#else quads.get(0).getSprite(), #endif
ColorMode.getColorMode(this.blockState.getBlock()));
}
else
{
// Backup method.
this.needPostTinting = false;
this.needShade = false;
this.tintIndex = 0;
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
}
}
else
{
// Liquid Block
this.needPostTinting = true;
this.needShade = false;
this.tintIndex = 0;
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
}
this.isColorResolved = true;
}
finally
{
RESOLVE_LOCK.unlock();
}
}
//TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
{
int count = 0;
int alpha = 0;
double red = 0;
double green = 0;
double blue = 0;
int tempColor;
// don't render Chiseled blocks.
// Since ColorMode is set per block, you only need to check this once.
if (colorMode != ColorMode.Chisel)
{
// textures normally use u and v instead of x and y
for (int v = 0; v < getTextureHeight(texture); v++)
{
for (int u = 0; u < getTextureWidth(texture); u++)
{
//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);
int r = (tempColor & 0x000000FF);
int g = (tempColor & 0x0000FF00) >>> 8;
int b = (tempColor & 0x00FF0000) >>> 16;
int a = (tempColor & 0xFF000000) >>> 24;
int scale = 1;
if (colorMode == ColorMode.Leaves)
{
//switch (//FIXME add config option)
// case BLACK:
// a = 255; //simulate black background of fast leaves
// break;
// case IGNORE:
if (a == 0) {
continue; //same long grass
}
else
{
a = 255; //just in case there are semi transparent pixels
}
// break;
// case TRANSPARENT:
// break; //do nothing, let it count towards transparency
}
else if (a == 0 && colorMode != ColorMode.Glass)
{
continue;
}
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r))
{
scale = FLOWER_COLOR_SCALE;
}
count += scale;
//apparently alpha is linear
alpha += a * scale;
//gamma correction is complicated
red += srgbToLinearTable[r] * a * scale;
green += srgbToLinearTable[g] * a * scale;
blue += srgbToLinearTable[b] * a * scale;
}
}
}
if (count == 0)
{
// this block is entirely transparent
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
}
else
{
// determine the average color
tempColor = ColorUtil.rgbToInt(
alpha / count,
linearToSrgb((float) (red / (double) alpha)),
linearToSrgb((float) (green / (double) alpha)),
linearToSrgb((float) (blue / (double) alpha)));
}
//check if not missing texture
if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182))
{
//make it not render at all
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
}
return tempColor;
}
private static int getTextureWidth(TextureAtlasSprite texture)
{
#if MC_VER < MC_1_19_4
return texture.getWidth();
#else
return texture.contents().width();
#endif
}
private static int getTextureHeight(TextureAtlasSprite texture)
{
#if MC_VER < MC_1_19_4
return texture.getHeight();
#else
return texture.contents().height();
#endif
}
/**
* This method was suggested by IMS from the Iris/Sodium team.
* That's where the numbers and code are based.
*/
private static int linearToSrgb(float c)
{
if (!(c > MIN_SRGB_BOUND)) {
c = MIN_SRGB_BOUND;
}
if (c > MAX_SRGB_BOUND) {
c = MAX_SRGB_BOUND;
}
int inputBits = Float.floatToRawIntBits(c);
int entry = linearToSrgbTable[((inputBits - MIN_SRGB_BITS) >> 20)];
int bias = (entry >>> 16) << 9;
int scale = entry & 0xffff;
int t = (inputBits >>> 12) & 0xff;
return (bias + (scale * t)) >>> 16;
}
//===============//
// public getter //
//===============//
public int getColor(BiomeWrapper biome, DhBlockPos pos)
{
// 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, this.levelWrapper), 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;
}
}
//================//
// helper classes //
//================//
enum ColorMode
{
Default,
Flower,
Leaves,
Chisel,
Glass;
static ColorMode getColorMode(Block block)
{
if (block instanceof LeavesBlock) return Leaves;
if (block instanceof FlowerBlock) return Flower;
if (block.toString().contains("glass")) return Glass;
if (block.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
return Default;
}
}
}
@@ -19,14 +19,20 @@
package com.seibel.distanthorizons.common.wrappers.block; package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.*; import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.FluidState;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
@@ -35,18 +41,54 @@ import net.minecraft.core.Holder;
public class TintWithoutLevelOverrider implements BlockAndTintGetter public class TintWithoutLevelOverrider implements BlockAndTintGetter
{ {
final BiomeWrapper biome; /**
* This will only ever be null if there was an issue with {@link IClientLevelWrapper#getPlainsBiomeWrapper()}
* but {@link Nullable} is there just in case.
*/
@Nullable
private final Biome biome;
public TintWithoutLevelOverrider(BiomeWrapper biome)
public TintWithoutLevelOverrider(BiomeWrapper biomeWrapper, IClientLevelWrapper clientLevelWrapper)
{ {
this.biome = biome; // try to get the wrapped biome
Biome unwrappedBiome = null;
if (biomeWrapper.biome != null)
{
unwrappedBiome = unwrap(biomeWrapper.biome);
} }
if(unwrappedBiome == null)
{
// we are looking at the empty biome wrapper, try using plains as a backup
BiomeWrapper plainsBiomeWrapper = ((BiomeWrapper) clientLevelWrapper.getPlainsBiomeWrapper());
if (plainsBiomeWrapper != null)
{
unwrappedBiome = unwrap(plainsBiomeWrapper.biome);
}
}
this.biome = unwrappedBiome;
}
@Override @Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
{ {
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ()); if (this.biome != null)
{
return colorResolver.getColor(this.biome, blockPos.getX(), blockPos.getZ());
} }
private Biome _unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome) else
{
// hopefully unneeded debug color
return ColorUtil.CYAN;
}
}
private static Biome unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{ {
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
return biome.value(); return biome.value();
@@ -55,30 +97,36 @@ public class TintWithoutLevelOverrider implements BlockAndTintGetter
#endif #endif
} }
//================//
// unused methods //
//================//
@Override @Override
public float getShade(Direction direction, boolean shade) public float getShade(@NotNull Direction direction, boolean shade)
{ {
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only."); throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
} }
@Override @Override
public LevelLightEngine getLightEngine() public @NotNull LevelLightEngine getLightEngine()
{ {
throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only."); throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only.");
} }
@Nullable @Nullable
@Override @Override
public BlockEntity getBlockEntity(BlockPos pos) public BlockEntity getBlockEntity(@NotNull BlockPos pos)
{ {
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only."); throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
} }
@Override @Override
public BlockState getBlockState(BlockPos pos) public @NotNull BlockState getBlockState(@NotNull BlockPos pos)
{ {
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only."); throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
} }
@Override @Override
public FluidState getFluidState(BlockPos pos) public @NotNull FluidState getFluidState(@NotNull BlockPos pos)
{ {
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only."); throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
} }
@@ -1,48 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.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 MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ClientLevelWrapper level;
public ClientBlockDetailMap(ClientLevelWrapper level) { this.level = level; }
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);
}
}
@@ -1,408 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.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 MC_VER >= 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 MC_VER < MC_1_19_2
public static final Random random = new Random(0);
#else
public static final RandomSource random = RandomSource.create();
#endif
public final 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,
Chisel,
Glass;
static ColorMode getColorMode(Block b)
{
if (b instanceof LeavesBlock) return Leaves;
if (b instanceof FlowerBlock) return Flower;
if (b.toString().contains("glass")) return Glass;
if (b.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
return Default;
}
}
//Way to efficiently do this was suggested by IMS from sodium. This is where those numbers and support code was lifted from.
private static final int MIN_BITS = 0x39000000; // 2^(-13)
private static final int MAX_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
private static final float MIN_BOUND = Float.intBitsToFloat(MIN_BITS);
private static final float MAX_BOUND = Float.intBitsToFloat(MAX_BITS);
private static final int[] linearToSrgbTable = new int[] {
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
};
private static final float[] srgbToLinearTable = new float[] {
0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f,
0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f,
0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
};
private static int linearToSrgb(float c) {
if (!(c > MIN_BOUND)) {
c = MIN_BOUND;
}
if (c > MAX_BOUND) {
c = MAX_BOUND;
}
int inputBits = Float.floatToRawIntBits(c);
int entry = linearToSrgbTable[((inputBits - MIN_BITS) >> 20)];
int bias = (entry >>> 16) << 9;
int scale = entry & 0xffff;
int t = (inputBits >>> 12) & 0xff;
return (bias + (scale * t)) >>> 16;
}
//////////////
private static int getWidth(TextureAtlasSprite texture)
{
#if MC_VER < MC_1_19_4
return texture.getWidth();
#else
return texture.contents().width();
#endif
}
private static int getHeight(TextureAtlasSprite texture)
{
#if MC_VER < MC_1_19_4
return texture.getHeight();
#else
return texture.contents().height();
#endif
}
//TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
{
int count = 0;
int alpha = 0;
double red = 0;
double green = 0;
double blue = 0;
int tempColor;
//make Chiseled block not render. Since ColorMode is set per block, you only need to check it once
if (colorMode != ColorMode.Chisel)
{
// textures normally use u and v instead of x and y
for (int v = 0; v < getHeight(texture); v++)
{
for (int u = 0; u < getWidth(texture); u++)
{
//note: Minecraft color format is: 0xAA BB GG RR
//________ DH mod color format is: 0xAA RR GG BB
//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);
int r = (tempColor & 0x000000FF);
int g = (tempColor & 0x0000FF00) >>> 8;
int b = (tempColor & 0x00FF0000) >>> 16;
int a = (tempColor & 0xFF000000) >>> 24;
int scale = 1;
if (colorMode == ColorMode.Leaves)
{
//switch (//FIXME add config option)
// case BLACK:
// a = 255; //simulate black background of fast leaves
// break;
// case IGNORE:
if (a == 0) {
continue; //same long grass
}
else
{
a = 255; //just in case there are semi transparent pixels
}
// break;
// case TRANSPARENT:
// break; //do nothing, let it count towards transparency
}
else if (a == 0 && colorMode != ColorMode.Glass)
{
continue;
}
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r))
{
scale = FLOWER_COLOR_SCALE;
}
count += scale;
//apparently alpha is linear
alpha += a * scale;
//gamma correction is complicated
red += srgbToLinearTable[r] * a * scale;
green += srgbToLinearTable[g] * a * scale;
blue += srgbToLinearTable[b] * a * scale;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
else
{
// determine the average color
tempColor = ColorUtil.rgbToInt(
alpha / count,
linearToSrgb((float) (red / (double) alpha)),
linearToSrgb((float) (green / (double) alpha)),
linearToSrgb((float) (blue / (double) alpha)));
}
//check if not missing texture
if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182))
{
//make it not render at all
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
}
return tempColor;
}
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); // TODO getQuads sometimes throws a "makeThreadingException", is there anything we can do about that? Note: this isn't a critical issue, it just prints an ugly error and the render data will need to be regenered.
if (quads != null && !quads.isEmpty() &&
!(blockState.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
break;
} ;
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 MC_VER < 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,43 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block.cache;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import net.minecraft.world.level.block.state.BlockState;
public class ServerBlockDetailMap
{
private final ConcurrentHashMap<BlockState, ServerBlockStateCache> blockCache = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ServerLevelWrapper level;
public ServerBlockDetailMap(ServerLevelWrapper level) { this.level = level; }
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(); }
}
@@ -1,104 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.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);
}
}
}
@@ -1,239 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.chunk;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Compact, efficient storage for light levels.
* all blocks only take up 4 bits in total,
* and if a 16x16x16 area is detected to have the same light level in all positions,
* then we store a single byte for that light level, instead of 2 kilobytes.
*
* @author Builderb0y
*/
public class ChunkLightStorage
{
/** the minimum Y level in the chunk which this storage is storing light levels for (inclusive). */
public int minY;
/** the maximum Y level in the chunk which this storage is storing light levels for (exclusive). */
public int maxY;
/** the data stored in this storage, split up into 16x16x16 areas. */
public LightSection[] lightSections;
/**
* If the get method is called on a Y position above what's stored
* this value will be returned. <br><br>
*
* This needs to be manually defined since sky and block lights behave differently
* for values both above and below what's defined.
*/
public int aboveMaxYValue;
/** @see ChunkLightStorage#aboveMaxYValue */
public int belowMinYValue;
//=============//
// constructor //
//=============//
public ChunkLightStorage(int minY, int maxY, int aboveMaxYValue, int belowMinYValue)
{
this.minY = minY;
this.maxY = maxY;
this.aboveMaxYValue = aboveMaxYValue;
this.belowMinYValue = belowMinYValue;
}
//=====================//
// getters and setters //
//=====================//
public int get(int x, int y, int z)
{
if (y < this.minY)
{
return this.belowMinYValue;
}
else if (y >= this.maxY)
{
return this.aboveMaxYValue;
}
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); }
}
}
@@ -23,22 +23,20 @@ import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.client.multiplayer.ClientChunkCache; import net.minecraft.client.multiplayer.ClientChunkCache;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess; 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.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.Heightmap;
@@ -73,13 +71,17 @@ import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
#endif #endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public class ChunkWrapper implements IChunkWrapper public class ChunkWrapper implements IChunkWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); 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. */ /** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos()); private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos());
@@ -103,6 +105,8 @@ public class ChunkWrapper implements IChunkWrapper
private int minNonEmptyHeight = Integer.MIN_VALUE; private int minNonEmptyHeight = Integer.MIN_VALUE;
private int maxNonEmptyHeight = Integer.MAX_VALUE; private int maxNonEmptyHeight = Integer.MAX_VALUE;
private int blockBiomeHashCode = 0;
/** /**
* Due to vanilla `isClientLightReady()` not being designed for use by a non-render thread, it may return 'true' * 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). * before the light engine has ticked, (right after all light changes is marked by the engine to be processed).
@@ -142,30 +146,34 @@ public class ChunkWrapper implements IChunkWrapper
//=========// //=========//
// methods // // getters //
//=========// //=========//
@Override @Override
public int getHeight() public int getHeight() { return getHeight(this.chunk); }
public static int getHeight(ChunkAccess chunk)
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
return 255; return 255;
#else #else
return this.chunk.getHeight(); return chunk.getHeight();
#endif #endif
} }
@Override @Override
public int getMinBuildHeight() public int getMinBuildHeight() { return getMinBuildHeight(this.chunk); }
public static int getMinBuildHeight(ChunkAccess chunk)
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
return 0; return 0;
#else #else
return this.chunk.getMinBuildHeight(); return chunk.getMinBuildHeight();
#endif #endif
} }
@Override @Override
public int getMaxBuildHeight() { return this.chunk.getMaxBuildHeight(); } public int getMaxBuildHeight() { return getMaxBuildHeight(this.chunk); }
public static int getMaxBuildHeight(ChunkAccess chunk) { return chunk.getMaxBuildHeight(); }
@Override @Override
public int getMinNonEmptyHeight() public int getMinNonEmptyHeight()
@@ -215,6 +223,9 @@ public class ChunkWrapper implements IChunkWrapper
LevelChunkSection[] sections = this.chunk.getSections(); LevelChunkSection[] sections = this.chunk.getSections();
for (int index = sections.length-1; index >= 0; index--) for (int index = sections.length-1; index >= 0; index--)
{ {
// update at each position to fix using the max height if the chunk is empty
this.maxNonEmptyHeight = this.getChunkSectionMinHeight(index) + 16;
if (sections[index] == null) if (sections[index] == null)
{ {
continue; continue;
@@ -222,7 +233,7 @@ public class ChunkWrapper implements IChunkWrapper
if (!isChunkSectionEmpty(sections[index])) if (!isChunkSectionEmpty(sections[index]))
{ {
this.maxNonEmptyHeight = this.getChunkSectionMinHeight(index) + 16; // non-empty section found
break; break;
} }
} }
@@ -239,15 +250,7 @@ public class ChunkWrapper implements IChunkWrapper
return section.hasOnlyAir(); return section.hasOnlyAir();
#endif #endif
} }
private int getChunkSectionMinHeight(int index) private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getMinBuildHeight(); }
{
// convert from an index to a block coordinate
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
return this.chunk.getSections()[index].bottomBlockY();
#else
return this.chunk.getSectionYFromSectionIndex(index) * 16;
#endif
}
@Override @Override
@@ -257,7 +260,6 @@ public class ChunkWrapper implements IChunkWrapper
public int getLightBlockingHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel); } public int getLightBlockingHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel); }
@Override @Override
public IBiomeWrapper getBiome(int relX, int relY, int relZ) public IBiomeWrapper getBiome(int relX, int relY, int relZ)
{ {
@@ -281,11 +283,35 @@ public class ChunkWrapper implements IChunkWrapper
#endif #endif
} }
@Override
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
BlockPos.MutableBlockPos blockPos = MUTABLE_BLOCK_POS_REF.get();
blockPos.setX(relX);
blockPos.setY(relY);
blockPos.setZ(relZ);
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
}
@Override @Override
public DhChunkPos getChunkPos() { return this.chunkPos; } public DhChunkPos getChunkPos() { return this.chunkPos; }
public ChunkAccess getChunk() { return this.chunk; } public ChunkAccess getChunk() { return this.chunk; }
public ChunkStatus getStatus() { return getStatus(this.getChunk()); }
public static ChunkStatus getStatus(ChunkAccess chunk)
{
#if MC_VER < MC_1_21_1
return chunk.getStatus();
#else
return chunk.getPersistedStatus();
#endif
}
@Override @Override
public int getMaxBlockX() { return this.chunk.getPos().getMaxBlockX(); } public int getMaxBlockX() { return this.chunk.getPos().getMaxBlockX(); }
@Override @Override
@@ -295,8 +321,11 @@ public class ChunkWrapper implements IChunkWrapper
@Override @Override
public int getMinBlockZ() { return this.chunk.getPos().getMinBlockZ(); } public int getMinBlockZ() { return this.chunk.getPos().getMinBlockZ(); }
@Override
public long getLongChunkPos() { return this.chunk.getPos().toLong(); }
//==========//
// lighting //
//==========//
@Override @Override
public void setIsDhLightCorrect(boolean isDhLightCorrect) { this.isDhLightCorrect = isDhLightCorrect; } public void setIsDhLightCorrect(boolean isDhLightCorrect) { this.isDhLightCorrect = isDhLightCorrect; }
@@ -304,8 +333,6 @@ public class ChunkWrapper implements IChunkWrapper
@Override @Override
public void setUseDhLighting(boolean useDhLighting) { this.useDhLighting = useDhLighting; } public void setUseDhLighting(boolean useDhLighting) { this.useDhLighting = useDhLighting; }
@Override @Override
public boolean isLightCorrect() public boolean isLightCorrect()
{ {
@@ -358,13 +385,13 @@ public class ChunkWrapper implements IChunkWrapper
{ {
if (this.blockLightStorage == null) if (this.blockLightStorage == null)
{ {
this.blockLightStorage = new ChunkLightStorage( this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(this);
this.getMinBuildHeight(), this.getMaxBuildHeight(),
// positions above and below the handled area should be unlit
LodUtil.MIN_MC_LIGHT, LodUtil.MIN_MC_LIGHT);
} }
return this.blockLightStorage; return this.blockLightStorage;
} }
public void setBlockLightStorage(ChunkLightStorage lightStorage) { this.blockLightStorage = lightStorage; }
@Override
public void clearDhBlockLighting() { this.getBlockLightStorage().clear(); }
@Override @Override
@@ -379,18 +406,18 @@ public class ChunkWrapper implements IChunkWrapper
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
this.getSkyLightStorage().set(relX, y, relZ, lightValue); this.getSkyLightStorage().set(relX, y, relZ, lightValue);
} }
@Override
public void clearDhSkyLighting() { this.getSkyLightStorage().clear(); }
private ChunkLightStorage getSkyLightStorage() private ChunkLightStorage getSkyLightStorage()
{ {
if (this.skyLightStorage == null) if (this.skyLightStorage == null)
{ {
this.skyLightStorage = new ChunkLightStorage( this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(this);
this.getMinBuildHeight(), this.getMaxBuildHeight(),
// positions above should be lit but positions below should be unlit
LodUtil.MAX_MC_LIGHT, LodUtil.MIN_MC_LIGHT);
} }
return this.skyLightStorage; return this.skyLightStorage;
} }
public void setSkyLightStorage(ChunkLightStorage lightStorage) { this.skyLightStorage = lightStorage; }
@Override @Override
@@ -436,7 +463,7 @@ public class ChunkWrapper implements IChunkWrapper
* before the list has finished populating. * before the list has finished populating.
*/ */
@Override @Override
public synchronized ArrayList<DhBlockPos> getBlockLightPosList() public synchronized ArrayList<DhBlockPos> getWorldBlockLightPosList()
{ {
// only populate the list once // only populate the list once
if (this.blockLightPosList == null) if (this.blockLightPosList == null)
@@ -452,7 +479,13 @@ public class ChunkWrapper implements IChunkWrapper
#else #else
this.chunk.findBlockLightSources((blockPos, blockState) -> this.chunk.findBlockLightSources((blockPos, blockState) ->
{ {
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ())); DhBlockPos pos = new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
// this can be uncommented if MC decides to return relative block positions in the future instead of world positions
//pos.mutateToChunkRelativePos(pos);
//pos.mutateOffset(this.chunkPos.getMinBlockX(), 0, this.chunkPos.getMinBlockZ(), pos);
this.blockLightPosList.add(pos);
}); });
#endif #endif
} }
@@ -460,55 +493,6 @@ public class ChunkWrapper implements IChunkWrapper
return this.blockLightPosList; 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() public static void syncedUpdateClientLightStatus()
{ {
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
@@ -568,64 +552,57 @@ public class ChunkWrapper implements IChunkWrapper
//===============//
// other methods //
//===============//
@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;
}
@Override
public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; }
//================// //================//
// helper methods // // base overrides //
//================// //================//
/** used to prevent accidentally attempting to get/set values outside this chunk's boundaries */ @Override
private void throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(int x, int y, int z) throws IndexOutOfBoundsException public String toString() { return this.chunk.getClass().getSimpleName() + this.chunk.getPos(); }
{
if (!RUN_RELATIVE_POS_INDEX_VALIDATION)
{
return;
}
// FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the constructor
int minHeight = this.getMinBuildHeight();
int maxHeight = this.getMaxBuildHeight() + 1;
if (x < 0 || x >= LodUtil.CHUNK_WIDTH
|| z < 0 || z >= LodUtil.CHUNK_WIDTH
|| y < minHeight || y > maxHeight)
{
String errorMessage = "Relative position [" + x + "," + y + "," + z + "] out of bounds. \n" +
"X/Z must be between 0 and 15 (inclusive) \n" +
"Y must be between [" + minHeight + "] and [" + maxHeight + "] (inclusive).";
throw new IndexOutOfBoundsException(errorMessage);
}
}
/**
* Converts a 3D position into a 1D array index. <br><br>
*
* Source: <br>
* <a href="https://stackoverflow.com/questions/7367770/how-to-flatten-or-index-3d-array-in-1d-array">stackoverflow</a>
*/
public int relativeBlockPosToIndex(int xRel, int y, int zRel)
{
int yRel = y - this.getMinBuildHeight();
return (zRel * LodUtil.CHUNK_WIDTH * this.getHeight()) + (yRel * LodUtil.CHUNK_WIDTH) + xRel;
}
/**
* Converts a 3D position into a 1D array index. <br><br>
*
* Source: <br>
* <a href="https://stackoverflow.com/questions/7367770/how-to-flatten-or-index-3d-array-in-1d-array">stackoverflow</a>
*/
public DhBlockPos indexToRelativeBlockPos(int index)
{
final int zRel = index / (LodUtil.CHUNK_WIDTH * this.getHeight());
index -= (zRel * LodUtil.CHUNK_WIDTH * this.getHeight());
final int y = index / LodUtil.CHUNK_WIDTH;
final int yRel = y + this.getMinBuildHeight();
final int xRel = index % LodUtil.CHUNK_WIDTH;
return new DhBlockPos(xRel, yRel, zRel);
}
//@Override
//public int hashCode()
//{
// if (this.blockBiomeHashCode == 0)
// {
// this.blockBiomeHashCode = this.getBlockBiomeHashCode();
// }
//
// return this.blockBiomeHashCode;
//}
} }
@@ -98,6 +98,7 @@ public class ClassicConfigGUI
public static final int SpaceFromRightScreen = 10; public static final int SpaceFromRightScreen = 10;
public static final int ButtonWidthSpacing = 5; public static final int ButtonWidthSpacing = 5;
public static final int ResetButtonWidth = 40; public static final int ResetButtonWidth = 40;
public static final int ResetButtonHeight = 20;
} }
@@ -157,19 +158,22 @@ public class ClassicConfigGUI
// button.active = entries.stream().allMatch(e -> e.inLimits); // button.active = entries.stream().allMatch(e -> e.inLimits);
if (((ConfigEntry) info).isValid(value) == 0 && info.getType() != List.class) if (info.getType() == String.class
|| info.getType() == List.class)
{
((ConfigEntry) info).uiSetWithoutSaving(stringValue);
}
else if (((ConfigEntry) info).isValid(value) == 0)
{ {
if (!cast) if (!cast)
{
((ConfigEntry) info).uiSetWithoutSaving(value); ((ConfigEntry) info).uiSetWithoutSaving(value);
}
else else
{
((ConfigEntry) info).uiSetWithoutSaving(value.intValue()); ((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; return true;
}; };
@@ -244,17 +248,23 @@ public class ClassicConfigGUI
} }
// Changelog button // Changelog button
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() && Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE) if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() && !ModInfo.IS_DEV_BUILD) // we only have changelogs for stable builds
{ {
this.addBtn(new TexturedButtonWidget( this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen // Where the button is on the screen
this.width - 28, this.height - 28, this.width - 28, this.height - 28,
// Width and height of the button // Width and height of the button
20, 20, 20, 20,
// Offset // texture UV Offset
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20, 0,
#if MC_VER < MC_1_21_1
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
#endif
20, 20,
// Create the button and tell it where to go // Create the button and tell it where to go
(buttonWidget) -> { (buttonWidget) -> {
ChangelogScreen changelogScreen = new ChangelogScreen(this); ChangelogScreen changelogScreen = new ChangelogScreen(this);
@@ -269,7 +279,11 @@ public class ClassicConfigGUI
} }
addBtn(MakeBtn(Translatable("distanthorizons.general.cancel"), this.width / 2 - 154, this.height - 28, 150, 20, button -> { addBtn(MakeBtn(Translatable("distanthorizons.general.cancel"),
this.width / 2 - 154, this.height - 28,
150, 20,
button ->
{
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile(); ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile();
Objects.requireNonNull(minecraft).setScreen(parent); Objects.requireNonNull(minecraft).setScreen(parent);
})); }));
@@ -279,8 +293,11 @@ public class ClassicConfigGUI
})); }));
this.list = new ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, 32, 25); this.list = new ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, 32, 25);
#if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
if (this.minecraft != null && this.minecraft.level != null) if (this.minecraft != null && this.minecraft.level != null)
this.list.setRenderBackground(false); this.list.setRenderBackground(false);
#endif
this.addWidget(this.list); this.addWidget(this.list);
@@ -311,6 +328,7 @@ public class ClassicConfigGUI
initEntry(info, this.translationPrefix); initEntry(info, this.translationPrefix);
Component name = Translatable(translationPrefix + info.getNameWCategory()); Component name = Translatable(translationPrefix + info.getNameWCategory());
if (ConfigEntry.class.isAssignableFrom(info.getClass())) if (ConfigEntry.class.isAssignableFrom(info.getClass()))
{ {
Button.OnPress btnAction = button -> { Button.OnPress btnAction = button -> {
@@ -319,12 +337,12 @@ public class ClassicConfigGUI
this.reload = true; this.reload = true;
Objects.requireNonNull(minecraft).setScreen(this); Objects.requireNonNull(minecraft).setScreen(this);
}; };
int a = this.width - ConfigScreenConfigs.SpaceFromRightScreen - 150 - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth; int posX = this.width - ConfigScreenConfigs.SpaceFromRightScreen - 150 - ConfigScreenConfigs.ButtonWidthSpacing - ConfigScreenConfigs.ResetButtonWidth;
int b = 0; int posZ = 0;
int c = ConfigScreenConfigs.ResetButtonWidth;
int d = 20;
Button resetButton = MakeBtn(Translatable("distanthorizons.general.reset").withStyle(ChatFormatting.RED), a, b, c, d, btnAction); Button resetButton = MakeBtn(Translatable("distanthorizons.general.reset").withStyle(ChatFormatting.RED),
posX, posZ, ConfigScreenConfigs.ResetButtonWidth, ConfigScreenConfigs.ResetButtonHeight,
btnAction);
if (((EntryInfo) info.guiValue).widget instanceof Map.Entry) if (((EntryInfo) info.guiValue).widget instanceof Map.Entry)
{ {
@@ -428,7 +446,9 @@ public class ClassicConfigGUI
String key = translationPrefix + (newInfo.category.isEmpty() ? "" : newInfo.category + ".") + newInfo.getName() + ".@tooltip"; String key = translationPrefix + (newInfo.category.isEmpty() ? "" : newInfo.category + ".") + newInfo.getName() + ".@tooltip";
if (((EntryInfo) newInfo.guiValue).error != null && text.equals(name)) if (((EntryInfo) newInfo.guiValue).error != null && text.equals(name))
{
DhRenderTooltip(matrices, font, ((EntryInfo) newInfo.guiValue).error.getValue(), mouseX, mouseY); DhRenderTooltip(matrices, font, ((EntryInfo) newInfo.guiValue).error.getValue(), mouseX, mouseY);
}
else if (I18n.exists(key) && (text != null && text.equals(name))) else if (I18n.exists(key) && (text != null && text.equals(name)))
{ {
List<Component> list = new ArrayList<>(); List<Component> list = new ArrayList<>();
@@ -36,7 +36,7 @@ public class GetConfigScreen
case JavaFX: case JavaFX:
return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title"); return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
default: default:
return null; throw new IllegalArgumentException("No config screen implementation defined for ["+useScreen+"].");
} }
} }
@@ -15,12 +15,12 @@ public class GuiHelper
/** /**
* Helper static methods for versional compat * Helper static methods for versional compat
*/ */
public static Button MakeBtn(Component base, int a, int b, int c, int d, Button.OnPress action) public static Button MakeBtn(Component base, int posX, int posZ, int width, int height, Button.OnPress action)
{ {
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
return new Button(a, b, c, d, base, action); return new Button(posX, posZ, width, height, base, action);
#else #else
return Button.builder(base, action).bounds(a, b, c, d).build(); return Button.builder(base, action).bounds(posX, posZ, width, height).build();
#endif #endif
} }
@@ -60,8 +60,12 @@ public class MinecraftScreen
screen.init(); // Init our own config screen screen.init(); // Init our own config screen
this.list = new ConfigListWidget(this.minecraft, this.width, this.height, 0, 0, 25); // Select the area to tint this.list = new ConfigListWidget(this.minecraft, this.width, this.height, 0, 0, 25); // Select the area to tint
#if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
if (this.minecraft != null && this.minecraft.level != null) // Check if in game if (this.minecraft != null && this.minecraft.level != null) // Check if in game
this.list.setRenderBackground(false); // Disable from rendering this.list.setRenderBackground(false); // Disable from rendering
#endif
this.addWidget(this.list); // Add the tint to the things to be rendered this.addWidget(this.list); // Add the tint to the things to be rendered
} }
@@ -101,11 +101,17 @@ public class ChangelogScreen extends DhScreen
this.changelog.add(""); this.changelog.add("");
this.changelog.add(""); this.changelog.add("");
String changelog = ModrinthGetter.changeLogs.get(versionID);
if (changelog == null)
{
// in case something goes wrong this will prevent null pointers
changelog = "";
}
// Get the release changelog and split it by the new lines // 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+ 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 // This formats markdown to minecraft's "§" charactersnew MarkdownFormatter.MinecraftFormat().convertTo(
ModrinthGetter.changeLogs.get(versionID) new MarkdownFormatter.MinecraftFormat().convertTo(changelog).split("\\n");
).split("\\n");
// Makes the words wrap around to not go off the screen // Makes the words wrap around to not go off the screen
for (String str : unwrappedChangelog) for (String str : unwrappedChangelog)
{ {
@@ -168,10 +174,9 @@ public class ChangelogScreen extends DhScreen
#endif #endif
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog // render order matters, otherwise on 1.20.6+ the blurred background will render on top of the text
super.render(matrices, mouseX, mouseY, delta); // Render the buttons super.render(matrices, mouseX, mouseY, delta); // Render the buttons
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
DhDrawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title DhDrawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title
} }
@@ -42,15 +42,17 @@ public class UpdateModScreen extends DhScreen
this.parent = parent; this.parent = parent;
this.newVersionID = newVersionID; this.newVersionID = newVersionID;
switch (Config.Client.Advanced.AutoUpdater.updateBranch.get()) {
case STABLE: EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
currentVer = ModInfo.VERSION; if (updateBranch == EDhApiUpdateBranch.STABLE)
nextVer = ModrinthGetter.releaseNames.get(this.newVersionID); {
break; this.currentVer = ModInfo.VERSION;
case NIGHTLY: this.nextVer = ModrinthGetter.releaseNames.get(this.newVersionID);
currentVer = ModJarInfo.Git_Commit.substring(0,7); }
nextVer = this.newVersionID.substring(0,7); else
break; {
this.currentVer = ModJarInfo.Git_Commit.substring(0,7);
this.nextVer = this.newVersionID.substring(0,7);
} }
} }
@@ -73,7 +75,13 @@ public class UpdateModScreen extends DhScreen
// Offset // Offset
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
0, new ResourceLocation(ModInfo.ID, "logo.png"), 130, 65, 0,
#if MC_VER < MC_1_21_1
new ResourceLocation(ModInfo.ID, "logo.png"),
#else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
#endif
130, 65,
// Create the button and tell it where to go // Create the button and tell it where to go
// For now it goes to the client option by default // 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) (buttonWidget) -> System.out.println("Nice, you found an easter egg :)"), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
@@ -88,7 +96,7 @@ public class UpdateModScreen extends DhScreen
e.printStackTrace(); e.printStackTrace();
} }
if (Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE) if (!ModInfo.IS_DEV_BUILD)
{ {
this.addBtn(new TexturedButtonWidget( this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen // Where the button is on the screen
@@ -98,7 +106,13 @@ public class UpdateModScreen extends DhScreen
// Offset // Offset
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
0, new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), 20, 20, 0,
#if MC_VER < MC_1_21_1
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
#endif
20, 20,
// Create the button and tell it where to go // Create the button and tell it where to go
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti) (buttonWidget) -> Objects.requireNonNull(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 // Add a title to the button
@@ -147,17 +161,15 @@ public class UpdateModScreen extends DhScreen
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#endif #endif
// TODO: add the tooltips for the buttons
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
// TODO: Add tooltips
// Render the text's // 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.text1"), this.width / 2, this.height / 2 - 35, 0xFFFFFF);
DhDrawCenteredString(matrices, this.font, DhDrawCenteredString(matrices, this.font,
Translatable(ModInfo.ID + ".updater.text2", currentVer, nextVer), Translatable(ModInfo.ID + ".updater.text2", currentVer, nextVer),
this.width / 2, this.height / 2 - 20, 0x52FD52); 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 @Override
@@ -31,6 +31,7 @@ import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -38,13 +39,13 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import net.minecraft.CrashReport; import net.minecraft.CrashReport;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
@@ -59,16 +60,15 @@ import org.jetbrains.annotations.Nullable;
* A singleton that wraps the Minecraft object. * A singleton that wraps the Minecraft object.
* *
* @author James Seibel * @author James Seibel
* @version 3-5-2022
*/ */
//@Environment(EnvType.CLIENT)
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private static final Minecraft MINECRAFT = Minecraft.getInstance();
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper(); public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
public final Minecraft mc = Minecraft.getInstance();
/** /**
* The lightmap for the current: * The lightmap for the current:
@@ -99,10 +99,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
* This doesn't affect OpenGL objects in any way. * This doesn't affect OpenGL objects in any way.
*/ */
@Override @Override
public void clearFrameObjectCache() public void clearFrameObjectCache() { this.lightMap = null; }
{
lightMap = null;
}
@@ -118,10 +115,10 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
{ {
default: default:
case AUTO: case AUTO:
if (this.mc.level != null) if (MINECRAFT.level != null)
{ {
Direction mcDir = McObjectConverter.Convert(lodDirection); Direction mcDir = McObjectConverter.Convert(lodDirection);
return this.mc.level.getShade(mcDir, true); return MINECRAFT.level.getShade(mcDir, true);
} }
else else
{ {
@@ -150,47 +147,63 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
} }
@Override @Override
public boolean hasSinglePlayerServer() { return mc.hasSingleplayerServer(); } public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); }
@Override @Override
public boolean clientConnectedToDedicatedServer() { return mc.getCurrentServer() != null && !this.hasSinglePlayerServer(); } public boolean clientConnectedToDedicatedServer() { return MINECRAFT.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
@Override
public boolean connectedToReplay() { return !MINECRAFT.hasSingleplayerServer() && MINECRAFT.getCurrentServer() == null; }
@Override @Override
public String getCurrentServerName() { return mc.getCurrentServer().name; } public String getCurrentServerName()
{
if (this.connectedToReplay())
{
return ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME;
}
else
{
ServerData server = MINECRAFT.getCurrentServer();
return (server != null) ? server.name : "NULL";
}
}
@Override @Override
public String getCurrentServerIp() { return mc.getCurrentServer().ip; } public String getCurrentServerIp()
{
if (this.connectedToReplay())
{
return "";
}
else
{
ServerData server = MINECRAFT.getCurrentServer();
return (server != null) ? server.ip : "NA";
}
}
@Override @Override
public String getCurrentServerVersion() public String getCurrentServerVersion()
{ {
return mc.getCurrentServer().version.getString(); ServerData server = MINECRAFT.getCurrentServer();
return (server != null) ? server.version.getString() : "UNKOWN";
} }
//=============// //=============//
// Simple gets // // Simple gets //
//=============// //=============//
public LocalPlayer getPlayer() public LocalPlayer getPlayer() { return MINECRAFT.player; }
{
return mc.player;
}
@Override @Override
public boolean playerExists() public boolean playerExists() { return MINECRAFT.player != null; }
{
return mc.player != null;
}
@Override @Override
public UUID getPlayerUUID() public UUID getPlayerUUID() { return this.getPlayer().getUUID(); }
{
return getPlayer().getUUID();
}
@Override @Override
public DhBlockPos getPlayerBlockPos() public DhBlockPos getPlayerBlockPos()
{ {
BlockPos playerPos = getPlayer().blockPosition(); BlockPos playerPos = this.getPlayer().blockPosition();
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ()); return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
} }
@@ -198,46 +211,38 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
public DhChunkPos getPlayerChunkPos() public DhChunkPos getPlayerChunkPos()
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
ChunkPos playerPos = new ChunkPos(getPlayer().blockPosition()); ChunkPos playerPos = new ChunkPos(this.getPlayer().blockPosition());
#else #else
ChunkPos playerPos = getPlayer().chunkPosition(); ChunkPos playerPos = this.getPlayer().chunkPosition();
#endif #endif
return new DhChunkPos(playerPos.x, playerPos.z); return new DhChunkPos(playerPos.x, playerPos.z);
} }
public ModelManager getModelManager()
{
return mc.getModelManager();
}
@Nullable @Nullable
@Override @Override
public IClientLevelWrapper getWrappedClientLevel() public IClientLevelWrapper getWrappedClientLevel()
{ {
if (this.mc.level == null) if (MINECRAFT.level == null)
{ {
return null; return null;
} }
return ClientLevelWrapper.getWrapperIgnoringOverride(this.mc.level); return ClientLevelWrapper.getWrapperIgnoringOverride(MINECRAFT.level);
}
/** Please move over to getInstallationDirectory() */
@Deprecated
@Override
public File getGameDirectory()
{
return getInstallationDirectory();
} }
@Override @Override
public IProfilerWrapper getProfiler() public IProfilerWrapper getProfiler()
{ {
if (profilerWrapper == null) if (this.profilerWrapper == null)
profilerWrapper = new ProfilerWrapper(mc.getProfiler()); {
else if (mc.getProfiler() != profilerWrapper.profiler) this.profilerWrapper = new ProfilerWrapper(MINECRAFT.getProfiler());
profilerWrapper.profiler = mc.getProfiler(); }
return profilerWrapper; else if (MINECRAFT.getProfiler() != this.profilerWrapper.profiler)
{
this.profilerWrapper.profiler = MINECRAFT.getProfiler();
}
return this.profilerWrapper;
} }
/** Returns all worlds available to the server */ /** Returns all worlds available to the server */
@@ -246,7 +251,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
{ {
ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>(); ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>();
Iterable<ServerLevel> serverWorlds = mc.getSingleplayerServer().getAllLevels(); Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels();
for (ServerLevel world : serverWorlds) for (ServerLevel world : serverWorlds)
{ {
worlds.add(ServerLevelWrapper.getWrapper(world)); worlds.add(ServerLevelWrapper.getWrapper(world));
@@ -260,12 +265,12 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override @Override
public void sendChatMessage(String string) public void sendChatMessage(String string)
{ {
LocalPlayer p = getPlayer(); LocalPlayer player = this.getPlayer();
if (p == null) return; if (player == null) return;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
p.sendMessage(new TextComponent(string), getPlayer().getUUID()); player.sendMessage(new TextComponent(string), getPlayer().getUUID());
#else #else
p.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string)); player.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
#endif #endif
} }
@@ -290,24 +295,15 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
} }
@Override @Override
public Object getOptionsObject() public Object getOptionsObject() { return MINECRAFT.options; }
{
return mc.options;
}
@Override @Override
public boolean isDedicatedServer() public boolean isDedicatedServer() { return false; }
{
return false;
}
@Override @Override
public File getInstallationDirectory() public File getInstallationDirectory() { return MINECRAFT.gameDirectory; }
{
return mc.gameDirectory;
}
@Override @Override
public void executeOnRenderThread(Runnable runnable) { this.mc.execute(runnable); } public void executeOnRenderThread(Runnable runnable) { MINECRAFT.execute(runnable); }
} }
@@ -12,16 +12,20 @@ public class MinecraftDedicatedServerWrapper implements IMinecraftSharedWrapper
private MinecraftDedicatedServerWrapper() { } private MinecraftDedicatedServerWrapper() { }
public DedicatedServer dedicatedServer = null; public DedicatedServer dedicatedServer = null;
@Override @Override
public boolean isDedicatedServer() public boolean isDedicatedServer() { return true; }
{
return true;
}
@Override @Override
public File getInstallationDirectory() public File getInstallationDirectory()
{ {
if (dedicatedServer == null) if (this.dedicatedServer == null)
{
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server complete initialization!"); throw new IllegalStateException("Trying to get Installation Direction before Dedicated server complete initialization!");
return dedicatedServer.getServerDirectory(); }
#if MC_VER < MC_1_21_1
return this.dedicatedServer.getServerDirectory();
#else
return this.dedicatedServer.getServerDirectory().toFile();
#endif
} }
} }
@@ -21,30 +21,26 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
import java.awt.Color; import java.awt.Color;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.nio.FloatBuffer; import java.util.concurrent.ConcurrentHashMap;
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.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.WrapperFactory; import com.seibel.distanthorizons.common.wrappers.WrapperFactory;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; 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.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
#if MC_VER >= MC_1_17_1
import net.minecraft.client.renderer.FogRenderer;
import com.mojang.blaze3d.systems.RenderSystem;
#endif
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
import org.joml.Vector3f;
#else
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Vector3f; import org.joml.Vector3f;
#else
#endif #endif
#if MC_VER >= MC_1_20_2 #if MC_VER >= MC_1_20_2
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher; import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
@@ -54,20 +50,14 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOpt
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import 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.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
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.effect.MobEffects;
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
import net.minecraft.tags.FluidTags; import net.minecraft.tags.FluidTags;
@@ -77,10 +67,8 @@ import org.lwjgl.opengl.GL15;
#else #else
import net.minecraft.world.level.material.FogType; import net.minecraft.world.level.material.FogType;
#endif #endif
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.joml.Matrix4f;
/** /**
@@ -105,7 +93,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
* In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps. <br> * In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps. <br>
* Requiring the use of multiple {@link LightMapWrapper}. * Requiring the use of multiple {@link LightMapWrapper}.
*/ */
public HashMap<IDimensionTypeWrapper, LightMapWrapper> lightmapByDimensionType = new HashMap<>(); public ConcurrentHashMap<IDimensionTypeWrapper, LightMapWrapper> lightmapByDimensionType = new ConcurrentHashMap<>();
/** /**
* Holds the render buffer that should be used when displaying levels to the screen. * Holds the render buffer that should be used when displaying levels to the screen.
@@ -122,14 +110,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z()); return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z());
} }
@Override
public DhBlockPos getCameraBlockPosition()
{
Camera camera = MC.gameRenderer.getMainCamera();
BlockPos blockPos = camera.getBlockPosition();
return new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
@Override @Override
/** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */ /** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */
public boolean playerHasBlindingEffect() public boolean playerHasBlindingEffect()
@@ -150,43 +130,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return new Vec3d(projectedView.x, projectedView.y, projectedView.z); return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
} }
@Override
public Mat4f getWorldViewMatrix()
{
Camera camera = MC.gameRenderer.getMainCamera();
Vector3f cameraVec3 = new Vector3f(
(float)camera.getPosition().x,
(float)camera.getPosition().y,
(float)camera.getPosition().z);
cameraVec3 = cameraVec3.negate();
Matrix4f matWorldView = new Matrix4f()
.rotateX((float)Math.toRadians(camera.getXRot()))
.rotateY((float)Math.toRadians(camera.getYRot() + 180f))
.translate(cameraVec3);
return new Mat4f(matWorldView);
}
@Override
public Mat4f getDefaultProjectionMatrix(float partialTicks)
{
#if MC_VER < MC_1_17_1
return McObjectConverter.Convert(Minecraft.getInstance().gameRenderer.getProjectionMatrix(Minecraft.getInstance().gameRenderer.getMainCamera(), partialTicks, true));
#else
return McObjectConverter.Convert(MC.gameRenderer.getProjectionMatrix(MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true)));
#endif
}
@Override
public double getGamma()
{
#if MC_VER < MC_1_19_2
return MC.options.gamma;
#else
return MC.options.gamma().get();
#endif
}
@Override @Override
public Color getFogColor(float partialTicks) public Color getFogColor(float partialTicks)
{ {
@@ -211,16 +154,25 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
{ {
if (MC.level.dimensionType().hasSkyLight()) if (MC.level.dimensionType().hasSkyLight())
{ {
#if MC_VER < MC_1_17_1 float frameTime;
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), MC.getFrameTime()); #if MC_VER < MC_1_21_1
frameTime = MC.getFrameTime();
#else #else
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), MC.getFrameTime()); frameTime = MC.getTimer().getRealtimeDeltaTicks();
#endif
#if MC_VER < MC_1_17_1
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), frameTime);
#else
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
#endif #endif
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z); return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
} }
else else
{
return new Color(0, 0, 0); return new Color(0, 0, 0);
} }
}
@Override @Override
public double getFov(float partialTicks) public double getFov(float partialTicks)
@@ -307,77 +259,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return getRenderTarget().viewHeight; 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;
@Override
public HashSet<DhChunkPos> getVanillaRenderedChunks()
{
ISodiumAccessor sodium = ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class);
if (sodium != null)
{
return sodium.getNormalRenderedChunks();
}
IOptifineAccessor optifine = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
if (optifine != null)
{
HashSet<DhChunkPos> pos = optifine.getNormalRenderedChunks();
if (pos == null)
pos = getMaximumRenderedChunks();
return pos;
}
if (!usingBackupGetVanillaRenderedChunks)
{
try
{
#if MC_VER < MC_1_20_2
LevelRenderer levelRenderer = MC.levelRenderer;
Collection<LevelRenderer.RenderChunkInfo> chunks =
#if MC_VER < MC_1_18_2 levelRenderer.renderChunks;
#else levelRenderer.renderChunkStorage.get().renderChunks; #endif
return (chunks.stream().map((chunk) -> {
AABB chunkBoundingBox =
#if MC_VER < MC_1_18_2 chunk.chunk.bb;
#else chunk.chunk.getBoundingBox(); #endif
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
}).collect(Collectors.toCollection(HashSet::new)));
#else
LevelRenderer levelRenderer = MC.levelRenderer;
Collection<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)));
#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)
{
}
LOGGER.error("getVanillaRenderedChunks Error: ", e);
usingBackupGetVanillaRenderedChunks = true;
}
}
return getMaximumRenderedChunks();
}
@Override @Override
public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); } public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
@@ -405,11 +286,8 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
// so this will have to do for now // so this will have to do for now
IDimensionTypeWrapper dimensionType = level.getDimensionType(); IDimensionTypeWrapper dimensionType = level.getDimensionType();
if (!this.lightmapByDimensionType.containsKey(dimensionType)) LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
{ wrapper.uploadLightmap(lightPixels);
this.lightmapByDimensionType.put(dimensionType, new LightMapWrapper());
}
this.lightmapByDimensionType.get(dimensionType).uploadLightmap(lightPixels);
} }
} }
@@ -36,14 +36,6 @@ public class LightMapWrapper implements ILightMapWrapper
public LightMapWrapper() { } public LightMapWrapper() { }
private void createLightmap(NativeImage image)
{
this.textureId = GL32.glGenTextures();
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
}
//=========// //=========//
@@ -53,14 +45,25 @@ public class LightMapWrapper implements ILightMapWrapper
public void uploadLightmap(NativeImage image) public void uploadLightmap(NativeImage image)
{ {
int currentBind = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); int currentBind = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
if (this.textureId == 0) if (this.textureId == 0)
{ {
this.createLightmap(image); this.createLightmap(image);
} }
else
{
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
}
image.upload(0, 0, 0, false); image.upload(0, 0, 0, false);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, currentBind); GL32.glBindTexture(GL32.GL_TEXTURE_2D, currentBind);
} }
private void createLightmap(NativeImage image)
{
this.textureId = GL32.glGenTextures();
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
}
@Override @Override
public void bind() public void bind()
@@ -1,17 +1,16 @@
package com.seibel.distanthorizons.common.wrappers.world; package com.seibel.distanthorizons.common.wrappers.world;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper; import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.cache.ClientBlockDetailMap; import com.seibel.distanthorizons.common.wrappers.block.ClientBlockStateColorCache;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; import com.seibel.distanthorizons.core.level.*;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
@@ -19,30 +18,42 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public class ClientLevelWrapper implements IClientLevelWrapper public class ClientLevelWrapper implements IClientLevelWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName()); private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName());
private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>(); // TODO can leak
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class); private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
private static final Minecraft MINECRAFT = Minecraft.getInstance();
private final ClientLevel level; private final ClientLevel level;
private final ClientBlockDetailMap blockMap = new ClientBlockDetailMap(this); private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockCache = new ConcurrentHashMap<>();
private BlockStateWrapper dirtBlockWrapper; private BlockStateWrapper dirtBlockWrapper;
private BiomeWrapper plainsBiomeWrapper;
@Deprecated // TODO circular references are bad
private IDhLevel parentDhLevel;
@@ -82,7 +93,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
{ {
try try
{ {
Iterable<ServerLevel> serverLevels = MinecraftClientWrapper.INSTANCE.mc.getSingleplayerServer().getAllLevels(); Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels();
// attempt to find the server level with the same dimension type // 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 // TODO this assumes only one level per dimension type, the SubDimensionLevelMatcher will need to be added for supporting multiple levels per dimension
@@ -102,7 +113,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
} }
catch (Exception e) catch (Exception e)
{ {
LOGGER.error("Failed to get server side wrapper for client level: " + level); LOGGER.error("Failed to get server side wrapper for client level: " + this.level);
return null; return null;
} }
} }
@@ -114,9 +125,13 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//====================// //====================//
@Override @Override
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState) public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockWrapper)
{ {
return this.blockMap.getColor(((BlockStateWrapper) blockState).blockState, (BiomeWrapper) biome, pos); ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent(
((BlockStateWrapper) blockWrapper).blockState,
(block) -> new ClientBlockStateColorCache(block, this));
return blockColorCache.getColor((BiomeWrapper) biome, pos);
} }
@Override @Override
@@ -136,7 +151,30 @@ public class ClientLevelWrapper implements IClientLevelWrapper
} }
} }
return this.blockMap.getColor(this.dirtBlockWrapper.blockState, BiomeWrapper.EMPTY_WRAPPER, DhBlockPos.ZERO); return this.getBlockColor(DhBlockPos.ZERO,BiomeWrapper.EMPTY_WRAPPER, this.dirtBlockWrapper);
}
@Override
public void clearBlockColorCache() { this.blockCache.clear(); }
@Override
public IBiomeWrapper getPlainsBiomeWrapper()
{
if (this.plainsBiomeWrapper == null)
{
try
{
this.plainsBiomeWrapper = (BiomeWrapper) BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, this);
}
catch (IOException e)
{
// shouldn't happen, but just in case
LOGGER.warn("Unable to get planes biome with resource location ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"] with level ["+this+"].", e);
return null;
}
}
return this.plainsBiomeWrapper;
} }
@Override @Override
@@ -154,7 +192,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); } public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
@Override @Override
public int getHeight() { return this.level.getHeight(); } public int getMaxHeight() { return this.level.getHeight(); }
@Override @Override
public int getMinHeight() public int getMinHeight()
@@ -169,12 +207,12 @@ public class ClientLevelWrapper implements IClientLevelWrapper
@Override @Override
public IChunkWrapper tryGetChunk(DhChunkPos pos) public IChunkWrapper tryGetChunk(DhChunkPos pos)
{ {
if (!this.level.hasChunk(pos.x, pos.z)) if (!this.level.hasChunk(pos.getX(), pos.getZ()))
{ {
return null; return null;
} }
ChunkAccess chunk = this.level.getChunk(pos.x, pos.z, ChunkStatus.EMPTY, false); ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.EMPTY, false);
if (chunk == null) if (chunk == null)
{ {
return null; return null;
@@ -203,7 +241,44 @@ public class ClientLevelWrapper implements IClientLevelWrapper
public ClientLevel getWrappedMcObject() { return this.level; } public ClientLevel getWrappedMcObject() { return this.level; }
@Override @Override
public void onUnload() { LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level); } public void onUnload()
{
LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level);
this.parentDhLevel = null;
}
//===================//
// generic rendering //
//===================//
@Override
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; }
@Override
public IDhApiCustomRenderRegister getRenderRegister()
{
if (this.parentDhLevel == null)
{
return null;
}
return this.parentDhLevel.getGenericRenderer();
}
@Override
public Color getCloudColor(float tickDelta)
{
Vec3 colorVec3 = this.level.getCloudColor(tickDelta);
return new Color((float)colorVec3.x, (float)colorVec3.y, (float)colorVec3.z);
}
//================//
// base overrides //
//================//
@Override @Override
public String toString() public String toString()
@@ -23,28 +23,31 @@ import java.io.File;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.cache.ServerBlockDetailMap;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper; import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkSource;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
/** /**
* @version 2022-9-16 * @version 2022-9-16
@@ -54,8 +57,9 @@ public class ServerLevelWrapper implements IServerLevelWrapper
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final ConcurrentHashMap<ServerLevel, ServerLevelWrapper> LEVEL_WRAPPER_BY_SERVER_LEVEL = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<ServerLevel, ServerLevelWrapper> LEVEL_WRAPPER_BY_SERVER_LEVEL = new ConcurrentHashMap<>();
final ServerLevel level; private final ServerLevel level;
ServerBlockDetailMap blockMap = new ServerBlockDetailMap(this); @Deprecated // TODO circular references are bad
private IDhLevel parentDhLevel;
@@ -76,19 +80,6 @@ public class ServerLevelWrapper implements IServerLevelWrapper
// methods // // methods //
//=========// //=========//
@Nullable
@Override
public IClientLevelWrapper tryGetClientLevelWrapper()
{
MinecraftClientWrapper client = MinecraftClientWrapper.INSTANCE;
if (client.mc.level == null)
{
return null;
}
return ClientLevelWrapper.getWrapper(client.mc.level);
}
@Override @Override
public File getSaveFolder() public File getSaveFolder()
{ {
@@ -122,7 +113,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper
} }
@Override @Override
public int getHeight() public int getMaxHeight()
{ {
return level.getHeight(); return level.getHeight();
} }
@@ -136,11 +127,12 @@ public class ServerLevelWrapper implements IServerLevelWrapper
return level.getMinBuildHeight(); return level.getMinBuildHeight();
#endif #endif
} }
@Override @Override
public IChunkWrapper tryGetChunk(DhChunkPos pos) public IChunkWrapper tryGetChunk(DhChunkPos pos)
{ {
if (!level.hasChunk(pos.x, pos.z)) return null; if (!level.hasChunk(pos.getX(), pos.getZ())) return null;
ChunkAccess chunk = level.getChunk(pos.x, pos.z, ChunkStatus.FULL, false); ChunkAccess chunk = level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FULL, false);
if (chunk == null) return null; if (chunk == null) return null;
return new ChunkWrapper(chunk, level, this); return new ChunkWrapper(chunk, level, this);
} }
@@ -166,18 +158,33 @@ public class ServerLevelWrapper implements IServerLevelWrapper
} }
@Override @Override
public ServerLevel getWrappedMcObject() public ServerLevel getWrappedMcObject() { return this.level; }
{
return level;
}
@Override @Override
public void onUnload() { LEVEL_WRAPPER_BY_SERVER_LEVEL.remove(this.level); } public void onUnload() { LEVEL_WRAPPER_BY_SERVER_LEVEL.remove(this.level); }
@Override @Override
public String toString() public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; }
@Override
public IDhApiCustomRenderRegister getRenderRegister()
{ {
return "Wrapped{" + level.toString() + "@" + getDimensionType().getDimensionName() + "}"; if (this.parentDhLevel == null)
{
return null;
} }
return this.parentDhLevel.getGenericRenderer();
}
//================//
// base overrides //
//================//
@Override
public String toString() { return "Wrapped{" + this.level.toString() + "@" + this.getDimensionType().getDimensionName() + "}"; }
} }
@@ -35,6 +35,7 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.objects.EventTimer; import com.seibel.distanthorizons.core.util.objects.EventTimer;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -66,7 +67,6 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator; 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.LevelChunk;
import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData; import net.minecraft.world.level.chunk.UpgradeData;
@@ -79,6 +79,12 @@ import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
/* /*
Total: 3.135214124s Total: 3.135214124s
===================================== =====================================
@@ -104,8 +110,6 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
new ConfigBasedLogger(LogManager.getLogger("LodWorldGen"), new ConfigBasedLogger(LogManager.getLogger("LodWorldGen"),
() -> Config.Client.Advanced.Logging.logWorldGenLoadEvent.get()); () -> Config.Client.Advanced.Logging.logWorldGenLoadEvent.get());
//TODO: Make actual proper support for StarLight
public static class PerfCalculator public static class PerfCalculator
{ {
private static final String[] TIME_NAMES = { private static final String[] TIME_NAMES = {
@@ -220,8 +224,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
// constructors // // constructors //
//==============// //==============//
public static ImmutableMap<EDhApiWorldGenerationStep, Integer> BorderNeeded; public static final ImmutableMap<EDhApiWorldGenerationStep, Integer> WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP;
public static int MaxBorderNeeded; public static final int MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
static static
{ {
@@ -249,8 +253,13 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0); builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0);
builder.put(EDhApiWorldGenerationStep.FEATURES, 0); builder.put(EDhApiWorldGenerationStep.FEATURES, 0);
builder.put(EDhApiWorldGenerationStep.LIGHT, 0); builder.put(EDhApiWorldGenerationStep.LIGHT, 0);
BorderNeeded = builder.build(); WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP = builder.build();
MaxBorderNeeded = BorderNeeded.values().stream().mapToInt(Integer::intValue).max().getAsInt();
// TODO this is a test to see if the additional boarder is actually necessary or not.
// If world generators end up having infinite loops or other unexplained issues,
// this should be set back to the commented out logic below
MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = 0;
//MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.values().stream().mapToInt(Integer::intValue).max().getAsInt();
} }
public BatchGenerationEnvironment(IDhServerLevel serverlevel) public BatchGenerationEnvironment(IDhServerLevel serverlevel)
@@ -289,6 +298,9 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
//=================//
// synchronization //
//=================//
public <T> T joinSync(CompletableFuture<T> future) public <T> T joinSync(CompletableFuture<T> future)
{ {
@@ -339,8 +351,11 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
} }
else if (event.hasTimeout(Config.Client.Advanced.WorldGenerator.worldGenerationTimeoutLengthInSeconds.get(), TimeUnit.SECONDS)) 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.warn(
EVENT_LOGGER.info("Dump PrefEvent: " + event.timer); "Batching World Generator: [" + event + "] timed out and terminated after ["+Config.Client.Advanced.WorldGenerator.worldGenerationTimeoutLengthInSeconds.get()+"] seconds. " +
"\nYour computer might be overloaded or your world gen mods might be causing world gen to take longer than expected. " +
"\nEither increase DH's world gen timeout or reduce your computer's CPU load.");
EVENT_LOGGER.debug("Dump PrefEvent: " + event.timer);
try try
{ {
if (!event.terminate()) if (!event.terminate())
@@ -363,31 +378,251 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
} }
} }
private static ProtoChunk EmptyChunk(ServerLevel level, ChunkPos chunkPos)
{
return new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if MC_VER >= MC_1_17_1 , level #endif
#if MC_VER >= MC_1_18_2 , level.registryAccess().registryOrThrow(
#if MC_VER < MC_1_19_4
Registry.BIOME_REGISTRY
#else
Registries.BIOME
#endif
), null #endif
);
//==================//
// world generation //
//==================//
public void generateLodFromList(GenerationEvent genEvent) throws InterruptedException
{
EVENT_LOGGER.debug("Lod Generate Event: " + genEvent.minPos);
// Minecraft's generation events expect odd chunk width areas (3x3, 7x7, or 11x11),
// but DH submits square generation events (4x4).
// We handle this later, although that handling would need to change if the gen size ever changed.
LodUtil.assertTrue(genEvent.size % 2 == 0, "Generation events are expected to be an evan number of chunks wide.");
int borderSize = MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
// genEvent.size - 1 converts the even width size to an odd number for MC compatability
int refSize = (genEvent.size - 1) + (borderSize * 2);
int refPosX = genEvent.minPos.getX() - borderSize;
int refPosZ = genEvent.minPos.getZ() - borderSize;
LightGetterAdaptor lightGetterAdaptor = new LightGetterAdaptor(this.params.level);
DummyLightEngine dummyLightEngine = new DummyLightEngine(lightGetterAdaptor);
//====================================//
// offset and generate odd width area //
//====================================//
// reused data between each offset
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkAccess> generatedChunkByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkWrapper> chunkWrappersByDhPos = new HashMap<>();
// offset 1 chunk in both X and Z direction so we can generate an even number of chunks wide
// while still submitting odd numbers to MC's internal generators
for (int xOffset = 0; xOffset < 2; xOffset++)
{
// final is so the offset can be used in lambdas
final int xOffsetFinal = xOffset;
for (int zOffset = 0; zOffset < 2; zOffset++)
{
final int zOffsetFinal = zOffset;
//================//
// variable setup //
//================//
int radius = refSize / 2;
int centerX = refPosX + radius + xOffset;
int centerZ = refPosZ + radius + zOffset;
// get/create the list of chunks we're going to generate
ArrayGridList<ChunkAccess> regionChunks = new ArrayGridList<>(
refSize,
(x, z) -> this.generateEmptyChunk(
x + refPosX + xOffsetFinal,
z + refPosZ + zOffsetFinal,
chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos));
ChunkAccess centerChunk = regionChunks.stream().filter(chunk -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ).findFirst().get();
genEvent.refreshTimeout();
DhLitWorldGenRegion region = new DhLitWorldGenRegion(
centerX, centerZ,
centerChunk,
this.params.level, dummyLightEngine, regionChunks,
ChunkStatus.STRUCTURE_STARTS, radius,
// this method shouldn't be necessary since we're passing in a pre-populated
// list of chunks, but just in case
(x, z) -> this.generateEmptyChunk(x, z, chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos)
);
lightGetterAdaptor.setRegion(region);
genEvent.threadedParam.makeStructFeat(region, this.params);
//=========================//
// create chunk wrappers //
// and get existing chunks //
//=========================//
ArrayGridList<ChunkWrapper> chunkWrapperList = new ArrayGridList<>(regionChunks.gridSize);
regionChunks.forEachPos((relX, relZ) ->
{
// ArrayGridList's use relative positions and don't have a center position
// so we need to use the offsetFinal to select the correct position
DhChunkPos chunkPos = new DhChunkPos(relX + xOffsetFinal, relZ + zOffsetFinal);
ChunkAccess chunk = regionChunks.get(relX, relZ);
if (chunkWrappersByDhPos.containsKey(chunkPos))
{
chunkWrapperList.set(relX, relZ, chunkWrappersByDhPos.get(chunkPos));
}
else if (chunk != null)
{
// wrap the chunk
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, region, this.serverlevel.getLevelWrapper());
chunkWrapperList.set(relX, relZ, chunkWrapper);
// try setting the wrapper's lighting
if (chunkBlockLightingByDhPos.containsKey(chunkWrapper.getChunkPos()))
{
chunkWrapper.setBlockLightStorage(chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos()));
chunkWrapper.setSkyLightStorage(chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos()));
chunkWrapper.setUseDhLighting(true);
chunkWrapper.setIsDhLightCorrect(true);
} }
public ChunkAccess loadOrMakeChunk(ChunkPos chunkPos) chunkWrappersByDhPos.put(chunkPos, chunkWrapper);
}
else //if (chunk == null)
{
LodUtil.assertNotReach("Programmer Error: No chunk found in grid list, position offset is likely wrong.");
}
});
//=================//
// generate chunks //
//=================//
this.generateDirect(genEvent, chunkWrapperList, borderSize, genEvent.targetGenerationStep, region);
genEvent.timer.nextEvent("cleanup");
}
}
//=========================//
// submit generated chunks //
//=========================//
for (DhChunkPos dhChunkPos : chunkWrappersByDhPos.keySet())
{
ChunkWrapper wrappedChunk = chunkWrappersByDhPos.get(dhChunkPos);
ChunkAccess target = wrappedChunk.getChunk();
if (target instanceof LevelChunk)
{
#if MC_VER == MC_1_16_5 || MC_VER == 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 = ChunkWrapper.getStatus(target) == ChunkStatus.FULL || target instanceof LevelChunk;
#if MC_VER >= MC_1_18_2
boolean isPartial = target.isOldNoiseGeneration();
#endif
if (isFull)
{
LOAD_LOGGER.debug("Detected full existing chunk at {}", target.getPos());
genEvent.resultConsumer.accept(wrappedChunk);
}
#if MC_VER >= MC_1_18_2
else if (isPartial)
{
LOAD_LOGGER.debug("Detected old existing chunk at {}", target.getPos());
genEvent.resultConsumer.accept(wrappedChunk);
}
#endif
else if (ChunkWrapper.getStatus(target) == 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.debugInc("{}", genEvent.timer);
}
}
private ChunkAccess generateEmptyChunk(
int x, int z,
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos,
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos,
HashMap<DhChunkPos, ChunkAccess> generatedChunkByDhPos)
{
ChunkPos chunkPos = new ChunkPos(x, z);
DhChunkPos dhChunkPos = new DhChunkPos(x, z);
if (generatedChunkByDhPos.containsKey(dhChunkPos))
{
return generatedChunkByDhPos.get(dhChunkPos);
}
ChunkAccess newChunk = null;
try
{
// get the chunk
CompoundTag chunkData = this.getChunkNbtData(chunkPos);
newChunk = this.loadOrMakeChunk(chunkPos, chunkData);
if (Config.Client.Advanced.LodBuilding.pullLightingForPregeneratedChunks.get())
{
// attempt to get chunk lighting
ChunkLoader.CombinedChunkLightStorage combinedLights = ChunkLoader.readLight(newChunk, chunkData);
if (combinedLights != null)
{
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
}
}
}
catch (RuntimeException loadChunkError)
{
// Continue...
}
if (newChunk == null)
{
newChunk = new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if MC_VER >= MC_1_17_1 , this.params.level #endif
#if MC_VER >= MC_1_18_2 , this.params.biomes, null #endif
);
}
generatedChunkByDhPos.put(dhChunkPos, newChunk);
return newChunk;
}
private CompoundTag getChunkNbtData(ChunkPos chunkPos)
{ {
ServerLevel level = this.params.level; ServerLevel level = this.params.level;
//====================//
// get the chunk data //
//====================//
CompoundTag chunkData = null; CompoundTag chunkData = null;
try try
{ {
@@ -420,21 +655,21 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
LOAD_LOGGER.error("DistantHorizons: Couldn't load or make chunk " + chunkPos + ". Error: " + e.getMessage(), e); LOAD_LOGGER.error("DistantHorizons: Couldn't load or make chunk " + chunkPos + ". Error: " + e.getMessage(), e);
} }
return chunkData;
}
//========================// private ChunkAccess loadOrMakeChunk(ChunkPos chunkPos, CompoundTag chunkData)
// convert the chunk data // {
//========================// ServerLevel level = this.params.level;
if (chunkData == null) if (chunkData == null)
{ {
return EmptyChunk(level, chunkPos); return CreateEmptyChunk(level, chunkPos);
} }
else else
{ {
try try
{ {
LOAD_LOGGER.info("DistantHorizons: Loading chunk [" + chunkPos + "] from disk."); LOAD_LOGGER.debug("DistantHorizons: Loading chunk [" + chunkPos + "] from disk.");
return ChunkLoader.read(level, chunkPos, chunkData); return ChunkLoader.read(level, chunkPos, chunkData);
} }
catch (Exception e) catch (Exception e)
@@ -446,149 +681,26 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
"Error: ["+e.getMessage()+"]." "Error: ["+e.getMessage()+"]."
, e); , e);
return EmptyChunk(level, chunkPos); return CreateEmptyChunk(level, chunkPos);
} }
} }
} }
private static ProtoChunk CreateEmptyChunk(ServerLevel level, ChunkPos chunkPos)
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border)
{ {
return new ArrayGridList<>(total, border, total.gridSize - border); return new ProtoChunk(chunkPos, UpgradeData.EMPTY
} #if MC_VER >= MC_1_17_1 , level #endif
#if MC_VER >= MC_1_18_2 , level.registryAccess().registryOrThrow(
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) #if MC_VER < MC_1_19_4
{ Registry.BIOME_REGISTRY
return GetCutoutFrom(total, MaxBorderNeeded - BorderNeeded.get(step)); #else
} Registries.BIOME
#endif
public void generateLodFromList(GenerationEvent genEvent) throws InterruptedException ), null #endif
{
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 MC_VER >= MC_1_17_1 , params.level #endif
#if MC_VER >= 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_VER == MC_1_16_5 || MC_VER == 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 MC_VER >= 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 MC_VER >= MC_1_18_2
else if (isPartial)
{
LOAD_LOGGER.info("Detected old existing chunk at {}", target.getPos());
genEvent.resultConsumer.accept(wrappedChunk);
}
#endif
else if (target.getStatus() == ChunkStatus.EMPTY)
{
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( public void generateDirect(
GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunksToGenerate, int border, GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunksToGenerate, int border,
@@ -689,7 +801,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
for (int i = 0; i < chunksToGenerate.size(); i++) // regular for loop since enhanced for loops increase GC pressure slightly for (int i = 0; i < chunksToGenerate.size(); i++) // regular for loop since enhanced for loops increase GC pressure slightly
{ {
ChunkWrapper chunkWrapper = chunksToGenerate.get(i); ChunkWrapper chunkWrapper = chunksToGenerate.get(i);
if (chunkWrapper.getChunk().getStatus() != ChunkStatus.EMPTY) if (chunkWrapper.getStatus() != ChunkStatus.EMPTY)
{ {
iChunkWrapperList.add(chunkWrapper); iChunkWrapperList.add(chunkWrapper);
} }
@@ -710,19 +822,20 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
// if this isn't done everything else afterward may fail // if this isn't done everything else afterward may fail
Heightmap.primeHeightmaps(((ChunkWrapper)centerChunk).getChunk(), ChunkStatus.FEATURES.heightmapsAfter()); Heightmap.primeHeightmaps(((ChunkWrapper)centerChunk).getChunk(), ChunkStatus.FEATURES.heightmapsAfter());
// populate the lighting // pre-generated chunks should have lighting but new ones won't
if (!centerChunk.isLightCorrect())
{
DhLightingEngine.INSTANCE.lightChunk(centerChunk, iChunkWrapperList, maxSkyLight); DhLightingEngine.INSTANCE.lightChunk(centerChunk, iChunkWrapperList, maxSkyLight);
} }
}
genEvent.refreshTimeout(); genEvent.refreshTimeout();
} }
} }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border) { return new ArrayGridList<>(total, border, total.gridSize - border); }
//private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, MaxBorderNeeded - WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.get(step)); }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, 0); }
public interface EmptyChunkGenerator
{
ChunkAccess generate(int x, int z);
}
@Override @Override
public int getEventCount() { return this.generationEventList.size(); } public int getEventCount() { return this.generationEventList.size(); }
@@ -771,6 +884,12 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
return genEvent.future; return genEvent.future;
} }
//================//
// helper methods //
//================//
/** /**
* Called before code that may run for an extended period of time. <br> * Called before code that may run for an extended period of time. <br>
* This is necessary to allow canceling world gen since waiting * This is necessary to allow canceling world gen since waiting
@@ -784,4 +903,16 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
} }
} }
//================//
// helper classes //
//================//
@FunctionalInterface
public interface IEmptyChunkGeneratorFunc
{
ChunkAccess generate(int x, int z);
}
} }
@@ -43,6 +43,7 @@ public final class GenerationEvent
public final int id; public final int id;
public final ThreadedParameters threadedParam; public final ThreadedParameters threadedParam;
public final DhChunkPos minPos; public final DhChunkPos minPos;
/** the number of chunks wide this event is */
public final int size; public final int size;
public final EDhApiWorldGenerationStep targetGenerationStep; public final EDhApiWorldGenerationStep targetGenerationStep;
public EventTimer timer = null; public EventTimer timer = null;
@@ -73,10 +74,10 @@ public final class GenerationEvent
EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
ExecutorService worldGeneratorThreadPool) ExecutorService worldGeneratorThreadPool)
{ {
if (size % 2 == 0) //if (size % 2 == 0)
{ //{
size += 1; // size must be odd for vanilla world gen regions to work // size += 1; // size must be odd for vanilla world gen regions to work
} //}
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer); GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer);
@@ -93,9 +94,7 @@ public final class GenerationEvent
//LOGGER.info("generating [{}]", event.minPos); //LOGGER.info("generating [{}]", event.minPos);
genEnvironment.generateLodFromList(generationEvent); genEnvironment.generateLodFromList(generationEvent);
} }
catch (InterruptedException ignored) catch (InterruptedException ignored) { }
{
}
finally finally
{ {
BatchGenerationEnvironment.isDistantGeneratorThread.remove(); BatchGenerationEnvironment.isDistantGeneratorThread.remove();
@@ -126,21 +125,6 @@ public final class GenerationEvent
return this.future.isCancelled(); return this.future.isCancelled();
} }
public boolean tooClose(int minX, int minZ, int width)
{
int aMinX = this.minPos.x;
int aMinZ = this.minPos.z;
int aSize = this.size;
// Account for required empty chunks in the border
aSize += 1;
width += 1;
// Do a AABB to AABB intersection test
return (aMinX + aSize >= minX &&
aMinX <= minX + width &&
aMinZ + aSize >= minZ &&
aMinZ <= minZ + width);
}
public void refreshTimeout() public void refreshTimeout()
{ {
this.timeoutTime = System.nanoTime(); this.timeoutTime = System.nanoTime();
@@ -22,9 +22,14 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.Dynamic; import com.mojang.serialization.Dynamic;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
@@ -44,6 +49,7 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.*; import net.minecraft.world.level.*;
@@ -76,6 +82,14 @@ import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.material.Fluids;
#endif #endif
#if MC_VER == MC_1_20_6
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
#elif MC_VER >= MC_1_21_1
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
#endif
import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluid;
@@ -95,133 +109,13 @@ public class ChunkLoader
private static final String FLUID_TICKS_TAG_PRE18 = "LiquidTicks"; private static final String FLUID_TICKS_TAG_PRE18 = "LiquidTicks";
private static final ConfigBasedLogger LOGGER = BatchGenerationEnvironment.LOAD_LOGGER; private static final ConfigBasedLogger LOGGER = BatchGenerationEnvironment.LOAD_LOGGER;
#if MC_VER >= MC_1_18_2 private static boolean lightingSectionErrorLogged = false;
private static BlendingData readBlendingData(CompoundTag chunkData)
{
BlendingData blendingData = null;
if (chunkData.contains("blending_data", 10))
{
@SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null);
}
return blendingData;
}
#endif
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if MC_VER >= MC_1_18_2
#if MC_VER < MC_1_19_4
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#else
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#endif
#if MC_VER < MC_1_18_2
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_19_2
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#endif
#endif
int i = #if MC_VER < 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) //============//
{ // read chunk //
CompoundTag tagSection = tagSections.getCompound(j); //============//
int sectionYPos = tagSection.getByte("Y");
#if MC_VER < MC_1_18_2
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12))
{
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
levelChunkSection.getStates().read(tagSection.getList("Palette", 10),
tagSection.getLongArray("BlockStates"));
levelChunkSection.recalcBlockCounts();
if (!levelChunkSection.isEmpty())
chunkSections[#if MC_VER < MC_1_17_1 sectionYPos #else level.getSectionIndexFromSectionY(sectionYPos) #endif ]
= levelChunkSection;
}
#else
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
if (sectionId >= 0 && sectionId < chunkSections.length)
{
PalettedContainer<BlockState> blockStateContainer;
#if MC_VER < MC_1_18_2
PalettedContainer<Biome> biomeContainer;
#else
PalettedContainer<Holder<Biome>> biomeContainer;
#endif
blockStateContainer = tagSection.contains("block_states", 10)
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#if MC_VER < MC_1_18_2
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, i, (String) string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#endif
#if MC_VER < MC_1_20_1
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
#else
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
#endif
}
#endif
}
return chunkSections;
}
private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
{
CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps");
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
{
String heightmap = type.getSerializationKey();
if (tagHeightmaps.contains(heightmap, 12))
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
}
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
}
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
{
ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
for (int n = 0; n < tagPostProcessings.size(); ++n)
{
ListTag listTag3 = tagPostProcessings.getList(n);
for (int o = 0; o < listTag3.size(); ++o)
{
chunk.addPackedPostProcess(listTag3.getShort(o), n);
}
}
}
public static ChunkStatus.ChunkType readChunkType(CompoundTag tagLevel)
{
ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status"));
if (chunkStatus != null)
{
return chunkStatus.getChunkType();
}
return ChunkStatus.ChunkType.PROTOCHUNK;
}
public static LevelChunk read(WorldGenLevel level, ChunkPos chunkPos, CompoundTag chunkData) public static LevelChunk read(WorldGenLevel level, ChunkPos chunkPos, CompoundTag chunkData)
{ {
@@ -262,19 +156,27 @@ public class ChunkLoader
} }
} }
ChunkStatus.ChunkType chunkType = readChunkType(tagLevel); #if MC_VER < MC_1_20_6
ChunkStatus.ChunkType chunkType;
#else
ChunkType chunkType;
#endif
chunkType = readChunkType(tagLevel);
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK) if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
return null; return null;
#else #else
BlendingData blendingData = readBlendingData(tagLevel); BlendingData blendingData = readBlendingData(tagLevel);
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise())) if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
return null; return null;
#else #else
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && blendingData == null) if (chunkType == #if MC_VER < MC_1_20_6 ChunkStatus.ChunkType.PROTOCHUNK #else ChunkType.PROTOCHUNK #endif && blendingData == null)
return null; return null;
#endif #endif
#endif #endif
long inhabitedTime = tagLevel.getLong("InhabitedTime"); long inhabitedTime = tagLevel.getLong("InhabitedTime");
@@ -331,10 +233,293 @@ public class ChunkLoader
readPostPocessings(chunk, chunkData); readPostPocessings(chunk, chunkData);
return chunk; return chunk;
} }
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
private static void logErrors(ChunkPos chunkPos, int i, String string)
{ {
LOGGER.error("Distant Horizons: Recoverable errors when loading section [" + chunkPos.x + ", " + i + ", " + chunkPos.z + "]: " + string); #if MC_VER >= MC_1_18_2
#if MC_VER < MC_1_19_4
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#else
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#endif
#if MC_VER < MC_1_18_2
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_19_2
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#endif
#endif
int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex];
boolean isLightOn = chunkData.getBoolean("isLightOn");
boolean hasSkyLight = level.dimensionType().hasSkyLight();
ListTag tagSections = chunkData.getList("Sections", 10);
if (tagSections.isEmpty()) tagSections = chunkData.getList("sections", 10);
for (int j = 0; j < tagSections.size(); ++j)
{
CompoundTag tagSection = tagSections.getCompound(j);
int sectionYPos = tagSection.getByte("Y");
#if MC_VER < MC_1_18_2
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12))
{
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
levelChunkSection.getStates().read(tagSection.getList("Palette", 10),
tagSection.getLongArray("BlockStates"));
levelChunkSection.recalcBlockCounts();
if (!levelChunkSection.isEmpty())
chunkSections[#if MC_VER < MC_1_17_1 sectionYPos #else level.getSectionIndexFromSectionY(sectionYPos) #endif ]
= levelChunkSection;
}
#else
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
if (sectionId >= 0 && sectionId < chunkSections.length)
{
PalettedContainer<BlockState> blockStateContainer;
#if MC_VER < MC_1_18_2
PalettedContainer<Biome> biomeContainer;
#else
PalettedContainer<Holder<Biome>> biomeContainer;
#endif
blockStateContainer = tagSection.contains("block_states", 10)
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
#if MC_VER < MC_1_20_6
.getOrThrow(false, LOGGER::error)
#else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
#endif
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#if MC_VER < MC_1_18_2
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
#if MC_VER < MC_1_20_6
.getOrThrow(false, LOGGER::error)
#else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
#endif
: new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#endif
#if MC_VER < MC_1_20_1
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
#else
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
#endif
}
#endif
}
return chunkSections;
}
private static
#if MC_VER < MC_1_20_6 ChunkStatus.ChunkType
#elif MC_VER < MC_1_21_1 ChunkType
#else ChunkType #endif
readChunkType(CompoundTag tagLevel)
{
ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status"));
if (chunkStatus != null)
{
return chunkStatus.getChunkType();
}
return
#if MC_VER <= MC_1_20_4 ChunkStatus.ChunkType.PROTOCHUNK;
#else ChunkType.PROTOCHUNK; #endif
}
private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
{
CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps");
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
{
String heightmap = type.getSerializationKey();
if (tagHeightmaps.contains(heightmap, 12))
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
}
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
}
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
{
ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
for (int n = 0; n < tagPostProcessings.size(); ++n)
{
ListTag listTag3 = tagPostProcessings.getList(n);
for (int o = 0; o < listTag3.size(); ++o)
{
chunk.addPackedPostProcess(listTag3.getShort(o), n);
}
}
}
#if MC_VER >= MC_1_18_2
private static BlendingData readBlendingData(CompoundTag chunkData)
{
BlendingData blendingData = null;
if (chunkData.contains("blending_data", 10))
{
@SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null);
}
return blendingData;
}
#endif
//=====================//
// read chunk lighting //
//=====================//
/**
* https://minecraft.wiki/w/Chunk_format
*/
public static CombinedChunkLightStorage readLight(ChunkAccess chunk, CompoundTag chunkData)
{
#if MC_VER <= MC_1_17_1
// MC 1.16 and 1.17 doesn't have the necessary NBT info
return null;
#else
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getMinBuildHeight(chunk), ChunkWrapper.getMaxBuildHeight(chunk));
ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage;
ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage;
boolean foundSkyLight = false;
//===================//
// get NBT tags info //
//===================//
Tag chunkSectionTags = chunkData.get("sections");
if (chunkSectionTags == null)
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("No sections found for chunk at pos ["+chunk.getPos()+"] chunk data may be out of date.");
}
return null;
}
else if (!(chunkSectionTags instanceof ListTag))
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("Chunk section tag list have unexpected type ["+chunkSectionTags.getClass().getName()+"], expected ["+ListTag.class.getName()+"].");
}
return null;
}
ListTag chunkSectionListTag = (ListTag) chunkSectionTags;
//===================//
// get lighting info //
//===================//
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
{
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
if (!(chunkSectionTag instanceof CompoundTag))
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("Chunk section tag has an unexpected type ["+chunkSectionTag.getClass().getName()+"], expected ["+CompoundTag.class.getName()+"].");
}
return null;
}
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
// if null all lights = 0
byte[] blockLightNibbleArray = chunkSectionCompoundTag.getByteArray("BlockLight");
byte[] skyLightNibbleArray = chunkSectionCompoundTag.getByteArray("SkyLight");
// if any sky light was found then all lights above will be max brightness
if (skyLightNibbleArray.length != 0)
{
foundSkyLight = true;
}
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
{
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
{
// chunk sections are also 16 blocks tall
for (int relY = 0; relY < LodUtil.CHUNK_WIDTH; relY++)
{
int blockPosIndex = relY*16*16 + relZ*16 + relX;
byte blockLight = (blockLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(blockLightNibbleArray, blockPosIndex);
byte skyLight = (skyLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(skyLightNibbleArray, blockPosIndex);
if (skyLightNibbleArray.length == 0 && foundSkyLight)
{
skyLight = LodUtil.MAX_MC_LIGHT;
}
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getMinBuildHeight(chunk);
blockLightStorage.set(relX, y, relZ, blockLight);
skyLightStorage.set(relX, y, relZ, skyLight);
}
}
}
}
return combinedStorage;
#endif
}
/** source: https://minecraft.wiki/w/Chunk_format#Block_Format */
private static byte getNibbleAtIndex(byte[] arr, int index)
{
if (index % 2 == 0)
{
return (byte)(arr[index/2] & 0x0F);
}
else
{
return (byte)((arr[index/2]>>4) & 0x0F);
}
}
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+message+"]. This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
}
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+message+"]. This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
}
//================//
// helper classes //
//================//
public static class CombinedChunkLightStorage
{
public ChunkLightStorage blockLightStorage;
public ChunkLightStorage skyLightStorage;
public CombinedChunkLightStorage(int minY, int maxY)
{
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(minY, maxY);
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(minY, maxY);
}
} }
} }
@@ -0,0 +1,23 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
#if MC_VER >= MC_1_21_1
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.world.level.ChunkPos;
public class DhGenerationChunkHolder extends GenerationChunkHolder
{
public DhGenerationChunkHolder(ChunkPos pos)
{
super(pos);
}
@Override
public int getTicketLevel() { return 0; }
@Override
public int getQueueLevel() { return 0; }
}
#endif
@@ -22,7 +22,7 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.List; import java.util.List;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
@@ -52,11 +52,23 @@ import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; 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.ImposterProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.lighting.LevelLightEngine;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.*;
#endif
#if MC_VER >= MC_1_21_1
import net.minecraft.util.StaticCache2D;
import com.google.common.collect.ImmutableList;
import net.minecraft.server.level.GenerationChunkHolder;
#endif
public class DhLitWorldGenRegion extends WorldGenRegion public class DhLitWorldGenRegion extends WorldGenRegion
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
@@ -65,7 +77,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
public final DummyLightEngine lightEngine; public final DummyLightEngine lightEngine;
public final BatchGenerationEnvironment.EmptyChunkGenerator generator; public final BatchGenerationEnvironment.IEmptyChunkGeneratorFunc generator;
public final int writeRadius; public final int writeRadius;
public final int size; public final int size;
@@ -106,11 +118,29 @@ public class DhLitWorldGenRegion extends WorldGenRegion
public DhLitWorldGenRegion( public DhLitWorldGenRegion(
int centerChunkX, int centerChunkZ,
ChunkAccess centerChunk,
ServerLevel serverLevel, DummyLightEngine lightEngine, ServerLevel serverLevel, DummyLightEngine lightEngine,
List<ChunkAccess> chunkList, ChunkStatus chunkStatus, int writeRadius, List<ChunkAccess> chunkList, ChunkStatus chunkStatus, int writeRadius,
BatchGenerationEnvironment.EmptyChunkGenerator generator) BatchGenerationEnvironment.IEmptyChunkGeneratorFunc generator)
{ {
super(serverLevel, chunkList #if MC_VER >= MC_1_17_1 , chunkStatus, writeRadius #endif ); #if MC_VER == MC_1_16_5
super(serverLevel, chunkList);
#elif MC_VER < MC_1_21_1
super(serverLevel, chunkList, chunkStatus, writeRadius);
#else
super(serverLevel,
StaticCache2D.create(
centerChunkX, centerChunkZ,
writeRadius * 2, (x,z) -> new DhGenerationChunkHolder(new ChunkPos(x, z))),
new ChunkStep(chunkStatus,
// reverse is needed because MC uses the index of the chunkStatus to determine how many items are in the list instead of the actual list count
new ChunkDependencies(ImmutableList.copyOf(ChunkStatus.getStatusList()).reverse()),
new ChunkDependencies(ImmutableList.copyOf(ChunkStatus.getStatusList()).reverse()),
writeRadius, (WorldGenContext var1, ChunkStep var2, StaticCache2D<GenerationChunkHolder> var3, ChunkAccess var4) -> null),
centerChunk);
#endif
this.firstPos = chunkList.get(0).getPos(); this.firstPos = chunkList.get(0).getPos();
this.generator = generator; this.generator = generator;
this.lightEngine = lightEngine; this.lightEngine = lightEngine;
@@ -204,6 +234,22 @@ public class DhLitWorldGenRegion extends WorldGenRegion
#endif #endif
} }
/**
* This needs to be manually overridden to make sure Lithium 0.11.2 and lower
* don't try to get null chunks. <br><br>
*
* Problematic Lithium code was removed in 0.13.0 (MC 1.21.1) and higher: <br>
* https://github.com/CaffeineMC/lithium-fabric/commit/b7cfd53a1ed0197e1d13dea2799b898eb52ecab3
*/
@NotNull
@Override
public BlockState getBlockState(BlockPos blockPos)
{
int chunkX = SectionPos.blockToSectionCoord(blockPos.getX());
int chunkZ = SectionPos.blockToSectionCoord(blockPos.getZ());
return this.getChunk(chunkX, chunkZ).getBlockState(blockPos);
}
/** Skip BlockEntity stuff. They aren't needed for our use case. */ /** Skip BlockEntity stuff. They aren't needed for our use case. */
@Override @Override
public boolean addFreshEntity(@NotNull Entity entity) { return true; } public boolean addFreshEntity(@NotNull Entity entity) { return true; }
@@ -274,7 +320,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
private ChunkAccess getChunkAccess(int chunkX, int chunkZ, ChunkStatus chunkStatus, boolean returnNonNull) private ChunkAccess getChunkAccess(int chunkX, int chunkZ, ChunkStatus chunkStatus, boolean returnNonNull)
{ {
ChunkAccess chunk = this.superHasChunk(chunkX, chunkZ) ? this.superGetChunk(chunkX, chunkZ) : null; ChunkAccess chunk = this.superHasChunk(chunkX, chunkZ) ? this.superGetChunk(chunkX, chunkZ) : null;
if (chunk != null && chunk.getStatus().isOrAfter(chunkStatus)) if (chunk != null && ChunkWrapper.getStatus(chunk).isOrAfter(chunkStatus))
{ {
return chunk; return chunk;
} }
@@ -23,15 +23,24 @@ import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IStarlightAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IStarlightAccessor;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.chunk.LightChunkGetter;
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.LevelHeightAccessor;
#endif #endif
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LightChunkGetter;
#if MC_VER >= MC_1_20_1 #if MC_VER >= MC_1_20_1
import net.minecraft.world.level.chunk.LightChunk; import net.minecraft.world.level.chunk.LightChunk;
#endif #endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public class LightGetterAdaptor implements LightChunkGetter public class LightGetterAdaptor implements LightChunkGetter
{ {
private final BlockGetter heightGetter; private final BlockGetter heightGetter;
@@ -17,6 +17,10 @@ import java.nio.file.Path;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
#if MC_VER >= MC_1_20_6
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
#endif
/** /**
* @deprecated should be replaced with net.minecraft.world.level.chunk.storage.IOWorker to * @deprecated should be replaced with net.minecraft.world.level.chunk.storage.IOWorker to
* prevent potential file corruption and issues with the C2ME mod. * prevent potential file corruption and issues with the C2ME mod.
@@ -180,8 +184,10 @@ public class RegionFileStorageExternalCache implements AutoCloseable
Path regionFilePath = storageFolderPath.resolve("r." + pos.getRegionX() + "." + pos.getRegionZ() + ".mca"); Path regionFilePath = storageFolderPath.resolve("r." + pos.getRegionX() + "." + pos.getRegionZ() + ".mca");
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
rFile = new RegionFile(regionFilePath.toFile(), storageFolderPath.toFile(), false); rFile = new RegionFile(regionFilePath.toFile(), storageFolderPath.toFile(), false);
#else #elif MC_VER <= MC_1_20_4
rFile = new RegionFile(regionFilePath, storageFolderPath, false); rFile = new RegionFile(regionFilePath, storageFolderPath, false);
#else
rFile = new RegionFile(new RegionStorageInfo("level", null, "level type"), regionFilePath, storageFolderPath, false);
#endif #endif
this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ()), rFile)); this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ()), rFile));
@@ -35,7 +35,6 @@ import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.levelgen.WorldGenSettings; import net.minecraft.world.level.levelgen.WorldGenSettings;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature; import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
@@ -57,6 +56,12 @@ import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.feature.StructureFeature; import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif #endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public class WorldGenStructFeatManager extends #if MC_VER < MC_1_19_2 StructureFeatureManager #else StructureManager #endif public class WorldGenStructFeatManager extends #if MC_VER < MC_1_19_2 StructureFeatureManager #else StructureManager #endif
@@ -27,15 +27,19 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGeneratio
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import net.minecraft.server.level.WorldGenRegion; import net.minecraft.server.level.WorldGenRegion;
#if MC_VER < MC_1_19_2
#endif
import net.minecraft.world.level.chunk.ChunkAccess; 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.chunk.ProtoChunk;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.Blender; import net.minecraft.world.level.levelgen.blending.Blender;
#endif #endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepBiomes public final class StepBiomes
{ {
public static final ChunkStatus STATUS = ChunkStatus.BIOMES; public static final ChunkStatus STATUS = ChunkStatus.BIOMES;
@@ -52,29 +56,44 @@ public final class StepBiomes
List<ChunkWrapper> chunkWrappers) List<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>(); ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue; if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk); chunksToDo.add(chunk);
} }
}
for (ChunkAccess chunk : chunksToDo) for (ChunkAccess chunk : chunksToDo)
{ {
// System.out.println("StepBiomes: "+chunk.getPos()); // System.out.println("StepBiomes: "+chunk.getPos());
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
environment.params.generator.createBiomes(environment.params.biomes, chunk); this.environment.params.generator.createBiomes(this.environment.params.biomes, chunk);
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, Blender.of(worldGenRegion), chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_19_4 #elif MC_VER < MC_1_19_4
chunk = environment.joinSync(environment.params.generator.createBiomes(environment.params.biomes, Runnable::run, environment.params.randomState, Blender.of(worldGenRegion), chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_21_1
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else #else
chunk = environment.joinSync(environment.params.generator.createBiomes(Runnable::run, environment.params.randomState, Blender.of(worldGenRegion), chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#endif #endif
} }
@@ -27,11 +27,16 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.Heightmap;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepFeatures public final class StepFeatures
{ {
@@ -54,14 +59,18 @@ public final class StepFeatures
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) if (chunkWrapper.getStatus().isOrAfter(STATUS))
{ {
// this chunk has already generated this step
continue; continue;
} }
else if (chunk instanceof ProtoChunk)
if (chunk instanceof ProtoChunk)
{ {
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
} }
@@ -71,7 +80,7 @@ public final class StepFeatures
worldGenRegion.setOverrideCenter(chunk.getPos()); worldGenRegion.setOverrideCenter(chunk.getPos());
environment.params.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeat); environment.params.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeat);
#else #else
if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().x, chunkWrapper.getChunkPos().z)) if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().getX(), chunkWrapper.getChunkPos().getZ()))
{ {
this.environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeat.forWorldGenRegion(worldGenRegion)); this.environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeat.forWorldGenRegion(worldGenRegion));
} }
@@ -28,17 +28,19 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParame
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import net.minecraft.server.level.WorldGenRegion; import net.minecraft.server.level.WorldGenRegion;
#if MC_VER >= MC_1_17_1
#endif
#if MC_VER < MC_1_19_2
#endif
import net.minecraft.world.level.chunk.ChunkAccess; 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.chunk.ProtoChunk;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.Blender; import net.minecraft.world.level.levelgen.blending.Blender;
#endif #endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepNoise public final class StepNoise
{ {
private static final ChunkStatus STATUS = ChunkStatus.NOISE; private static final ChunkStatus STATUS = ChunkStatus.NOISE;
@@ -56,13 +58,21 @@ public final class StepNoise
List<ChunkWrapper> chunkWrappers) List<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>(); ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue; if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
continue;
}
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk); chunksToDo.add(chunk);
} }
@@ -70,15 +80,18 @@ public final class StepNoise
{ {
// System.out.println("StepNoise: "+chunk.getPos()); // System.out.println("StepNoise: "+chunk.getPos());
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk); this.environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk);
#elif MC_VER < MC_1_18_2 #elif MC_VER < MC_1_18_2
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_21_1
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else #else
chunk = environment.joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), environment.params.randomState, chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Blender.of(worldGenRegion), this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#endif #endif
UncheckedInterruptedException.throwIfInterrupted(); UncheckedInterruptedException.throwIfInterrupted();
@@ -27,12 +27,16 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGeneratio
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import net.minecraft.server.level.WorldGenRegion; import net.minecraft.server.level.WorldGenRegion;
#if MC_VER < MC_1_19_2
#endif
import net.minecraft.world.level.chunk.ChunkAccess; 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.chunk.ProtoChunk;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepStructureReference public final class StepStructureReference
{ {
private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES; private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES;
@@ -55,10 +59,21 @@ public final class StepStructureReference
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue; if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk); chunksToDo.add(chunk);
} }
}
for (ChunkAccess chunk : chunksToDo) for (ChunkAccess chunk : chunksToDo)
{ {
@@ -30,10 +30,16 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParame
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.server.level.WorldGenRegion; import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess; 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.chunk.ProtoChunk;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepStructureStart public final class StepStructureStart
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -70,20 +76,29 @@ public final class StepStructureStart
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (!chunk.getStatus().isOrAfter(STATUS)) if (chunkWrapper.getStatus().isOrAfter(STATUS))
{ {
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk); chunksToDo.add(chunk);
} }
} }
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
if (environment.params.worldGenSettings.generateFeatures()) if (this.environment.params.worldGenSettings.generateFeatures())
{ {
#elif MC_VER < MC_1_19_4 #elif MC_VER < MC_1_19_4
if (environment.params.worldGenSettings.generateStructures()) { if (this.environment.params.worldGenSettings.generateStructures()) {
#else #else
if (environment.params.worldOptions.generateStructures()) if (this.environment.params.worldOptions.generateStructures())
{ {
#endif #endif
for (ChunkAccess chunk : chunksToDo) for (ChunkAccess chunk : chunksToDo)
@@ -28,9 +28,14 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParame
import net.minecraft.server.level.WorldGenRegion; import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess; 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.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepSurface public final class StepSurface
{ {
@@ -53,10 +58,22 @@ public final class StepSurface
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk.getStatus().isOrAfter(STATUS)) continue; if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk); chunksToDo.add(chunk);
} }
}
for (ChunkAccess chunk : chunksToDo) for (ChunkAccess chunk : chunksToDo)
{ {
@@ -0,0 +1,46 @@
accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
# used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList;
#accessible method net/minecraft/client/renderer/LevelRenderer renderSectionLayer (Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V
# world generation
# accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage;
accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
accessible field net/minecraft/world/level/chunk/storage/IOWorker storage Lnet/minecraft/world/level/chunk/storage/RegionFileStorage;
accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage regionCache Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage folder Ljava/nio/file/Path;
# grabbing textures
accessible class net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameX (I)I
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameY (I)I
accessible field net/minecraft/client/renderer/texture/SpriteContents animatedTexture Lnet/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture;
accessible field net/minecraft/client/renderer/texture/SpriteContents originalImage Lcom/mojang/blaze3d/platform/NativeImage;
# UI stuff
accessible field net/minecraft/client/gui/components/AbstractButton SPRITES Lnet/minecraft/client/gui/components/WidgetSprites;
# Handles inserting the config button
accessible field net/minecraft/client/gui/layouts/HeaderAndFooterLayout headerFrame Lnet/minecraft/client/gui/layouts/FrameLayout;
accessible field net/minecraft/client/gui/layouts/FrameLayout children Ljava/util/List;
accessible class net/minecraft/client/gui/layouts/FrameLayout$ChildContainer
accessible field net/minecraft/client/gui/layouts/LinearLayout wrapped Lnet/minecraft/client/gui/layouts/GridLayout;
# hacky stuff
accessible field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
mutable field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
accessible field net/minecraft/client/gui/components/AbstractSelectionList scrollAmount D # Hack to bypass vanilla's setScrollAmount's clamp
+7 -4
View File
@@ -45,7 +45,7 @@ def addMod(path, enabled) {
} }
dependencies { dependencies {
minecraft "com.mojang:minecraft:${minecraft_version}" minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
mappings loom.layered() { mappings loom.layered() {
// Mojmap mappings // Mojmap mappings
officialMojangMappings() officialMojangMappings()
@@ -70,12 +70,14 @@ dependencies {
addModJar(fabricApi.module("fabric-rendering-v1", rootProject.fabric_api_version)) // TODO: Remove this as it is only needed in 1 line (FabricClientProxy) addModJar(fabricApi.module("fabric-rendering-v1", rootProject.fabric_api_version)) // TODO: Remove this as it is only needed in 1 line (FabricClientProxy)
addModJar(fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version)) addModJar(fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version))
// used by mod menu in MC 1.20.6+
addModJar(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-key-binding-api-v1", rootProject.fabric_api_version))
// Mod Menu // Mod Menu
modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}")
// Starlight // Starlight
addMod("curse.maven:starlight-521783:${rootProject.starlight_version_fabric}", rootProject.enable_starlight) addMod("curse.maven:starlight-521783:${rootProject.starlight_version_fabric}", rootProject.enable_starlight)
@@ -103,8 +105,9 @@ dependencies {
addMod("io.vram:canvas-fabric-${project.canvas_version}", rootProject.enable_canvas) addMod("io.vram:canvas-fabric-${project.canvas_version}", rootProject.enable_canvas)
// Immersive Portals // Immersive Portals
if (rootProject.enable_immersive_portals == "1") if (rootProject.enable_immersive_portals == "1") {
modCompileOnly("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${project.immersive_portals_version}") modCompileOnly("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${project.immersive_portals_version}")
}
else if (rootProject.enable_immersive_portals == "2") { else if (rootProject.enable_immersive_portals == "2") {
modImplementation ("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${project.immersive_portals_version}") { modImplementation ("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${project.immersive_portals_version}") {
exclude(group: "net.fabricmc.fabric-api") exclude(group: "net.fabricmc.fabric-api")
@@ -19,6 +19,8 @@
package com.seibel.distanthorizons.fabric; package com.seibel.distanthorizons.fabric;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
@@ -27,15 +29,14 @@ import com.mojang.blaze3d.platform.InputConstants;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.fabric.wrappers.modAccessor.SodiumAccessor; import com.seibel.distanthorizons.fabric.wrappers.modAccessor.SodiumAccessor;
//import io.netty.buffer.ByteBuf; //import io.netty.buffer.ByteBuf;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
@@ -52,12 +53,14 @@ import net.minecraft.client.gui.screens.TitleScreen;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
#endif #endif
import java.util.HashSet; import java.util.HashSet;
import java.util.concurrent.ThreadPoolExecutor;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.joml.Matrix4f;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
/** /**
@@ -113,12 +116,16 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// ClientChunkLoadEvent // ClientChunkLoadEvent
ClientChunkEvents.CHUNK_LOAD.register((level, chunk) -> ClientChunkEvents.CHUNK_LOAD.register((level, chunk) ->
{
if (MC.clientConnectedToDedicatedServer())
{ {
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level); IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel); SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
}
}); });
// (kinda) block break event // (kinda) block break event
// Since fabric doesn't have a client-side break-block API event, this is the next best thing
AttackBlockCallback.EVENT.register((player, level, interactionHand, blockPos, direction) -> AttackBlockCallback.EVENT.register((player, level, interactionHand, blockPos, direction) ->
{ {
// if we have access to the server, use the chunk save event instead // if we have access to the server, use the chunk save event instead
@@ -126,7 +133,14 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(blockPos.getX(), blockPos.getZ())) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(blockPos.getX(), blockPos.getZ()))
{ {
// Since fabric doesn't have a client-side break-block API event, this is the next best thing // executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(blockPos); ChunkAccess chunk = level.getChunk(blockPos);
if (chunk != null) if (chunk != null)
{ {
@@ -138,6 +152,8 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
wrappedLevel wrappedLevel
); );
} }
});
}
} }
} }
@@ -146,16 +162,24 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
}); });
// (kinda) block place event // (kinda) block place event
// Since fabric doesn't have a client-side place-block API event, this is the next best thing
UseBlockCallback.EVENT.register((player, level, hand, hitResult) -> UseBlockCallback.EVENT.register((player, level, hand, hitResult) ->
{ {
// if we have access to the server, use the chunk save event instead // if we have access to the server, use the chunk save event instead
if (MC.clientConnectedToDedicatedServer()) if (MC.clientConnectedToDedicatedServer())
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(hitResult.getBlockPos().getX(), hitResult.getBlockPos().getZ()))
{
// Since fabric doesn't have a client-side place-block API event, this is the next best thing
if (hitResult.getType() == HitResult.Type.BLOCK if (hitResult.getType() == HitResult.Type.BLOCK
&& !hitResult.isInside()) && !hitResult.isInside())
{
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(hitResult.getBlockPos().getX(), hitResult.getBlockPos().getZ()))
{
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{ {
ChunkAccess chunk = level.getChunk(hitResult.getBlockPos()); ChunkAccess chunk = level.getChunk(hitResult.getBlockPos());
if (chunk != null) if (chunk != null)
@@ -168,6 +192,8 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
wrappedLevel wrappedLevel
); );
} }
});
}
} }
} }
} }
@@ -177,14 +203,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
}); });
// Client Chunk Save
ClientChunkEvents.CHUNK_UNLOAD.register((level, chunk) ->
{
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
SharedApi.INSTANCE.chunkUnloadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
});
//==============// //==============//
// render event // // render event //
@@ -192,10 +210,24 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
WorldRenderEvents.AFTER_SETUP.register((renderContext) -> WorldRenderEvents.AFTER_SETUP.register((renderContext) ->
{ {
Mat4f projectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f modelViewMatrix;
#if MC_VER < MC_1_20_6
modelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
#else
modelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
#endif
this.clientApi.renderLods(ClientLevelWrapper.getWrapper(renderContext.world()), this.clientApi.renderLods(ClientLevelWrapper.getWrapper(renderContext.world()),
McObjectConverter.Convert(renderContext.matrixStack().last().pose()), modelViewMatrix,
McObjectConverter.Convert(renderContext.projectionMatrix()), projectionMatrix,
renderContext.tickDelta()); #if MC_VER < MC_1_21_1
renderContext.tickDelta()
#else
renderContext.tickCounter().getGameTimeDeltaTicks()
#endif
);
}); });
// Debug keyboard event // Debug keyboard event
@@ -77,7 +77,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
// If sodium is installed Indium is also necessary in order to use the Fabric rendering API // If sodium is installed Indium is also necessary in order to use the Fabric rendering API
if (!modChecker.isModLoaded("indium")) if (!modChecker.isModLoaded("indium"))
{ {
String indiumMissingMessage = ModInfo.READABLE_NAME + " now relies on Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium"; String indiumMissingMessage = ModInfo.READABLE_NAME + " needs Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium";
LOGGER.fatal(indiumMissingMessage); LOGGER.fatal(indiumMissingMessage);
if (!GraphicsEnvironment.isHeadless()) if (!GraphicsEnvironment.isHeadless())
@@ -124,7 +124,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
#if MC_VER >= MC_1_20_1 #if MC_VER >= MC_1_20_1
if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium")) if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium"))
ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false); // FIXME: This is a tmp fix for sodium 0.5.0, and 0.5.1. This is fixed in sodium 0.5.2 ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false);
#endif #endif
if (ConfigBase.INSTANCE == null) if (ConfigBase.INSTANCE == null)
@@ -1,5 +1,7 @@
package com.seibel.distanthorizons.fabric; package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.api.methods.events.DhApiEventRegister;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper; import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
@@ -10,6 +12,7 @@ import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
@@ -75,6 +78,14 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
// ServerTickEvent // ServerTickEvent
ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent()); ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent());
// can be enabled to test world gen overrides without having to build a separate API project
if (false)
{
DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent());
}
// ServerWorldLoadEvent // ServerWorldLoadEvent
//TODO: Check if both of these use the correct timed events. (i.e. is it 'ed' or 'ing' one?) //TODO: Check if both of these use the correct timed events. (i.e. is it 'ed' or 'ing' one?)
ServerLifecycleEvents.SERVER_STARTING.register((server) -> ServerLifecycleEvents.SERVER_STARTING.register((server) ->
@@ -1,69 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.fabric.mixins.client;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.common.util.ILightTextureMarker;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(DynamicTexture.class)
public class MixinDynamicTexture implements ILightTextureMarker
{
/** Used to prevent accidentally using other dynamic textures as a lightmap */
@Unique
private boolean isLightTexture = false;
@Shadow
@Final
private NativeImage pixels;
@Inject(method = "upload()V", at = @At("HEAD"))
public void updateLightTexture(CallbackInfo ci)
{
// since the light map is always updated on the client render thread we should be able to access the client level at the same time
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (!this.isLightTexture
|| mc == null
|| mc.getWrappedClientLevel() == null
)
{
return;
}
//ApiShared.LOGGER.info("Lightmap update");
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.pixels, clientLevel);
}
public void markLightTexture() { this.isLightTexture = true; }
}
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.fabric.mixins.client;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
import org.lwjgl.opengl.GL32;
#else #else
import org.joml.Matrix4f; import org.joml.Matrix4f;
#endif #endif
@@ -29,23 +30,17 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Mat4f;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.LightTexture;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import net.minecraft.client.Camera;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.lighting.LevelLightEngine;
import org.lwjgl.opengl.GL15;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -84,33 +79,49 @@ public class MixinLevelRenderer
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V", method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true) cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback) private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
#else #elif MC_VER < MC_1_20_6
@Inject(at = @At("HEAD"), @Inject(at = @At("HEAD"),
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V", method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true) cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback) private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback)
#else
@Inject(at = @At("HEAD"),
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;DDDLorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f projectionMatrix, Matrix4f frustumMatrix, CallbackInfo callback)
#endif #endif
{ {
#if MC_VER == MC_1_16_5 #if MC_VER == MC_1_16_5
// get the matrices from the OpenGL fixed pipeline // get the matrices from the OpenGL fixed pipeline
float[] mcProjMatrixRaw = new float[16]; float[] mcProjMatrixRaw = new float[16];
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw); GL32.glGetFloatv(GL32.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw); Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
mcProjectionMatrix.transpose(); mcProjectionMatrix.transpose();
Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose()); Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
#else #elif MC_VER <= MC_1_20_4
// get the matrices directly from MC // get the matrices directly from MC
Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose()); Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix); Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#else
// MC combined the model view and projection matricies
Mat4f mcModelViewMatrix = McObjectConverter.Convert(projectionMatrix);
Mat4f mcProjectionMatrix = new Mat4f();
mcProjectionMatrix.setIdentity();
#endif #endif
if (renderType.equals(RenderType.translucent())) { if (renderType.equals(RenderType.translucent()))
{
ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(this.level), ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(this.level),
mcModelViewMatrix, mcModelViewMatrix,
mcProjectionMatrix, mcProjectionMatrix,
Minecraft.getInstance().getFrameTime()); #if MC_VER < MC_1_21_1
Minecraft.getInstance().getFrameTime()
#else
Minecraft.getInstance().getTimer().getRealtimeDeltaTicks()
#endif
);
} }
// FIXME completely disables rendering when sodium is installed // FIXME completely disables rendering when sodium is installed
@@ -126,9 +137,12 @@ public class MixinLevelRenderer
#elif MC_VER < MC_1_20_1 #elif MC_VER < MC_1_20_1
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runUpdates(IZZ)I"), method = "renderLevel") @Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runUpdates(IZZ)I"), method = "renderLevel")
public void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci) public void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
#else #elif MC_VER < MC_1_20_6
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I"), method = "renderLevel") @Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I"), method = "renderLevel")
private void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci) private void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
#else
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I"), method = "renderLevel")
private void callAfterRunUpdates(CallbackInfo ci)
#endif #endif
{ {
ChunkWrapper.syncedUpdateClientLightStatus(); ChunkWrapper.syncedUpdateClientLightStatus();
@@ -20,9 +20,14 @@
package com.seibel.distanthorizons.fabric.mixins.client; package com.seibel.distanthorizons.fabric.mixins.client;
import com.seibel.distanthorizons.common.util.ILightTextureMarker; import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -35,13 +40,20 @@ public class MixinLightTexture
{ {
@Shadow @Shadow
@Final @Final
private DynamicTexture lightTexture; private NativeImage lightPixels;
@Inject(method = "<init>", at = @At("RETURN"))
public void markLightTexture(CallbackInfo ci) @Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci)
{ {
// IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
((ILightTextureMarker) this.lightTexture).markLightTexture(); if (mc == null || mc.getWrappedClientLevel() == null)
{
return;
}
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel);
} }
} }
@@ -8,10 +8,12 @@ import com.seibel.distanthorizons.core.jar.installer.GitlabGetter;
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter; import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import net.minecraft.client.Minecraft; import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@@ -25,6 +27,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Minecraft.class) @Mixin(Minecraft.class)
public class MixinMinecraft public class MixinMinecraft
{ {
/**
* Can be enabled for testing the auto updater UI. <br/>
* will always show the auto updater if set to true.
*/
@Unique
private static final boolean DEBUG_ALWAYS_SHOW_UPDATER = false;
#if MC_VER < MC_1_20_2 #if MC_VER < MC_1_20_2
#if MC_VER == MC_1_20_1 #if MC_VER == MC_1_20_1
@Redirect( @Redirect(
@@ -41,13 +52,13 @@ public class MixinMinecraft
public void onOpenScreen(Minecraft instance, Screen guiScreen) public void onOpenScreen(Minecraft instance, Screen guiScreen)
{ {
#endif #endif
if (!Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get()) // Don't do anything if the user doesn't want it if (!Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() && !DEBUG_ALWAYS_SHOW_UPDATER) // Don't do anything if the user doesn't want it
{ {
instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
return; return;
} }
if (SelfUpdater.onStart()) if (SelfUpdater.onStart() || DEBUG_ALWAYS_SHOW_UPDATER)
{ {
instance.setScreen(new UpdateModScreen( instance.setScreen(new UpdateModScreen(
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
@@ -69,17 +80,31 @@ public class MixinMinecraft
private void buildInitialScreens(Runnable runnable) private void buildInitialScreens(Runnable runnable)
{ {
if ( if (
Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() // Don't do anything if the user doesn't want it DEBUG_ALWAYS_SHOW_UPDATER ||
(
// Don't do anything if the user doesn't want it
Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get()
&& SelfUpdater.onStart() && SelfUpdater.onStart()
) )
)
{ {
runnable = () -> { runnable = () ->
{
String versionId;
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
if (updateBranch == EDhApiUpdateBranch.STABLE)
{
versionId = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
}
else
{
versionId = GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha");
}
Minecraft.getInstance().setScreen(new UpdateModScreen( Minecraft.getInstance().setScreen(new UpdateModScreen(
// TODO: Change to runnable, instead of tittle screen // TODO: Change to runnable, instead of tittle screen
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE versionId
? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion())
: GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
)); ));
}; };
} }
@@ -23,7 +23,6 @@ import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget; import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import net.minecraft.client.gui.screens.OptionsScreen;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
@@ -31,51 +30,127 @@ import net.minecraft.network.chat.TranslatableComponent;
#endif #endif
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
#if MC_VER >= MC_1_20_6
import net.minecraft.client.gui.layouts.LinearLayout;
import net.minecraft.client.gui.layouts.HeaderAndFooterLayout;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Shadow;
#endif
#if MC_VER < MC_1_21_1
import net.minecraft.client.gui.screens.OptionsScreen;
#else
import net.minecraft.client.gui.screens.options.OptionsScreen;
#endif
/** /**
* Adds a button to the menu to goto the config * Adds a button to the menu to goto the config
* *
* @author coolGi * @author coolGi
* @version 12-02-2021 * @version 2024-5-20
*/ */
@Mixin(OptionsScreen.class) @Mixin(OptionsScreen.class)
public class MixinOptionsScreen extends Screen public class MixinOptionsScreen extends Screen
{ {
// Get the texture for the button /** Texture used for the config opening button */
private static final ResourceLocation ICON_TEXTURE = new ResourceLocation(ModInfo.ID, "textures/gui/button.png"); @Unique
protected MixinOptionsScreen(Component title) private static final ResourceLocation ICON_TEXTURE =
{ #if MC_VER < MC_1_21_1
super(title); new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
} #else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png");
#endif
@Inject(at = @At("HEAD"), method = "init")
@Unique
private TexturedButtonWidget optionsButton = null;
#if MC_VER >= MC_1_20_6
@Shadow
@Final
protected HeaderAndFooterLayout layout;
#endif
//==============//
// constructors //
//==============//
protected MixinOptionsScreen(Component title) { super(title); }
@Inject(at = @At("RETURN"), method = "init")
private void lodconfig$init(CallbackInfo ci) private void lodconfig$init(CallbackInfo ci)
{ {
if (Config.Client.optionsButton.get()) if (Config.Client.optionsButton.get())
this. #if MC_VER < MC_1_17_1 addButton #else addRenderableWidget #endif {
(new TexturedButtonWidget( #if MC_VER < MC_1_17_1
this.addButton(this.getOptionsButton());
#elif MC_VER < MC_1_20_6
this.addRenderableWidget(this.getOptionsButton());
#else
// add the button so it's rendered
this.addRenderableWidget(this.getOptionsButton());
// add the button to the correct location in the UI
// TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements?
LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.get(0).child;
// determine how wide the other option buttons are so we can put our botton to the left of them all
AtomicInteger width = new AtomicInteger(0);
layout.visitChildren(x -> { width.addAndGet(x.getWidth()); });
width.addAndGet(-10); // padding between the DH button and the FOV slider
layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> { settings.paddingLeft(width.get() * -1); });
layout.arrangeElements();
#endif
}
}
//================//
// helper methods //
//================//
@Unique
public TexturedButtonWidget getOptionsButton()
{
if (this.optionsButton == null)
{
this.optionsButton
= new TexturedButtonWidget(
// Where the button is on the screen // Where the button is on the screen
this.width / 2 - 180, this.height / 6 - 12, this.width / 2 - 180, this.height / 6 - 12,
// Width and height of the button // Width and height of the button
20, 20, 20, 20,
// Offset // texture UV Offset
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
20, ICON_TEXTURE, 20, 40, 20, ICON_TEXTURE, 20, 40,
// Create the button and tell it where to go // Create the button and tell it where to go
// For now it goes to the client option by default // For now it goes to the client option by default
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(GetConfigScreen.getScreen(this)), (buttonWidget) -> Objects.requireNonNull(this.minecraft).setScreen(GetConfigScreen.getScreen(this)),
// Add a title to the utton // Add a title to the button
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
new TranslatableComponent(ModInfo.ID + ".title"))); new TranslatableComponent(ModInfo.ID + ".title"));
#else #else
Component.translatable(ModInfo.ID + ".title"))); Component.translatable(ModInfo.ID + ".title"));
#endif #endif
} }
return this.optionsButton;
}
} }
@@ -1,8 +1,6 @@
package com.seibel.distanthorizons.fabric.mixins.server; package com.seibel.distanthorizons.fabric.mixins.server;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.commonMixins.MixinChunkMapCommon;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
@@ -32,62 +30,6 @@ public class MixinChunkMap
// don't need the chunk(s) before MC has finished saving them // don't need the chunk(s) before MC has finished saving them
@Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE)) @Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE))
private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci) private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{ { MixinChunkMapCommon.onChunkSave(this.level, chunk, ci); }
// true means a chunk was saved to disk
if (ci.getReturnValue())
{
// TODO is this validation necessary since we are checking above if
// the callback return value should state if the chunk was actually saved or not?
// Do we trust it to always be correct?
//=====================================//
// corrupt/incomplete chunk validation //
//=====================================//
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
// this logic should prevent that from happening
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
{
return;
}
#else
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
{
return;
}
#endif
//==================//
// biome validation //
//==================//
// some chunks may be missing their biomes, which cause issues when attempting to save them
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.getBiomes() == null)
{
return;
}
#else
try
{
// this will throw an exception if the biomes aren't set up
chunk.getNoiseBiome(0,0,0);
}
catch (Exception e)
{
return;
}
#endif
ServerApi.INSTANCE.serverChunkSaveEvent(
new ChunkWrapper(chunk, this.level, ServerLevelWrapper.getWrapper(this.level)),
ServerLevelWrapper.getWrapper(this.level)
);
}
}
} }
@@ -1,60 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.fabric.mixins.server.unsafe;
import org.spongepowered.asm.mixin.Mixin;
//FIXME: Is this still needed?
#if MC_VER >= MC_1_18_2
import net.minecraft.util.ThreadingDetector;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.concurrent.Semaphore;
/**
* Why does this exist? But okay! (Will be probably removed when the experimental generator is done)
* FIXME: Recheck this
*/
@Mixin(ThreadingDetector.class)
public class MixinThreadingDetector
{
@Mutable
@Shadow
private Semaphore lock;
@Inject(method = "<init>", at = @At("RETURN"))
private void setSemaphore(CallbackInfo ci)
{
this.lock = new Semaphore(2);
}
}
#else
import net.minecraft.server.level.ServerLevel;
@Mixin(ServerLevel.class)
public class MixinThreadingDetector { } //FIXME: Is there some way to make this file just not be added?
#endif
@@ -0,0 +1,28 @@
package com.seibel.distanthorizons.fabric.testing;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.server.level.ServerLevel;
import org.apache.logging.log4j.Logger;
public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@Override
public void onLevelLoad(DhApiEventParam<DhApiLevelLoadEvent.EventParam> event)
{
LOGGER.info("DH Level: ["+event.value.levelWrapper.getDimensionType()+"] loaded.");
// Note: whenever you use a wrapper method on a new Minecraft version it is recommended that you
// call wrapper.getClass() to determine which object the API will return before you try casting it.
ServerLevel level = (ServerLevel) event.value.levelWrapper.getWrappedMcObject();
// override the core DH world generator for this level
IDhApiWorldGenerator exampleWorldGen = new TestWorldGenerator(level);
DhApi.worldGenOverrides.registerWorldGeneratorOverride(event.value.levelWrapper, exampleWorldGen);
}
}
@@ -0,0 +1,114 @@
package com.seibel.distanthorizons.fabric.testing;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGeneratorReturnType;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.AbstractDhApiChunkWorldGenerator;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.objects.data.DhApiChunk;
import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.config.Config;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
public class TestWorldGenerator extends AbstractDhApiChunkWorldGenerator
{
private final ServerLevel level;
private final IDhApiLevelWrapper levelWrapper;
//=============//
// constructor //
//=============//
public TestWorldGenerator(ServerLevel level)
{
this.level = level;
this.levelWrapper = ServerLevelWrapper.getWrapper(level);
}
//============//
// properties //
//============//
@Override
public EDhApiWorldGeneratorReturnType getReturnType() { return EDhApiWorldGeneratorReturnType.API_CHUNKS; }
@Override
public boolean runApiChunkValidation() { return true; }
//==================//
// chunk generation //
//==================//
@Override
public Object[] generateChunk(int chunkX, int chunkZ, EDhApiDistantGeneratorMode eDhApiDistantGeneratorMode)
{
ChunkAccess chunk = this.level.getChunk(chunkX, chunkZ);
return new Object[] { chunk, this.level };
}
@Override
public DhApiChunk generateApiChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode)
{
// this test is only validated for 1.18.2 and up
// (and it is only needed when testing world gen overrides/API chunks, so it isn't normally needed)
#if MC_VER >= MC_1_18_2
ChunkAccess chunk = this.level.getChunk(chunkPosX, chunkPosZ);
int minBuildHeight = this.level.getMinBuildHeight();
int maxBuildHeight = this.level.getMaxBuildHeight();
DhApiChunk apiChunk = DhApiChunk.create(chunkPosX, chunkPosZ, minBuildHeight, maxBuildHeight);
for (int x = 0; x < 16; x++)
{
for (int z = 0; z < 16; z++)
{
ArrayList<DhApiTerrainDataPoint> dataPoints = new ArrayList<>();
IDhApiBlockStateWrapper block = null;
IDhApiBiomeWrapper biome = null;
for (int y = minBuildHeight; y < maxBuildHeight; y++)
{
block = DhApi.Delayed.wrapperFactory.getBlockStateWrapper(new Object[]{chunk.getBlockState(new BlockPos(x, y, z))}, this.levelWrapper);
biome = DhApi.Delayed.wrapperFactory.getBiomeWrapper(new Object[]{chunk.getNoiseBiome(x, y, z)}, this.levelWrapper);
dataPoints.add(DhApiTerrainDataPoint.create((byte) 0, 0, 15, y, y + 1, block, biome));
}
apiChunk.setDataPoints(x, z, dataPoints);
}
}
return apiChunk;
#else
return null;
#endif
}
@Override
public void preGeneratorTaskStart() { /* do nothing */ }
//=========//
// cleanup //
//=========//
@Override
public void close() { /* do nothing */ }
}
@@ -1,11 +1,11 @@
package com.seibel.distanthorizons.fabric.wrappers.modAccessor; package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IBCLibAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IBCLibAccessor;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 || MC_VER == MC_1_20_4 // These versions either don't have BCLib, or the implementation is different #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 || MC_VER == MC_1_20_4 || MC_VER == MC_1_20_6 // These versions either don't have BCLib, or the implementation is different
#elif MC_VER == MC_1_18_2 #elif MC_VER == MC_1_18_2
import ru.bclib.config.ClientConfig; import ru.bclib.config.ClientConfig;
import ru.bclib.config.Configs; import ru.bclib.config.Configs;
#else #elif MC_VER < MC_1_21_1
import org.betterx.bclib.config.ClientConfig; import org.betterx.bclib.config.ClientConfig;
import org.betterx.bclib.config.Configs; import org.betterx.bclib.config.Configs;
#endif #endif
@@ -17,7 +17,8 @@ public class BCLibAccessor implements IBCLibAccessor
public void setRenderCustomFog(boolean newValue) public void setRenderCustomFog(boolean newValue)
{ {
#if !(MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 || MC_VER == MC_1_20_4) // These versions either don't have BCLib, or the implementation is different // only some MC versions have BCLib and require this fix
#if (MC_VER > MC_1_17_1 && MC_VER < MC_1_20_4)
// Change the value of CUSTOM_FOG_RENDERING in the bclib client config // Change the value of CUSTOM_FOG_RENDERING in the bclib client config
// This disabled fog from rendering within bclib // This disabled fog from rendering within bclib
@@ -22,7 +22,11 @@ package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
#if MC_VER >= MC_1_19_4 #if MC_VER >= MC_1_19_4
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
#if MC_VER <= MC_1_20_4
import net.coderbot.iris.Iris; import net.coderbot.iris.Iris;
#else
import net.irisshaders.iris.Iris;
#endif
import net.irisshaders.iris.api.v0.IrisApi; import net.irisshaders.iris.api.v0.IrisApi;
public class IrisAccessor implements IIrisAccessor public class IrisAccessor implements IIrisAccessor
@@ -19,20 +19,18 @@
package com.seibel.distanthorizons.fabric.wrappers.modAccessor; package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
import java.util.HashSet; import java.lang.invoke.MethodHandle;
import java.util.stream.Collectors; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; #if MC_VER < MC_1_20_1
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer; import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
import net.minecraft.client.Minecraft; #endif
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.Packet;
@@ -41,102 +39,99 @@ import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
#else #else
import net.minecraft.world.level.LevelHeightAccessor;
#endif #endif
public class SodiumAccessor implements ISodiumAccessor public class SodiumAccessor implements ISodiumAccessor
{ {
private final IWrapperFactory factory = SingletonInjector.INSTANCE.get(IWrapperFactory.class); #if MC_VER >= MC_1_20_1
private final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); private static MethodHandle setFogOcclusionMethod;
private static Object sodiumPerformanceOptions;
#endif
public IClientLevelWrapper levelWrapper;
public Mat4f mcModelViewMatrix;
public Mat4f mcProjectionMatrix; //======================//
public float partialTicks; // mod accessor methods //
//======================//
@Override @Override
public String getModName() public String getModName() { return "Sodium-Fabric"; }
//================//
// sodium methods //
//================//
/** An overwrite for a config in sodium 0.5 to fix their terrain from showing */
@Override
public void setFogOcclusion(boolean occlusionEnabled)
{ {
return "Sodium-Fabric"; #if MC_VER >= MC_1_20_1
try
{
if (sodiumPerformanceOptions == null)
{
boolean sodiumV6 = classPresent("net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer");
if (!sodiumV6)
{
// sodium 0.5
Class<?> optionsClass = Class.forName("me.jellysquid.mods.sodium.client.gui.SodiumGameOptions");
Object basicOptions = MethodHandles.lookup().findStatic(
Class.forName("me.jellysquid.mods.sodium.client.SodiumClientMod"),
"options", MethodType.methodType(optionsClass)).invoke();
sodiumPerformanceOptions = optionsClass.getDeclaredField("performance").get(basicOptions);
setFogOcclusionMethod = MethodHandles.lookup()
.findSetter(Class.forName(
"me.jellysquid.mods.sodium.client.gui.SodiumGameOptions$PerformanceSettings"),
"useFogOcclusion", boolean.class);
// alternate option if referencing Sodium 0.5 directly
//me.jellysquid.mods.sodium.client.SodiumClientMod.options().performance.useFogOcclusion = b;
}
else
{
// sodium 0.6
Class<?> optionsClass = Class.forName("net.caffeinemc.mods.sodium.client.gui.SodiumGameOptions");
Object basicOptions = MethodHandles.lookup().findStatic(
Class.forName("net.caffeinemc.mods.sodium.client.SodiumClientMod"),
"options", MethodType.methodType(optionsClass)).invoke();
sodiumPerformanceOptions = optionsClass.getDeclaredField("performance").get(basicOptions);
setFogOcclusionMethod = MethodHandles.lookup()
.findSetter(Class.forName(
"net.caffeinemc.mods.sodium.client.gui.SodiumGameOptions$PerformanceSettings"),
"useFogOcclusion", boolean.class);
}
} }
#if MC_VER >= MC_1_17_1 setFogOcclusionMethod.invoke(sodiumPerformanceOptions, occlusionEnabled);
@Override }
public HashSet<DhChunkPos> getNormalRenderedChunks() catch (Throwable e)
{ {
SodiumWorldRenderer renderer = SodiumWorldRenderer.instance(); throw new RuntimeException(e);
LevelHeightAccessor height = Minecraft.getInstance().level; }
#endif
}
#if MC_VER >= MC_1_20_1
// TODO: This is just a tmp solution, use a proper solution later
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> { //================//
return (renderer.isBoxVisible( // helper methods //
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1, //================//
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15));
}).collect(Collectors.toCollection(HashSet::new)); private static boolean classPresent(String className)
#elif MC_VER >= MC_1_18_2 {
// 0b11 = Lighted chunk & loaded chunk try
return renderer.getChunkTracker().getChunks(0b00).filter( {
(long l) -> { Class.forName(className);
return true; return true;
}).mapToObj(DhChunkPos::new).collect(Collectors.toCollection(HashSet::new));
#else
// TODO: Maybe use a mixin to make this more efficient, and maybe ignore changes behind the camera
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
return (renderer.isBoxVisible(
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1,
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15));
}).collect(Collectors.toCollection(HashSet::new));
#endif
} }
#else catch (ClassNotFoundException e)
@Override
public HashSet<DhChunkPos> getNormalRenderedChunks() {
SodiumWorldRenderer renderer = SodiumWorldRenderer.getInstance();
LevelAccessor height = Minecraft.getInstance().level;
// TODO: Maybe use a mixin to make this more efficient
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
FakeChunkEntity AABB = new FakeChunkEntity(chunk.x, chunk.z, height.getMaxBuildHeight());
return (renderer.isEntityVisible(AABB));
}).collect(Collectors.toCollection(HashSet::new));
}
private static class FakeChunkEntity extends Entity {
public int cx;
public int cz;
public int my;
public FakeChunkEntity(int chunkX, int chunkZ, int maxHeight) {
super(EntityType.AREA_EFFECT_CLOUD, null);
cx = chunkX;
cz = chunkZ;
my = maxHeight;
}
@Override
public AABB getBoundingBoxForCulling() {
return new AABB(cx*16+1, 1, cz*16+1,
cx*16+15, my-1, cz*16+15);
}
@Override
protected void defineSynchedData() {}
@Override
protected void readAdditionalSaveData(CompoundTag paramCompoundTag) {}
@Override
protected void addAdditionalSaveData(CompoundTag paramCompoundTag) {}
@Override
public Packet<?> getAddEntityPacket() {
throw new UnsupportedOperationException("This is a FAKE CHUNK ENTITY... For tricking the Sodium to check a AABB.");
}
}
#endif
/** A temporary overwrite for a config in sodium 0.5 to fix their terrain from showing, will be removed once a proper fix is added */
// FIXME
@Override
public void setFogOcclusion(boolean b)
{ {
#if MC_VER >= MC_1_20_1 return false;
me.jellysquid.mods.sodium.client.SodiumClientMod.options().performance.useFogOcclusion = b; }
#endif
} }
} }
@@ -3,7 +3,6 @@
"minVersion": "0.8", "minVersion": "0.8",
"package": "com.seibel.distanthorizons.fabric.mixins", "package": "com.seibel.distanthorizons.fabric.mixins",
"mixins": [ "mixins": [
"server.unsafe.MixinThreadingDetector",
"server.MixinChunkGenerator", "server.MixinChunkGenerator",
"server.MixinChunkMap", "server.MixinChunkMap",
"server.MixinUtilBackgroundThread" "server.MixinUtilBackgroundThread"
@@ -15,7 +14,6 @@
"client.MixinFogRenderer", "client.MixinFogRenderer",
"client.MixinGameRenderer", "client.MixinGameRenderer",
"client.MixinLevelRenderer", "client.MixinLevelRenderer",
"client.MixinDynamicTexture",
"client.MixinLightTexture", "client.MixinLightTexture",
"client.MixinOptionsScreen", "client.MixinOptionsScreen",
"client.MixinMinecraft", "client.MixinMinecraft",
@@ -56,7 +56,6 @@
}, },
"suggests": { "suggests": {
"blendium": "*"
}, },
"breaks": $fabric_incompatibility_list, "breaks": $fabric_incompatibility_list,
+13 -1
View File
@@ -4,7 +4,7 @@ plugins {
id "architectury-plugin" version "3.4-SNAPSHOT" id "architectury-plugin" version "3.4-SNAPSHOT"
} }
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17 sourceCompatibility = targetCompatibility = JavaVersion.VERSION_21
architectury { architectury {
platformSetupLoomIde() platformSetupLoomIde()
@@ -78,6 +78,18 @@ dependencies {
addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft) addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft)
if ( // Only run on MC 1.20.6 or later
// FIXME: Add an environment variable for the Major, Minor, and Patch version number of Minecraft
minecraft_version.split("\\.")[1].toInteger() >= 20 &&
(
minecraft_version.split("\\.").length > 1 && // Incase there isn't a minor version
minecraft_version.split("\\.")[2].toInteger() >= 6
)
) {
// (potential) hack fix, force jopt-simple to be exactly 5.0.4 because Mojang ships that version, but some transitive dependencies request 6.0+
implementation('net.sf.jopt-simple:jopt-simple:5.0.4') //{ version { strictly '5.0.4' } }
}
} }
task deleteResources(type: Delete) { task deleteResources(type: Delete) {
@@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
@@ -65,6 +66,8 @@ import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import java.util.concurrent.ThreadPoolExecutor;
/** /**
* This handles all events sent to the client, * This handles all events sent to the client,
* and is the starting point for most of the mod. * and is the starting point for most of the mod.
@@ -144,7 +147,7 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
public void clientLevelUnloadEvent(WorldEvent.Unload event) public void clientLevelUnloadEvent(WorldEvent.Unload event)
#else #else
public void clientLevelUnloadEvent(LevelEvent.Load event) public void clientLevelUnloadEvent(LevelEvent.Unload event)
#endif #endif
{ {
LOGGER.info("level unload"); LOGGER.info("level unload");
@@ -172,6 +175,8 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
@SubscribeEvent @SubscribeEvent
public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event) public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
{
if (MC.clientConnectedToDedicatedServer())
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{ {
@@ -186,11 +191,21 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
LevelAccessor level = event.getLevel(); LevelAccessor level = event.getLevel();
#endif #endif
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(event.getPos()); ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk); this.onBlockChangeEvent(level, chunk);
});
}
}
} }
@SubscribeEvent @SubscribeEvent
public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event) public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
{
if (MC.clientConnectedToDedicatedServer())
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{ {
@@ -205,8 +220,16 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
LevelAccessor level = event.getLevel(); LevelAccessor level = event.getLevel();
#endif #endif
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(event.getPos()); ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk); this.onBlockChangeEvent(level, chunk);
});
}
}
} }
private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk) private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk)
{ {
@@ -214,20 +237,15 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
SharedApi.INSTANCE.chunkBlockChangedEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel); SharedApi.INSTANCE.chunkBlockChangedEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
} }
@SubscribeEvent @SubscribeEvent
public void clientChunkLoadEvent(ChunkEvent.Load event) public void clientChunkLoadEvent(ChunkEvent.Load event)
{
if (MC.clientConnectedToDedicatedServer())
{ {
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event)); ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel); IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel); SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel);
} }
@SubscribeEvent
public void clientChunkUnloadEvent(ChunkEvent.Unload event)
{
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
SharedApi.INSTANCE.chunkUnloadEvent(chunk, wrappedLevel);
} }
@@ -328,9 +346,7 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
#elif MC_VER >= MC_1_18_2 #elif MC_VER >= MC_1_18_2
if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_SOLID_BLOCKS) if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_SOLID_BLOCKS)
#else #else
// FIXME: Is this the correct location for 1.16 & 1.17??? if (event.type.equals(TickEvent.RenderTickEvent.Type.RENDER))
// I couldnt find anything for rendering after the level, so is rendering after overlays ok?
if (event.type.equals(TickEvent.RenderTickEvent.Type.WORLD))
#endif #endif
{ {
try try
@@ -22,6 +22,8 @@ package com.seibel.distanthorizons.forge;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen; import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
@@ -104,6 +106,20 @@ public class ForgeMain extends AbstractModInitializer
ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class, ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class,
() -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> GetConfigScreen.getScreen(parent))); () -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> GetConfigScreen.getScreen(parent)));
#endif #endif
if (Config.Client.Advanced.Logging.showModCompatibilityWarningsOnStartup.get())
{
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
if (modChecker.isModLoaded("alexscaves"))
{
String message =
// orange text
"\u00A76" + "Distant Horizons: Alex's Cave detected." + "\u00A7r\n" +
"You may have to change Alex's config for DH to render. ";
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
}
} }
@Override @Override
@@ -137,14 +137,6 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper); IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
this.serverApi.serverChunkLoadEvent(chunk, levelWrapper); this.serverApi.serverChunkLoadEvent(chunk, levelWrapper);
} }
@SubscribeEvent
public void serverChunkSaveEvent(ChunkEvent.Unload event)
{
ILevelWrapper levelWrapper = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
this.serverApi.serverChunkSaveEvent(chunk, levelWrapper);
}
@@ -1,74 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.forge.mixins.client;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.common.util.ILightTextureMarker;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.jetbrains.annotations.Nullable;
@Mixin(DynamicTexture.class)
public abstract class MixinDynamicTexture implements ILightTextureMarker
{
/** Used to prevent accidentally using other dynamic textures as a lightmap */
@Unique
private boolean isLightTexture = false;
@Shadow
@Final
private NativeImage pixels;
@Inject(method = "upload()V", at = @At("HEAD"))
public void updateLightTexture(CallbackInfo ci)
{
// since the light map is always updated on the client render thread we should be able to access the client level at the same time
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (!this.isLightTexture
|| mc == null
|| mc.getWrappedClientLevel() == null
)
{
return;
}
//ApiShared.LOGGER.info("Lightmap update");
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.pixels, clientLevel);
}
public void markLightTexture() { this.isLightTexture = true; }
}
@@ -22,6 +22,10 @@ package com.seibel.distanthorizons.forge.mixins.client;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LightTexture;
#else #else
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@@ -34,13 +38,9 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Mat4f;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -49,7 +49,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.nio.FloatBuffer; import javax.annotation.Nullable;
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
@@ -68,7 +68,8 @@ import org.lwjgl.opengl.GL15;
@Mixin(LevelRenderer.class) @Mixin(LevelRenderer.class)
public class MixinLevelRenderer public class MixinLevelRenderer
{ {
@Shadow @Nullable
@Shadow //# if MC_VER >= MC_1_20_4 (remap = false) # endif
private ClientLevel level; private ClientLevel level;
@Unique @Unique
private static float previousPartialTicks = 0; private static float previousPartialTicks = 0;
@@ -111,11 +112,16 @@ public class MixinLevelRenderer
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V", method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true) cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback) private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
#else #elif MC_VER < MC_1_20_4
@Inject(at = @At("HEAD"), @Inject(at = @At("HEAD"),
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V", method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true) cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback) private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback)
#else
@Inject(at = @At("HEAD"),
method = "renderSectionLayer",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback)
#endif #endif
{ {
// get MC's model view and projection matrices // get MC's model view and projection matrices
@@ -20,10 +20,13 @@
package com.seibel.distanthorizons.forge.mixins.client; package com.seibel.distanthorizons.forge.mixins.client;
import com.seibel.distanthorizons.common.util.ILightTextureMarker; import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -35,11 +38,22 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LightTexture.class) @Mixin(LightTexture.class)
public class MixinLightTexture public class MixinLightTexture
{ {
@Shadow @Shadow //# if MC_VER >= MC_1_20_4 (remap = false) # endif
@Final @Final
private DynamicTexture lightTexture; private NativeImage lightPixels;
@Inject(method = "<init>", at = @At("RETURN"))
public void markLightTexture(CallbackInfo ci) { ((ILightTextureMarker) this.lightTexture).markLightTexture(); } @Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci)
{
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (mc == null || mc.getWrappedClientLevel() == null)
{
return;
}
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel);
}
} }
@@ -73,11 +73,23 @@ public class MixinMinecraft
&& SelfUpdater.onStart() && SelfUpdater.onStart()
) )
{ {
runnable = () -> { runnable = () ->
{
String versionId;
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
if (updateBranch == EDhApiUpdateBranch.STABLE)
{
versionId = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
}
else
{
versionId = GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha");
}
Minecraft.getInstance().setScreen(new UpdateModScreen( Minecraft.getInstance().setScreen(new UpdateModScreen(
// TODO: Change to runnable, instead of tittle screen // TODO: Change to runnable, instead of tittle screen
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()) : GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha")) versionId
)); ));
}; };
} }
@@ -1,156 +0,0 @@
package com.seibel.distanthorizons.forge.mixins.client;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.world.DimensionTypeWrapper;
import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure;
import com.seibel.distanthorizons.core.level.DhServerLevel;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
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.resources.ResourceKey;
import net.minecraft.util.worldupdate.WorldUpgrader;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.WorldGenSettings;
import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraft.world.level.storage.LevelStorageSource;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import java.io.File;
import java.nio.file.Path;
#if FALSE
@Mixin(WorldUpgrader.class)
public class MixinWorldUpgrader {
static class FakeLevelWrapper implements IServerLevelWrapper {
private Path saveFolder;
private LevelStem stem;
private DimensionType dimension;
private DimensionTypeWrapper dimensionTypeWrapper;
public FakeLevelWrapper(LevelStorageSource.LevelStorageAccess storage, WorldGenSettings gen, ResourceKey<Level> dim) {
saveFolder = storage.getDimensionPath(dim);
stem = gen.dimensions().getOrThrow(WorldGenSettings.levelToLevelStem(dim));
dimension = stem.typeHolder().value();
dimensionTypeWrapper = DimensionTypeWrapper.getDimensionTypeWrapper(dimension);
}
@Override
public EDhApiLevelType getLevelType() {
return EDhApiLevelType.SERVER_LEVEL;
}
@Override
public IDhApiDimensionTypeWrapper getDimensionType() {
return dimensionTypeWrapper;
}
@Override
public int getBlockLight(int x, int y, int z) {
return 0;
}
@Override
public int getSkyLight(int x, int y, int z) {
return 0;
}
@Override
public boolean hasCeiling() {
return dimension.hasCeiling();
}
@Override
public boolean hasSkyLight() {
return dimension.hasSkyLight();
}
@Override
public int getHeight() {
return dimension.height();
}
@Override
public int getMinHeight() {
return dimension.minY();
}
@Override
public boolean hasChunkLoaded(int chunkX, int chunkZ) {
return false;
}
@Override
public IBlockStateWrapper getBlockState(DhBlockPos pos) {
return BlockStateWrapper.AIR;
}
@Override
public IBiomeWrapper getBiome(DhBlockPos pos) {
throw new UnsupportedOperationException("Not implemented yet");
}
@Override
public Object getWrappedMcObject() {
return null;
}
@Nullable
@Override
public IClientLevelWrapper tryGetClientLevelWrapper() {
return null;
}
@Override
public File getSaveFolder() {
return saveFolder.toFile();
}
}
@Unique
private DhServerLevel dhServerLevel;
@Unique
private FakeLevelWrapper fakeLevelWrapper;
@Unique
public LocalSaveStructure saveStructure;
@Shadow @Final
private DimensionDataStorage overworldDataStorage;
@Shadow @Final
private LevelStorageSource.LevelStorageAccess levelStorage;
@Shadow @Final
private WorldGenSettings worldGenSettings;
@Inject(method = "Lnet/minecraft/util/worldupdate/WorldUpgrader;work()V",
at = @At(value = "INVOKE")
)
private void initWorldUpgrade() {
saveStructure = new LocalSaveStructure();
}
@Inject(method = "Lnet/minecraft/util/worldupdate/WorldUpgrader;work()V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/worldupdate/WorldUpgrader;getAllChunkPos(Lnet/minecraft/resources/ResourceKey;)Ljava/util/List;", shift = At.Shift.AFTER),
locals = LocalCapture.CAPTURE_FAILSOFT
)
private void startWorldUpgrade(CallbackInfo info, ResourceKey resourceKey) {
ResourceKey<Level> key = resourceKey;
fakeLevelWrapper = new FakeLevelWrapper(levelStorage, worldGenSettings, key);
dhServerLevel = new DhServerLevel(saveStructure, fakeLevelWrapper);
}
}
#endif
@@ -0,0 +1,35 @@
package com.seibel.distanthorizons.forge.mixins.server;
import com.seibel.distanthorizons.common.commonMixins.MixinChunkMapCommon;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ChunkMap.class)
public class MixinChunkMap
{
@Unique
private static final String CHUNK_SERIALIZER_WRITE
= "Lnet/minecraft/world/level/chunk/storage/ChunkSerializer;write(" +
"Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/ChunkAccess;)" +
"Lnet/minecraft/nbt/CompoundTag;";
@Shadow
@Final
ServerLevel level;
// firing at INVOKE causes issues with C2ME and is probably unnecessary since we
// don't need the chunk(s) before MC has finished saving them
@Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE))
private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{ MixinChunkMapCommon.onChunkSave(this.level, chunk, ci); }
}
@@ -1,59 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.forge.mixins.server.unsafe;
import org.spongepowered.asm.mixin.Mixin;
#if MC_VER >= MC_1_18_2
import net.minecraft.util.ThreadingDetector;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.concurrent.Semaphore;
/**
* Why does this exist? But okay! (Will be probably removed when the experimental generator is done)
* FIXME: Recheck this // STILL check this
*/
@Mixin(ThreadingDetector.class)
public class MixinThreadingDetector
{
@Mutable
@Shadow
private Semaphore lock;
@Inject(method = "<init>", at = @At("RETURN"))
private void setSemaphore(CallbackInfo ci)
{
this.lock = new Semaphore(2);
}
}
#else
import net.minecraft.world.level.chunk.ChunkGenerator;
@Mixin(ChunkGenerator.class)
public class MixinThreadingDetector { }
#endif
@@ -3,10 +3,10 @@
"minVersion": "0.8", "minVersion": "0.8",
"package": "com.seibel.distanthorizons.forge.mixins", "package": "com.seibel.distanthorizons.forge.mixins",
"mixins": [ "mixins": [
"server.unsafe.MixinThreadingDetector",
"server.MixinUtilBackgroundThread", "server.MixinUtilBackgroundThread",
"server.MixinChunkGenerator", "server.MixinChunkGenerator",
"server.MixinTFChunkGenerator" "server.MixinTFChunkGenerator",
"server.MixinChunkMap"
], ],
"client": [ "client": [
"client.MixinClientPacketListener", "client.MixinClientPacketListener",
@@ -14,7 +14,6 @@
"client.MixinFogRenderer", "client.MixinFogRenderer",
"client.MixinGameRenderer", "client.MixinGameRenderer",
"client.MixinLevelRenderer", "client.MixinLevelRenderer",
"client.MixinDynamicTexture",
"client.MixinLightTexture", "client.MixinLightTexture",
"client.MixinOptionsScreen", "client.MixinOptionsScreen",
"client.MixinTextureUtil" "client.MixinTextureUtil"
+12 -10
View File
@@ -5,12 +5,12 @@ org.gradle.caching=true
# Mod Info # Mod Info
mod_name=DistantHorizons mod_name=DistantHorizons
mod_version=2.0.3-a-dev mod_version=2.2.1-a
api_version=1.1.0 api_version=3.0.0
maven_group=com.seibel.distanthorizons maven_group=com.seibel.distanthorizons
mod_readable_name=Distant Horizons mod_readable_name=Distant Horizons
mod_description=This mod generates and renders simplified terrain beyond the normal view distance at a low performance cost. Allowing you to see much farther without turning your game into a slideshow. mod_description=This mod generates and renders simplified terrain beyond the normal view distance at a low performance cost. Allowing you to see much farther without turning your game into a slideshow.
# Note: In forge's mods.toml this is hard coded because Architectury throws an error with setting it as a varuable # Note: In forge's mods.toml this is hard coded because Architectury throws an error with setting it as a variable
mod_authors=["James Seibel", "Leonardo Amato", "Cola", "coolGi", "Ran", "Leetom"] mod_authors=["James Seibel", "Leonardo Amato", "Cola", "coolGi", "Ran", "Leetom"]
mod_homepage=https://modrinth.com/mod/distanthorizons mod_homepage=https://modrinth.com/mod/distanthorizons
mod_source=https://gitlab.com/jeseibel/distant-horizons mod_source=https://gitlab.com/jeseibel/distant-horizons
@@ -18,17 +18,19 @@ mod_issues=https://gitlab.com/jeseibel/distant-horizons/-/issues
mod_discord=https://discord.gg/xAB8G4cENx mod_discord=https://discord.gg/xAB8G4cENx
# Global Plugin Versions # Global Plugin Versions
manifold_version=2024.1.9 manifold_version=2023.1.17
# 2023.1.17 can be used if there are mystery Java compiler issues
nightconfig_version=3.6.6 nightconfig_version=3.6.6
lz4_version=1.8.0 lz4_version=1.8.0
zstd_version=1.5.5-11
xz_version=1.9 xz_version=1.9
sqlite_jdbc_version=3.43.0.0 sqlite_jdbc_version=3.43.0.0
fastutil_version=8.5.13 # 8.2.1 is the newest version we can use since that's the version MC 1.16.5 uses
# (at least until we can fix the gradle script so core and main can use/shade different fastutil versions)
fastutil_version=8.2.1
#svgSalamander_version=1.1.3 #svgSalamander_version=1.1.3
# Minecraft related libaries (included in MC's jar) # Minecraft related libraries (included in MC's jar)
log4j_version=2.20.0 log4j_version=2.23.1
netty_version=4.1.94.Final netty_version=4.1.94.Final
lwjgl_version=3.3.1 lwjgl_version=3.3.1
joml_version=1.10.2 joml_version=1.10.2
@@ -47,7 +49,7 @@ versionStr=
# This defines what MC version Intellij will use for the preprocessor # This defines what MC version Intellij will use for the preprocessor
# and what version is used automatically by build and run commands # and what version is used automatically by build and run commands
mcVer=1.20.4 mcVer=1.21.1
# Defines the maximum amount of memory Minecraft is allowed when run in a developement environment # Defines the maximum amount of memory Minecraft is allowed when run in a development environment
#minecraftMemoryJavaArg="-Xmx4G" #minecraftMemoryJavaArg="-Xmx4G"
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
+15 -41
View File
@@ -11,41 +11,17 @@ architectury {
neoForge() neoForge()
} }
// TODO this is already defined in the main settings.gradle file, why doesn't it work unless also defined here? (If compiling does work without this block feel free to remove)
repositories {
maven {
name "Neoforge"
url "https://maven.neoforged.net/releases/"
}
}
//loom {
// forge {
// convertAccessWideners.set(true)
// extraAccessWideners.add("lod.accesswidener")
// mixinConfigs("DistantHorizons.mixins.json")
// }
//}
loom { loom {
silentMojangMappingsLicense() // Shut the licencing warning silentMojangMappingsLicense() // Shut the licencing warning
accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
neoForge { neoForge {
// Access wideners are now defined in the `remapJar.atAccessWideners` // Access wideners are defined in the `remapJar.atAccessWideners`
// convertAccessWideners = true
// extraAccessWideners.add loom.accessWidenerPath.get().asFile.name
// Mixins are now defined in the `mods.toml` // Mixins are defined in the `mods.toml`
// mixinConfigs = [
// "DistantHorizons.mixins.json"
// ]
} }
mixin { mixin {
// Mixins are now defined in the `mods.toml` // Mixins are defined in the `mods.toml`
// mixinConfigs = [
// "DistantHorizons.mixins.json"
// ]
} }
// "runs" isn't required, but when we do need it then it can be useful // "runs" isn't required, but when we do need it then it can be useful
@@ -66,13 +42,7 @@ loom {
} }
} }
remapJar {
inputFile = shadowJar.archiveFile
dependsOn shadowJar
// classifier null
atAccessWideners.add("distanthorizons.accesswidener")
}
def addMod(path, enabled) { def addMod(path, enabled) {
if (enabled == "2") if (enabled == "2")
@@ -80,21 +50,24 @@ def addMod(path, enabled) {
else if (enabled == "1") else if (enabled == "1")
dependencies { modCompileOnly(path) } dependencies { modCompileOnly(path) }
} }
dependencies { dependencies {
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
mappings loom.layered() { mappings loom.layered()
{
// Mojmap mappings // Mojmap mappings
officialMojangMappings() officialMojangMappings()
// Parchment mappings (it adds parameter mappings & javadoc) // Parchment mappings (it adds parameter mappings & javadoc)
parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip") parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip")
} }
// Neoforge // Neoforge
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}" neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft) addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft)
} }
task deleteResources(type: Delete) { task deleteResources(type: Delete) {
delete file("build/resources/main") delete file("build/resources/main")
} }
@@ -113,14 +86,15 @@ tasks.named('runClient') {
finalizedBy(deleteResources) finalizedBy(deleteResources)
} }
remapJar {
inputFile = shadowJar.archiveFile
dependsOn shadowJar
atAccessWideners.add("distanthorizons.accesswidener")
}
sourcesJar { sourcesJar {
def commonSources = project(":common").sourcesJar def commonSources = project(":common").sourcesJar
dependsOn commonSources dependsOn commonSources
from commonSources.archiveFile.map { zipTree(it) } from commonSources.archiveFile.map { zipTree(it) }
} }
//components.java {
// withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
// skip()
// }
//}
@@ -21,19 +21,21 @@ package com.seibel.distanthorizons.neoforge;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.util.ProxyUtil; import com.seibel.distanthorizons.common.util.ProxyUtil;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
//import io.netty.buffer.ByteBuf;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
@@ -54,10 +56,18 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.neoforged.neoforge.client.event.InputEvent; import net.neoforged.neoforge.client.event.InputEvent;
import net.neoforged.neoforge.event.TickEvent;
import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.bus.api.SubscribeEvent;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
#if MC_VER < MC_1_20_6
import net.neoforged.neoforge.event.TickEvent;
#else
import net.neoforged.neoforge.client.event.ClientTickEvent;
import java.util.concurrent.ThreadPoolExecutor;
#endif
/** /**
* This handles all events sent to the client, * This handles all events sent to the client,
* and is the starting point for most of the mod. * and is the starting point for most of the mod.
@@ -72,8 +82,10 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
// private static SimpleChannel multiversePluginChannel; // private static SimpleChannel multiversePluginChannel;
// Not the cleanest way of passing this to the LOD renderer, but it'll have to do for now
public static Mat4f currentModelViewMatrix = new Mat4f();
public static Mat4f currentProjectionMatrix = new Mat4f();
private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); }
@@ -90,6 +102,7 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
// tick events // // tick events //
//=============// //=============//
#if MC_VER < MC_1_20_6
@SubscribeEvent @SubscribeEvent
public void clientTickEvent(TickEvent.ClientTickEvent event) public void clientTickEvent(TickEvent.ClientTickEvent event)
{ {
@@ -98,6 +111,13 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
ClientApi.INSTANCE.clientTickEvent(); ClientApi.INSTANCE.clientTickEvent();
} }
} }
#else
@SubscribeEvent
public void clientTickEvent(ClientTickEvent.Pre event)
{
ClientApi.INSTANCE.clientTickEvent();
}
#endif
@@ -122,7 +142,7 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper); ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper);
} }
@SubscribeEvent @SubscribeEvent
public void clientLevelUnloadEvent(LevelEvent.Load event) public void clientLevelUnloadEvent(LevelEvent.Unload event)
{ {
LOGGER.info("level unload"); LOGGER.info("level unload");
@@ -145,33 +165,57 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
@SubscribeEvent @SubscribeEvent
public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event) public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
{
if (MC.clientConnectedToDedicatedServer())
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{ {
return; return;
} }
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos()); //LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
LevelAccessor level = event.getLevel(); LevelAccessor level = event.getLevel();
ChunkAccess chunk = level.getChunk(event.getPos()); ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk); this.onBlockChangeEvent(level, chunk);
});
}
}
} }
@SubscribeEvent @SubscribeEvent
public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event) public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
{
if (MC.clientConnectedToDedicatedServer())
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{ {
return; return;
} }
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
//LOGGER.trace("break or block attack at blockPos: " + event.getPos()); //LOGGER.trace("break or block attack at blockPos: " + event.getPos());
LevelAccessor level = event.getLevel(); LevelAccessor level = event.getLevel();
ChunkAccess chunk = level.getChunk(event.getPos()); ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk); this.onBlockChangeEvent(level, chunk);
});
}
}
} }
private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk) private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk)
{ {
@@ -180,22 +224,6 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
} }
@SubscribeEvent
public void clientChunkLoadEvent(ChunkEvent.Load event)
{
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel);
}
@SubscribeEvent
public void clientChunkUnloadEvent(ChunkEvent.Unload event)
{
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
SharedApi.INSTANCE.chunkUnloadEvent(chunk, wrappedLevel);
}
//==============// //==============//
// key bindings // // key bindings //
@@ -281,6 +309,16 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
// rendering // // rendering //
//===========// //===========//
@SubscribeEvent
public void beforeLevelRenderEvent(RenderLevelStageEvent event)
{
if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_SKY)
{
currentModelViewMatrix = McObjectConverter.Convert(event.getModelViewMatrix());
currentProjectionMatrix = McObjectConverter.Convert(event.getProjectionMatrix());
}
}
@SubscribeEvent @SubscribeEvent
public void afterLevelRenderEvent(RenderLevelStageEvent event) public void afterLevelRenderEvent(RenderLevelStageEvent event)
{ {
@@ -301,4 +339,12 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
} }
//================//
// helper methods //
//================//
private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); }
} }
@@ -22,6 +22,8 @@ package com.seibel.distanthorizons.neoforge;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen; import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
@@ -35,13 +37,18 @@ import net.neoforged.fml.ModLoadingContext;
import net.neoforged.fml.common.Mod; import net.neoforged.fml.common.Mod;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;
import net.neoforged.fml.event.lifecycle.FMLDedicatedServerSetupEvent; import net.neoforged.fml.event.lifecycle.FMLDedicatedServerSetupEvent;
import net.neoforged.neoforge.client.ConfigScreenHandler;
import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.RegisterCommandsEvent; import net.neoforged.neoforge.event.RegisterCommandsEvent;
import net.neoforged.neoforge.event.server.ServerStartingEvent; import net.neoforged.neoforge.event.server.ServerStartingEvent;
import java.util.function.Consumer; import java.util.function.Consumer;
#if MC_VER < MC_1_20_6
import net.neoforged.neoforge.client.ConfigScreenHandler;
#else
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
#endif
/** /**
* Initialize and setup the Mod. <br> * Initialize and setup the Mod. <br>
* If you are looking for the real start of the mod * If you are looking for the real start of the mod
@@ -70,8 +77,28 @@ public class NeoforgeMain extends AbstractModInitializer
{ {
this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new); this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new);
#if MC_VER < MC_1_20_6
ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class, ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class,
() -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> GetConfigScreen.getScreen(parent))); () -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> GetConfigScreen.getScreen(parent)));
#else
ModLoadingContext.get().registerExtensionPoint(IConfigScreenFactory.class,
// TODO fix potential null pointer
() -> (client, parent) -> GetConfigScreen.getScreen(parent));
#endif
if (Config.Client.Advanced.Logging.showModCompatibilityWarningsOnStartup.get())
{
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
if (modChecker.isModLoaded("alexscaves"))
{
String message =
// orange text
"\u00A76" + "Distant Horizons: Alex's Cave detected." + "\u00A7r\n" +
"You may have to change Alex's config for DH to render. ";
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
}
} }
@Override @Override
@@ -12,7 +12,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.TickEvent;
import net.neoforged.neoforge.event.level.ChunkEvent; import net.neoforged.neoforge.event.level.ChunkEvent;
import net.neoforged.neoforge.event.level.LevelEvent; import net.neoforged.neoforge.event.level.LevelEvent;
import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.bus.api.SubscribeEvent;
@@ -20,11 +19,17 @@ import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.neoforge.event.server.ServerAboutToStartEvent; import net.neoforged.neoforge.event.server.ServerAboutToStartEvent;
import net.neoforged.neoforge.event.server.ServerStoppingEvent; import net.neoforged.neoforge.event.server.ServerStoppingEvent;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.util.function.Supplier; import java.util.function.Supplier;
#if MC_VER < MC_1_20_6
import net.neoforged.neoforge.event.TickEvent;
#else
import net.neoforged.neoforge.event.tick.ServerTickEvent;
#endif
public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
{ {
private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); } private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); }
@@ -58,7 +63,7 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
// events // // events //
//========// //========//
// ServerTickEvent (at end) #if MC_VER < MC_1_20_6
@SubscribeEvent @SubscribeEvent
public void serverTickEvent(TickEvent.ServerTickEvent event) public void serverTickEvent(TickEvent.ServerTickEvent event)
{ {
@@ -67,6 +72,13 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
this.serverApi.serverTickEvent(); this.serverApi.serverTickEvent();
} }
} }
#else
@SubscribeEvent
public void serverTickEvent(ServerTickEvent.Post event)
{
this.serverApi.serverTickEvent();
}
#endif
// ServerWorldLoadEvent // ServerWorldLoadEvent
@SubscribeEvent @SubscribeEvent
@@ -110,14 +122,6 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper); IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
this.serverApi.serverChunkLoadEvent(chunk, levelWrapper); this.serverApi.serverChunkLoadEvent(chunk, levelWrapper);
} }
@SubscribeEvent
public void serverChunkSaveEvent(ChunkEvent.Unload event)
{
ILevelWrapper levelWrapper = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
this.serverApi.serverChunkSaveEvent(chunk, levelWrapper);
}
@@ -1,75 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforge.mixins.client;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.common.util.ILightTextureMarker;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(DynamicTexture.class)
public abstract class MixinDynamicTexture implements ILightTextureMarker
{
/** Used to prevent accidentally using other dynamic textures as a lightmap */
@Unique
private boolean isLightTexture = false;
@Shadow
#if MC_VER >= MC_1_20_4
(remap = false)
#endif
@Final
private NativeImage pixels;
@Inject(method = "upload()V", at = @At("HEAD"))
public void updateLightTexture(CallbackInfo ci)
{
// since the light map is always updated on the client render thread we should be able to access the client level at the same time
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (!this.isLightTexture
|| mc == null
|| mc.getWrappedClientLevel() == null
)
{
return;
}
//ApiShared.LOGGER.info("Lightmap update");
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.pixels, clientLevel);
}
public void markLightTexture() { this.isLightTexture = true; }
}
@@ -19,10 +19,13 @@
package com.seibel.distanthorizons.neoforge.mixins.client; package com.seibel.distanthorizons.neoforge.mixins.client;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
#else #else
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.neoforge.NeoforgeClientProxy;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.GameRenderer;
@@ -34,7 +37,6 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
@@ -67,30 +69,6 @@ public class MixinLevelRenderer
(remap = false) (remap = false)
#endif #endif
private ClientLevel level; private ClientLevel level;
@Unique
private static float previousPartialTicks = 0;
// TODO: Is there any reason why this is here? Can it be deleted?
public MixinLevelRenderer()
{
throw new NullPointerException("Null cannot be cast to non-null type.");
}
#if MC_VER < MC_1_17_1
@Inject(at = @At("RETURN"), method = "renderSky(Lcom/mojang/blaze3d/vertex/PoseStack;F)V")
private void renderSky(PoseStack matrixStackIn, float partialTicks, CallbackInfo callback)
#else
@Inject(method = "renderClouds", at = @At("HEAD"), cancellable = true)
public void renderClouds(PoseStack poseStack, Matrix4f projectionMatrix, float partialTicks, double cameraX, double cameraY, double cameraZ, CallbackInfo ci)
#endif
{
// FIXME this is only called when clouds are enabled and vanilla render distance is far enough
// not having the partial ticks doesn't appear to be critical currently, but might cause weird issues down the line
// get the partial ticks since renderBlockLayer doesn't
// have access to them
previousPartialTicks = partialTicks;
}
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
@@ -108,14 +86,18 @@ public class MixinLevelRenderer
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V", method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true) cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback) private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
#else #elif MC_VER < MC_1_20_6
@Inject(at = @At("HEAD"), @Inject(at = @At("HEAD"),
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V", method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true) cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback) private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback)
#else
@Inject(at = @At("HEAD"),
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;DDDLorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f projectionMatrix, Matrix4f frustumMatrix, CallbackInfo callback)
#endif #endif
{ {
// get MC's model view and projection matrices
#if MC_VER == MC_1_16_5 #if MC_VER == MC_1_16_5
// get the matrices from the OpenGL fixed pipeline // get the matrices from the OpenGL fixed pipeline
float[] mcProjMatrixRaw = new float[16]; float[] mcProjMatrixRaw = new float[16];
@@ -125,22 +107,33 @@ public class MixinLevelRenderer
Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose()); Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
#else #elif MC_VER <= MC_1_20_4
// get the matrices directly from MC // get the matrices directly from MC
Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose()); Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix); Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#else
// get the matrices from neoForge's render event.
// We can't call the renderer there because we don't have access to the level that's being rendered
Mat4f mcModelViewMatrix = NeoforgeClientProxy.currentModelViewMatrix;
Mat4f mcProjectionMatrix = NeoforgeClientProxy.currentProjectionMatrix;
#endif #endif
float frameTime;
#if MC_VER < MC_1_21_1
frameTime = Minecraft.getInstance().getFrameTime();
#else
frameTime = Minecraft.getInstance().getTimer().getRealtimeDeltaTicks();
#endif
// only render before solid blocks // only render before solid blocks
if (renderType.equals(RenderType.solid())) if (renderType.equals(RenderType.solid()))
{ {
ClientApi.INSTANCE.renderLods(ClientLevelWrapper.getWrapper(level), mcModelViewMatrix, mcProjectionMatrix, Minecraft.getInstance().getFrameTime()); ClientApi.INSTANCE.renderLods(ClientLevelWrapper.getWrapper(this.level), mcModelViewMatrix, mcProjectionMatrix, frameTime);
} }
else if (renderType.equals(RenderType.translucent())) else if (renderType.equals(RenderType.translucent()))
{ {
ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(level), mcModelViewMatrix, mcProjectionMatrix, Minecraft.getInstance().getFrameTime()); ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(this.level), mcModelViewMatrix, mcProjectionMatrix, frameTime);
} }
if (Config.Client.Advanced.Debugging.lodOnlyMode.get()) if (Config.Client.Advanced.Debugging.lodOnlyMode.get())
@@ -155,12 +148,16 @@ public class MixinLevelRenderer
#elif MC_VER < MC_1_20_1 #elif MC_VER < MC_1_20_1
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runUpdates(IZZ)I"), method = "renderLevel") @Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runUpdates(IZZ)I"), method = "renderLevel")
public void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci) public void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
#else #elif MC_VER < MC_1_20_6
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I"), method = "renderLevel") @Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I"), method = "renderLevel")
private void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci) private void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
#else
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I"), method = "renderLevel")
private void callAfterRunUpdates(CallbackInfo ci)
#endif #endif
{ {
ChunkWrapper.syncedUpdateClientLightStatus(); ChunkWrapper.syncedUpdateClientLightStatus();
} }
} }
@@ -20,10 +20,13 @@
package com.seibel.distanthorizons.neoforge.mixins.client; package com.seibel.distanthorizons.neoforge.mixins.client;
import com.seibel.distanthorizons.common.util.ILightTextureMarker; import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.texture.DynamicTexture;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -36,13 +39,21 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class MixinLightTexture public class MixinLightTexture
{ {
@Shadow @Shadow
#if MC_VER >= MC_1_20_4
(remap = false)
#endif
@Final @Final
private DynamicTexture lightTexture; private NativeImage lightPixels;
@Inject(method = "<init>", at = @At("RETURN"))
public void markLightTexture(CallbackInfo ci) { ((ILightTextureMarker) this.lightTexture).markLightTexture(); } @Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci)
{
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (mc == null || mc.getWrappedClientLevel() == null)
{
return;
}
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel);
}
} }
@@ -8,6 +8,7 @@ import com.seibel.distanthorizons.core.jar.installer.GitlabGetter;
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter; import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -24,41 +25,44 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Minecraft.class) @Mixin(Minecraft.class)
public class MixinMinecraft public class MixinMinecraft
{ {
#if MC_VER < MC_1_20_2 // commented out due to a bug with Manifold and having nested preprocessors
#if MC_VER == MC_1_20_1 // and since neoforge doesn't work for anything before MC 1.20.6 anyway it doesn't need to be included
@Redirect(
method = "Lnet/minecraft/client/Minecraft;setInitialScreen(Lcom/mojang/realmsclient/client/RealmsClient;Lnet/minecraft/server/packs/resources/ReloadInstance;Lnet/minecraft/client/main/GameConfig$QuickPlayData;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setScreen(Lnet/minecraft/client/gui/screens/Screen;)V")
)
public void onOpenScreen(Minecraft instance, Screen guiScreen)
{
#else
@Redirect(
method = "<init>(Lnet/minecraft/client/main/GameConfig;)V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setScreen(Lnet/minecraft/client/gui/screens/Screen;)V")
)
public void onOpenScreen(Minecraft instance, Screen guiScreen)
{
#endif
if (!Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get()) // Don't do anything if the user doesn't want it
{
instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
return;
}
if (SelfUpdater.onStart()) //#if MC_VER < MC_1_20_2
{ //#if MC_VER == MC_1_20_1
instance.setScreen(new UpdateModScreen( //@Redirect(
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons // method = "Lnet/minecraft/client/Minecraft;setInitialScreen(Lcom/mojang/realmsclient/client/RealmsClient;Lnet/minecraft/server/packs/resources/ReloadInstance;Lnet/minecraft/client/main/GameConfig$QuickPlayData;)V",
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha")) // at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setScreen(Lnet/minecraft/client/gui/screens/Screen;)V")
)); //)
} //public void onOpenScreen(Minecraft instance, Screen guiScreen)
else //{
{ //#else
instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened //@Redirect(
} // method = "<init>(Lnet/minecraft/client/main/GameConfig;)V",
} // at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;setScreen(Lnet/minecraft/client/gui/screens/Screen;)V")
#endif //)
//public void onOpenScreen(Minecraft instance, Screen guiScreen)
//{
//#endif
// if (!Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get()) // Don't do anything if the user doesn't want it
// {
// instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
// return;
// }
//
// if (SelfUpdater.onStart())
// {
// instance.setScreen(new UpdateModScreen(
// new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
// (Config.Client.Advanced.AutoUpdater.updateBranch.get() == EUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
// ));
// }
// else
// {
// instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
// }
//}
//#endif
#if MC_VER >= MC_1_20_2 #if MC_VER >= MC_1_20_2
@Redirect( @Redirect(
@@ -72,11 +76,23 @@ public class MixinMinecraft
&& SelfUpdater.onStart() && SelfUpdater.onStart()
) )
{ {
runnable = () -> { runnable = () ->
{
String versionId;
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
if (updateBranch == EDhApiUpdateBranch.STABLE)
{
versionId = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
}
else
{
versionId = GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha");
}
Minecraft.getInstance().setScreen(new UpdateModScreen( Minecraft.getInstance().setScreen(new UpdateModScreen(
// TODO: Change to runnable, instead of tittle screen // TODO: Change to runnable, instead of tittle screen
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()) : GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha")) versionId
)); ));
}; };
} }
@@ -23,7 +23,6 @@ import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget; import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import net.minecraft.client.gui.screens.OptionsScreen;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
@@ -31,51 +30,127 @@ import net.minecraft.network.chat.TranslatableComponent;
#endif #endif
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
#if MC_VER >= MC_1_20_6
import net.minecraft.client.gui.layouts.LinearLayout;
import net.minecraft.client.gui.layouts.HeaderAndFooterLayout;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Shadow;
#endif
#if MC_VER < MC_1_21_1
import net.minecraft.client.gui.screens.OptionsScreen;
#else
import net.minecraft.client.gui.screens.options.OptionsScreen;
#endif
/** /**
* Adds a button to the menu to goto the config * Adds a button to the menu to goto the config
* *
* @author coolGi * @author coolGi
* @version 12-02-2021 * @version 2024-5-20
*/ */
@Mixin(OptionsScreen.class) @Mixin(OptionsScreen.class)
public class MixinOptionsScreen extends Screen public class MixinOptionsScreen extends Screen
{ {
// Get the texture for the button /** Texture used for the config opening button */
private static final ResourceLocation ICON_TEXTURE = new ResourceLocation(ModInfo.ID, "textures/gui/button.png"); @Unique
protected MixinOptionsScreen(Component title) private static final ResourceLocation ICON_TEXTURE =
{ #if MC_VER < MC_1_21_1
super(title); new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
} #else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png");
#endif
@Inject(at = @At("HEAD"), method = "init")
@Unique
private TexturedButtonWidget optionsButton = null;
#if MC_VER >= MC_1_20_6
@Shadow
@Final
protected HeaderAndFooterLayout layout;
#endif
//==============//
// constructors //
//==============//
protected MixinOptionsScreen(Component title) { super(title); }
@Inject(at = @At("RETURN"), method = "init")
private void lodconfig$init(CallbackInfo ci) private void lodconfig$init(CallbackInfo ci)
{ {
if (Config.Client.optionsButton.get()) if (Config.Client.optionsButton.get())
this. #if MC_VER < MC_1_17_1 addButton #else addRenderableWidget #endif {
(new TexturedButtonWidget( #if MC_VER < MC_1_17_1
this.addButton(this.getOptionsButton());
#elif MC_VER < MC_1_20_6
this.addRenderableWidget(this.getOptionsButton());
#else
// add the button so it's rendered
this.addRenderableWidget(this.getOptionsButton());
// add the button to the correct location in the UI
// TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements?
LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.get(0).child;
// determine how wide the other option buttons are so we can put our botton to the left of them all
AtomicInteger width = new AtomicInteger(0);
layout.visitChildren(x -> { width.addAndGet(x.getWidth()); });
width.addAndGet(-10); // padding between the DH button and the FOV slider
layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> { settings.paddingLeft(width.get() * -1); });
layout.arrangeElements();
#endif
}
}
//================//
// helper methods //
//================//
@Unique
public TexturedButtonWidget getOptionsButton()
{
if (this.optionsButton == null)
{
this.optionsButton
= new TexturedButtonWidget(
// Where the button is on the screen // Where the button is on the screen
this.width / 2 - 180, this.height / 6 - 12, this.width / 2 - 180, this.height / 6 - 12,
// Width and height of the button // Width and height of the button
20, 20, 20, 20,
// Offset // texture UV Offset
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
20, ICON_TEXTURE, 20, 40, 20, ICON_TEXTURE, 20, 40,
// Create the button and tell it where to go // Create the button and tell it where to go
// For now it goes to the client option by default // For now it goes to the client option by default
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(GetConfigScreen.getScreen(this)), (buttonWidget) -> Objects.requireNonNull(this.minecraft).setScreen(GetConfigScreen.getScreen(this)),
// Add a title to the button // Add a title to the button
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
new TranslatableComponent(ModInfo.ID + ".title"))); new TranslatableComponent(ModInfo.ID + ".title"));
#else #else
Component.translatable(ModInfo.ID + ".title"))); Component.translatable(ModInfo.ID + ".title"));
#endif #endif
} }
return this.optionsButton;
}
} }
@@ -1,126 +0,0 @@
package com.seibel.distanthorizons.neoforge.mixins.client;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
#if FALSE
@Mixin(WorldUpgrader.class)
public class MixinWorldUpgrader {
static class FakeLevelWrapper implements IServerLevelWrapper {
private Path saveFolder;
private LevelStem stem;
private DimensionType dimension;
private DimensionTypeWrapper dimensionTypeWrapper;
public FakeLevelWrapper(LevelStorageSource.LevelStorageAccess storage, WorldGenSettings gen, ResourceKey<Level> dim) {
saveFolder = storage.getDimensionPath(dim);
stem = gen.dimensions().getOrThrow(WorldGenSettings.levelToLevelStem(dim));
dimension = stem.typeHolder().value();
dimensionTypeWrapper = DimensionTypeWrapper.getDimensionTypeWrapper(dimension);
}
@Override
public EDhApiLevelType getLevelType() {
return EDhApiLevelType.SERVER_LEVEL;
}
@Override
public IDhApiDimensionTypeWrapper getDimensionType() {
return dimensionTypeWrapper;
}
@Override
public int getBlockLight(int x, int y, int z) {
return 0;
}
@Override
public int getSkyLight(int x, int y, int z) {
return 0;
}
@Override
public boolean hasCeiling() {
return dimension.hasCeiling();
}
@Override
public boolean hasSkyLight() {
return dimension.hasSkyLight();
}
@Override
public int getHeight() {
return dimension.height();
}
@Override
public int getMinHeight() {
return dimension.minY();
}
@Override
public boolean hasChunkLoaded(int chunkX, int chunkZ) {
return false;
}
@Override
public IBlockStateWrapper getBlockState(DhBlockPos pos) {
return BlockStateWrapper.AIR;
}
@Override
public IBiomeWrapper getBiome(DhBlockPos pos) {
throw new UnsupportedOperationException("Not implemented yet");
}
@Override
public Object getWrappedMcObject() {
return null;
}
@Nullable
@Override
public IClientLevelWrapper tryGetClientLevelWrapper() {
return null;
}
@Override
public File getSaveFolder() {
return saveFolder.toFile();
}
}
@Unique
private DhServerLevel dhServerLevel;
@Unique
private FakeLevelWrapper fakeLevelWrapper;
@Unique
public LocalSaveStructure saveStructure;
@Shadow @Final
private DimensionDataStorage overworldDataStorage;
@Shadow @Final
private LevelStorageSource.LevelStorageAccess levelStorage;
@Shadow @Final
private WorldGenSettings worldGenSettings;
@Inject(method = "Lnet/minecraft/util/worldupdate/WorldUpgrader;work()V",
at = @At(value = "INVOKE")
)
private void initWorldUpgrade() {
saveStructure = new LocalSaveStructure();
}
@Inject(method = "Lnet/minecraft/util/worldupdate/WorldUpgrader;work()V",
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/worldupdate/WorldUpgrader;getAllChunkPos(Lnet/minecraft/resources/ResourceKey;)Ljava/util/List;", shift = At.Shift.AFTER),
locals = LocalCapture.CAPTURE_FAILSOFT
)
private void startWorldUpgrade(CallbackInfo info, ResourceKey resourceKey) {
ResourceKey<Level> key = resourceKey;
fakeLevelWrapper = new FakeLevelWrapper(levelStorage, worldGenSettings, key);
dhServerLevel = new DhServerLevel(saveStructure, fakeLevelWrapper);
}
}
#endif
@@ -0,0 +1,35 @@
package com.seibel.distanthorizons.neoforge.mixins.server;
import com.seibel.distanthorizons.common.commonMixins.MixinChunkMapCommon;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ChunkMap.class)
public class MixinChunkMap
{
@Unique
private static final String CHUNK_SERIALIZER_WRITE
= "Lnet/minecraft/world/level/chunk/storage/ChunkSerializer;write(" +
"Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/ChunkAccess;)" +
"Lnet/minecraft/nbt/CompoundTag;";
@Shadow
@Final
ServerLevel level;
// firing at INVOKE causes issues with C2ME and is probably unnecessary since we
// don't need the chunk(s) before MC has finished saving them
@Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE))
private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{ MixinChunkMapCommon.onChunkSave(this.level, chunk, ci); }
}
@@ -1,59 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforge.mixins.server.unsafe;
import org.spongepowered.asm.mixin.Mixin;
#if MC_VER >= MC_1_18_2
import net.minecraft.util.ThreadingDetector;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.concurrent.Semaphore;
/**
* Why does this exist? But okay! (Will be probably removed when the experimental generator is done)
* FIXME: Recheck this // STILL check this
*/
@Mixin(ThreadingDetector.class)
public class MixinThreadingDetector
{
@Mutable
@Shadow
private Semaphore lock;
@Inject(method = "<init>", at = @At("RETURN"))
private void setSemaphore(CallbackInfo ci)
{
this.lock = new Semaphore(2);
}
}
#else
import net.minecraft.world.level.chunk.ChunkGenerator;
@Mixin(ChunkGenerator.class)
public class MixinThreadingDetector { }
#endif
@@ -3,10 +3,10 @@
"minVersion": "0.8", "minVersion": "0.8",
"package": "com.seibel.distanthorizons.neoforge.mixins", "package": "com.seibel.distanthorizons.neoforge.mixins",
"mixins": [ "mixins": [
"server.unsafe.MixinThreadingDetector",
"server.MixinUtilBackgroundThread", "server.MixinUtilBackgroundThread",
"server.MixinChunkGenerator", "server.MixinChunkGenerator",
"server.MixinTFChunkGenerator" "server.MixinTFChunkGenerator",
"server.MixinChunkMap"
], ],
"client": [ "client": [
"client.MixinClientPacketListener", "client.MixinClientPacketListener",
@@ -14,7 +14,6 @@
"client.MixinFogRenderer", "client.MixinFogRenderer",
"client.MixinGameRenderer", "client.MixinGameRenderer",
"client.MixinLevelRenderer", "client.MixinLevelRenderer",
"client.MixinDynamicTexture",
"client.MixinLightTexture", "client.MixinLightTexture",
"client.MixinOptionsScreen", "client.MixinOptionsScreen",
"client.MixinTextureUtil" "client.MixinTextureUtil"
@@ -24,13 +24,12 @@ issueTrackerURL = "${issues}"
acceptableRemoteVersions = "*" acceptableRemoteVersions = "*"
# We may need this to make forge (lexforge) & neoforge work together # We may need this to make forge (lexforge) & neoforge work together
#[[mixins]] [[mixins]]
# config = "DistantHorizons.neoforge.mixins.json" config = "DistantHorizons.neoforge.mixins.json"
[[dependencies.distanthorizons]] [[dependencies.distanthorizons]]
modId = "minecraft" modId = "minecraft"
mandatory = true # Forge syntax type = "required"
type = "required" # Neoforge syntax
versionRange = "${compatible_forgemc_versions}" # Where we set what version of mc it is avalible for versionRange = "${compatible_forgemc_versions}" # Where we set what version of mc it is avalible for
ordering = "NONE" ordering = "NONE"
side = "BOTH" side = "BOTH"
+1 -1
View File
@@ -22,7 +22,7 @@ fabric_api_version=0.42.0+1.16
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "*" } fabric_incompatibility_list={ "iris": "*" }
fabric_recommend_list={ "indium": "*" } fabric_recommend_list={}
# Fabric mod run # Fabric mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
+1 -1
View File
@@ -22,7 +22,7 @@ fabric_api_version=0.46.1+1.17
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "*" } fabric_incompatibility_list={ "iris": "*" }
fabric_recommend_list={ "indium": "*" } fabric_recommend_list={}
# Fabric mod run # Fabric mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
+1 -1
View File
@@ -23,7 +23,7 @@ fabric_api_version=0.76.0+1.18.2
canvas_version=mc118:1.0.2616 canvas_version=mc118:1.0.2616
fabric_incompatibility_list={ "iris": "*" } fabric_incompatibility_list={ "iris": "*" }
fabric_recommend_list={ "indium": "*" } fabric_recommend_list={}
# Fabric mod run # Fabric mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
+1 -1
View File
@@ -22,7 +22,7 @@ fabric_api_version=0.76.1+1.19.2
canvas_version=mc119-1.0.2480 canvas_version=mc119-1.0.2480
fabric_incompatibility_list={ "iris": "*" } fabric_incompatibility_list={ "iris": "*" }
fabric_recommend_list={ "indium": "*" } fabric_recommend_list={}
# Fabric mod run # Fabric mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
+1 -1
View File
@@ -21,7 +21,7 @@ fabric_api_version=0.87.1+1.19.4
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "*" } fabric_incompatibility_list={ "iris": "*" }
fabric_recommend_list={ "indium": "*" } fabric_recommend_list={}
# Fabric mod run # Fabric mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
+3 -3
View File
@@ -7,7 +7,7 @@ accessWidenerVersion=1_20
builds_for=fabric,forge builds_for=fabric,forge
# Fabric loader # Fabric loader
fabric_loader_version=0.14.24 fabric_loader_version=0.15.6
fabric_api_version=0.90.4+1.20.1 fabric_api_version=0.90.4+1.20.1
# Fabric mod versions # Fabric mod versions
modmenu_version=7.2.2 modmenu_version=7.2.2
@@ -20,8 +20,8 @@ fabric_api_version=0.90.4+1.20.1
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.6.20" } fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={ "indium": "*" } fabric_recommend_list={}
# Fabric mod run # Fabric mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
+3 -3
View File
@@ -7,7 +7,7 @@ accessWidenerVersion=1_20_2
builds_for=fabric,forge builds_for=fabric,forge
# Fabric loader # Fabric loader
fabric_loader_version=0.14.24 fabric_loader_version=0.15.6
fabric_api_version=0.90.4+1.20.2 fabric_api_version=0.90.4+1.20.2
# Fabric mod versions # Fabric mod versions
modmenu_version=8.0.0 modmenu_version=8.0.0
@@ -20,8 +20,8 @@ fabric_api_version=0.90.4+1.20.2
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.6.20" } fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={ "indium": "*" } fabric_recommend_list={}
# Fabric mod run # Fabric mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
+3 -3
View File
@@ -8,7 +8,7 @@ builds_for=fabric,forge
# neoforge can be added once the issue with mixins has been resolved # neoforge can be added once the issue with mixins has been resolved
# Fabric loader # Fabric loader
fabric_loader_version=0.15.1 fabric_loader_version=0.15.6
fabric_api_version=0.91.2+1.20.4 fabric_api_version=0.91.2+1.20.4
# Fabric mod versions # Fabric mod versions
modmenu_version=9.0.0-pre.1 modmenu_version=9.0.0-pre.1
@@ -21,8 +21,8 @@ fabric_api_version=0.91.2+1.20.4
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.6.20" } fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={ "indium": "*" } fabric_recommend_list={}
# Fabric mod run # Fabric mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
+53
View File
@@ -0,0 +1,53 @@
# 1.20.6 version
java_version=21
minecraft_version=1.20.6
parchment_version=1.20.6:2024.05.01
compatible_minecraft_versions=["1.20.6"]
accessWidenerVersion=1_20_6
builds_for=fabric,neoforge
# forge is broken due to gradle/build script issues
# Fabric loader
fabric_loader_version=0.15.10
fabric_api_version=0.97.8+1.20.6
# Fabric mod versions
modmenu_version=10.0.0-beta.1
starlight_version_fabric=
phosphor_version_fabric=
lithium_version=
sodium_version=mc1.20.6-0.5.8
iris_version=1.7.0+1.20.6
bclib_version=
immersive_portals_version=
canvas_version=
fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={}
# Fabric mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight=0
enable_phosphor=0
enable_sodium=1
enable_lithium=0
enable_iris=1
enable_bclib=0
enable_immersive_portals=0
enable_canvas=0
# (Neo)Forge loader
forge_version=50.0.19
neoforge_version=20.6.70-beta
# (Neo)Forge mod versions
starlight_version_forge=
terraforged_version=
# (Neo)Forge mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight_forge=0
enable_terraforged=0
enable_terrafirmacraft=0
+53
View File
@@ -0,0 +1,53 @@
# 1.21.1 version
java_version=21
minecraft_version=1.21.1
parchment_version=1.20.6:2024.05.01
compatible_minecraft_versions=["1.21", "1.21.1"]
accessWidenerVersion=1_20_6
builds_for=fabric,neoforge
# forge is broken due to gradle/build script issues
# Fabric loader
fabric_loader_version=0.15.11
fabric_api_version=0.100.1+1.21
# Fabric mod versions
modmenu_version=11.0.0-beta.1
starlight_version_fabric=
phosphor_version_fabric=
lithium_version=
sodium_version=mc1.21-0.5.9
iris_version=1.7.1+1.21
bclib_version=
immersive_portals_version=
canvas_version=
fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={}
# Fabric mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight=0
enable_phosphor=0
enable_sodium=1
enable_lithium=0
enable_iris=1
enable_bclib=0
enable_immersive_portals=0
enable_canvas=0
# (Neo)Forge loader
forge_version=
neoforge_version=21.1.6
# (Neo)Forge mod versions
starlight_version_forge=
terraforged_version=
# (Neo)Forge mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight_forge=0
enable_terraforged=0
enable_terrafirmacraft=0