Compare commits

..

551 Commits

Author SHA1 Message Date
s809 4a771a3e21 Restore ordering of session config entries 2024-09-08 21:21:42 +05:00
s809 0aa109a341 Refactor session config 2024-09-08 19:05:47 +05:00
s809 6d44255175 Fix crash on F3 when commit hash was failed to retrieve 2024-09-07 21:47:45 +05:00
s809 f8bb35cb2b Fix compilation on older versions 2024-09-06 00:12:09 +05:00
s809 52f1aed124 Fix client crashing 2024-09-05 23:45:08 +05:00
s809 6ae1ba1e17 Prefill levelKeyPrefix in new worlds 2024-09-05 14:22:08 +05:00
s809 9105f30fec Fix compilation 2024-09-04 22:53:06 +05:00
s809 839c849a9d Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into serverside 2024-09-04 18:22:52 +05:00
James Seibel d208b0ab19 Up version number 2.2.1 -> 2.2.2-dev 2024-09-04 06:59:07 -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
s809 275f507096 Limit number of retries on request errors
Add a delay if rate limit is hit
2024-09-02 15:31:43 +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
s809 01c5a4072a Delay loading first level when on server 2024-08-31 20:47:03 +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
s809 1697707114 Restore LOD fetching by distance 2024-08-29 17:09:13 +05:00
James Seibel 9fb3b196d2 Add a quick DH cloud UI config 2024-08-28 07:16:33 -05:00
s809 34b0b7dc31 Print entire buffer in log 2024-08-28 16:19:17 +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
s809 691e1022e3 Do not start generator until server responds with config 2024-08-27 16:35:27 +05:00
s809 18fd157474 Show incompatible protocol version in F3 2024-08-27 16:02:26 +05:00
s809 4948158fa8 Fix sync on login 2024-08-26 16:54:05 +05:00
s809 e3b972c928 Fix missing mixins in (Neo-)Forge 2024-08-24 12:07:13 +05:00
s809 ad056cb64e Fix compilation 2024-08-24 03:06:29 +05:00
s809 3e22ddead5 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into serverside 2024-08-24 02:59:56 +05:00
s809 5c7e27c52d Fix network compression pool not shutting down 2024-08-23 17:11:37 +05:00
s809 3a5c6f12c0 Add back Forge packet ID in pre-1.20.6 2024-08-23 14:51:09 +05:00
s809 627eee7a6d "Fix" buffer release errors in FullDataPayload 2024-08-23 14:16:35 +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
s809 7c4c99089b Use same packet resource for all versions 2024-08-19 17:56:34 +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
s809 00ec14d319 Add logging of request group lifecycle 2024-08-15 15:47:13 +05:00
s809 56188bc7d2 Fix colors in verifyall.ps1 2024-08-14 13:29:32 +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
s809 9b2429d388 Fix compilation 2024-08-11 21:21:15 +05:00
s809 2aa68ec41d Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into serverside 2024-08-11 20:59:39 +05:00
James Seibel 398a3fb0bc Add alex's cave warning message 2024-08-11 09:55:12 -05:00
s809 13c9f95750 Fix compilation 2024-08-11 00:53:12 +05:00
s809 b0549cecff Do not clear keyed level on close event 2024-08-10 23:53:33 +05:00
s809 95fb4a0e6a Make encode/decode error handling work correctly 2024-08-10 23:26:09 +05:00
s809 2a3aadc2fa Make error handling somewhat work 2024-08-10 19:49:40 +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
s809 52bddca0df Merge branch 'serverside-experimental/prevent-disconnects' into serverside 2024-08-07 22:20:39 +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
s809 606dede9ef Fix real-time updates 2024-08-06 23:12:18 +05:00
s809 e8c9d8391a Fix Neoforge not being able to connect to vanilla servers 2024-08-05 14:51:32 +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
s809 88ca223bbc Fix dimension switching (untested) 2024-08-03 15:43:19 +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
s809 4a51bbc796 [skip ci] Prevent disconnects on encode/decode/handle errors 2024-08-01 22:19:21 +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
s809 94ca0e2729 Increase defaults for network compression threads 2024-07-30 10:52:32 +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
s809 5befdbcddc Use FullDataPayload instead of reusing messages 2024-07-29 13:10:11 +05:00
James Seibel 4481e8634a Fix incorrect DhApiChunk create constructor parameter order (again) 2024-07-28 20:18:31 -05:00
s809 3e02d7f9dc Add build number on F3 screen 2024-07-28 20:19:41 +05:00
s809 48b38d9285 Fix compilation 2024-07-28 19:58:35 +05:00
s809 8c7c787a8f Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into serverside 2024-07-28 19:44:46 +05:00
James Seibel 3e432682fb fix incorrect positions being fed into biome color code 2024-07-28 09:34:15 -05:00
s809 fc0cf56b48 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into serverside 2024-07-28 19:01:35 +05:00
James Seibel 05569c03a4 Revert and Deprecate DhApiChunk and DhApiTerrainDataPoint constructors 2024-07-28 08:56:26 -05:00
s809 32c63ae15b Reapply "Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into serverside"
This reverts commit a2d5e8cdb3.
2024-07-28 17:06:36 +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
s809 bbf62f7ee3 Add verifyall.ps1 2024-07-27 23:09:46 +05:00
s809 6211542708 Update core 2024-07-27 23:08:45 +05:00
s809 a2d5e8cdb3 Revert "Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into serverside"
This reverts commit 692b304898, reversing
changes made to 0ca93e311c.
2024-07-27 23:07:43 +05:00
s809 692b304898 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into serverside 2024-07-27 21:43:54 +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
s809 0ca93e311c Fix handler registration 2024-07-20 23:56:34 +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
s809 c7aab012ae Update core 2024-07-17 00:02:22 +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
s809 80ccab21da Fix level handling for real time updates 2024-07-12 23:11:40 +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
s809 663ce3cde6 Add cache for ignoring responses for cancelled requests 2024-07-11 23:06:26 +05:00
s809 43fee18ae4 Clean up a bit 2024-07-11 22:08:21 +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
s809 014310631e Fix player tracking on server 2024-07-10 23:51:49 +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
s809 4f7e934c98 Add missing semaphore release 2024-07-09 18:06:20 +05:00
James Seibel 2f985d0926 Add beacon colors 2024-07-09 07:33:30 -05:00
s809 563dcd7de6 Prevent request cancellation deadlock 2024-07-09 14:47:49 +05:00
James Seibel 2a3c544fba Increase cloud rendering performance 2024-07-08 19:56:29 -05:00
s809 0a5cc734de Use dedicated thread pool for data compression 2024-07-08 23:08:04 +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
s809 0da158fecc Disable parent update propagation 2024-07-07 19:46:52 +05:00
s809 a7e8bbaf6a Reset state of level detection on world exit 2024-07-07 01:49:47 +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
s809 80732943c8 Repoint core to main repo 2024-07-01 14:20:34 +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
s809 994c86d5ba Make data source encoding lazy and move it off server thread 2024-07-01 00:16:00 +05:00
s809 18ccbbbb8e Move fixing the dimension name into even more correct place 2024-06-28 23:18:34 +05:00
s809 31c3fb9a1e Move fixing the dimension name into correct place 2024-06-28 19:28:40 +05:00
s809 8bdb383b2e Fix paths unable to be created on Windows 2024-06-28 17:25:18 +05:00
s809 d13ec687ed Fix test compilation failing 2024-06-28 15:52:51 +05:00
s809 43069703cd Update core 2024-06-28 15:44:06 +05:00
s809 6d76ace604 Use level's ResourceLocation instead of dimension type's 2024-06-28 15:35:28 +05:00
s809 9b139575e7 Clean up package structure 2024-06-26 23:57:50 +05:00
s809 efb9129edf Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into refactor/remove-tcp-connection 2024-06-26 15:13:22 +05:00
s809 6a380f6a7b Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into refactor/remove-tcp-connection 2024-06-26 14:55:39 +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
s809 0798ca01c5 Force GradleVanilla version 2024-06-21 13:49:18 +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
s809 8bfc1430c8 Put messages received before player joining into queue 2024-06-16 00:45:23 +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
s809 bd6621183d Avoid reloading levels when unnecessary 2024-06-14 16:25: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
s809 ccc8076c66 Use level keys 2024-06-13 16:44:43 +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
s809 004103551f Prevent disconnection on decode failure 2024-06-10 23:55:33 +05:00
s809 ea47e11ecf Show dimension names 2024-06-10 23:40:01 +05:00
s809 8a3837b04b Better error on invalid dimension 2024-06-09 22:01:32 +05:00
s809 edfeaa618b Restore old cache key 2024-06-09 21:35:05 +05:00
s809 961cdbe868 Fix indent 2024-06-09 21:21:26 +05:00
s809 ff6ab67b69 Change job name 2024-06-09 21:19:39 +05:00
s809 c01fb4a716 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into refactor/remove-tcp-connection 2024-06-09 21:18:18 +05:00
s809 8bd524791f Use message wrapper in forge 2024-06-09 14:31:15 +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
s809 26059d5656 Fix logging errors 2024-06-08 00:07:33 +05:00
s809 3f23281e8d Add toString to messages & fix incorrect call 2024-06-06 21:25:32 +05:00
s809 fb25423deb Fix compilation for older versions 2 2024-06-05 21:17:21 +05:00
s809 6928c8dc28 Fix compilation for older versions 2024-06-04 23:33:44 +05:00
s809 7dfe0e4c50 Fix compilation 2024-06-02 20:00:16 +05:00
s809 1429ae5434 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into refactor/remove-tcp-connection 2024-06-02 19:59:34 +05:00
s809 3fb4d5a233 Increase section reload delay 2024-06-01 19:49:27 +05:00
s809 872226e5c5 Fix requests breaking on rejoining 2024-06-01 12:17:08 +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
s809 fb46820b1f Kinda works, rejoining is broken 2024-05-29 23:36:38 +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
s809 a786678bcb [skip ci] Incomplete 2024-05-21 22:55:06 +05: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
s809 0d35a3ea8e Move overrides in LAN to config 2024-05-18 22:14:01 +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
s809 6f87e22048 Fix forge config loading too late 2024-05-17 23:02:36 +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
s809 7c07c1e5bd Fix awt dependency error 2024-05-12 17:35:01 +05:00
James Seibel e29a7786e4 Potentially fix LODs not loading in 2024-05-11 16:23:50 -05:00
s809 4ddb9659f5 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into feature/2.0.4 2024-05-11 19:17:13 +05:00
s809 e74b8c89ba Fix levels not unloading on forge 2024-05-11 18:38:25 +05:00
s809 1b536e4aef Fix compilation 2024-05-11 17:51:00 +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
s809 335f11acd3 Fix dimension switching (at cost of breaking immersive portals) 2024-05-10 23:56:15 +05:00
s809 b55b560cce Update core 2024-05-10 21:15:46 +05:00
s809 d87a152052 Fix generated sections not appearing 2024-05-10 21:15:02 +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
s809 db5a9c7c84 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into feature/2.0.4 2024-05-05 19:51:32 +05:00
s809 2508efdd91 Fix updates 2024-05-05 16:55:20 +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
s809 4852720e14 Generation works, updates don't 2024-05-03 22:56:07 +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
s809 f39d156b11 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-05-02 21:47:42 +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
s809 e21eb61335 Fix 1.16.5 and 1.17.1 builds 2024-04-17 21:02:19 +05:00
s809 c49f385e3c Update core 2024-04-16 22:00:10 +05:00
s809 0584b0e593 Fix integer overflows 2024-04-12 23:15:02 +05:00
s809 220ac89ad8 Fix Neoforge 2024-04-12 22:38:21 +05:00
s809 e8e56b66a3 Merge branch 'feature/plugin-channel' 2024-04-02 22:35:28 +05:00
s809 634d8a5b52 Remove localhost from check 2024-04-02 22:27:44 +05:00
s809 b621c9fb8d Some small changes 2024-04-02 21:55:59 +05:00
s809 696f84a064 Fix compilation 2024-04-01 21:52:25 +05:00
s809 5c13ff8960 Fix errors when on vanilla server 2024-04-01 00:55:23 +05:00
s809 e102cd78cd Fix reconnection logic 2024-03-30 23:40:17 +05:00
s809 c5a782acba Fix broken mod compat on server 💀 2024-03-28 09:24:57 +05:00
s809 2008ea2b0c Incomplete 2024-03-26 00:31:13 +05:00
s809 0f349461d9 Fix Forge 2024-03-18 21:38:08 +05:00
s809 baf464aa02 Add thread pool task buildup limiter 2024-03-16 12:28:42 +05:00
s809 51d83d803a [no ci] Incomplete fix forge 2024-03-16 11:22:52 +05:00
s809 2b3244159b Server side plugin networking
Untested port for Forge
2024-03-14 21:46:33 +05:00
s809 9255c22d0e Fix world setting 2024-03-10 21:11:04 +05:00
s809 4d573c4aea Initial buggy plugin channel support 2024-03-05 22:10:05 +05:00
s809 a59f359133 Do not wait for world gen tasks to stop while shutting down queue 2024-02-25 18:05:50 +05:00
s809 c83d02c30c Always print git info 2024-02-25 13:11:37 +05:00
s809 d01573b03e Show server side messages in F3 on disconnect 2024-02-25 00:40:31 +05:00
s809 b1fb74539f Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-02-22 23:06:40 +05:00
s809 40962ec9ba Use separate build caches 2024-02-15 19:22:29 +00:00
s809 0515ac2919 Fix immersive portals 2024-02-15 23:15:11 +05:00
s809 8072695391 Fix connection exception handling 2024-02-13 22:26:24 +05:00
s809 2c140bf7d7 Fix compilation 2024-02-12 21:39:52 +05:00
s809 d3e88b40f4 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-02-12 21:39:04 +05:00
s809 5e0e15777c Add another config option
Fix rate limiting issues (kinda)
2024-02-11 19:39:42 +05:00
s809 b57fc5c9aa Fix compilation 2024-02-08 20:44:46 +05:00
s809 91ea68075e Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-02-08 20:39:21 +05:00
s809 0840ec8b00 Update core 2024-02-07 21:03:48 +05:00
s809 d9f37ad7d6 Fix compilation 2024-02-07 21:03:37 +05:00
s809 992a7129e5 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-02-07 20:50:06 +05:00
s809 1d5ea329de Remove unused packets 2024-02-04 21:58:01 +05:00
s809 00d0a0d2fc Fix queue not filled when generation is toggled 2024-02-04 21:00:06 +05:00
s809 95d4314982 Merge remote-tracking branch 'origin/main' 2024-02-04 02:36:31 +05:00
s809 b588b937ea Update core 2024-02-04 02:35:30 +05:00
s809 f524ad027e Fix editorconfig 2024-02-03 22:38:27 +05:00
s809 b3111b9548 Update core 2024-02-02 05:57:32 +00:00
s809 6c38cb3dee Limit rate+concurrency instead of only concurrency
Rename post-relog update to Login sync
2024-01-29 22:36:21 +05:00
s809 d75b2dfc1c Update core 2024-01-28 22:40:43 +05:00
s809 a5b44d65c6 Change retry a bit 2024-01-28 22:40:36 +05:00
s809 7a1c61405c Retry failed jobs 2024-01-27 19:48:58 +05:00
s809 bcb8794d30 Post-relog updates 2024-01-27 19:45:38 +05:00
s809 f9775b115e Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-01-21 17:20:55 +05:00
s809 a56a2c2ba3 Fix compilation on <1.18.2 2024-01-16 20:28:48 +05:00
s809 b2efb33a5b Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-01-16 20:20:31 +05:00
s809 5928cfe4b4 Fix compilation 2024-01-16 20:18:32 +05:00
s809 234204590f Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-01-16 20:17:48 +05:00
s809 e3838c560a Fix Neoforge compilation 2024-01-13 20:36:12 +05:00
s809 6cbc9db1d8 Disable API and pages compilation 2024-01-13 20:32:25 +05:00
s809 127dcfcb46 Fix generation 2024-01-13 19:44:54 +05:00
s809 d1abdd822d Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-01-13 16:53:04 +05:00
s809 85b7ccb38e Fix crash when changing config while disconnected 2024-01-13 16:11:08 +05:00
s809 fc157b738e Remove Netty from jar and use one from MC instead 2024-01-13 16:11:08 +05:00
s809 977afffe98 Merge branch 'main' of https://gitlab.com/s809/minecraft-lod-mod 2024-01-11 23:34:21 +05:00
s809 180b22f21a Fix cast exception on changing dimensions
(I missed the fact that there's a method for obtaining level without weird hoops 💀)
2024-01-11 23:32:34 +05:00
s809 d0330ad62f Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2024-01-08 17:50:10 +05:00
s809 4d22b18f3c Refactor initializer code 2024-01-07 20:25:34 +05:00
s809 df797b240e Fix 1.16.5 and 1.17.1 builds 2024-01-05 22:37:32 +05:00
s809 a50ef86302 Update core 2024-01-05 22:08:50 +05:00
s809 4aede27d75 Forge versions are functional 2024-01-04 17:36:27 +05:00
s809 e616c63ebe [skip ci] Remove unnecessary build line 2024-01-02 21:03:03 +05:00
s809 03ec333a26 Fix forge builds
Server side is not functional yet
2024-01-02 20:47:11 +05:00
s809 27446965db Make 1.16.5 build
Make Netty version specific for each MC version
2023-12-30 21:57:03 +05:00
s809 c6d7313225 Fix fix 2023-12-29 18:42:10 +00:00
s809 160912a17a Fix 1.19.2 build 2023-12-29 21:13:17 +05:00
s809 d408aacbc8 Fix 1.19.2 build 2023-12-29 21:11:19 +05:00
s809 4889287e69 Start porting to previous versions 2023-12-29 20:31:07 +05:00
s809 6acdfabb7f Update core 2023-12-28 18:59:49 +05:00
s809 3c01d00772 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2023-12-28 18:59:15 +05:00
s809 736bb022d2 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2023-12-24 19:16:16 +05:00
s809 e92130d6cc Merge branch 'main' of https://gitlab.com/s809/minecraft-lod-mod 2023-12-21 19:29:38 +05:00
s809 a44dfa92a6 Update core 2023-12-21 19:29:17 +05:00
s809 4c8ee862e5 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2023-12-21 12:25:02 +05:00
s809 73b89fe573 Disable not working versions 2023-12-19 18:37:14 +00:00
s809 fed2600dbe Add protection against hang on shutdown 2023-12-19 23:20:30 +05:00
s809 1d7d94cfec Update core 2023-12-18 23:15:44 +05:00
s809 5577261774 Update wipe script
Update core
2023-12-18 23:11:47 +05:00
s809 bf0f3131a0 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2023-12-18 19:27:24 +05:00
s809 1b98ea59d7 Remove current post-relog update feature 2023-12-18 19:14:05 +05:00
s809 c38efd9d6b Fix 1.20.1 compilation 2023-12-15 22:31:58 +05:00
s809 4889119061 Merge branch 'main' of https://gitlab.com/s809/minecraft-lod-mod 2023-12-15 21:00:54 +05:00
s809 accb2d6af3 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2023-12-15 20:31:50 +05:00
s809 24b426a17b Disable building of forge jars 2023-12-06 17:24:32 +00:00
s809 8c1bccb430 Update core 2023-12-06 22:07:06 +05:00
s809 3cb2a70f4d Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2023-12-06 21:17:16 +05:00
s809 a313cb3db1 Fix Double missing from console command argument types 2023-11-29 22:39:16 +05:00
s809 c29c6d1870 Fix configs loading too late on server 2023-11-29 22:25:38 +05:00
s809 b4a64b0c3d Update core 2023-11-28 00:06:48 +05:00
s809 1439ca5c08 Remove unnecessary log 2023-11-26 21:34:07 +05:00
s809 97ad3bc78f Update core 2023-11-25 18:05:02 +05:00
s809 b59d8cff8d Fix command response not displayed in chat 2023-11-25 18:04:53 +05:00
s809 0e93aa4aea Basic chat commands 2023-11-25 17:44:52 +05:00
s809 28f8099777 Dummy chat command 2023-11-23 20:58:47 +05:00
s809 c83b0ae533 Sync core 2023-11-20 17:43:09 +05:00
s809 5ceeebe567 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons 2023-11-19 22:57:14 +05:00
s809 e8db9803be Bandaid fix to prevent duplicate section requests 2023-11-12 17:18:39 +05:00
s809 f009fd41ab Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-11-11 19:20:50 +05:00
s809 9faa3b0a54 Do not update client light on a dedicated server 2023-11-04 16:39:23 +05:00
s809 0e70478dd3 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-11-03 21:23:37 +05:00
s809 309b6c6013 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-10-30 15:26:43 +05:00
s809 f20af18b62 Make post-relog update independent from generation toggle 2023-10-23 23:18:57 +05:00
Steveplays28 7d3329fd98 chore: Add missing language entries for the server networking config 2023-10-20 14:07:59 +02:00
Steveplays28 9a880c3ab1 style: Rename renderDistance to renderDistanceRadius to stay in sync with the main config 2023-10-20 14:03:28 +02:00
Steveplays28 0b247f04b4 fix: Fix getting LOD render distance radius from the config
This config option was renamed recently, to fix LODs not rendering out to the LOD render distance border. Also removed an unused import.
2023-10-20 13:50:03 +02:00
Steveplays28 7f1d664731 Merge branch 'main-upstream'
# Conflicts:
#	coreSubProjects
2023-10-20 13:41:50 +02:00
s809 a2288d740b Fix compilation 2023-10-14 22:13:22 +05:00
s809 958f0347a3 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-10-14 22:09:31 +05:00
s809 859bb2e34e Fix compilation 2023-09-28 23:28:23 +05:00
s809 a2acd08e0d Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-09-28 22:54:51 +05:00
Steveplays28 b986583fc4 Merge branch 'main-upstream'
# Conflicts:
#	coreSubProjects
#	fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLevelRenderer.java
2023-09-27 14:10:44 +02:00
s809 c6355f96a6 Improve management of frequent real time updates 2023-09-26 22:03:17 +05:00
s809 723d171746 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-09-26 11:40:59 +05:00
s809 63098d8519 Fix unloaded file check 2023-09-24 18:48:18 +05:00
Steveplays28 44b743267b fix: Fix compiling after merge 2023-09-24 11:34:04 +02:00
Steveplays28 720155c408 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	coreSubProjects
2023-09-24 11:28:43 +02:00
Steveplays28 334b487e23 Merge branch 'main-upstream'
# Conflicts:
#	coreSubProjects
#	fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java
2023-09-24 11:26:12 +02:00
s809 6e674f3732 Add gen task rate display 2023-09-24 14:24:38 +05:00
Steveplays28 d130a602d6 Merge remote-tracking branch 'origin/main' 2023-09-23 16:34:08 +02:00
Steveplays28 8d1ff02937 feat: Add config option for server port 2023-09-23 16:33:42 +02:00
s809 15b1603e35 Fix compiling
Remove unused config category
Use pooling when receiving sections
Fix use of real time update config
Fix debug renderer not unregistered on queue close
2023-09-23 15:32:37 +05:00
s809 4ad0a22db7 Update core 2023-09-21 21:05:31 +05:00
s809 35bc1df32c Add config for a few slow features 2023-09-21 21:00:35 +05:00
s809 bb2a1158c2 Move context levels to requests 2023-09-21 10:16:42 +05:00
s809 76f6f6f746 Allow selecting specific IDebugRenderable's for rendering 2023-09-19 21:33:05 +05:00
s809 3b771b5300 Validate response types 2023-09-19 20:19:31 +05:00
s809 9497918e06 Fix chunk updates sent to incomplete connections
Hide useless warns, for cases when listener is not configured yet
2023-09-19 18:58:16 +05:00
s809 e52c5d5c67 Fix chunk updates sent to incomplete connections
Hide useless warns, for cases when listener is not configured yet
2023-09-19 18:57:55 +05:00
s809 1ce4750dfc Fix future id collisions between c<->s
(cause of occasional hangs on disconnection)
Add packet trace logging
2023-09-19 17:28:49 +05:00
s809 2b371340ff Fix compiling 2023-09-18 13:44:25 +05:00
s809 e8d906b407 Update core 2023-09-18 13:25:06 +05:00
s809 572973c69a Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-09-18 13:24:48 +05:00
Steveplays28 9f2d6b9c78 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	coreSubProjects
2023-09-12 17:35:49 +02:00
s809 54277040e9 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-09-10 12:03:44 +05:00
Steveplays28 bbe333fab7 fix: Fix missing IServerKeyedClientLevel import 2023-09-09 21:03:40 +02:00
Steveplays28 37fed5a924 Merge branch 'main-upstream'
# Conflicts:
#	coreSubProjects
#	forge/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java
2023-09-09 20:54:59 +02:00
Steveplays28 5fc29334cd Merge branch 'main-upstream'
# Conflicts:
#	coreSubProjects
2023-09-09 14:47:18 +02:00
Steveplays28 9d32e92e0a Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	coreSubProjects
2023-09-08 19:55:04 +02:00
s809 b08ee76a00 Fix compiling 2023-09-07 21:07:20 +05:00
s809 2a4cc6e63d Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-09-07 20:07:42 +05:00
s809 6b2560f98b Add post-rejoin updates 2023-09-02 01:30:54 +05:00
s809 08111d8874 Fix compilation 2023-08-27 19:56:32 +05:00
s809 80f48cd5a4 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-08-27 19:43:13 +05:00
s809 40cd6c1386 Fix loading ClientLevel on server 2023-08-27 18:42:07 +05:00
s809 e9a13dffb8 Update core 2023-08-26 21:34:46 +05:00
s809 22680baad7 Merge branch 'feat/server-updates' 2023-08-24 01:01:48 +05:00
s809 cca480e5d8 Merge branch 'main' of https://gitlab.com/s809/minecraft-lod-mod 2023-08-24 00:03:55 +05:00
s809 249c3423d9 Update core 2023-08-24 00:02:53 +05:00
s809 15cb6468d9 Update core 2023-08-23 23:55:48 +05:00
s809 f24019d648 Update core 2023-08-22 20:42:06 +05:00
s809 733d03c630 test 2023-08-22 20:41:42 +05:00
s809 c3e84648f1 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod into feat/server-updates 2023-08-22 19:12:56 +05:00
Steveplays28 76c33b6b58 fix: Add null check to config GUI when adding a screen change listener 2023-08-22 10:52:18 +02:00
s809 26a947ea13 Incomplete 2023-08-21 22:09:16 +05:00
Steveplays28 91334c0e5c feat: Update rendering block ignores
Barrier blocks, structure void blocks, light blocks, and air blocks now share 2 `HashMap`s that define blocks that should be ignored by the LOD builder.
2023-08-21 03:41:26 +02:00
s809 172b05aeac Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-08-19 23:45:16 +05:00
s809 b84e5ca265 Fix hang when near completion of loading from server
Replace total value with sum of pending+finished requests
2023-08-19 22:21:27 +05:00
s809 20ef5a10fc Fix hang when near completion of loading from server
Replace total value with sum of pending+finished requests
2023-08-19 22:21:01 +05:00
s809 33640fd35d Fix prioritization not working properly 2023-08-19 21:24:40 +05:00
s809 e5f7c5728f Fix prioritization not working properly 2023-08-19 21:22:51 +05:00
Steveplays28 5a9bbf9829 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	coreSubProjects
2023-08-19 11:42:09 +02:00
Steveplays28 23ca022ee9 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	coreSubProjects
#	fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java
#	fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLevelRenderer.java
#	fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/mods/sodium/MixinSodiumRenderer.java
2023-08-18 15:11:59 +02:00
Steveplays28 6c80165523 chore: Remove username from Fabric build script 2023-08-18 15:08:48 +02:00
s809 d598061fa7 Add missing level wrapper argument (and a bunch of null checks) 2023-08-18 15:04:49 +02:00
Steveplays28 02498aa189 fix: Fix index out of bounds errors and LOD pillars
The `BiomeWrapper#equals` and `BlockStateWrapper#equals` methods didn't use the right `LevelWrapper` for serializing the object to compare to, this is now fixed.
2023-08-16 13:25:23 +02:00
Steveplays28 d535f6aa57 fix: Comment out leftover Immersive Portals compat
A bit I didn't notice while making the previous commit.
2023-08-16 11:42:02 +02:00
Steveplays28 933753a38a fix: Comment out Immersive Portals compat
The renderer compat was commented out in upstream, but the rest of the compat wasn't commented out, or lost in a merge.
2023-08-16 11:40:46 +02:00
Steveplays28 5483cdbc51 refactor: Update to Sodium 0.5.1
This reverts commits 815aed53 and 21144a7c.
2023-08-15 16:18:05 +02:00
Steveplays28 1ea62fd5ec Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/misc/ServerPlayerWrapper.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ServerLevelWrapper.java
#	coreSubProjects
#	fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java
#	forge/src/main/java/com/seibel/distanthorizons/forge/ForgeClientProxy.java
2023-08-15 15:55:33 +02:00
Steveplays28 aaf0361c50 fix: Fix BlockStateWrapper and Biome serializing to empty strings 2023-08-15 00:03:12 +02:00
Steveplays28 a08feebde0 fix: Fix server trying to access client instance 2023-08-14 19:23:57 +02:00
Steveplays28 f7131fd2ca fix: Fix 1.20.1 compilation 2023-08-14 15:44:45 +02:00
Steveplays28 2100f4a6fe Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java
#	coreSubProjects
2023-08-14 15:40:31 +02:00
Steveplays28 996f097b9d refactor: Re-add old serialization method
This is used by some override methods that don't have access to the level.
These old serialization methods have `FIXME`s so they're easy to find in the future.
2023-08-13 23:18:52 +02:00
Steveplays28 2e1d737268 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java
#	coreSubProjects
2023-08-13 22:34:07 +02:00
s809 d374870fb7 Generation task prioritization (loaded > unloaded > ungenerated) 2023-08-13 18:59:42 +05:00
s809 5c303df32c Update core 2023-08-13 01:53:45 +05:00
s809 dfc4b51e89 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-08-13 01:53:22 +05:00
s809 765202d582 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-08-08 20:58:21 +05:00
s809 0d2e5adbed Skip version if step failed in buildAll 2023-08-08 20:30:19 +05:00
s809 68e9d64779 Fix compiling (except 1.16.5 & 1.17.1) 2023-08-08 20:25:07 +05:00
s809 b64b44f301 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-08-08 12:48:37 +05:00
s809 8c07bb37f3 Update core 2023-08-05 22:30:50 +05:00
s809 c1f6c2dbbd Split fabric run folder into server and client 2023-08-05 22:30:02 +05:00
s809 2eb1c16837 Add Fabric run configurations 2023-08-05 22:29:09 +05:00
s809 e5fb8477d3 Update core 2023-08-04 22:12:28 +05:00
s809 428cacd262 Update core 2023-08-04 21:52:26 +05:00
s809 76926d38db Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-08-04 21:11:03 +05:00
s809 9fec679b03 Attempt to fix dimension switching 2023-08-01 17:04:40 +05:00
s809 853a1d1490 Update core 2023-08-01 12:55:53 +05:00
s809 01273e31bc Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-07-31 17:43:45 +05:00
s809 ebb4808bdc Avoid losing requests due to concurrency 2023-07-31 17:42:59 +05:00
s809 fd09adb1a7 World generation 2023-07-31 15:24:33 +05:00
s809 82b7b439a4 Update core 2023-07-24 19:16:21 +05:00
s809 f1b3ae120b something 2023-07-24 19:16:05 +05:00
s809 70ee20e817 Implement transferring of missing full data source types 2023-07-24 14:15:03 +05:00
s809 f713d65f9c Update core 2023-07-24 11:01:20 +05:00
s809 c2b98ef694 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-07-24 10:48:28 +05:00
s809 140c9f6a50 Update core 2023-07-24 10:48:14 +05:00
s809 c704d61d03 Update core 2023-07-24 01:46:31 +05:00
s809 aae484fbdf Update core 2023-07-23 23:48:58 +05:00
s809 a546a97e34 Got chunks to generate on server 2023-07-23 20:34:40 +05:00
s809 3ca9d98c05 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-07-23 11:09:07 +05:00
s809 460b73e35c Update core 2023-07-19 22:29:49 +05:00
Steveplays28 8901a191d9 feat: Implement syncing of DhSectionPos for chunk requests/responses 2023-07-19 16:49:31 +02:00
s809 1bbe35c9f1 Update core 2023-07-19 17:41:06 +05:00
s809 f92e957826 Update core 2023-07-19 15:54:45 +05:00
s809 636e6736a3 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-07-19 12:21:35 +05:00
s809 8e29f65815 Update core 2023-07-19 12:19:54 +05:00
s809 a9309078f3 Merge branch 'main' of https://gitlab.com/jeseibel/minecraft-lod-mod 2023-07-16 12:48:39 +05:00
s809 9692e978de Update core 2023-07-16 12:38:49 +05:00
s809 914372b858 Repoint back to fork 2023-07-15 23:12:30 +05:00
134 changed files with 5404 additions and 3597 deletions
+2 -2
View File
@@ -538,7 +538,7 @@ ij_groovy_wrap_chain_calls_after_dot = false
ij_groovy_wrap_long_lines = false ij_groovy_wrap_long_lines = false
[{*.har,*.json,*.png.mcmeta,mcmod.info,pack.mcmeta}] [{*.har,*.json,*.png.mcmeta,mcmod.info,pack.mcmeta}]
indent_size = 4 indent_size = 2
ij_json_array_wrapping = split_into_lines ij_json_array_wrapping = split_into_lines
ij_json_keep_blank_lines_in_code = 0 ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false ij_json_keep_indents_on_empty_lines = false
@@ -688,7 +688,7 @@ ij_markdown_wrap_text_inside_blockquotes = true
ij_toml_keep_indents_on_empty_lines = false ij_toml_keep_indents_on_empty_lines = false
[{*.yaml,*.yml}] [{*.yaml,*.yml}]
indent_size = 4 indent_size = 2
ij_yaml_align_values_properties = do_not_align ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false ij_yaml_block_mapping_on_new_line = false
+10 -5
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
@@ -18,19 +18,24 @@ variables:
.build_java: .build_java:
#image: eclipse-temurin:17 #image: eclipse-temurin:17
cache: cache:
key: "gradleCache" key: "gradleCache_$CI_JOB_NAME_SLUG"
policy: pull-push policy: pull-push
paths: paths:
- .gradle - .gradle
- cache/ - cache/
allow_failure: true allow_failure: true
retry:
max: 2
when:
- runner_system_failure
- stuck_or_timeout_failure
build: 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/;
@@ -63,7 +68,7 @@ build:
extends: .build_java extends: .build_java
api: .api:
stage: api stage: api
needs: [] needs: []
script: script:
@@ -87,7 +92,7 @@ api:
# generate and publish API javadocs # generate and publish API javadocs
pages: .pages:
stage: pages stage: pages
needs: [] needs: []
script: script:
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "coreSubProjects"] [submodule "coreSubProjects"]
path = coreSubProjects path = coreSubProjects
url = https://gitlab.com/jeseibel/distant-horizons-core.git url = https://gitlab.com/jeseibel/distant-horizons-core.git
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Fabric Client &amp; Server" type="CompoundRunConfigurationType">
<toRun name="Fabric Client (:fabric)" type="Application" />
<toRun name="Fabric Server (:fabric)" type="Application" />
<method v="2" />
</configuration>
</component>
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Forge Client &amp; Server" type="CompoundRunConfigurationType">
<toRun name="Forge Client (gradle)" type="GradleRunConfiguration" />
<toRun name="Forge Server (gradle)" type="GradleRunConfiguration" />
<method v="2" />
</configuration>
</component>
+24
View File
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Forge Client (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="forge:runClient" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
+24
View File
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Forge Server (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="forge:runServer" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Neoforge Client &amp; Server" type="CompoundRunConfigurationType">
<toRun name="Neoforge Client (gradle)" type="GradleRunConfiguration" />
<toRun name="Neoforge Server (gradle)" type="GradleRunConfiguration" />
<method v="2" />
</configuration>
</component>
+24
View File
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Neoforge Client (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="neoforge:runClient" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
+24
View File
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Neoforge Server (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="neoforge:runServer" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
+71 -37
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
} }
@@ -27,8 +27,8 @@ def writeBuildGradlePredefine(List<String> mcVers, int mcIndex)
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n"); sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n");
for (int i = 0; i < mcVers.size(); i++) for (int i = 0; i < mcVers.size(); i++)
{ {
String verStr = mcVers[i].replace(".", "_"); String verStr = mcVers[i].replace(".", "_");
@@ -54,7 +54,7 @@ def writeBuildGradlePredefine(List<String> mcVers, int mcIndex)
// Transfers the values set in settings.gradle to the rest of the project // Transfers the values set in settings.gradle to the rest of the project
project.gradle.ext.getProperties().each { prop -> project.gradle.ext.getProperties().each { prop ->
rootProject.ext.set(prop.key, prop.value) rootProject.ext.set(prop.key, prop.value)
// println "Added prop [key:" + prop.key + ", value:" + prop.value + "]" //println "Added prop [key:" + prop.key + ", value:" + prop.value + "]"
} }
// Sets up manifold stuff // Sets up manifold stuff
writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex) writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
@@ -104,20 +104,21 @@ subprojects { p ->
apply plugin: "com.github.johnrengelman.shadow" apply plugin: "com.github.johnrengelman.shadow"
if (isMinecraftSubProject) if (isMinecraftSubProject)
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)
manifold { manifold {
manifoldVersion = rootProject.manifold_version manifoldVersion = rootProject.manifold_version
} }
// set up custom configurations (configurations are a way to handle dependencies) // set up custom configurations (configurations are a way to handle dependencies)
configurations { configurations {
@@ -190,23 +191,34 @@ subprojects { p ->
forgeShadowMe("org.joml:joml:${rootProject.joml_version}") forgeShadowMe("org.joml:joml:${rootProject.joml_version}")
else else
implementation("org.joml:joml:${rootProject.joml_version}") implementation("org.joml:joml:${rootProject.joml_version}")
// JUnit tests // JUnit tests
implementation("org.junit.jupiter:junit-jupiter:5.8.2") implementation("org.junit.jupiter:junit-jupiter:5.8.2")
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
implementation("io.netty:netty-buffer:${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}") {
@@ -264,22 +276,39 @@ subprojects { p ->
if (isMinecraftSubProject && p != project(":common")) { if (isMinecraftSubProject && p != project(":common")) {
configurations.push(project.configurations.shadowCommon) // Shadow the common subproject configurations.push(project.configurations.shadowCommon) // Shadow the common subproject
relocate "com.seibel.distanthorizons.common", "loaderCommon.${p.name}.com.seibel.distanthorizons.common" // Move the loader files to a different location relocate "com.seibel.distanthorizons.common", "loaderCommon.${p.name}.com.seibel.distanthorizons.common" // Move the loader files to a different location
if (findProject(":fabricLike") && p != project(":fabricLike")) { if (findProject(":fabricLike") && p != project(":fabricLike")) {
configurations.push(project.configurations.shadowFabricLike) // Shadow the fabricLike subproject configurations.push(project.configurations.shadowFabricLike) // Shadow the fabricLike subproject
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
// Don't relocate, it causes problems with using MC's FriendlyByteBufs
// 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,9 +326,10 @@ 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"
] ]
def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder
@@ -358,14 +388,14 @@ subprojects { p ->
fabric_incompatibility_list : fabric_incompatibility_list, fabric_incompatibility_list : fabric_incompatibility_list,
fabric_recommend_list : fabric_recommend_list, fabric_recommend_list : fabric_recommend_list,
] ]
// replace any properties in the sub-projects with the values defined here // replace any properties in the sub-projects with the values defined here
inputs.properties replaceProperties inputs.properties replaceProperties
replaceProperties.put "project", project replaceProperties.put "project", project
filesMatching(resourceTargets) { filesMatching(resourceTargets) {
expand replaceProperties expand replaceProperties
} }
intoTargets.each { target -> intoTargets.each { target ->
if (file(target).exists()) { if (file(target).exists()) {
copy { copy {
@@ -401,9 +431,11 @@ subprojects { p ->
jar { jar {
from "LICENSE.txt" from "LICENSE.txt"
manifest { manifest {
attributes 'Implementation-Title': rootProject.mod_name, attributes(
'Implementation-Version': rootProject.mod_version, 'Implementation-Title': rootProject.mod_name,
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain' // When changing the main of the jar change this line 'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain' // When changing the main of the jar change this line
)
} }
} }
@@ -492,15 +524,17 @@ 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 {
dirs "${rootDir}/mods/fabric" dirs "${rootDir}/mods/fabric"
@@ -605,4 +639,4 @@ task cleanMergedJars() {
} }
} }
// add cleanMergedJars to the end of the "clean" task // add cleanMergedJars to the end of the "clean" task
tasks["clean"].finalizedBy(cleanMergedJars) tasks["clean"].finalizedBy(cleanMergedJars)
+12 -6
View File
@@ -1,7 +1,8 @@
#!/bin/sh #!/bin/sh
echo "==================== Note: All build jars will be in the folder called 'buildAllJars' ====================" echo "==================== Note: All build jars will be in the folder called 'buildAllJars' ===================="
mkdir -p buildAllJars | true mkdir -p buildAllJars
rm -rf buildAllJars/*
# Loop trough everything in the version properties folder # Loop trough everything in the version properties folder
for d in versionProperties/*; do for d in versionProperties/*; do
@@ -11,12 +12,17 @@ for d in versionProperties/*; do
# Clean out the folders, build it, and merge it # Clean out the folders, build it, and merge it
# (We could use "./" to run gradlew, but as it is a shell script im going to be running it with the "sh" command) # (We could use "./" to run gradlew, but as it is a shell script im going to be running it with the "sh" command)
echo "==================== Cleaning workspace to build $version ====================" echo "==================== Cleaning workspace to build $version ===================="
sh gradlew clean -PmcVer=$version --no-daemon || true sh gradlew clean -PmcVer=$version
if [ $? != 0 ]; then continue; fi
echo "====================Building $version ====================" echo "====================Building $version ===================="
sh gradlew build -PmcVer=$version --no-daemon || true sh gradlew build -PmcVer=$version
if [ $? != 0 ]; then continue; fi
echo "==================== Merging $version ====================" echo "==================== Merging $version ===================="
sh gradlew mergeJars -PmcVer=$version --no-daemon || true sh gradlew mergeJars -PmcVer=$version
if [ $? != 0 ]; then continue; fi
echo "==================== Moving jar ====================" echo "==================== Moving jar ===================="
mv Merged/*.jar buildAllJars/ || true mv Merged/*.jar buildAllJars/
# The "| true" at the end of those are just to make sure the script continues even if a build fails
done done
+17 -2
View File
@@ -1,3 +1,19 @@
buildscript {
configurations.configureEach {
resolutionStrategy {
force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82'
}
}
}
// 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"
@@ -26,5 +42,4 @@ publishing {
repositories { repositories {
// Add repositories to publish to here. // Add repositories to publish to here.
} }
} }
@@ -1,18 +1,30 @@
package com.seibel.distanthorizons.common; package com.seibel.distanthorizons.common;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.*;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
import com.seibel.distanthorizons.common.wrappers.DependencySetup; import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftDedicatedServerWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftDedicatedServerWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase; import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler; import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
import com.seibel.distanthorizons.core.config.types.AbstractConfigType;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
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.jar.ModJarInfo; import com.seibel.distanthorizons.core.jar.ModJarInfo;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.world.DhServerWorld;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
@@ -23,9 +35,25 @@ import net.minecraft.server.dedicated.DedicatedServer;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import static com.mojang.brigadier.arguments.DoubleArgumentType.doubleArg;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_ENABLE_CODEC_CRASH_MESSAGE;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
#if MC_VER >= MC_1_19_2
import net.minecraft.network.chat.Component;
#else // < 1.19.2
import net.minecraft.network.chat.TranslatableComponent;
#endif
/** /**
* Base for all mod loader initializers * Base for all mod loader initializers
* and handles most setup. * and handles most setup.
@@ -67,7 +95,7 @@ public abstract class AbstractModInitializer
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup(); this.startup();
this.printModInfo(true); this.printModInfo();
this.createClientProxy().registerEvents(); this.createClientProxy().registerEvents();
this.createServerProxy(false).registerEvents(); this.createServerProxy(false).registerEvents();
@@ -91,7 +119,7 @@ public abstract class AbstractModInitializer
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup(); this.startup();
this.printModInfo(false); this.printModInfo();
// This prevents returning uninitialized Config values, // This prevents returning uninitialized Config values,
// resulting from a circular reference mid-initialization in a static class // resulting from a circular reference mid-initialization in a static class
@@ -100,6 +128,8 @@ public abstract class AbstractModInitializer
this.createServerProxy(true).registerEvents(); this.createServerProxy(true).registerEvents();
this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " Initialized"); LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
@@ -130,17 +160,14 @@ public abstract class AbstractModInitializer
this.createInitialBindings(); this.createInitialBindings();
} }
private void printModInfo(boolean printGitInfo) private void printModInfo()
{ {
LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION); LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
if (printGitInfo) // Useful for dev builds
{ LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch);
// Useful for dev builds LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit);
LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch); LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source);
LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit);
LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source);
}
} }
protected <T extends IModAccessor> void tryCreateModCompatAccessor(String modId, Class<? super T> accessorClass, Supplier<T> accessorConstructor) protected <T extends IModAccessor> void tryCreateModCompatAccessor(String modId, Class<? super T> accessorClass, Supplier<T> accessorConstructor)
@@ -166,7 +193,136 @@ public abstract class AbstractModInitializer
LOGGER.info("Mod Post-Initialized"); LOGGER.info("Mod Post-Initialized");
} }
private void initCommands() { /* currently unimplemented */ } @SuppressWarnings({"rawtypes", "unchecked"})
private void initCommands()
{
LiteralArgumentBuilder<CommandSourceStack> builder = literal("dhconfig")
.requires(source -> source.hasPermission(4));
for (AbstractConfigType<?, ?> type : ConfigBase.INSTANCE.entries)
{
if (!(type instanceof ConfigEntry))
{
continue;
}
//noinspection PatternVariableCanBeUsed
ConfigEntry configEntry = (ConfigEntry) type;
if (configEntry.getServersideShortName() == null)
{
continue;
}
Function<
Function<CommandContext<CommandSourceStack>, Object>,
Command<CommandSourceStack>
> makeConfigUpdater = getter -> c -> {
Object value = getter.apply(c);
c.getSource().sendSuccess(
#if MC_VER >= MC_1_20_1
() -> Component.literal("Changed the value of "+configEntry.getServersideShortName()+" to "+value),
#elif MC_VER >= MC_1_19_2
Component.literal("Changed the value of "+configEntry.getServersideShortName()+" to "+value),
#else
new TranslatableComponent("Changed the value of "+configEntry.getServersideShortName()+" to "+value),
#endif
true);
configEntry.set(value);
return 1;
};
LiteralArgumentBuilder<CommandSourceStack> subcommand = literal(configEntry.getServersideShortName())
.executes(c -> {
#if MC_VER >= MC_1_20_1
c.getSource().sendSuccess(() -> Component.literal("Current value of "+configEntry.getServersideShortName()+" is "+configEntry.get()), true);
#elif MC_VER >= MC_1_19_2
c.getSource().sendSuccess(Component.literal("Current value of "+configEntry.getServersideShortName()+" is "+configEntry.get()), true);
#else // < 1.19.2
c.getSource().sendSuccess(new TranslatableComponent("Current value of "+configEntry.getServersideShortName()+" is "+configEntry.get()), true);
#endif
return 1;
});
if (Enum.class.isAssignableFrom(configEntry.getType()))
{
for (Object choice : configEntry.getType().getEnumConstants())
{
subcommand.then(
literal(choice.toString())
.executes(makeConfigUpdater.apply(c -> choice))
);
}
}
else
{
boolean setterAdded = false;
for (java.util.Map.Entry<Class<?>, Pair<Supplier<ArgumentType<?>>, BiFunction<CommandContext<?>, String, ?>>> pair : new HashMap<
Class<?>,
Pair<
Supplier<ArgumentType<?>>,
BiFunction<CommandContext<?>, String, ?>>
>() {{
this.put(Integer.class, new Pair<>(() -> integer((int) configEntry.getMin(), (int) configEntry.getMax()), IntegerArgumentType::getInteger));
this.put(Double.class, new Pair<>(() -> doubleArg((double) configEntry.getMin(), (double) configEntry.getMax()), DoubleArgumentType::getDouble));
this.put(Boolean.class, new Pair<>(BoolArgumentType::bool, BoolArgumentType::getBool));
this.put(String.class, new Pair<>(StringArgumentType::string, StringArgumentType::getString));
}}.entrySet())
{
if (!pair.getKey().isAssignableFrom(configEntry.getType()))
{
continue;
}
subcommand.then(argument("value", pair.getValue().first.get())
.executes(makeConfigUpdater.apply(c -> pair.getValue().second.apply(c, "value"))));
setterAdded = true;
break;
}
if (!setterAdded)
{
throw new RuntimeException("Config type of "+type.getName()+" is not supported: "+configEntry.getType().getSimpleName());
}
}
builder.then(subcommand);
}
this.commandDispatcher.register(builder);
if (DEBUG_ENABLE_CODEC_CRASH_MESSAGE)
{
LiteralArgumentBuilder<CommandSourceStack> dhcrash = literal("dhcrash")
.requires(source -> source.hasPermission(4))
.then(literal("encode")
.executes(c -> {
assert SharedApi.getIDhServerWorld() != null;
((DhServerWorld) SharedApi.getIDhServerWorld()).remotePlayerConnectionHandler
#if MC_VER >= MC_1_19_2
.getConnectedPlayer(ServerPlayerWrapper.getWrapper(Objects.requireNonNull(c.getSource().getPlayer())))
#else
.getConnectedPlayer(ServerPlayerWrapper.getWrapper(Objects.requireNonNull(c.getSource().getPlayerOrException())))
#endif
.session.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.ENCODE));
return 1;
}))
.then(literal("decode")
.executes(c -> {
assert SharedApi.getIDhServerWorld() != null;
((DhServerWorld) SharedApi.getIDhServerWorld()).remotePlayerConnectionHandler
#if MC_VER >= MC_1_19_2
.getConnectedPlayer(ServerPlayerWrapper.getWrapper(Objects.requireNonNull(c.getSource().getPlayer())))
#else
.getConnectedPlayer(ServerPlayerWrapper.getWrapper(Objects.requireNonNull(c.getSource().getPlayerOrException())))
#endif
.session.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.DECODE));
return 1;
}));
this.commandDispatcher.register(dhcrash);
}
}
@@ -179,4 +335,4 @@ public abstract class AbstractModInitializer
void registerEvents(); void registerEvents();
} }
} }
@@ -0,0 +1,114 @@
package com.seibel.distanthorizons.common;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageEvent;
import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorEvent;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
import com.seibel.distanthorizons.core.network.messages.NetworkMessage;
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import io.netty.buffer.ByteBufUtil;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import org.apache.logging.log4j.LogManager;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Objects;
public abstract class AbstractPluginPacketSender implements IPluginPacketSender
{
private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
() -> Config.Client.Advanced.Logging.logNetworkEvent.get());
#if MC_VER >= MC_1_21_1
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
#else
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
#endif
@Override
public final void sendPluginPacketServer(IServerPlayerWrapper serverPlayer, NetworkMessage message)
{
this.sendPluginPacketServer((ServerPlayer) serverPlayer.getWrappedMcObject(), message);
}
@Override
public abstract void sendPluginPacketClient(NetworkMessage message);
public abstract void sendPluginPacketServer(ServerPlayer serverPlayer, NetworkMessage message);
@Nullable
public static NetworkMessage decodeMessage(FriendlyByteBuf in)
{
NetworkMessage message = null;
try
{
in.markReaderIndex();
int protocolVersion = in.readShort();
if (protocolVersion != ModInfo.PROTOCOL_VERSION)
{
return new IncompatibleMessageEvent(protocolVersion);
}
message = MessageRegistry.INSTANCE.createMessage(in.readUnsignedShort());
message.decode(in);
if (in.isReadable())
{
throw new IOException("Buffer has not been fully read");
}
return message;
}
catch (Exception e)
{
in.resetReaderIndex();
LOGGER.error("Failed to decode message", e);
LOGGER.error("Buffer: {}", in);
LOGGER.error("Buffer contents: [{}]", ByteBufUtil.hexDump(in));
return new ProtocolErrorEvent(e, message, true);
}
finally
{
// Prevent connection crashing if not entire buffer has been read
in.readerIndex(in.writerIndex());
}
}
public static void encodeMessage(FriendlyByteBuf out, NetworkMessage message)
{
// This is intentionally unhandled, because errors related to this are unlikely to appear in wild
Objects.requireNonNull(message);
out.writeShort(ModInfo.PROTOCOL_VERSION);
try
{
out.markWriterIndex();
out.writeShort(MessageRegistry.INSTANCE.getMessageId(message));
message.encode(out);
}
catch (Exception e)
{
LOGGER.error("Failed to encode message", e);
LOGGER.error("Message: {}", message);
message.getSession().tryHandleMessage(new ProtocolErrorEvent(e, message, false));
// Encode close reason message instead
out.resetWriterIndex();
message = new CloseReasonMessage("Internal error on other side");
out.writeShort(MessageRegistry.INSTANCE.getMessageId(message));
message.encode(out);
}
}
}
@@ -0,0 +1,45 @@
package com.seibel.distanthorizons.common;
#if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.core.network.messages.NetworkMessage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public record CommonPacketPayload(
@Nullable NetworkMessage message
) implements CustomPacketPayload
{
public static final Type<CommonPacketPayload> TYPE = new Type<>(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE);
@NotNull
@Override
public Type<? extends CustomPacketPayload> type()
{
return TYPE;
}
public static class Codec implements StreamCodec<FriendlyByteBuf, CommonPacketPayload>
{
@NotNull
@Override
public CommonPacketPayload decode(@NotNull FriendlyByteBuf in)
{
return new CommonPacketPayload(AbstractPluginPacketSender.decodeMessage(in));
}
@Override
public void encode(@NotNull FriendlyByteBuf out, CommonPacketPayload payload)
{
AbstractPluginPacketSender.encodeMessage(out, payload.message());
}
}
}
#endif
@@ -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
@@ -142,19 +179,9 @@ public class WrapperFactory implements IWrapperFactory
// level wrapper // level wrapper
ILevelWrapper levelWrapper; ILevelWrapper levelWrapper = level.isClientSide()
if (level instanceof ServerLevel) ? ClientLevelWrapper.getWrapper((ClientLevel)level)
{ : ServerLevelWrapper.getWrapper((ServerLevel)level);
levelWrapper = ServerLevelWrapper.getWrapper((ServerLevel)level);
}
else if (level instanceof ClientLevel)
{
levelWrapper = ClientLevelWrapper.getWrapper((ClientLevel)level);
}
else
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
return new ChunkWrapper(chunk, lightSource, levelWrapper); return new ChunkWrapper(chunk, lightSource, levelWrapper);
@@ -164,16 +191,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 +201,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] or [ClientLevel]" // Classes are not referenced by names to avoid exception when one of them is missing
}; };
#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 +225,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 +246,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 +254,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 +266,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 +278,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 +296,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 +308,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,53 +272,82 @@ public class BiomeWrapper implements IBiomeWrapper
return EMPTY_WRAPPER; return EMPTY_WRAPPER;
} }
if (WRAPPER_BY_RESOURCE_LOCATION.containsKey(finalResourceStateString))
// parse the resource location
int separatorIndex = resourceLocationString.indexOf(":");
if (separatorIndex == -1)
{ {
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "]."); return WRAPPER_BY_RESOURCE_LOCATION.get(finalResourceStateString);
} }
ResourceLocation resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
// if no wrapper is found, default to the empty wrapper
BiomeWrapper foundWrapper = EMPTY_WRAPPER;
try try
{ {
Level level = (Level)levelWrapper.getWrappedMcObject(); // parse the resource location
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); int separatorIndex = resourceLocationString.indexOf(":");
if (separatorIndex == -1)
boolean success;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (biome != null);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#else
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#endif
if (!success)
{ {
if (!brokenResourceLocationStrings.contains(resourceLocationString)) throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
{
brokenResourceLocationStrings.add(resourceLocationString);
LOGGER.warn("Unable to deserialize biome from string: [" + resourceLocationString + "]");
}
return EMPTY_WRAPPER;
} }
return getBiomeWrapper(biome, levelWrapper); 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
{
Level level = (Level) levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
boolean success;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (biome != null);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#else
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#endif
if (!success)
{
if (!brokenResourceLocationStrings.contains(resourceLocationString))
{
brokenResourceLocationStrings.add(resourceLocationString);
LOGGER.warn("Unable to deserialize biome from string: [" + resourceLocationString + "]");
}
return EMPTY_WRAPPER;
}
foundWrapper = (BiomeWrapper) getBiomeWrapper(biome, levelWrapper);
return foundWrapper;
}
catch (Exception e)
{
throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BiomeWrapper: " + e.getMessage(), e);
}
} }
catch (Exception e) finally
{ {
throw new IOException("Failed to deserialize the string [" + resourceLocationString + "] into a BiomeWrapper: " + e.getMessage(), e); WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
} }
} }
@@ -19,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
@@ -46,7 +54,6 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.EmptyBlockGetter;
#else #else
import net.minecraft.client.Minecraft;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
@@ -65,15 +72,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 +95,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 +138,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 +213,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;
} }
// add all possible blockstates (to account for light blocks with different light values and such)
List<BlockState> blockStatesToIgnore = DefaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates(); BlockStateWrapper defaultBlockStateToIgnore = (BlockStateWrapper) deserialize(cleanedResourceLocation, levelWrapper);
for (BlockState blockState : blockStatesToIgnore) blockStateWrappers.add(defaultBlockStateToIgnore);
if (defaultBlockStateToIgnore != AIR)
{ {
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper); // add all possible blockstates (to account for light blocks with different light values and such)
blockStateWrappers.add(newBlockToIgnore); List<BlockState> blockStatesToIgnore = defaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates();
for (BlockState blockState : blockStatesToIgnore)
{
BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper);
blockStateWrappers.add(newBlockToIgnore);
}
}
else
{
// air is a special case so it must be handled separately
blockStateWrappers.add(AIR);
} }
} }
catch (IOException e) 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 +333,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 +418,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,103 +483,137 @@ 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))
// try to parse out the BlockState
String blockStatePropertiesString = null; // will be null if no properties were included
int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR);
if (stateSeparatorIndex != -1)
{ {
// blockstate properties found return WRAPPER_BY_RESOURCE_LOCATION.get(finalResourceStateString);
blockStatePropertiesString = resourceStateString.substring(stateSeparatorIndex + STATE_STRING_SEPARATOR.length());
resourceStateString = resourceStateString.substring(0, stateSeparatorIndex);
} }
// parse the resource location
int resourceSeparatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR);
if (resourceSeparatorIndex == -1)
{
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
}
ResourceLocation resourceLocation = new ResourceLocation(resourceStateString.substring(0, resourceSeparatorIndex), resourceStateString.substring(resourceSeparatorIndex + 1));
// if no wrapper is found, default to air
// attempt to get the BlockState from all possible BlockStates BlockStateWrapper foundWrapper = AIR;
try try
{ {
// try to parse out the BlockState
#if MC_VER > MC_1_17_1 String blockStatePropertiesString = null; // will be null if no properties were included
// use the given level if possible, otherwise try using the currently loaded one int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR);
Level level = (levelWrapper != null ? (Level)levelWrapper.getWrappedMcObject() : null); if (stateSeparatorIndex != -1)
level = (level == null ? Minecraft.getInstance().level : level);
#endif
Block block;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
block = Registry.BLOCK.get(resourceLocation);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
#else
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
#endif
if (block == null)
{ {
// shouldn't normally happen, but here to make the compiler happy // blockstate properties found
if (!BrokenResourceLocations.contains(resourceLocation)) blockStatePropertiesString = resourceStateString.substring(stateSeparatorIndex + STATE_STRING_SEPARATOR.length());
{ resourceStateString = resourceStateString.substring(0, stateSeparatorIndex);
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.");
} // parse the resource location
return AIR; int separatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR);
if (separatorIndex == -1)
{
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
}
ResourceLocation resourceLocation;
try
{
#if MC_VER < MC_1_21_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() + "].");
} }
// attempt to find the blockstate from all possibilities
BlockState foundState = null;
if (blockStatePropertiesString != null)
{
List<BlockState> possibleStateList = block.getStateDefinition().getPossibleStates();
for (BlockState possibleState : possibleStateList)
{
String possibleStatePropertiesString = serializeBlockStateProperties(possibleState);
if (possibleStatePropertiesString.equals(blockStatePropertiesString))
{
foundState = possibleState;
break;
}
}
}
// use the default if no state was found or given // attempt to get the BlockState from all possible BlockStates
if (foundState == null) try
{ {
if (blockStatePropertiesString != null)
#if MC_VER > MC_1_17_1
Level level = (Level)Objects.requireNonNull(levelWrapper.getWrappedMcObject());
#endif
Block block;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
block = Registry.BLOCK.get(resourceLocation);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
#else
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
#endif
if (block == null)
{ {
// we should have found a blockstate, but didn't // shouldn't normally happen, but here to make the compiler happy
if (!BrokenResourceLocations.contains(resourceLocation)) if (!BrokenResourceLocations.contains(resourceLocation))
{ {
BrokenResourceLocations.add(resourceLocation); BrokenResourceLocations.add(resourceLocation);
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state."); LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
}
return AIR;
}
// attempt to find the blockstate from all possibilities
BlockState foundState = null;
if (blockStatePropertiesString != null)
{
List<BlockState> possibleStateList = block.getStateDefinition().getPossibleStates();
for (BlockState possibleState : possibleStateList)
{
String possibleStatePropertiesString = serializeBlockStateProperties(possibleState);
if (possibleStatePropertiesString.equals(blockStatePropertiesString))
{
foundState = possibleState;
break;
}
} }
} }
foundState = block.defaultBlockState(); // use the default if no state was found or given
if (foundState == null)
{
if (blockStatePropertiesString != null)
{
// we should have found a blockstate, but didn't
if (!BrokenResourceLocations.contains(resourceLocation))
{
BrokenResourceLocations.add(resourceLocation);
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
}
}
foundState = block.defaultBlockState();
}
foundWrapper = new BlockStateWrapper(foundState, levelWrapper);
return foundWrapper;
}
catch (Exception e)
{
throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BlockStateWrapper: " + e.getMessage(), e);
} }
return new BlockStateWrapper(foundState, levelWrapper);
} }
catch (Exception e) finally
{ {
throw new IOException("Failed to deserialize the string [" + resourceStateString + "] into a BlockStateWrapper: " + e.getMessage(), e); // put if absent in case two threads deserialize at the same time
// unfortunately we can't put everything in a computeIfAbsent() since we also throw exceptions
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
} }
} }
@@ -475,11 +653,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 +670,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 +687,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 +699,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 +713,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,38 +722,38 @@ 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());
}
else
{
// hopefully unneeded debug color
return ColorUtil.CYAN;
}
} }
private Biome _unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
private static Biome unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{ {
#if 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); }
}
}
@@ -22,23 +22,24 @@ package com.seibel.distanthorizons.common.wrappers.chunk;
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.worldGeneration.mimicObject.DhLitWorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
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.util.LodUtil;
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
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 +74,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 +108,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).
@@ -136,36 +143,41 @@ public class ChunkWrapper implements IChunkWrapper
// FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the relative position validator // FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the relative position validator
chunksNeedingClientLightUpdating.add(this); if (SharedApi.getEnvironment() != EWorldEnvironment.Server_Only)
chunksNeedingClientLightUpdating.add(this);
} }
//=========// //=========//
// 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 +227,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 +237,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 +254,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 +264,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 +287,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 +325,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 +337,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()
{ {
@@ -321,7 +352,7 @@ public class ChunkWrapper implements IChunkWrapper
if (this.chunk instanceof LevelChunk) if (this.chunk instanceof LevelChunk)
{ {
LevelChunk levelChunk = (LevelChunk) this.chunk; LevelChunk levelChunk = (LevelChunk) this.chunk;
if (levelChunk.getLevel() instanceof ClientLevel) if (levelChunk.getLevel().isClientSide())
{ {
// connected to a server // connected to a server
return this.isMcClientLightingCorrect; return this.isMcClientLightingCorrect;
@@ -358,13 +389,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 +410,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 +467,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 +483,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 +497,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 +556,57 @@ public class ChunkWrapper implements IChunkWrapper
//================// //===============//
// helper methods // // other methods //
//================// //===============//
/** 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 boolean doNearbyChunksExist()
{ {
if (!RUN_RELATIVE_POS_INDEX_VALIDATION) if (this.lightSource instanceof DhLitWorldGenRegion)
{ {
return; return true;
} }
for (int dx = -1; dx <= 1; dx++)
// 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" + for (int dz = -1; dz <= 1; dz++)
"X/Z must be between 0 and 15 (inclusive) \n" + {
"Y must be between [" + minHeight + "] and [" + maxHeight + "] (inclusive)."; if (dx == 0 && dz == 0)
throw new IndexOutOfBoundsException(errorMessage); {
continue;
}
else if (this.lightSource.getChunk(dx + this.chunk.getPos().x, dz + this.chunk.getPos().z, ChunkStatus.BIOMES, false) == null)
{
return false;
}
}
} }
}
/**
* 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; return true;
final int yRel = y + this.getMinBuildHeight();
final int xRel = index % LodUtil.CHUNK_WIDTH;
return new DhBlockPos(xRel, yRel, zRel);
} }
@Override
public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; }
}
//================//
// base overrides //
//================//
@Override
public String toString() { return this.chunk.getClass().getSimpleName() + this.chunk.getPos(); }
//@Override
//public int hashCode()
//{
// if (this.blockBiomeHashCode == 0)
// {
// this.blockBiomeHashCode = this.getBlockBiomeHashCode();
// }
//
// return this.blockBiomeHashCode;
//}
}
@@ -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,18 +279,25 @@ 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"),
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile(); this.width / 2 - 154, this.height - 28,
Objects.requireNonNull(minecraft).setScreen(parent); 150, 20,
})); button ->
{
ConfigBase.INSTANCE.configFileINSTANCE.loadFromFile();
Objects.requireNonNull(minecraft).setScreen(parent);
}));
doneButton = addBtn(MakeBtn(Translatable("distanthorizons.general.done"), this.width / 2 + 4, this.height - 28, 150, 20, (button) -> { doneButton = addBtn(MakeBtn(Translatable("distanthorizons.general.done"), this.width / 2 + 4, this.height - 28, 150, 20, (button) -> {
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile(); ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(minecraft).setScreen(parent); Objects.requireNonNull(minecraft).setScreen(parent);
})); }));
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
} }
@@ -41,16 +41,18 @@ public class UpdateModScreen extends DhScreen
super(Translatable(ModInfo.ID + ".updater.title")); super(Translatable(ModInfo.ID + ".updater.title"));
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
@@ -1,21 +1,20 @@
package com.seibel.distanthorizons.common.wrappers.level; package com.seibel.distanthorizons.common.wrappers.level;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
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 net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class KeyedClientLevelManager implements IKeyedClientLevelManager public class KeyedClientLevelManager implements IKeyedClientLevelManager
{ {
public static final KeyedClientLevelManager INSTANCE = new KeyedClientLevelManager(); public static final KeyedClientLevelManager INSTANCE = new KeyedClientLevelManager();
/** This is set and managed by the ClientApi for servers with support for DH. */ /** This is set and managed by the ClientApi for servers with support for DH. */
private IServerKeyedClientLevel overrideWrapper = null; @Nullable
private boolean useOverrideWrapper = false; private IServerKeyedClientLevel serverKeyedLevel = null;
private boolean enabled = false;
//=============// //=============//
@@ -30,25 +29,39 @@ public class KeyedClientLevelManager implements IKeyedClientLevelManager
// level override logic // // level override logic //
//======================// //======================//
@Override
public void setServerKeyedLevel(IServerKeyedClientLevel clientLevel) { this.overrideWrapper = clientLevel; }
@Override
public IServerKeyedClientLevel getOverrideWrapper() { return this.overrideWrapper; }
@Override @Override
public IServerKeyedClientLevel getServerKeyedLevel(ILevelWrapper level, String serverLevelKey) @Nullable
public IServerKeyedClientLevel getServerKeyedLevel()
{ {
Objects.requireNonNull(level); return this.serverKeyedLevel;
Objects.requireNonNull(serverLevelKey);
return new ServerKeyedClientLevel((ClientLevel) level.getWrappedMcObject(), serverLevelKey);
} }
@Override
public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String levelKey)
{
IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevel((ClientLevel) clientLevel.getWrappedMcObject(), levelKey);
this.serverKeyedLevel = keyedLevel;
this.enabled = true;
return keyedLevel;
}
@Override @Override
public void setUseOverrideWrapper(boolean useOverrideWrapper) { this.useOverrideWrapper = useOverrideWrapper; } public void clearServerKeyedLevel()
{
this.serverKeyedLevel = null;
}
@Override @Override
public boolean getUseOverrideWrapper() { return this.useOverrideWrapper; } public boolean isEnabled()
{
return this.enabled;
}
@Override
public void disable()
{
this.enabled = false;
}
} }
@@ -18,4 +18,11 @@ public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServe
@Override @Override
public String getServerLevelKey() { return this.serverLevelKey; } public String getServerLevelKey() { return this.serverLevelKey; }
}
@Override
public String getDimensionName()
{
return this.getServerLevelKey();
}
}
@@ -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,14 @@ 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.ClientLevel;
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
@@ -52,6 +54,7 @@ import net.minecraft.network.chat.TextComponent;
#endif #endif
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -59,16 +62,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 +101,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 +117,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 +149,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 +213,46 @@ 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) return this.getWrappedClientLevel(false);
}
@Override
@Nullable
public IClientLevelWrapper getWrappedClientLevel(boolean bypassMultiverse)
{
ClientLevel level = MINECRAFT.level;
if (level == null)
{ {
return null; return null;
} }
return ClientLevelWrapper.getWrapperIgnoringOverride(this.mc.level); return ClientLevelWrapper.getWrapper(level, bypassMultiverse);
}
/** 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 +261,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 +275,15 @@ 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 +308,21 @@ 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); }
@Override
public boolean isWorldInitialized()
{
throw new NotImplementedException("TODO");
}
} }
@@ -12,16 +12,26 @@ 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
} }
} @Override
public boolean isWorldInitialized()
{
return this.dedicatedServer.getWorldData().overworldData().isInitialized();
}
}
@@ -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,15 +154,24 @@ 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
@@ -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);
} }
} }
@@ -0,0 +1,11 @@
package com.seibel.distanthorizons.common.wrappers.misc;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.Nullable;
public interface IMixinServerPlayer
{
@Nullable
ServerLevel distantHorizons$getDimensionChangeDestination();
}
@@ -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,54 +1,120 @@
package com.seibel.distanthorizons.common.wrappers.misc; package com.seibel.distanthorizons.common.wrappers.misc;
import com.google.common.base.Objects;
import com.google.common.collect.MapMaker; import com.google.common.collect.MapMaker;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.phys.Vec3;
import java.util.UUID; import java.net.SocketAddress;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
/**
* This wrapper transparently ensures that underlying {@link ServerPlayer} is always valid,
* unless the player has disconnected.
*/
public class ServerPlayerWrapper implements IServerPlayerWrapper public class ServerPlayerWrapper implements IServerPlayerWrapper
{ {
private static final ConcurrentMap<ServerPlayer, ServerPlayerWrapper> private static final ConcurrentMap<ServerGamePacketListenerImpl, ServerPlayerWrapper> serverPlayerWrapperMap = new MapMaker().weakKeys().weakValues().makeMap();
serverPlayerWrapperMap = new MapMaker().weakKeys().makeMap();
private final ServerPlayer serverPlayer; private final ServerGamePacketListenerImpl connection;
private ServerPlayer serverPlayer()
{
return this.connection.player;
}
public static ServerPlayerWrapper getWrapper(ServerPlayer serverPlayer) public static ServerPlayerWrapper getWrapper(ServerPlayer serverPlayer)
{ {
return serverPlayerWrapperMap.computeIfAbsent(serverPlayer, ServerPlayerWrapper::new); return serverPlayerWrapperMap.computeIfAbsent(serverPlayer.connection, ignored -> new ServerPlayerWrapper(serverPlayer.connection));
} }
private ServerPlayerWrapper(ServerPlayer serverPlayer) private ServerPlayerWrapper(ServerGamePacketListenerImpl connection)
{ {
this.serverPlayer = serverPlayer; this.connection = connection;
} }
public UUID getUUID()
@Override
public String getName()
{ {
return serverPlayer.getUUID(); return this.serverPlayer().getName().getString();
} }
@Override
public IServerLevelWrapper getLevel() public IServerLevelWrapper getLevel()
{ {
#if MC_VER < MC_1_20_1 ServerLevel level = ((IMixinServerPlayer) this.serverPlayer()).distantHorizons$getDimensionChangeDestination();
return ServerLevelWrapper.getWrapper(this.serverPlayer.getLevel()); if (level == null)
#else {
return ServerLevelWrapper.getWrapper(this.serverPlayer.serverLevel()); #if MC_VER < MC_1_20_1
level = this.serverPlayer().getLevel();
#else
level = this.serverPlayer().serverLevel();
#endif
}
return ServerLevelWrapper.getWrapper(level);
}
@Override
public Vec3d getPosition()
{
Vec3 position = this.serverPlayer().position();
return new Vec3d(position.x, position.y, position.z);
}
@Override
public int getViewDistance()
{
return this.serverPlayer().server.getPlayerList().getViewDistance();
}
@Override
public SocketAddress getRemoteAddress()
{
#if MC_VER >= MC_1_19_4
return this.serverPlayer().connection.getRemoteAddress();
#else // < 1.19.4
return this.serverPlayer().connection.connection.getRemoteAddress();
#endif #endif
} }
@Override
public Object getWrappedMcObject() public Object getWrappedMcObject()
{ {
return serverPlayer; return this.serverPlayer();
} }
@Override @Override
public String toString() public String toString()
{ {
return "Wrapped{" + serverPlayer.toString() + "}"; return "Wrapped{" + this.serverPlayer() + "}";
} }
} @Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (!(o instanceof ServerPlayerWrapper))
{
return false;
}
ServerPlayerWrapper that = (ServerPlayerWrapper) o;
return Objects.equal(this.connection, that.connection);
}
@Override
public int hashCode()
{
return Objects.hashCode(this.connection);
}
}
@@ -1,17 +1,17 @@
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.level.IServerKeyedClientLevel;
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 +19,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;
@@ -58,23 +70,31 @@ public class ClientLevelWrapper implements IClientLevelWrapper
// wrapper logic // // wrapper logic //
//===============// //===============//
@Nullable public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level)
public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level)
{ {
if (level == null) return getWrapper(level, false);
{ }
return null;
} @Nullable
public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level, boolean bypassLevelKeyManager)
// used if the client is connected to a server that defines the currently loaded level {
if (KEYED_CLIENT_LEVEL_MANAGER.getUseOverrideWrapper()) if (!bypassLevelKeyManager)
{ {
return KEYED_CLIENT_LEVEL_MANAGER.getOverrideWrapper(); if (level == null)
} {
return null;
return getWrapperIgnoringOverride(level); }
// used if the client is connected to a server that defines the currently loaded level
IServerKeyedClientLevel overrideLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
if (overrideLevel != null)
{
return overrideLevel;
}
}
return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new);
} }
public static IClientLevelWrapper getWrapperIgnoringOverride(@NotNull ClientLevel level) { return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new); }
@Nullable @Nullable
@Override @Override
@@ -82,7 +102,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,21 +122,23 @@ 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;
} }
} }
//====================// //====================//
// base level methods // // base level methods //
//====================// //====================//
@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,11 +158,41 @@ 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
public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); } public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
@Override
public String getDimensionName()
{
return this.level.dimension().location().toString();
}
@Override @Override
public EDhApiLevelType getLevelType() { return EDhApiLevelType.CLIENT_LEVEL; } public EDhApiLevelType getLevelType() { return EDhApiLevelType.CLIENT_LEVEL; }
@@ -154,7 +206,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 +221,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 +255,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()
@@ -213,7 +302,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return "Wrapped{null}"; return "Wrapped{null}";
} }
return "Wrapped{" + this.level.toString() + "@" + this.getDimensionType().getDimensionName() + "}"; return "Wrapped{" + this.level.toString() + "@" + this.getDimensionName() + "}";
} }
} }
@@ -44,7 +44,9 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
{ {
//first we check if the biome has already been wrapped //first we check if the biome has already been wrapped
if (dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null) if (dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
{
return dimensionTypeWrapperMap.get(dimensionType); return dimensionTypeWrapperMap.get(dimensionType);
}
//if it hasn't been created yet, we create it and save it in the map //if it hasn't been created yet, we create it and save it in the map
@@ -61,22 +63,26 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
} }
@Override private String getDimensionName()
public String getDimensionName()
{ {
return dimensionType.effectsLocation().getPath(); #if MC_VER >= MC_1_17_1
return this.dimensionType.effectsLocation().getPath();
#else // < 1.17.1
// effectsLocation() is marked as client only, so using the backing field directly
return dimensionType.effectsLocation.getPath();
#endif
} }
@Override @Override
public boolean hasCeiling() public boolean hasCeiling()
{ {
return dimensionType.hasCeiling(); return this.dimensionType.hasCeiling();
} }
@Override @Override
public boolean hasSkyLight() public boolean hasSkyLight()
{ {
return dimensionType.hasSkyLight(); return this.dimensionType.hasSkyLight();
} }
@Override @Override
@@ -85,7 +91,15 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
return this.dimensionType; return this.dimensionType;
} }
@Override
public double getTeleportationScale(IDimensionTypeWrapper to)
{
return DimensionType.getTeleportationScale(this.dimensionType, (DimensionType) to.getWrappedMcObject());
}
// there's definitely a better way of doing this, but it should work well enough for now
@Override
public boolean isTheEnd() { return this.getDimensionName().equalsIgnoreCase("the_end"); }
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj)
@@ -102,4 +116,4 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
} }
} }
@@ -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,29 +80,22 @@ 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()
{ {
return level.getChunkSource().getDataStorage().dataFolder; return this.level.getChunkSource().getDataStorage().dataFolder;
} }
@Override @Override
public DimensionTypeWrapper getDimensionType() public DimensionTypeWrapper getDimensionType()
{ {
return DimensionTypeWrapper.getDimensionTypeWrapper(level.dimensionType()); return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
}
@Override
public String getDimensionName()
{
return this.level.dimension().location().toString();
} }
@Override @Override
@@ -106,25 +103,25 @@ public class ServerLevelWrapper implements IServerLevelWrapper
public ServerLevel getLevel() public ServerLevel getLevel()
{ {
return level; return this.level;
} }
@Override @Override
public boolean hasCeiling() public boolean hasCeiling()
{ {
return level.dimensionType().hasCeiling(); return this.level.dimensionType().hasCeiling();
} }
@Override @Override
public boolean hasSkyLight() public boolean hasSkyLight()
{ {
return level.dimensionType().hasSkyLight(); return this.level.dimensionType().hasSkyLight();
} }
@Override @Override
public int getHeight() public int getMaxHeight()
{ {
return level.getHeight(); return this.level.getHeight();
} }
@Override @Override
@@ -133,51 +130,73 @@ public class ServerLevelWrapper implements IServerLevelWrapper
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
return 0; return 0;
#else #else
return level.getMinBuildHeight(); return this.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 (!this.level.hasChunk(pos.getX(), pos.getZ()))
ChunkAccess chunk = level.getChunk(pos.x, pos.z, ChunkStatus.FULL, false); {
if (chunk == null) return null; return null;
return new ChunkWrapper(chunk, level, this); }
ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FULL, false);
if (chunk == null)
{
return null;
}
return new ChunkWrapper(chunk, this.level, this);
} }
@Override @Override
public boolean hasChunkLoaded(int chunkX, int chunkZ) public boolean hasChunkLoaded(int chunkX, int chunkZ)
{ {
// world.hasChunk(chunkX, chunkZ); THIS DOES NOT WORK FOR CLIENT LEVEL CAUSE MOJANG ALWAYS RETURN TRUE FOR THAT! // world.hasChunk(chunkX, chunkZ); THIS DOES NOT WORK FOR CLIENT LEVEL CAUSE MOJANG ALWAYS RETURN TRUE FOR THAT!
ChunkSource source = level.getChunkSource(); ChunkSource source = this.level.getChunkSource();
return source.hasChunk(chunkX, chunkZ); return source.hasChunk(chunkX, chunkZ);
} }
@Override @Override
public IBlockStateWrapper getBlockState(DhBlockPos pos) public IBlockStateWrapper getBlockState(DhBlockPos pos)
{ {
return BlockStateWrapper.fromBlockState(level.getBlockState(McObjectConverter.Convert(pos)), this); return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this);
} }
@Override @Override
public IBiomeWrapper getBiome(DhBlockPos pos) public IBiomeWrapper getBiome(DhBlockPos pos)
{ {
return BiomeWrapper.getBiomeWrapper(level.getBiome(McObjectConverter.Convert(pos)), this); return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this);
} }
@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.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
);
}
public ChunkAccess loadOrMakeChunk(ChunkPos chunkPos)
//==================//
// 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);
}
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,175 +655,52 @@ 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)
{ {
LOAD_LOGGER.error( LOAD_LOGGER.error(
"DistantHorizons: couldn't load or make chunk at ["+chunkPos+"]." + "DistantHorizons: couldn't load or make chunk at ["+chunkPos+"]." +
"Please try optimizing your world to fix this issue. \n" + "Please try optimizing your world to fix this issue. \n" +
"World optimization can be done from the singleplayer world selection screen.\n" + "World optimization can be done from the singleplayer world selection screen.\n" +
"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(
#if MC_VER < MC_1_19_4
Registry.BIOME_REGISTRY
#else
Registries.BIOME
#endif
), null #endif
);
} }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step)
{
return GetCutoutFrom(total, MaxBorderNeeded - BorderNeeded.get(step));
}
public void generateLodFromList(GenerationEvent genEvent) throws InterruptedException
{
EVENT_LOGGER.debug("Lod Generate Event: " + genEvent.minPos);
ArrayGridList<ChunkWrapper> chunkWrapperList;
DhLitWorldGenRegion region;
DummyLightEngine lightEngine;
LightGetterAdaptor adaptor;
int borderSize = MaxBorderNeeded;
int refSize = genEvent.size + borderSize * 2;
int refPosX = genEvent.minPos.x - borderSize;
int refPosZ = genEvent.minPos.z - borderSize;
try
{
ArrayGridList<ChunkAccess> totalChunks;
adaptor = new LightGetterAdaptor(this.params.level);
lightEngine = new DummyLightEngine(adaptor);
EmptyChunkGenerator generator = (int x, int z) ->
{
ChunkPos chunkPos = new ChunkPos(x, z);
ChunkAccess target = null;
try
{
target = this.loadOrMakeChunk(chunkPos);
}
catch (RuntimeException e2)
{
// Continue...
}
if (target == null)
{
target = new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if 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
DhLightingEngine.INSTANCE.lightChunk(centerChunk, iChunkWrapperList, maxSkyLight); if (!centerChunk.isLightCorrect())
{
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)
{
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) //============//
{ // read chunk //
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)
{ {
@@ -234,7 +128,7 @@ public class ChunkLoader
ChunkPos actualPos = new ChunkPos(tagLevel.getInt("xPos"), tagLevel.getInt("zPos")); ChunkPos actualPos = new ChunkPos(tagLevel.getInt("xPos"), tagLevel.getInt("zPos"));
if (!Objects.equals(chunkPos, actualPos)) if (!Objects.equals(chunkPos, actualPos))
{ {
#if MC_VER > MC_1_17_1 #if MC_VER >= MC_1_18_2
if (actualPos.equals(ChunkPos.ZERO)) if (actualPos.equals(ChunkPos.ZERO))
#else #else
if (actualPos.equals(ChunkPos.INVALID_CHUNK_POS)) if (actualPos.equals(ChunkPos.INVALID_CHUNK_POS))
@@ -262,19 +156,27 @@ public class ChunkLoader
} }
} }
ChunkStatus.ChunkType chunkType = readChunkType(tagLevel); #if MC_VER < MC_1_20_6
#if MC_VER < MC_1_18_2 ChunkStatus.ChunkType chunkType;
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
return null;
#else #else
BlendingData blendingData = readBlendingData(tagLevel); ChunkType chunkType;
#if MC_VER < MC_1_19_2
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
return null;
#else
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && blendingData == null)
return null;
#endif #endif
chunkType = readChunkType(tagLevel);
#if MC_VER < MC_1_18_2
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
return null;
#else
BlendingData blendingData = readBlendingData(tagLevel);
#if MC_VER < MC_1_19_2
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
return null;
#else
if (chunkType == #if MC_VER < MC_1_20_6 ChunkStatus.ChunkType.PROTOCHUNK #else ChunkType.PROTOCHUNK #endif && blendingData == null)
return null;
#endif
#endif #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))
((ProtoChunk) chunk).setStatus(STATUS); {
chunksToDo.add(chunk); // this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
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,9 +59,20 @@ 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))
((ProtoChunk) chunk).setStatus(STATUS); {
chunksToDo.add(chunk); // this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
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,9 +58,21 @@ 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))
((ProtoChunk) chunk).setStatus(STATUS); {
chunksToDo.add(chunk); // this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
} }
for (ChunkAccess chunk : chunksToDo) for (ChunkAccess chunk : chunksToDo)
@@ -47,4 +47,7 @@ accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite frames
accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite framesY [I accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite framesY [I
accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite mainImage [Lcom/mojang/blaze3d/platform/NativeImage; accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite mainImage [Lcom/mojang/blaze3d/platform/NativeImage;
# DimensionTypeWrapper workaround
accessible field net/minecraft/world/level/dimension/DimensionType effectsLocation Lnet/minecraft/resources/ResourceLocation;
extendable class com/mojang/math/Matrix4f extendable class com/mojang/math/Matrix4f
@@ -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
+27 -13
View File
@@ -5,19 +5,25 @@ plugins {
loom { loom {
accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
// Custom logging
log4jConfigs.from(file("log4j-dev.xml"))
// "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
runs { runs {
client { client {
client() client()
setConfigName("Fabric Client") setConfigName("Fabric Client")
ideConfigGenerated(true) ideConfigGenerated(true)
runDir("../run") runDir("../run/client")
vmArgs("-Dio.netty.leakDetection.level=advanced")
programArgs("--username", "Dev")
} }
server { server {
server() server()
setConfigName("Fabric Server") setConfigName("Fabric Server")
ideConfigGenerated(true) ideConfigGenerated(true)
runDir("../run") runDir("../run/server")
vmArgs("-Dio.netty.leakDetection.level=advanced")
} }
} }
} }
@@ -45,7 +51,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()
@@ -69,13 +75,20 @@ dependencies {
addModJar(fabricApi.module("fabric-events-interaction-v0", rootProject.fabric_api_version)) addModJar(fabricApi.module("fabric-events-interaction-v0", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-rendering-v1", rootProject.fabric_api_version)) // TODO: Remove this as it is only needed in 1 line (FabricClientProxy) addModJar(fabricApi.module("fabric-rendering-v1", rootProject.fabric_api_version)) // 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))
addModJar(fabricApi.module("fabric-entity-events-v1", rootProject.fabric_api_version))
if (minecraft_version >= "1.19.2")
addModJar(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version))
else // < 1.19.2
addModJar(fabricApi.module("fabric-command-api-v1", rootProject.fabric_api_version))
// 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)
@@ -89,22 +102,23 @@ dependencies {
modImplementation(fabricApi.module("fabric-rendering-data-attachment-v1", rootProject.fabric_api_version)) modImplementation(fabricApi.module("fabric-rendering-data-attachment-v1", rootProject.fabric_api_version))
modImplementation(fabricApi.module("fabric-rendering-fluids-v1", rootProject.fabric_api_version)) modImplementation(fabricApi.module("fabric-rendering-fluids-v1", rootProject.fabric_api_version))
} }
// Lithium // Lithium
addMod("maven.modrinth:lithium:${rootProject.lithium_version}", rootProject.enable_lithium) addMod("maven.modrinth:lithium:${rootProject.lithium_version}", rootProject.enable_lithium)
// Iris // Iris
addMod("maven.modrinth:iris:${rootProject.iris_version}", rootProject.enable_iris) addMod("maven.modrinth:iris:${rootProject.iris_version}", rootProject.enable_iris)
// BCLib // BCLib
addMod("com.github.quiqueck:BCLib:${rootProject.bclib_version}", rootProject.enable_bclib) addMod("com.github.quiqueck:BCLib:${rootProject.bclib_version}", rootProject.enable_bclib)
// Canvas // Canvas
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")
@@ -146,4 +160,4 @@ 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) }
} }
+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="Dev" monitorInterval="10">
<Loggers>
<Logger name="com.seibel.distanthorizons.core.network" level="trace" additivity="false">
<AppenderRef ref="DebugFile" level="${sys:fabric.log.debug.level:-debug}" />
<AppenderRef ref="SysOut" />
<AppenderRef ref="LatestFile" level="${sys:fabric.log.level:-info}" />
<AppenderRef ref="ServerGuiConsole" />
</Logger>
</Loggers>
</Configuration>
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.fabric; package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
@@ -27,31 +28,38 @@ 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 net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.fabricmc.fabric.api.event.player.AttackBlockCallback; import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
#if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.common.CommonPacketPayload;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
#else
import com.seibel.distanthorizons.core.network.messages.NetworkMessage;
#endif
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
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;
@@ -86,6 +94,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
* Registers Fabric Events * Registers Fabric Events
* @author Ran * @author Ran
*/ */
@Override
public void registerEvents() public void registerEvents()
{ {
LOGGER.info("Registering Fabric Client Events"); LOGGER.info("Registering Fabric Client Events");
@@ -114,11 +123,15 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// ClientChunkLoadEvent // ClientChunkLoadEvent
ClientChunkEvents.CHUNK_LOAD.register((level, chunk) -> ClientChunkEvents.CHUNK_LOAD.register((level, chunk) ->
{ {
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level); if (MC.clientConnectedToDedicatedServer())
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel); {
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
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,18 +139,27 @@ 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
ChunkAccess chunk = level.getChunk(blockPos); // if the getChunk() takes longer than expected
if (chunk != null) // (which can be caused by certain mods)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{ {
//LOGGER.trace("attack block at blockPos: " + blockPos); executor.execute(() ->
{
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); ChunkAccess chunk = level.getChunk(blockPos);
SharedApi.INSTANCE.chunkBlockChangedEvent( if (chunk != null)
new ChunkWrapper(chunk, level, wrappedLevel), {
wrappedLevel //LOGGER.trace("attack block at blockPos: " + blockPos);
);
} IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
SharedApi.INSTANCE.chunkBlockChangedEvent(
new ChunkWrapper(chunk, level, wrappedLevel),
wrappedLevel
);
}
});
}
} }
} }
@@ -146,27 +168,37 @@ 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())) if (hitResult.getType() == HitResult.Type.BLOCK
&& !hitResult.isInside())
{ {
// Since fabric doesn't have a client-side place-block API event, this is the next best thing if (SharedApi.isChunkAtBlockPosAlreadyUpdating(hitResult.getBlockPos().getX(), hitResult.getBlockPos().getZ()))
if (hitResult.getType() == HitResult.Type.BLOCK
&& !hitResult.isInside())
{ {
ChunkAccess chunk = level.getChunk(hitResult.getBlockPos()); // executor to prevent locking up the render/event thread
if (chunk != null) // if the getChunk() takes longer than expected
// (which can be caused by certain mods)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{ {
//LOGGER.trace("use block at blockPos: " + hitResult.getBlockPos()); executor.execute(() ->
{
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); ChunkAccess chunk = level.getChunk(hitResult.getBlockPos());
SharedApi.INSTANCE.chunkBlockChangedEvent( if (chunk != null)
new ChunkWrapper(chunk, level, wrappedLevel), {
wrappedLevel //LOGGER.trace("use block at blockPos: " + hitResult.getBlockPos());
);
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
SharedApi.INSTANCE.chunkBlockChangedEvent(
new ChunkWrapper(chunk, level, wrappedLevel),
wrappedLevel
);
}
});
} }
} }
} }
@@ -177,14 +209,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,12 +216,26 @@ 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
// FIXME: Use better hooks so it doesn't trigger key press events in text boxes // FIXME: Use better hooks so it doesn't trigger key press events in text boxes
ClientTickEvents.END_CLIENT_TICK.register(client -> ClientTickEvents.END_CLIENT_TICK.register(client ->
@@ -214,17 +252,29 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// networking event // // networking event //
//==================// //==================//
// ClientPlayNetworking.registerGlobalReceiver(new ResourceLocation(ModInfo.NETWORKING_RESOURCE_NAMESPACE, ModInfo.MULTIVERSE_PLUGIN_NAMESPACE), #if MC_VER >= MC_1_20_6
// (Minecraft client, ClientPacketListener handler, FriendlyByteBuf friendlyByteBuf, PacketSender responseSender) -> PayloadTypeRegistry.playC2S().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec());
// { PayloadTypeRegistry.playS2C().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec());
// // converting to a ByteBuf is necessary otherwise Fabric will complain when the game boots ClientPlayNetworking.registerGlobalReceiver(CommonPacketPayload.TYPE, (payload, context) ->
// ByteBuf nettyByteBuf = friendlyByteBuf.asByteBuf(); {
// if (payload.message() == null)
// // remove the Bukkit/Forge packet ID byte {
// nettyByteBuf.readByte(); return;
// }
// ClientApi.INSTANCE.serverMessageReceived(nettyByteBuf); ClientApi.INSTANCE.pluginMessageReceived(payload.message());
// }); });
#else
ClientPlayNetworking.registerGlobalReceiver(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE, (client, handler, buffer, packetSender) ->
{
// Forge packet ID
buffer.readByte();
NetworkMessage message = AbstractPluginPacketSender.decodeMessage(buffer);
if (message != null)
{
ClientApi.INSTANCE.pluginMessageReceived(message);
}
});
#endif
} }
public void onKeyInput() public void onKeyInput()
@@ -253,14 +303,14 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// Diff and trigger events // Diff and trigger events
for (int keyCode : currentKeyDown) for (int keyCode : currentKeyDown)
{ {
if (!previouslyPressKeyCodes.contains(keyCode)) if (!this.previouslyPressKeyCodes.contains(keyCode))
{ {
ClientApi.INSTANCE.keyPressedEvent(keyCode); ClientApi.INSTANCE.keyPressedEvent(keyCode);
} }
} }
// Update the set // Update the set
previouslyPressKeyCodes = currentKeyDown; this.previouslyPressKeyCodes = currentKeyDown;
} }
} }
@@ -27,6 +27,7 @@ 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.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.*; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.*;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.fabric.wrappers.modAccessor.*; import com.seibel.distanthorizons.fabric.wrappers.modAccessor.*;
@@ -40,6 +41,12 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
#if MC_VER >= MC_1_19_2
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
#else // < 1.19.2
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
#endif
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -51,14 +58,22 @@ import java.util.function.Consumer;
*/ */
public class FabricMain extends AbstractModInitializer implements ClientModInitializer, DedicatedServerModInitializer public class FabricMain extends AbstractModInitializer implements ClientModInitializer, DedicatedServerModInitializer
{ {
private static final ResourceLocation INITIAL_PHASE = ResourceLocation.tryParse("distanthorizons:dedicated_server_initial"); #if MC_VER >= MC_1_21_1
private static final ResourceLocation INITIAL_PHASE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH);
#else
private static final ResourceLocation INITIAL_PHASE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH);
#endif
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@Override @Override
protected void createInitialBindings() { SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE); } protected void createInitialBindings()
{
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new FabricPluginPacketSender());
}
@Override @Override
protected IEventProxy createClientProxy() { return new FabricClientProxy(); } protected IEventProxy createClientProxy() { return new FabricClientProxy(); }
@@ -77,7 +92,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())
@@ -102,7 +117,12 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
} }
@Override @Override
protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler) { } protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler)
{
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess #if MC_VER >= MC_1_19_2 , environment #endif ) -> {
eventHandler.accept(dispatcher);
});
}
@Override @Override
protected void subscribeClientStartedEvent(Runnable eventHandler) { ClientLifecycleEvents.CLIENT_STARTED.register((mc) -> eventHandler.run()); } protected void subscribeClientStartedEvent(Runnable eventHandler) { ClientLifecycleEvents.CLIENT_STARTED.register((mc) -> eventHandler.run()); }
@@ -120,15 +140,21 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
SingletonInjector.INSTANCE.runDelayedSetup(); SingletonInjector.INSTANCE.runDelayedSetup();
if (Config.Client.Advanced.Graphics.Fog.disableVanillaFog.get() && SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("bclib")) if (Config.Client.Advanced.Graphics.Fog.disableVanillaFog.get() && SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("bclib"))
{
ModAccessorInjector.INSTANCE.get(IBCLibAccessor.class).setRenderCustomFog(false); // Remove BCLib's fog ModAccessorInjector.INSTANCE.get(IBCLibAccessor.class).setRenderCustomFog(false); // Remove BCLib's fog
}
#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)
{
throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method."); throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method.");
}
} }
} }
@@ -0,0 +1,46 @@
package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
import com.seibel.distanthorizons.core.network.messages.NetworkMessage;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.server.level.ServerPlayer;
#if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.common.CommonPacketPayload;
#else // < 1.20.6
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.minecraft.network.FriendlyByteBuf;
#endif
public class FabricPluginPacketSender extends AbstractPluginPacketSender
{
@Override
public void sendPluginPacketClient(NetworkMessage message)
{
#if MC_VER >= MC_1_20_6
ClientPlayNetworking.send(new CommonPacketPayload(message));
#else // < 1.20.6
FriendlyByteBuf buffer = PacketByteBufs.create();
// Forge packet ID
buffer.writeByte(0);
AbstractPluginPacketSender.encodeMessage(buffer, message);
ClientPlayNetworking.send(WRAPPER_PACKET_RESOURCE, buffer);
#endif
}
@Override
public void sendPluginPacketServer(ServerPlayer serverPlayer, NetworkMessage message)
{
#if MC_VER >= MC_1_20_6
ServerPlayNetworking.send(serverPlayer, new CommonPacketPayload(message));
#else // < 1.20.6
FriendlyByteBuf buffer = PacketByteBufs.create();
// Forge packet ID
buffer.writeByte(0);
AbstractPluginPacketSender.encodeMessage(buffer, message);
ServerPlayNetworking.send(serverPlayer, WRAPPER_PACKET_RESOURCE, buffer);
#endif
}
}
@@ -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,11 +12,14 @@ 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.entity.event.v1.ServerEntityWorldChangeEvents;
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;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
@@ -22,6 +27,14 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
#if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.common.CommonPacketPayload;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
#else
import com.seibel.distanthorizons.core.network.messages.NetworkMessage;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
#endif
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
@@ -51,7 +64,7 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
private boolean isValidTime() private boolean isValidTime()
{ {
if (isDedicated) if (this.isDedicated)
{ {
return true; return true;
} }
@@ -65,6 +78,7 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
private ServerPlayerWrapper getServerPlayerWrapper(ServerPlayer player) { return ServerPlayerWrapper.getWrapper(player); } private ServerPlayerWrapper getServerPlayerWrapper(ServerPlayer player) { return ServerPlayerWrapper.getWrapper(player); }
/** Registers Fabric Events */ /** Registers Fabric Events */
@Override
public void registerEvents() public void registerEvents()
{ {
LOGGER.info("Registering Fabric Server Events"); LOGGER.info("Registering Fabric Server Events");
@@ -75,19 +89,27 @@ 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) ->
{ {
if (isValidTime()) if (this.isValidTime())
{ {
ServerApi.INSTANCE.serverLoadEvent(isDedicated); ServerApi.INSTANCE.serverLoadEvent(this.isDedicated);
} }
}); });
// ServerWorldUnloadEvent // ServerWorldUnloadEvent
ServerLifecycleEvents.SERVER_STOPPED.register((server) -> ServerLifecycleEvents.SERVER_STOPPED.register((server) ->
{ {
if (isValidTime()) if (this.isValidTime())
{ {
ServerApi.INSTANCE.serverUnloadEvent(); ServerApi.INSTANCE.serverUnloadEvent();
} }
@@ -96,25 +118,25 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
// ServerLevelLoadEvent // ServerLevelLoadEvent
ServerWorldEvents.LOAD.register((server, level) -> ServerWorldEvents.LOAD.register((server, level) ->
{ {
if (isValidTime()) if (this.isValidTime())
{ {
ServerApi.INSTANCE.serverLevelLoadEvent(getServerLevelWrapper(level)); ServerApi.INSTANCE.serverLevelLoadEvent(this.getServerLevelWrapper(level));
} }
}); });
// ServerLevelUnloadEvent // ServerLevelUnloadEvent
ServerWorldEvents.UNLOAD.register((server, level) -> ServerWorldEvents.UNLOAD.register((server, level) ->
{ {
if (isValidTime()) if (this.isValidTime())
{ {
ServerApi.INSTANCE.serverLevelUnloadEvent(getServerLevelWrapper(level)); ServerApi.INSTANCE.serverLevelUnloadEvent(this.getServerLevelWrapper(level));
} }
}); });
// ServerChunkLoadEvent // ServerChunkLoadEvent
ServerChunkEvents.CHUNK_LOAD.register((server, chunk) -> ServerChunkEvents.CHUNK_LOAD.register((server, chunk) ->
{ {
ILevelWrapper level = getServerLevelWrapper((ServerLevel) chunk.getLevel()); ILevelWrapper level = this.getServerLevelWrapper((ServerLevel) chunk.getLevel());
if (isValidTime()) if (this.isValidTime())
{ {
ServerApi.INSTANCE.serverChunkLoadEvent( ServerApi.INSTANCE.serverChunkLoadEvent(
new ChunkWrapper(chunk, chunk.getLevel(), level), new ChunkWrapper(chunk, chunk.getLevel(), level),
@@ -125,18 +147,56 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> ServerPlayConnectionEvents.JOIN.register((handler, sender, server) ->
{ {
if (isValidTime()) if (this.isValidTime())
{ {
ServerApi.INSTANCE.serverPlayerJoinEvent(getServerPlayerWrapper(handler.player)); ServerApi.INSTANCE.serverPlayerJoinEvent(this.getServerPlayerWrapper(handler.player));
} }
}); });
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> ServerPlayConnectionEvents.DISCONNECT.register((handler, server) ->
{ {
if (isValidTime()) if (this.isValidTime())
{ {
ServerApi.INSTANCE.serverPlayerDisconnectEvent(getServerPlayerWrapper(handler.player)); ServerApi.INSTANCE.serverPlayerDisconnectEvent(this.getServerPlayerWrapper(handler.player));
} }
}); });
ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, origin, dest) ->
{
if (this.isValidTime())
{
ServerApi.INSTANCE.serverPlayerLevelChangeEvent(
this.getServerPlayerWrapper(player),
this.getServerLevelWrapper(origin),
this.getServerLevelWrapper(dest)
);
}
});
if (this.isDedicated)
{
#if MC_VER >= MC_1_20_6
PayloadTypeRegistry.playC2S().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec());
PayloadTypeRegistry.playS2C().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec());
ServerPlayNetworking.registerGlobalReceiver(CommonPacketPayload.TYPE, (payload, context) ->
{
if (payload.message() == null)
{
return;
}
ServerApi.INSTANCE.pluginMessageReceived(ServerPlayerWrapper.getWrapper(context.player()), payload.message());
});
#else
ServerPlayNetworking.registerGlobalReceiver(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE, (server, serverPlayer, handler, buffer, packetSender) ->
{
// Forge packet ID
buffer.readByte();
NetworkMessage message = AbstractPluginPacketSender.decodeMessage(buffer);
if (message != null)
{
ServerApi.INSTANCE.pluginMessageReceived(ServerPlayerWrapper.getWrapper(serverPlayer), message);
}
});
#endif
}
} }
} }
@@ -65,4 +65,4 @@ public class MixinClientLevel
} }
#endif #endif
} }
@@ -21,33 +21,9 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@Mixin(ClientPacketListener.class) @Mixin(ClientPacketListener.class)
public class MixinClientPacketListener public class MixinClientPacketListener
{ {
@Shadow
private ClientLevel level;
@Unique
private ClientLevel previousLevel;
@Inject(method = "handleLogin", at = @At("RETURN")) @Inject(method = "handleLogin", at = @At("RETURN"))
void onHandleLoginEnd(CallbackInfo ci) { ClientApi.INSTANCE.onClientOnlyConnected(); } void onHandleLoginEnd(CallbackInfo ci) { ClientApi.INSTANCE.onClientOnlyConnected(); }
@Inject(method = "handleRespawn", at = @At("HEAD"))
void onHandleRespawnStart(CallbackInfo ci) { this.previousLevel = this.level; }
@Inject(method = "handleRespawn", at = @At("RETURN"))
void onHandleRespawnEnd(CallbackInfo ci)
{
// If the player changes dimensions the "this.level" will be changed halfway through the respawn method.
// By checking if the object references are the same, we can see if the previous level should be unloaded
// or if the player just respawned in the same level.
if (this.previousLevel != this.level)
{
ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper(this.previousLevel));
ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper(this.level));
}
this.previousLevel = null;
}
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
@Inject(method = "cleanup", at = @At("HEAD")) @Inject(method = "cleanup", at = @At("HEAD"))
#else #else
@@ -55,10 +31,6 @@ public class MixinClientPacketListener
#endif #endif
void onCleanupStart(CallbackInfo ci) void onCleanupStart(CallbackInfo ci)
{ {
if (this.level != null)
{
ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper(this.level));
}
ClientApi.INSTANCE.onClientOnlyDisconnected(); ClientApi.INSTANCE.onClientOnlyDisconnected();
} }
@@ -72,4 +44,4 @@ public class MixinClientPacketListener
#endif #endif
} }
@@ -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)
#elif MC_VER < MC_1_20_6
@Inject(at = @At(value = "TAIL", target = "Lnet/minecraft/world/level/lighting/LevelLightEngine;runLightUpdates()I"), method = "renderLevel")
private void callAfterRunUpdates(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci)
#else #else
@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(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);
} }
} }
@@ -2,16 +2,22 @@ package com.seibel.distanthorizons.fabric.mixins.client;
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch; import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
import com.seibel.distanthorizons.common.wrappers.gui.updater.UpdateModScreen; import com.seibel.distanthorizons.common.wrappers.gui.updater.UpdateModScreen;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.installer.GitlabGetter; 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 net.minecraft.client.multiplayer.ClientLevel;
import org.spongepowered.asm.mixin.Mixin; 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.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;
@@ -23,8 +29,23 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* @author coolGi * @author coolGi
*/ */
@Mixin(Minecraft.class) @Mixin(Minecraft.class)
public class MixinMinecraft public abstract class MixinMinecraft
{ {
@Shadow
public abstract boolean isLocalServer();
@Unique
private ClientLevel lastLevel;
/**
* 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 +62,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 +90,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 ||
&& SelfUpdater.onStart() (
) // Don't do anything if the user doesn't want it
Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get()
&& SelfUpdater.onStart()
)
)
{ {
runnable = () -> { runnable = () ->
{
String versionId;
EDhApiUpdateBranch updateBranch = EDhApiUpdateBranch.convertAutoToStableOrNightly(Config.Client.Advanced.AutoUpdater.updateBranch.get());
if (updateBranch == EDhApiUpdateBranch.STABLE)
{
versionId = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
}
else
{
versionId = GitlabGetter.INSTANCE.projectPipelines.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"))
)); ));
}; };
} }
@@ -88,7 +123,21 @@ public class MixinMinecraft
} }
#endif #endif
@Inject(at = @At("HEAD"), method = "updateLevelInEngines")
public void updateLevelInEngines(ClientLevel level, CallbackInfo ci)
{
if (this.lastLevel != null && level != this.lastLevel)
{
ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper(this.lastLevel));
}
if (level != null)
{
ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper(level, true));
}
this.lastLevel = level;
}
@Inject(at = @At("HEAD"), method = "close()V") @Inject(at = @At("HEAD"), method = "close()V")
public void close(CallbackInfo ci) { SelfUpdater.onClose(); } public void close(CallbackInfo ci) { SelfUpdater.onClose(); }
} }
@@ -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
// Where the button is on the screen this.addButton(this.getOptionsButton());
this.width / 2 - 180, this.height / 6 - 12, #elif MC_VER < MC_1_20_6
// Width and height of the button this.addRenderableWidget(this.getOptionsButton());
20, 20, #else
// Offset
0, 0, // add the button so it's rendered
// Some textuary stuff this.addRenderableWidget(this.getOptionsButton());
20, ICON_TEXTURE, 20, 40,
// Create the button and tell it where to go // add the button to the correct location in the UI
// For now it goes to the client option by default // TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements?
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(GetConfigScreen.getScreen(this)), LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.get(0).child;
// Add a title to the utton
#if MC_VER < MC_1_19_2 // determine how wide the other option buttons are so we can put our botton to the left of them all
new TranslatableComponent(ModInfo.ID + ".title"))); AtomicInteger width = new AtomicInteger(0);
#else layout.visitChildren(x -> { width.addAndGet(x.getWidth()); });
Component.translatable(ModInfo.ID + ".title"))); width.addAndGet(-10); // padding between the DH button and the FOV slider
#endif
layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> { settings.paddingLeft(width.get() * -1); });
layout.arrangeElements();
#endif
}
}
//================//
// helper methods //
//================//
@Unique
public TexturedButtonWidget getOptionsButton()
{
if (this.optionsButton == null)
{
this.optionsButton
= new TexturedButtonWidget(
// Where the button is on the screen
this.width / 2 - 180, this.height / 6 - 12,
// Width and height of the button
20, 20,
// texture UV Offset
0, 0,
// Some textuary stuff
20, ICON_TEXTURE, 20, 40,
// Create the button and tell it where to go
// For now it goes to the client option by default
(buttonWidget) -> Objects.requireNonNull(this.minecraft).setScreen(GetConfigScreen.getScreen(this)),
// Add a title to the button
#if MC_VER < MC_1_19_2
new TranslatableComponent(ModInfo.ID + ".title"));
#else
Component.translatable(ModInfo.ID + ".title"));
#endif
}
return this.optionsButton;
} }
} }
@@ -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)
);
}
}
} }
@@ -0,0 +1,77 @@
/*
* 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;
import com.seibel.distanthorizons.common.wrappers.misc.IMixinServerPlayer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
#if MC_VER >= MC_1_21_1
import net.minecraft.world.level.portal.DimensionTransition;
#endif
@Mixin(ServerPlayer.class)
public class MixinServerPlayer implements IMixinServerPlayer
{
@Unique
@Nullable
private ServerLevel dimensionChangeDestination;
@Override
@Nullable
public ServerLevel distantHorizons$getDimensionChangeDestination()
{
return this.dimensionChangeDestination;
}
@Inject(at = @At("HEAD"), method = "changeDimension")
#if MC_VER >= MC_1_21_1
public void changeDimension(DimensionTransition dimensionTransition, CallbackInfoReturnable<Entity> cir)
{
this.dimensionChangeDestination = dimensionTransition.newLevel();
}
#else
public void changeDimension(ServerLevel destination, CallbackInfoReturnable<Entity> cir)
{
this.dimensionChangeDestination = destination;
}
#endif
#if MC_VER >= MC_1_20_1
@Inject(at = @At("RETURN"), method = "setServerLevel")
public void setServerLevel(ServerLevel level, CallbackInfo ci)
#else
@Inject(at = @At("RETURN"), method = "setLevel")
public void setLevel(ServerLevel level, CallbackInfo ci)
#endif
{
this.dimensionChangeDestination = null;
}
}
@@ -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;
@@ -40,103 +38,100 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; 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;
public IClientLevelWrapper levelWrapper;
public Mat4f mcModelViewMatrix;
public Mat4f mcProjectionMatrix;
public float partialTicks;
@Override
public String getModName()
{
return "Sodium-Fabric";
}
#if MC_VER >= MC_1_17_1
@Override
public HashSet<DhChunkPos> getNormalRenderedChunks()
{
SodiumWorldRenderer renderer = SodiumWorldRenderer.instance();
LevelHeightAccessor height = Minecraft.getInstance().level;
#if MC_VER >= MC_1_20_1
// TODO: This is just a tmp solution, use a proper solution later
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
return (renderer.isBoxVisible(
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1,
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15));
}).collect(Collectors.toCollection(HashSet::new));
#elif MC_VER >= MC_1_18_2
// 0b11 = Lighted chunk & loaded chunk
return renderer.getChunkTracker().getChunks(0b00).filter(
(long l) -> {
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
@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 #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
//======================//
// mod accessor methods //
//======================//
@Override @Override
public void setFogOcclusion(boolean b) 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)
{ {
#if MC_VER >= MC_1_20_1 #if MC_VER >= MC_1_20_1
me.jellysquid.mods.sodium.client.SodiumClientMod.options().performance.useFogOcclusion = b; 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);
}
}
setFogOcclusionMethod.invoke(sodiumPerformanceOptions, occlusionEnabled);
}
catch (Throwable e)
{
throw new RuntimeException(e);
}
#endif #endif
} }
//================//
// helper methods //
//================//
private static boolean classPresent(String className)
{
try
{
Class.forName(className);
return true;
}
catch (ClassNotFoundException e)
{
return false;
}
}
} }
@@ -3,10 +3,10 @@
"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",
"server.MixinServerPlayer"
], ],
"client": [ "client": [
"client.MixinClientLevel", "client.MixinClientLevel",
@@ -15,7 +15,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",
@@ -26,4 +25,4 @@
"defaultRequire": 1 "defaultRequire": 1
}, },
"plugin": "com.seibel.distanthorizons.fabric.mixins.FabricMixinPlugin" "plugin": "com.seibel.distanthorizons.fabric.mixins.FabricMixinPlugin"
} }
@@ -56,7 +56,6 @@
}, },
"suggests": { "suggests": {
"blendium": "*"
}, },
"breaks": $fabric_incompatibility_list, "breaks": $fabric_incompatibility_list,
+20 -6
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()
@@ -37,15 +37,17 @@ loom {
client { client {
client() client()
setConfigName("Forge Client") setConfigName("Forge Client")
ideConfigGenerated(true) ideConfigGenerated(false)
runDir("../run") runDir("../run/client")
// vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg) vmArgs("-Dio.netty.leakDetection.level=advanced")
programArgs("--username", "Dev")
} }
server { server {
server() server()
setConfigName("Forge Server") setConfigName("Forge Server")
ideConfigGenerated(true) ideConfigGenerated(false)
runDir("../run") runDir("../run/server")
vmArgs("-Dio.netty.leakDetection.level=advanced")
} }
} }
} }
@@ -78,6 +80,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,13 +27,12 @@ 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;
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 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;
@@ -52,8 +51,6 @@ import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
//import net.minecraftforge.network.NetworkRegistry;
//import net.minecraftforge.network.simple.SimpleChannel;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@@ -65,6 +62,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.
@@ -76,8 +75,6 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
{ {
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
// private static SimpleChannel multiversePluginChannel;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
@@ -92,7 +89,7 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
public void registerEvents() public void registerEvents()
{ {
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
this.setupNetworkingListeners(); ForgePluginPacketSender.setPacketHandler(ClientApi.INSTANCE::pluginMessageReceived);
} }
@@ -124,7 +121,7 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
#endif #endif
{ {
LOGGER.info("level load"); LOGGER.info("level load");
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld(); LevelAccessor level = event.getWorld();
#else #else
@@ -136,7 +133,7 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
} }
ClientLevel clientLevel = (ClientLevel) level; ClientLevel clientLevel = (ClientLevel) level;
IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel); IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel, true);
// TODO this causes a crash due to level being set to null somewhere // TODO this causes a crash due to level being set to null somewhere
ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper); ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper);
} }
@@ -144,11 +141,11 @@ 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");
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld(); LevelAccessor level = event.getWorld();
#else #else
@@ -173,61 +170,76 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
@SubscribeEvent @SubscribeEvent
public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event) public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) if (MC.clientConnectedToDedicatedServer())
{ {
return; if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{
return;
}
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
#endif
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk);
});
}
} }
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
#endif
ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk);
} }
@SubscribeEvent @SubscribeEvent
public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event) public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) if (MC.clientConnectedToDedicatedServer())
{ {
return; if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{
return;
}
//LOGGER.trace("break or block attack at blockPos: " + event.getPos());
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
#endif
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk);
});
}
} }
//LOGGER.trace("break or block attack at blockPos: " + event.getPos());
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
#endif
ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk);
} }
private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk) private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk)
{ {
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(level); ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(level);
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)
{ {
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event)); if (MC.clientConnectedToDedicatedServer())
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel); {
SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel); ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
} IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
@SubscribeEvent SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel);
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);
} }
@@ -252,66 +264,6 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
} }
//============//
// networking //
//============//
public void setupNetworkingListeners()
{
// multiversePluginChannel = NetworkRegistry.newSimpleChannel(
// new ResourceLocation(ModInfo.NETWORKING_RESOURCE_NAMESPACE, ModInfo.MULTIVERSE_PLUGIN_NAMESPACE),
// // network protocol version
// () -> ModInfo.MULTIVERSE_PLUGIN_PROTOCOL_VERSION +"",
// // client accepted versions
// ForgeClientProxy::isReceivedProtocolVersionAcceptable,
// // server accepted versions
// ForgeClientProxy::isReceivedProtocolVersionAcceptable
// );
//
// multiversePluginChannel.registerMessage(0/*should be incremented for each simple channel we listen to*/, ByteBuf.class,
// // encoder
// (pack, friendlyByteBuf) -> { },
// // decoder
// (friendlyByteBuf) -> friendlyByteBuf.asByteBuf(),
// // message consumer
// (nettyByteBuf, contextRef) ->
// {
// ClientApi.INSTANCE.serverMessageReceived(nettyByteBuf);
// contextRef.get().setPacketHandled(true);
// }
// );
}
public static boolean isReceivedProtocolVersionAcceptable(String versionString)
{
if (versionString.toLowerCase().contains("allowvanilla"))
{
// allow using networking on vanilla servers
return true;
}
else if (versionString.toLowerCase().contains("absent"))
{
// allow using networking even if DH isn't installed on the server
return true;
}
else
{
// DH is installed on the server, check if the version is valid to use
try
{
int version = Integer.parseInt(versionString);
return ModInfo.MULTIVERSE_PLUGIN_PROTOCOL_VERSION == version;
}
catch (NumberFormatException ignored)
{
return false;
}
}
}
//===========// //===========//
// rendering // // rendering //
//===========// //===========//
@@ -328,9 +280,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
@@ -347,5 +297,4 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
} }
} }
}
}
@@ -22,7 +22,11 @@ package com.seibel.distanthorizons.forge;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen; import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
@@ -34,15 +38,16 @@ import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.*; import net.minecraftforge.fml.event.lifecycle.*;
#if MC_VER == MC_1_16_5 #if MC_VER == MC_1_16_5
import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent;
#elif MC_VER == MC_1_17_1 #elif MC_VER == MC_1_17_1
import net.minecraftforge.fmlserverevents.FMLServerStartingEvent; import net.minecraftforge.fmlserverevents.FMLServerAboutToStartEvent;
#else #else
import net.minecraftforge.event.server.ServerStartingEvent; import net.minecraftforge.event.server.ServerAboutToStartEvent;
#endif #endif
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
@@ -81,7 +86,11 @@ public class ForgeMain extends AbstractModInitializer
} }
@Override @Override
protected void createInitialBindings() { SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE); } protected void createInitialBindings()
{
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new ForgePluginPacketSender());
}
@Override @Override
protected IEventProxy createClientProxy() { return new ForgeClientProxy(); } protected IEventProxy createClientProxy() { return new ForgeClientProxy(); }
@@ -104,6 +113,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
@@ -121,7 +144,7 @@ public class ForgeMain extends AbstractModInitializer
@Override @Override
protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler) protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler)
{ {
MinecraftForge.EVENT_BUS.addListener((#if MC_VER >= MC_1_18_2 ServerStartingEvent #else FMLServerStartingEvent #endif e) -> MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGH, (#if MC_VER >= MC_1_18_2 ServerAboutToStartEvent #else FMLServerAboutToStartEvent #endif e) ->
{ {
eventHandler.accept(e.getServer()); eventHandler.accept(e.getServer());
}); });
@@ -130,4 +153,4 @@ public class ForgeMain extends AbstractModInitializer
@Override @Override
protected void runDelayedSetup() { SingletonInjector.INSTANCE.runDelayedSetup(); } protected void runDelayedSetup() { SingletonInjector.INSTANCE.runDelayedSetup(); }
} }
@@ -0,0 +1,130 @@
package com.seibel.distanthorizons.forge;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.core.network.messages.NetworkMessage;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import net.minecraft.server.level.ServerPlayer;
#if MC_VER >= MC_1_20_2
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.ChannelBuilder;
import net.minecraftforge.network.SimpleChannel;
#elif MC_VER >= MC_1_18_2
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;
#elif MC_VER >= MC_1_17_1
import net.minecraftforge.fmllegacy.network.NetworkRegistry;
import net.minecraftforge.fmllegacy.network.PacketDistributor;
import net.minecraftforge.fmllegacy.network.simple.SimpleChannel;
#else // < 1.17.1
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.network.simple.SimpleChannel;
import net.minecraftforge.fml.network.PacketDistributor;
#endif
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class ForgePluginPacketSender extends AbstractPluginPacketSender
{
public static final SimpleChannel PLUGIN_CHANNEL =
#if MC_VER >= MC_1_20_2
ChannelBuilder.named(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE)
.networkProtocolVersion(1)
.serverAcceptedVersions((status, version) -> true)
.clientAcceptedVersions((status, version) -> true)
.simpleChannel();
#else // < 1.20.2
NetworkRegistry.newSimpleChannel(
AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE,
() -> "1",
ignored -> true,
ignored -> true
);
#endif
public static void setPacketHandler(Consumer<NetworkMessage> consumer)
{
setPacketHandler((player, message) -> consumer.accept(message));
}
public static void setPacketHandler(BiConsumer<IServerPlayerWrapper, NetworkMessage> consumer)
{
#if MC_VER >= MC_1_20_2
PLUGIN_CHANNEL.messageBuilder(MessageWrapper.class, 0)
.encoder((wrapper, out) -> AbstractPluginPacketSender.encodeMessage(out, wrapper.message))
.decoder(in -> new MessageWrapper(AbstractPluginPacketSender.decodeMessage(in)))
.consumerNetworkThread((wrapper, context) ->
{
if (wrapper.message != null)
{
if (context.getSender() != null)
{
consumer.accept(ServerPlayerWrapper.getWrapper(context.getSender()), wrapper.message);
}
else
{
consumer.accept(null, wrapper.message);
}
}
context.setPacketHandled(true);
})
.add();
#else // < 1.20.2
PLUGIN_CHANNEL.registerMessage(0, MessageWrapper.class,
(wrapper, out) -> AbstractPluginPacketSender.encodeMessage(out, wrapper.message),
in -> new MessageWrapper(AbstractPluginPacketSender.decodeMessage(in)),
(wrapper, context) ->
{
if (wrapper.message != null)
{
if (context.get().getSender() != null)
{
consumer.accept(ServerPlayerWrapper.getWrapper(context.get().getSender()), wrapper.message);
}
else
{
consumer.accept(null, wrapper.message);
}
}
context.get().setPacketHandled(true);
}
);
#endif
}
@Override
public void sendPluginPacketClient(NetworkMessage message)
{
#if MC_VER >= MC_1_20_2
PLUGIN_CHANNEL.send(new MessageWrapper(message), PacketDistributor.SERVER.noArg());
#else // < 1.20.2
PLUGIN_CHANNEL.send(PacketDistributor.SERVER.noArg(), new MessageWrapper(message));
#endif
}
@Override
public void sendPluginPacketServer(ServerPlayer serverPlayer, NetworkMessage message)
{
#if MC_VER >= MC_1_20_2
PLUGIN_CHANNEL.send(new MessageWrapper(message), PacketDistributor.PLAYER.with(serverPlayer));
#else // < 1.20.2
PLUGIN_CHANNEL.send(PacketDistributor.PLAYER.with(() -> serverPlayer), new MessageWrapper(message));
#endif
}
// Forge doesn't support using abstract classes
@SuppressWarnings({"ClassCanBeRecord", "RedundantSuppression"})
public static class MessageWrapper
{
public final NetworkMessage message;
public MessageWrapper(NetworkMessage message)
{
this.message = message;
}
}
}
@@ -3,16 +3,20 @@ package com.seibel.distanthorizons.forge;
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.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.api.internal.ServerApi; import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.event.world.WorldEvent;
@@ -22,6 +26,13 @@ import net.minecraftforge.event.level.LevelEvent;
#endif #endif
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
#if MC_VER >= MC_1_19_4
import net.minecraft.core.registries.Registries;
#else // < 1.19.4
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
#endif
#if MC_VER == MC_1_16_5 #if MC_VER == MC_1_16_5
import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent; import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent;
import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; import net.minecraftforge.fml.event.server.FMLServerStoppingEvent;
@@ -47,7 +58,6 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
#endif #endif
private final ServerApi serverApi = ServerApi.INSTANCE; private final ServerApi serverApi = ServerApi.INSTANCE;
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private final boolean isDedicated; private final boolean isDedicated;
public static Supplier<Boolean> isGenerationThreadChecker = null; public static Supplier<Boolean> isGenerationThreadChecker = null;
@@ -57,6 +67,10 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
public void registerEvents() public void registerEvents()
{ {
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
if (this.isDedicated)
{
ForgePluginPacketSender.setPacketHandler(ServerApi.INSTANCE::pluginMessageReceived);
}
} }
@@ -111,7 +125,7 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
{ {
if (GetEventLevel(event) instanceof ServerLevel) if (GetEventLevel(event) instanceof ServerLevel)
{ {
this.serverApi.serverLevelLoadEvent(this.getServerLevelWrapper((ServerLevel) GetEventLevel(event))); this.serverApi.serverLevelLoadEvent(getServerLevelWrapper((ServerLevel) GetEventLevel(event)));
} }
} }
@@ -125,7 +139,7 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
{ {
if (GetEventLevel(event) instanceof ServerLevel) if (GetEventLevel(event) instanceof ServerLevel)
{ {
this.serverApi.serverLevelUnloadEvent(this.getServerLevelWrapper((ServerLevel) GetEventLevel(event))); this.serverApi.serverLevelUnloadEvent(getServerLevelWrapper((ServerLevel) GetEventLevel(event)));
} }
} }
@@ -137,13 +151,25 @@ 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 @SubscribeEvent
public void serverChunkSaveEvent(ChunkEvent.Unload event) public void playerLoggedInEvent(PlayerEvent.PlayerLoggedInEvent event)
{ {
ILevelWrapper levelWrapper = ProxyUtil.getLevelWrapper(GetEventLevel(event)); this.serverApi.serverPlayerJoinEvent(getServerPlayerWrapper(event));
}
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper); @SubscribeEvent
this.serverApi.serverChunkSaveEvent(chunk, levelWrapper); public void playerLoggedOutEvent(PlayerEvent.PlayerLoggedOutEvent event)
{
this.serverApi.serverPlayerDisconnectEvent(getServerPlayerWrapper(event));
}
@SubscribeEvent
public void playerChangedDimensionEvent(PlayerEvent.PlayerChangedDimensionEvent event)
{
this.serverApi.serverPlayerLevelChangeEvent(
getServerPlayerWrapper(event),
getServerLevelWrapper(event.getFrom(), event),
getServerLevelWrapper(event.getTo(), event)
);
} }
@@ -155,4 +181,20 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
private static ServerLevelWrapper getServerLevelWrapper(ServerLevel level) { return ServerLevelWrapper.getWrapper(level); } private static ServerLevelWrapper getServerLevelWrapper(ServerLevel level) { return ServerLevelWrapper.getWrapper(level); }
private static ServerLevelWrapper getServerLevelWrapper(ResourceKey<Level> resourceKey, PlayerEvent event)
{
//noinspection DataFlowIssue (possible NPE after getServer())
return getServerLevelWrapper(event.getEntity().getServer().getLevel(resourceKey));
}
private static ServerPlayerWrapper getServerPlayerWrapper(PlayerEvent event) {
return ServerPlayerWrapper.getWrapper(
#if MC_VER >= MC_1_19_2
(ServerPlayer) event.getEntity()
#else
(ServerPlayer) event.getPlayer()
#endif
);
}
} }
@@ -26,4 +26,4 @@ public class MixinClientPacketListener
ClientApi.INSTANCE.onClientOnlyDisconnected(); ClientApi.INSTANCE.onClientOnlyDisconnected();
} }
} }
@@ -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);
}
} }
@@ -2,6 +2,8 @@ package com.seibel.distanthorizons.forge.mixins.client;
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch; import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
import com.seibel.distanthorizons.common.wrappers.gui.updater.UpdateModScreen; import com.seibel.distanthorizons.common.wrappers.gui.updater.UpdateModScreen;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.installer.GitlabGetter; import com.seibel.distanthorizons.core.jar.installer.GitlabGetter;
@@ -11,7 +13,9 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.multiplayer.ClientLevel;
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;
@@ -73,11 +77,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
)); ));
}; };
} }
@@ -92,4 +108,4 @@ public class MixinMinecraft
SelfUpdater.onClose(); SelfUpdater.onClose();
} }
} }
@@ -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); }
}
@@ -0,0 +1,67 @@
/*
* 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;
import com.seibel.distanthorizons.common.wrappers.misc.IMixinServerPlayer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.common.util.ITeleporter;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ServerPlayer.class)
public class MixinServerPlayer implements IMixinServerPlayer
{
@Unique
@Nullable
private volatile ServerLevel distantHorizons$dimensionChangeDestination;
@Override
@Nullable
public ServerLevel distantHorizons$getDimensionChangeDestination()
{
return this.distantHorizons$dimensionChangeDestination;
}
@Inject(at = @At("HEAD"), method = "changeDimension", remap = false)
public void changeDimension(ServerLevel destination, ITeleporter teleporter, CallbackInfoReturnable<Entity> cir)
{
this.distantHorizons$dimensionChangeDestination = destination;
}
#if MC_VER >= MC_1_20_1
@Inject(at = @At("RETURN"), method = "setServerLevel")
public void setServerLevel(ServerLevel level, CallbackInfo ci)
#else
@Inject(at = @At("RETURN"), method = "setLevel")
public void setLevel(ServerLevel level, CallbackInfo ci)
#endif
{
this.distantHorizons$dimensionChangeDestination = null;
}
}
@@ -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

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