Compare commits

..

494 Commits

Author SHA1 Message Date
James Seibel fce2868c62 remove dev from version number for release 2025-03-06 07:41:52 -06:00
James Seibel 0fba015f54 Fix crashing on MC 1.20.1 and older when updates aren't found 2 2025-03-01 09:19:52 -06:00
James Seibel f251c90472 Fix crashing on MC 1.20.1 and older when updates aren't found 2025-03-01 09:13:47 -06:00
s809 492a051a3b Replace chunk counts with speed in pregen 2025-02-27 21:08:31 +05:00
s809 0aa4743c1b Should be division instead of multiplication 2025-02-26 23:17:05 +05:00
s809 85f16944b2 Offset generation bounds by teleportation scale 2025-02-26 22:13:43 +05:00
James Seibel dddb0be2ac duct tape fix to reduce chance of LOD uploading requiring MC reboot 2025-02-25 07:26:12 -06:00
s809 4a3effa2f5 Remember split section responses temporarily 2025-02-22 20:55:18 +05:00
s809 a0a9151bfd Fix foreground thread sometimes blocking server shutdown 2025-02-19 21:17:42 +05:00
s809 aa3d15f38f Show section numbers in pregen 2025-02-19 20:37:29 +05:00
James Seibel adcb2a3a05 Fix IDhApiConfigValue.clearValue() failing for some deprecated functions 2025-02-17 21:16:27 -06:00
James Seibel 78f2cb24cc Fix DB leaks in FullDataV2Repo 2025-02-16 20:07:13 -06:00
James Seibel 67945509ed Fix errors related to player pos being unloaded 2025-02-16 19:54:11 -06:00
James Seibel c653e526a5 Revert 10 minute memoization for world gen 2025-02-15 11:56:25 -06:00
James Seibel 49b50c4c88 Fix beacon culling with auto overdraw prevention 2025-02-15 11:12:57 -06:00
James Seibel 7449f46c5e Add missing cave blocks for cave culling 2025-02-15 11:06:57 -06:00
James Seibel 069fc39aad up fabric api version for 1.21.1 to allow Immersive Portals testing 2025-02-14 07:48:32 -06:00
s809 4979ccf3e2 Invert generateOnlyInHighestDetail and rename to enableNSizedGeneration 2025-02-11 22:08:29 +05:00
James Seibel dd7f9c20b6 Put N-sized generation and upsampling behind experimental configs 2025-02-11 07:47:36 -06:00
James Seibel e96f9de1f0 Fix dimension wrapper creating duplicates for the same name 2025-02-11 07:05:34 -06:00
James Seibel c902e1957f Fix auto updater failing for nightly builds 2025-02-10 07:47:03 -06:00
James Seibel d40afb7a2a Fix C2ME slowdown again 2025-02-08 21:43:49 -06:00
James Seibel 01474d72e3 remove unneeded IVersionConstant methods 2025-02-08 11:39:26 -06:00
James Seibel 7c5af1836b add FIXME comment related to getChunkNbtDataAsync()
this method appears to be called much more often than it should be, very often 25 times per chunk position.
Very strange.
2025-02-08 11:12:06 -06:00
James Seibel a9bf6ae7e4 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-02-08 10:46:16 -06:00
James Seibel aef3162246 reduce test N-sized generator height 2025-02-08 10:45:55 -06:00
James Seibel 97ce869076 Fix C2ME causing memory use to explode with DH world gen 2025-02-08 10:45:38 -06:00
s809 91b3c83ffd Update core 2025-02-07 23:23:25 +05:00
James Seibel 1522df19cb Attempt to fix threadpool shutdown rejection exception 2025-02-07 07:26:07 -06:00
James Seibel 3845564128 Reduce world gen down time when using extremely fast generators 2025-02-07 07:15:31 -06:00
s809 23ef7cf27a Fix incorrect distance being used in update propagation SQL and reduce queue size 2025-02-07 01:05:18 +05:00
James Seibel bec28a5694 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-02-05 17:32:37 -06:00
James Seibel 8f20f103ad Fix empty data sources when moving in multiplayer or with N-sized world gen
Increases networking protocol from 9 -> 10
2025-02-05 17:32:30 -06:00
James Seibel 246e77cc56 Fix render enabled config getting set by world gen progress config 2025-02-02 19:55:06 -06:00
James Seibel 87fa29c77a Fix compiling with missing "E" 2025-02-02 15:52:20 -06:00
s809 5010256ce6 Update core 2025-02-02 20:30:57 +05:00
s809 a66ad19343 Balance tasks in thread pool using elapsed time instead of priorities 2025-02-02 20:30:04 +05:00
s809 913a458a1a Auto-move old save data to new location 2025-02-02 15:38:06 +05:00
s809 093d3a801e Remove generationProgressDisableMessageDisplayTimeInSeconds from server config command 2025-02-01 19:38:46 +05:00
s809 61ccf7bf60 Decrease delay between missing generation rechecks 2025-01-31 14:54:32 +05:00
s809 f948072253 Decrease delay between missing generation rechecks 2025-01-31 14:53:57 +05:00
s809 b748f27a1c Fix beacon beams flickering 2025-01-30 22:30:08 +05:00
s809 4dd4bb9ef0 Fix nightly self-updater after moving jars into zip root 2025-01-30 18:11:39 +05:00
s809 5051bde3b0 Add pycache into gitignore 2025-01-30 00:22:03 +05:00
s809 42cf639acc Merge branch 'test/artifacts-in-zip-root' 2025-01-30 00:21:02 +05:00
s809 9e6953a596 Fix relocation breaking runClient & runServer 2025-01-29 23:59:23 +05:00
s809 7f4f8a40eb Merge branch 'experimental/relocate_sqlite' 2025-01-29 23:23:36 +05:00
s809 89ca535a6f Add all the extra comments 2025-01-29 23:23:21 +05:00
s809 145182502e Do not relocate when python is not installed 2025-01-29 23:01:28 +05:00
s809 d61dfc9e03 Revert "Improve chunk processing throughput" 2025-01-28 22:56:52 +05:00
James Seibel 611d7d87ae Fix compiling for MC 1.19.2 and below 2025-01-26 18:12:01 -06:00
James Seibel 2f6a2d99ab Remove unneeded MixinLevelTicks (!73)
https://gitlab.com/distant-horizons-team/distant-horizons/-/merge_requests/73#note_2281882248
2025-01-26 17:47:32 -06:00
James Seibel d88ca0c98d Improve CPU usage and chunk update throughput 2025-01-26 17:13:28 -06:00
s809 0f64df7be0 Add missing enabled check 2025-01-26 22:08:17 +05:00
s809 23a1f0b025 Clean up code 2025-01-26 17:52:30 +05:00
s809 4a72e02550 Sign natives for mac 2025-01-25 18:25:58 +05:00
James Seibel 521bcdcc0f fix recalculate heightmap breaking stairs, slabs, and glass 2025-01-24 07:24:43 -06:00
s809 4eb20d5ce8 Fix using wrong path on linux 2025-01-24 11:28:51 +05:00
s809 3ad68aaf42 Merge branch 'main' into experimental/relocate_sqlite 2025-01-24 11:27:41 +05:00
s809 2a9a03771e Check if session is ready before ignoring local chunks 2025-01-24 11:20:49 +05:00
James Seibel 8f7823a4d2 Fix holes when moving with N-sized world gen/server side support 2025-01-23 19:45:25 -06:00
James Seibel cc4b965966 Speed up PhantomArrayListPool for large checkouts 2025-01-23 19:21:13 -06:00
s809 a6418de927 Merge branch 'main' into experimental/relocate_sqlite 2025-01-23 23:23:12 +05:00
s809 5303415d05 Ignore local chunks if realtime updates are enabled 2025-01-23 23:21:40 +05:00
s809 836515934f Fix column order check breaking on tiny columns 2 2025-01-23 00:18:38 +05:00
s809 228dc46d6b Fix column order check breaking on tiny columns 2025-01-23 00:15:18 +05:00
James Seibel a91f9670dc Show instructions to disable world gen progress message for short time 2025-01-21 07:49:36 -06:00
James Seibel 81313252f2 fix links in issue templates 2025-01-21 07:07:53 -06:00
s809 f65d411978 Fix task splitting causing generation of already generated sections 2025-01-21 17:26:58 +05:00
James Seibel 8c8a5ffeaf Fix cached RenderSource closing while in use 2025-01-20 21:51:24 -06:00
s809 68793fbe8d Process elf correctly 2025-01-21 00:33:43 +05:00
s809 d8401a8f49 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons into experimental/relocate_sqlite 2025-01-20 23:39:16 +05:00
s809 07ff00f7c9 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	coreSubProjects
2025-01-20 11:00:29 +05:00
s809 fadaff1113 Add generation bounds 2025-01-20 10:59:33 +05:00
James Seibel ff6bf7b4c9 Fix beacons disappearing and not updating correctly
Note: there appears to still be some off-by-one errors, although they happen relatively infrequently in my testing.
2025-01-19 17:48:36 -06:00
s809 082b1224a8 Relocate sqlite library 2025-01-19 03:26:53 +05:00
s809 bc475373fc Add a check for duplicate config command names and fix duplicate name 2025-01-18 16:27:16 +05:00
s809 498e958eca Add a check for duplicate config command names and fix duplicate name 2025-01-18 16:26:42 +05:00
s809 82e0cfe0b4 Fix server not shutting down, again 2025-01-15 23:34:48 +05:00
James Seibel 31d89e3349 Reduce duplicate warning logs when handling old worlds 2025-01-14 21:17:57 -06:00
James Seibel a3775c1f88 remove unneeded debug log 2025-01-14 19:48:30 -06:00
s809 e070bf4244 More consistent names but reverse 2025-01-14 21:25:58 +05:00
s809 8287192cd0 More consistent names 2025-01-14 21:15:01 +05:00
s809 d40f4dfe19 Artifacts in zip root 4 2025-01-14 21:01:30 +05:00
s809 595cdf011a Artifacts in zip root 3 2025-01-14 20:56:44 +05:00
s809 96f2f8c3b2 Artifacts in zip root 2 2025-01-14 20:48:17 +05:00
s809 c883ded7c4 Artifacts in zip root 2025-01-14 20:39:21 +05:00
James Seibel 834269da67 Reduce holes when flying around a partially loaded world 2025-01-14 07:35:39 -06:00
James Seibel a9bebf03d5 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-01-14 07:04:24 -06:00
James Seibel 939f6304bf Put several queries in try-finally blocks 2025-01-14 07:04:11 -06:00
s809 6e9f466570 Release full data sources after sending to clients 2025-01-13 23:37:26 +05:00
James Seibel a0b5cc7a5c Fix potential world gen error if center chunk is missing 2025-01-13 07:31:50 -06:00
s809 82708d998d Use dynamic precision for displaying pregen status 2025-01-12 21:56:54 +05:00
James Seibel 613e444490 Remove year range from licensing headers
The license is still valid, now I just don't have to update them every year
2025-01-11 21:27:25 -06:00
James Seibel f493e201d4 Add commented out logic for LevelChunkSection cloning
It was far to difficult a task for a issue I've only ever heard about once
2025-01-11 21:16:15 -06:00
James Seibel 2a8013b1d6 reminder comment about potential LodQuadTree concurrent issue 2025-01-11 21:06:58 -06:00
James Seibel 54fed62507 Add automatic overdraw prevention to improve fading 2025-01-11 17:59:27 -06:00
James Seibel e51bec9ce4 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-01-11 14:56:39 -06:00
s809 e47a83b706 Pregen improvements 2025-01-11 23:55:23 +05:00
James Seibel 8029c7b00c Fix some generic object rendering concurrent modification issues 2025-01-11 09:35:36 -06:00
James Seibel 2b38dc2575 Fix GL state corruption for mods not using MC's GlStateManager
This specifically fixes a bug with Iris where `GL46C.glDisable(GL46C.GL_CULL_FACE);` is called (instead of `GlStateManager._disableCull()`) during the transparent rendering pass, causing vanilla MC water chunks to have their normals inverted.
2025-01-11 08:24:28 -06:00
James Seibel 46cafb4cbe Fix compiling, forgot to move some QuadTree objects 2025-01-10 22:16:23 -06:00
James Seibel ff96533c93 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-01-10 22:00:24 -06:00
James Seibel 89e73f6383 Fix rendering LODs from different levels after changing dimensions 2025-01-10 21:57:57 -06:00
s809 069ebfe24e Add pregen command 2025-01-11 02:55:09 +05:00
s809 8b374c4734 Fix compilation 2025-01-11 02:54:30 +05:00
James Seibel 1febade083 Improve initial LOD loading speed and add KeyedLockContainer 2025-01-10 07:26:44 -06:00
James Seibel deedd85914 Fix delayedSaveCache and fix slow LOD updating regression 2025-01-09 21:27:00 -06:00
James Seibel 218c27adae Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-01-09 18:44:09 -06:00
James Seibel fde48b6f1a replace stacktrace prints with loggers 2025-01-09 18:43:59 -06:00
James Seibel f2a36e73d0 Fix typo in BatchGenEnvironment 2025-01-09 17:13:03 -06:00
s809 c5429ad139 Ignore task rejections if shutting down 2025-01-08 18:46:22 +05:00
James Seibel 7bc2ee296c Improve auto updater logging and fix potential issues with updating 2025-01-07 21:34:07 -06:00
James Seibel 4e26e4ab31 Add quick config to show/hide world gen progress 2025-01-07 20:30:34 -06:00
James Seibel a91685b590 Fix missing LODs (especially when world gen is active) 2025-01-07 20:14:46 -06:00
James Seibel 06f73c9b0a Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-01-07 20:00:55 -06:00
James Seibel 062dc29fd4 Add world gen progress updates to the overlay 2025-01-07 19:18:30 -06:00
s809 4096a24306 Fix some small issues and add comments 2025-01-07 17:08:28 +05:00
s809 2563de3ba3 Merge branch 'refactor/thread-pool-executors' 2025-01-07 16:36:57 +05:00
s809 4a99b42fa8 Add a config option to force server to always send only the highest detail 2025-01-07 01:25:36 +05:00
s809 f12f119ce2 Refactor thread pool handling 2025-01-06 23:32:29 +05:00
James Seibel 775adfaad5 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-01-05 19:15:28 -06:00
James Seibel 2a9c319935 Log a warning if the vanilla render distance is too high 2025-01-05 19:15:18 -06:00
s809 fda44c41a8 Add comments to UPDATE_POS_MANAGER.maxSize and refactor 2025-01-04 22:11:26 +05:00
James Seibel 8247f5f215 add comments to DhLitWorldGenRegion.getBlockTicks() 2025-01-04 10:50:56 -06:00
James Seibel 7eba6848bb Merge branch 'distant-horizons-fix/disable-scheduled-ticks' 2025-01-04 10:17:09 -06:00
James Seibel 6e84cac0d2 Fix infinitely growing memory and add warnings if memory isn't enough 2025-01-04 10:12:03 -06:00
s809 2bf96ea781 Update core 2025-01-04 19:25:48 +05:00
James Seibel d5de4a8171 Fix sqlite memory leaks 2025-01-03 14:47:42 -06:00
James Seibel 3cd7c7f1a3 remove pmcVer from forge runClient 2025-01-03 14:47:18 -06:00
ishland 1741ebf8b2 change: remove block and fluid scheduled tick access as it is unneeded 2025-01-03 16:57:34 +08:00
s809 55776f8beb Update core 2025-01-03 01:04:13 +05:00
s809 a469770e5d Check for updates on launch on dedicated servers 2025-01-03 00:30:15 +05:00
s809 851dabc18b Fix 1.16.5 compilation 2025-01-02 20:20:32 +05:00
s809 dccdbaeb73 Rename serversideShortName to chatCommandName 2025-01-02 19:43:01 +05:00
s809 f4126f5378 Add descriptions to config entries in commands 2025-01-01 20:33:01 +05:00
s809 97af075c7d Use correct world folder name in LAN level key prefix 2025-01-01 18:13:25 +05:00
James Seibel 1dfdd422db Potentially fix EXCEPTION_ACCESS_VIOLATION rendering crash 2024-12-31 09:08:25 -06:00
James Seibel 072082c56a handle corrupted data better 2024-12-28 13:45:43 -06:00
James Seibel 43caa2a55c Fix neoforge running in release 2024-12-28 13:45:17 -06:00
James Seibel b8bad9b6bf fix compiling for MC 1.20.4 2024-12-27 18:12:14 -06:00
James Seibel 8afdd6ed2b Fix typo preventing threadpools from running 2024-12-27 18:11:58 -06:00
James Seibel 34968a6945 Add schedule fix mixin to neoforge
also fix old fabric compiling
2024-12-27 09:25:54 -06:00
James Seibel 851a79a77e Fix compiling on older MC versions 2024-12-27 09:18:00 -06:00
James Seibel 1ca1db705f Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2024-12-27 08:48:16 -06:00
James Seibel 8f57c2cce2 Only have a single thread config 2024-12-27 08:47:20 -06:00
s809 942e3093cb Respond to incompatible packets so the client is able to display an error in F3 2024-12-27 17:05:07 +05:00
s809 f3e65010f1 Fix failed counter working incorrectly 2024-12-27 15:51:47 +05:00
s809 b3cfb35fac Update core 2024-12-26 16:09:29 +05:00
s809 b60d778303 Use library provided method for filling *ArrayLists 2024-12-25 23:12:58 +05:00
James Seibel 9ec28aa661 Fix beacons not appearing chunks pulled from distant generation 2024-12-25 09:31:01 -06:00
James Seibel 98400a9d3c Fix mod compat warning chat not hiding with config 2024-12-25 09:17:44 -06:00
James Seibel ae8658ae77 Fix DH preventing server shutdown on close 2024-12-25 09:04:55 -06:00
James Seibel e928fe3ecd Fix not loaded tick schedule warning in world gen
"Trying to schedule tick in not loaded position" can log in MC 1.21.4 when sand or other FallingBlock.class blocks attempt to generate in a DH context (IE the chunk isn't loaded in the server).
2024-12-25 08:23:33 -06:00
James Seibel 963dc4a404 minor DhLitWorldGenregion refactor 2024-12-25 08:21:42 -06:00
James Seibel 04f42999df potentially fix rare null pointer 2024-12-24 08:54:28 -06:00
James Seibel 730d014f13 Fix compiling on for MC before 1.21.1 2024-12-24 08:09:02 -06:00
James Seibel 1f81c50ce1 Fix lag/errors when pulling pre-existing chunks 2024-12-24 08:02:01 -06:00
James Seibel f60c550879 Massively reduce memory use and fix object leaks 2024-12-22 09:17:49 -06:00
James Seibel 0d556b5d95 Attempt to fix stuttering in MC 1.21.4 for pre-existing chunk pulling 2024-12-20 15:36:36 -06:00
James Seibel 70d897f09c add config assumePreExistingChunksAreFinished 2024-12-20 15:27:03 -06:00
James Seibel 9accb6d584 refactor testGenericWorldGen to support hotswapping 2024-12-20 14:02:30 -06:00
James Seibel 843dc580c8 Repo and Obj Pool rewrite
This should provide a significant reduction in garbage generated, reducing GC pressure.
2024-12-20 13:38:47 -06:00
James Seibel 77aa4773ef Fix some bugs and reduce GC load slightly 2024-12-14 23:35:36 -06:00
James Seibel fc3c944e3d remove timeout from chunk IoWorker in world gen 2024-12-14 23:35:29 -06:00
James Seibel 7d6aecc4c7 Merge branch 'distant-horizons-feature/server-backed-distant-gen'
Also remove ChunkWrapper LevelReader parameter
2024-12-14 14:07:16 -06:00
James Seibel 528beb8384 Revert chunky core changes 2024-12-14 12:21:56 -06:00
James Seibel 5f1180a5dd Revert "Fix holes when using Chunky" 2024-12-14 12:21:39 -06:00
James Seibel a9f1e8587c Revert "change the chunky warning message"
This reverts commit 77a366065d.
2024-12-14 12:20:27 -06:00
James Seibel 7a076f5509 re-add full (server) distant generator mode 2024-12-13 07:26:55 -06:00
James Seibel 3349e51655 minor batchGenEnv renaming 2024-12-13 07:25:47 -06:00
James Seibel 77a366065d change the chunky warning message 2024-12-12 21:05:19 -06:00
James Seibel c2d6ecaae6 Fix holes when using Chunky
(at the cost of some server lag)
2024-12-12 20:56:48 -06:00
James Seibel c9bc830058 Fix null pointer in LodRenderSection 2024-12-12 07:46:41 -06:00
James Seibel 6125031d9d Remove world gen timeout config
It didn't work as originally intended. It was originally added to prevent world gen lock-ups if a thread hits and infinite loop, but it didn't do anything for that use case and just ended up being annoying for low-end users.
2024-12-12 06:59:10 -06:00
s809 c041cde574 Show thread preset in config entries in server command 2024-12-10 20:19:03 +05:00
s809 bd946af229 Add player into request group before trying to fulfill 2024-12-09 16:48:24 +05:00
s809 559626456a Prune world gen tasks above limit in multiplayer 2024-12-09 12:03:53 +05:00
s809 c6843c1d95 Revert "Fix gen tasks sometimes not submitting after LOD level changes"
This reverts commit 585a288f
2024-12-08 19:33:20 +05:00
James Seibel 0d32fab434 Fix off-by-one error in DhAPI Terrain Repo
Specifically getting by blockpos
2024-12-07 15:08:40 -06:00
James Seibel 94571de00f Improve Chunk LOD building (thanks builderb0y!) 2024-12-07 11:45:02 -06:00
James Seibel d062eff8b4 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2024-12-07 11:20:28 -06:00
James Seibel b1e46faf6f Fix GLWrapper compiling for MC 1.16 2024-12-07 11:20:09 -06:00
James Seibel 04ed5b2e03 Replace many GL32 calls with GLMC (IMinecraftGLWrapper) 2024-12-07 09:56:50 -06:00
s809 15d9ff503f Re-add pruning of visitedPositions 2024-12-06 23:32:59 +05:00
s809 64942d77e4 Use N-sized generation on server when available 2024-12-06 23:10:53 +05:00
s809 f287dbd4d3 Merge branch 'NSizedMultiplayerTest' 2024-12-04 23:40:00 +05:00
James Seibel c387f57d9c Add MC 1.21.4 to the CI build script 2024-12-03 20:21:10 -06:00
James Seibel 2dacb91b79 Add MC 1.21.4 support 2024-12-03 20:17:50 -06:00
James Seibel d809568cc3 update Iris/Sodium dependency versions for 1.21.1
Just used for compiling and consistency, shouldn't change anything
2024-12-03 19:44:04 -06:00
James Seibel beee44df14 Add fabric-api >= 0.110.1 requirement for MC 1.21.3 2024-12-03 19:40:29 -06:00
s809 f651fc4b50 Up protocol version 2024-12-03 21:23:41 +05:00
s809 596e4eae0e Fix gen tasks sometimes not submitting after LOD level changes 2024-12-03 21:13:01 +05:00
James Seibel 6e70073ae4 Add visited position removal timer in RemoteFullDataSourceProvider 2024-12-02 07:51:12 -06:00
James Seibel 1beef2b4ad Fix a potential null pointer in world gen chunk loading 2024-12-02 07:18:42 -06:00
James Seibel a0e4ed8371 Fix 1.20.4 compiling 2024-12-01 21:06:30 -06:00
James Seibel f40adfd9c6 Merge !70 (Fix invisible gui button in MC 1.21.3) 2024-12-01 17:52:32 -06:00
James Seibel 7fb5e95809 Add Fabric 1.21.3 support
Also fixes:
`Mod was built with a newer version of Loom (1.8.9), you are using Loom (1.7.415)`
2024-12-01 17:43:09 -06:00
James Seibel 03d5cb9289 Fix compiling for Java 8 2024-12-01 17:06:53 -06:00
James Seibel 3b4b4d6b7e Remove unused world gen MixinData thread local 2024-12-01 17:00:22 -06:00
James Seibel b9a97a0fda Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2024-12-01 15:51:42 -06:00
James Seibel 0dae6942cb Update coreSubProjects 2024-12-01 15:51:27 -06:00
James Seibel 8bb6aeb526 up the manifold version 2024.1.35 -> 37 2024-12-01 15:50:54 -06:00
James Seibel 32ec420248 Merge !68 (Fix slowdown when C2ME is installed)
Thanks ishland for the fix!
2024-12-01 15:50:41 -06:00
s809 1925537da0 Lower log level of out of range warning 2024-11-22 14:54:48 +05:00
James Seibel 3bb4c21fa2 Fix forge compiling for some versions 2024-11-21 19:22:25 -06:00
James Seibel 5b61a98196 Fix race condition when generating terrain extremely quickly 2024-11-21 19:00:57 -06:00
James Seibel 7c6eba983a Fix auto updater attempting to update to "null" 2024-11-19 07:32:54 -06:00
James Seibel 0023ab09ed Fix auto updater not appearing on neoforge 2024-11-19 07:32:27 -06:00
James Seibel c289f5d717 minor format cleanup 2024-11-19 06:58:12 -06:00
watertrainer 6df3ad722c delete done TODO 2024-11-19 11:30:43 +00:00
watertrainer 56f6abd858 ensure clientLevel is loaded, even if player gets redirected (fixes #869) 2024-11-19 11:30:00 +00:00
James Seibel c326e0ba81 Fix a rare error where chunk lighting is set to -1 2024-11-18 07:46:27 -06:00
James Seibel 60b28fcf2e Fix sometimes not loading high-detail LODs when on a server 2024-11-18 07:40:24 -06:00
James Seibel a9ffd3bd3b Forgot to remove all of manifold string gradle 2024-11-18 07:15:33 -06:00
James Seibel bc72e925df Remove manifold string plugin 2024-11-17 08:03:15 -06:00
James Seibel 108df99633 up version number 2.3.0-a -> 2.3.0-b 2024-11-16 22:07:41 -06:00
James Seibel ba245ea266 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2024-11-16 22:03:48 -06:00
James Seibel ec721ce172 minor build.gradle formatting change 2024-11-16 21:59:28 -06:00
James Seibel 3ad2a95c10 up fabric loader version in 1.21.1 2024-11-16 21:59:15 -06:00
James Seibel e22e241d06 revert accidental change to gradle.properties default version 2024-11-16 21:58:18 -06:00
James Seibel cd70d55b4d comment to remove unused fabricLike gradle references 2024-11-16 21:30:58 -06:00
James Seibel b6c98d3bde Append mod loader to merged jars 2024-11-16 21:25:50 -06:00
s809 5fe605540d Replace Apache's base32 with guava 2024-11-15 09:33:10 +05:00
s809 c232b64f24 Add a comment to #pluginMessageReceived methods 2024-11-14 15:52:46 +05:00
s809 f94b6dbaa5 Replace truncating the hashed seed with encoding it into base32 2024-11-14 15:23:57 +05:00
James Seibel 928bc5df6c Fix setting saturation to 0 crashing the game 2024-11-13 18:26:32 -06:00
James Seibel a5865b3545 Decrease Min mcmeta version from 16 -> 7
should still fix #832
mcmeta files were re-added since forge needs them for certain resource files to load
2024-11-11 07:13:04 -06:00
James Seibel 8f15ab7ccd Revert "Close #832 (remove pack.mcmeta)"
This reverts commit 70550a21a0.
2024-11-11 06:58:15 -06:00
James Seibel 131e4124f3 fix getHashedSeed again from merge 2024-11-09 21:21:21 -06:00
James Seibel ae6333c7b5 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2024-11-09 21:19:36 -06:00
James Seibel d38c622d9b Fix compiling due to incomplete IDhLevel changes 2024-11-09 21:19:01 -06:00
James Seibel fe625e5b55 Fix ColorUtil impot in ClientLevelWrapper 2024-11-09 21:07:23 -06:00
James Seibel 38a6ad552b Fix height fog 2024-11-09 20:58:59 -06:00
s809 222a008514 Remove seed hash from local & server worlds, and expose DH's level identifier to API 2024-11-09 22:37:05 +05:00
James Seibel 56684bdf4c Fix some render thread tasks not running (causing holes) 2024-11-09 09:53:45 -06:00
James Seibel eb4525c68d Update coreSubProjects 2024-11-09 08:49:23 -06:00
James Seibel 66eb2ed0f9 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2024-11-09 08:49:05 -06:00
James Seibel 9866842e01 Improve Buffer uploading speed and remove buffer upload thread 2024-11-09 08:48:29 -06:00
coolGi e9487b0481 Updated readme to use the new DH links 2024-11-09 14:40:00 +00:00
James Seibel 82c5aa907b Buffer upload speed test 2024-11-08 07:46:17 -06:00
James Seibel 47b145cb90 up neoforge version 21.3.4 -> 21.3.11 2024-11-07 07:30:46 -06:00
James Seibel ff46b925b2 minor config screen refactoring 2024-11-07 07:30:30 -06:00
James Seibel 8ca7ff5ae0 Fix crashing after server shutdown in serverPlayerDisconnectEvent 2024-11-06 07:08:36 -06:00
James Seibel 70550a21a0 Close #832 (remove pack.mcmeta)
DH doesn't use resource pack files so these files are unnecessary and just cause warnings.
2024-11-06 07:01:55 -06:00
James Seibel 50f911f63c Fix unnecessary logging for JarUtil jarFile getting
Closes #733
2024-11-05 07:32:49 -06:00
James Seibel 4b0dca5823 re-add missing ColorUtil import 2024-11-04 19:34:50 -06:00
James Seibel 0e777f04bc Add hashed seed to server level folders to replace multiverse similarity
Closes !514 and Closes !476
2024-11-04 18:31:04 -06:00
s809 b12f27cc18 Fix Flashback crash on dimension loading 2024-11-03 19:20:20 +05:00
James Seibel 1a66f457af add recalculate heightmap config (disabled by default) 2024-11-02 13:09:03 -05:00
James Seibel b4dca6a1b4 Fix MC_CLIENT.getPlayerCount() null pointer 2024-11-02 13:06:10 -05:00
James Seibel 7759a2f9ea Fix air.isSolid() crash 2024-11-02 13:05:56 -05:00
James Seibel fe9bccb91f add 1.21.3 to nightly builds (still no fabric) 2024-11-02 11:29:55 -05:00
James Seibel fc6fd310f6 use gradle for shared fabric launch config 2024-11-02 11:21:20 -05:00
James Seibel 8be161b381 Add MC 1.21.3 support for Neoforge (no fabric) 2024-11-02 11:21:03 -05:00
James Seibel 8eba8cb40b gradle wrapper 2024-10-30 20:49:22 -05:00
James Seibel 44326344e6 update fabric api dependency for 1.21.1 2024-10-29 20:01:59 -05:00
James Seibel 17b7ee9045 Increase the default render distance 128 -> 256 2024-10-27 16:13:54 -05:00
s809 6a47a2150f Fix too many chunks spam 2024-10-26 00:33:48 +05:00
James Seibel 13728a7540 Closes !825 (Cache arrays used in generic object upload) 2024-10-25 07:40:48 -05:00
James Seibel c51e941413 Fix the update screen logo scaling 2024-10-24 07:07:51 -05:00
James Seibel 2a05c32a2f Fix DH fade corrupting the GL state 2024-10-22 16:28:07 -05:00
James Seibel 353a507914 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2024-10-22 15:33:16 -05:00
James Seibel f3b73d54f8 Update coreSubProjects 2024-10-22 15:33:13 -05:00
s809 92ed9b8070 Update core 2024-10-22 23:55:50 +05:00
s809 9c3c37bc3e Use earlier event for storing server instance 2024-10-22 23:55:37 +05:00
James Seibel 79fb7c654a Add warning if chunky is installed 2024-10-21 13:27:28 -04:00
James Seibel 520b4e0930 add ClientUtil.argbToInt() 2024-10-21 08:53:26 -04:00
s809 45a07206c9 Update core 2024-10-21 16:43:46 +05:00
s809 ad0b78936a Enable multiplayer in 1.16.5 2024-10-21 16:43:39 +05:00
s809 2babae40de Multiply update queue size by player count 2024-10-20 20:04:35 +05:00
s809 17713a9f93 Make sure data source received from file handler is fully generated before sending to client 2024-10-20 15:15:15 +05:00
s809 48120a4a38 Move commands under /dh, add /dh debug command 2024-10-20 15:14:03 +05:00
s809 69ffd795c9 Split commands into classes 2024-10-20 01:39:16 +05:00
s809 1df5dd5458 Up protocol version 2024-10-18 11:18:34 +05:00
James Seibel b77acaa3b2 Fix javadoc compiling and some Mac lockup issues 2024-10-15 07:47:59 -05:00
s809 d8024ab488 Use version specific caches 2024-10-15 00:33:15 +05:00
James Seibel 064d8b3506 Fix default beacon culling setting 2024-10-14 07:41:13 -05:00
James Seibel 9ca6a2116b Fix Forge compiling 2024-10-14 07:40:24 -05:00
James Seibel 449c87982c Add additional run configurations 2024-10-14 07:12:44 -05:00
James Seibel 7edfc40b9d Split the config file, update the config UI, and remove a few unused configs 2024-10-12 20:39:42 -05:00
James Seibel 9acda97f54 Fix N-sized world gen causing holes when moving 2024-10-12 09:53:46 -05:00
James Seibel 14edd63029 Fix adjacent chunk lighting (thanks CreepermeYT) 2024-10-11 22:14:01 -05:00
James Seibel f3a1235dd3 Fix core standalone jar running (hopefully this doesn't bork Quilt) 2024-10-10 07:44:15 -05:00
James Seibel de93bfee2f Fix 1.16.5 and 1.17 compiling 2024-10-10 06:56:03 -05:00
James Seibel 6eb80ace00 Fix world gen incorrectly returning some data sources to the pool 2024-10-08 20:17:33 -05:00
s809 3ac8d6a8b1 Fix incorrect name of a method 2024-10-08 22:59:00 +05:00
s809 269c8dc4e3 Fix incorrect folder being used without level keys 2024-10-08 22:37:55 +05:00
James Seibel c8173005a7 Decrease vertical quality drop off 2024-10-08 07:51:39 -05:00
James Seibel ed846ea564 merge !67 (Add Mojang Maven repository to fix arm64 MacOS builds) 2024-10-08 07:23:39 -05:00
James Seibel f6a8f5c6b5 Prevent returning out of bounds ColumnArrayView's 2024-10-08 07:07:53 -05:00
s809 3c60a7d842 Fix keyed levels not changing 2024-10-08 09:05:27 +05:00
James Seibel 3c97feeaf8 add comment about spongepowered vanilla gradle versions 2024-10-07 20:16:27 -05:00
James Seibel b2ac91f7db Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2024-10-07 19:45:37 -05:00
James Seibel 1ebad39fc1 Update the API to allow for N-sized world generation requests
This breaks old world generators
2024-10-07 19:45:33 -05:00
James Seibel 38a2edff06 fix batch gen environment exception name 2024-10-07 18:19:39 -05:00
s809 b2cf38798e Fix unhandled message spam in replay mod 2024-10-06 01:47:51 +05:00
s809 ed83e41f19 Use level key prefixes to in LAN multiplayer 2024-10-06 00:20:07 +05:00
s809 6d52cdba0a Prevent server crash on shutdown 2024-10-05 21:30:01 +05:00
s809 05449c9e86 Decouple beacon beam data handling from render handling, send beacon beams to clients 2024-10-05 14:03:08 +05:00
James Seibel 33ef1297ba Close !66 (add IDhApiLevelWrapper.getDhSaveFolder()) 2024-10-04 07:46:09 -05:00
James Seibel 3b13786990 Improve beacon fade rendering 2024-10-03 20:25:26 -05:00
James Seibel 222d06898e Fix zoom mods breaking DH's fade/near clip plane 2024-10-03 17:25:54 -05:00
James Seibel 4438adad24 Improve fade config, add localization, and add fading to the quality presets 2024-10-03 17:10:56 -05:00
James Seibel 986e474657 Add fading to Forge and Neo, fix fading rain, fix old MC compiling 2024-10-03 07:36:11 -05:00
James Seibel e8288a0df9 Fix fade rendering when DH rendering is disabled 2024-10-02 18:16:46 -05:00
James Seibel 74cab8f4ad replace random noise with Bayer for dithering 2024-10-02 18:08:57 -05:00
James Seibel 5e35572de8 Improve overdraw prevention quality 2024-10-02 07:49:46 -05:00
James Seibel 7e0e02bafc Add dithered DH fading, double pass fading, and fix LOD clouds 2024-10-01 22:02:45 -05:00
James Seibel a301532443 merge core changes 2024-09-30 22:00:06 -05:00
James Seibel 940448f219 Add experimental DH/vanilla fading 2024-09-30 21:59:25 -05:00
James Seibel 8510deb6aa Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2024-09-28 15:28:08 -05:00
s809 0d0ba5d3bf Do not sync sections after generation, fix incorrect timestamp fetch pos range calculation 2024-09-29 01:18:04 +05:00
James Seibel 036371dd76 Remove multiverse similarity percent and add IDhApiSaveStructure
Similarity percent should no longer be needed since the server support was added
2024-09-28 14:30:38 -05:00
James Seibel 5aae584ffe Fix F3 menu showing the same level multiple times 2024-09-28 10:22:47 -05:00
James Seibel 201c619915 Add Api world load/unload events and DhApiWorldProxy.get/setReadOnly() 2024-09-28 08:33:30 -05:00
James Seibel 7cd6a3bb79 Add temporary vertical Quality CUSTOM option
This is temporary since it's just for determining what values we want long term and adding full control will over-complicate the config
2024-09-26 22:34:38 -05:00
James Seibel ca36d4797d Add hashCode() to FullDataPointIdMap 2024-09-26 07:42:31 -05:00
James Seibel b5fe3bcbf9 Up manifold version 2024.1.30 -> 2024.1.32 2024-09-25 21:58:08 -05:00
James Seibel e43f9e76f6 Change FullDataSourceDTO checksum/hash logic to hopefully be more consistent 2024-09-25 21:57:54 -05:00
James Seibel b8901c3edd Fix default generic rendering instanced mode 2024-09-25 18:44:47 -05:00
James Seibel d28938f8af fix readme logo filepaths 2024-09-22 17:25:19 -05:00
James Seibel 9f108220f5 Update readme logos 2024-09-22 17:23:41 -05:00
James Seibel ffe53a7196 Add new logos 2024-09-22 17:19:00 -05:00
James Seibel 9e12849107 Allow toggling generic obj instanced rendering via config 2024-09-22 16:32:51 -05:00
s809 0a8c093682 Fix missing testAnnotationProcessor 2024-09-23 01:18:39 +05:00
s809 60056e1654 Merge branch 'feature/lan-support' 2024-09-23 00:46:56 +05:00
s809 11a752a99d Fix dedicated server failing to send packets 2024-09-23 00:43:00 +05:00
s809 68097f61eb LAN multiplayer kinda works 2024-09-23 00:02:57 +05:00
James Seibel 8b514b07dc Move mod compat warnings into AbstractModInit and add WWOO to the list 2024-09-22 08:09:31 -05:00
s809 8e6010bbe5 Abstract away serverside parts of world & level 2024-09-22 03:29:07 +05:00
James Seibel 95eb07ca79 add commented out attempt at relocating Sqlite 2024-09-21 11:47:40 -05:00
James Seibel e6e03e78ea try upping manifold_version 2024.1.30 -> 2023.1.17 again
Please revert if compiler messages don't appear correctly
2024-09-21 11:47:16 -05:00
James Seibel d2c572414c up Sqlite version 3.43.0.0 -> 3.46.1.0 2024-09-21 11:46:42 -05:00
James Seibel d2e0d5b32b relocate org.slf4j to partially fix old XaeroPlus 2024-09-21 11:46:17 -05:00
James Seibel d4ba227a44 Fix config file handler corruption due to reading/writing concurrently 2024-09-20 07:29:41 -05:00
James Seibel caba007899 Add pshsh, to the author list 2024-09-20 07:08:37 -05:00
James Seibel d5cfe9b8e9 Revert Iris 1.8 requirement since it didn't work correctly 2024-09-19 19:59:52 -05:00
James Seibel f1707236fa Replace incompatible Iris<=1.7.4 with <1.8.0 to hopefully reduce confusion 2024-09-19 07:31:14 -05:00
James Seibel c55cbdb69a Mark Oculus 1.7 and lower as incompatible on forge 2024-09-19 07:30:10 -05:00
s809 00b69dcc68 Fix mixin crashes on 1.16.5 2024-09-18 01:06:06 +05:00
s809 cd74e33c54 Bump protocol version because of removed InvalidSectionPosException 2024-09-17 12:41:52 +05:00
s809 3f14e5dfa5 Fix naming of some things and comments 2024-09-17 09:48:15 +05:00
James Seibel 8c9e6ea79a Fix very high file handler jobs and pool some render data sources 2024-09-16 20:36:10 -05:00
James Seibel c71de31f57 Fix a few pre-processors blocked renamings 2024-09-16 19:37:34 -05:00
James Seibel ac869bf06e Up API version 3.0.1 -> 4.0.0 2024-09-15 21:16:24 -05:00
James Seibel 3175bc0439 Up version 2.2.2 -> 2.3.0 2024-09-15 21:16:13 -05:00
James Seibel 23ac6ec957 merge server side and minor refactoring 2024-09-15 21:15:40 -05:00
James Seibel f080a59b41 Add netty leak detection and server/client folder separation 2024-09-11 17:38:02 -05:00
James Seibel 603200ed8b only log git info on startup in dev builds 2024-09-11 17:19:33 -05:00
James Seibel b0774052a0 add default intellij gradle run scripts 2024-09-11 17:12:34 -05:00
James Seibel 7e163ce626 Add ILevelWrapper.getDimensionName() 2024-09-11 07:18:57 -05:00
James Seibel d19654cf15 Remove unneeded ChunkWrapper lighting checks 2024-09-10 07:35:02 -05:00
James Seibel 838d8be08b minor refactoring and style cleanup 2024-09-10 07:29:42 -05:00
James Seibel 2de82b1223 Add Netty to the build script 2024-09-10 06:55:40 -05:00
James Seibel 650012fb08 allow CI/CD to auto retry if there was a system failure 2024-09-10 06:53:06 -05:00
James Seibel a1ef3466ad Add test scripts folder 2024-09-10 06:52:21 -05:00
James Seibel 97421feb33 Add Oculus 1.7 to the list of incompatible fabric mods 2024-09-09 07:43:24 -05:00
James Seibel 0c90af6515 Fix low quality LODs not loading when flying in a new straight line 2024-09-09 07:41:20 -05:00
James Seibel 9cebd0298c add brown mushrooms to the list of ignored blocks to fix swamp issues 2024-09-07 14:22:45 -05:00
James Seibel 9afcddca4f Add faster sky light engine from Builderb0y
Closes !67
2024-09-07 12:07:59 -05:00
James Seibel 02fb7eedba Add missing LightingTestChunkWrapper methods 2024-09-06 21:55:55 -05:00
James Seibel 4aa9bec15c Fix unit test compiling 2024-09-06 19:36:41 -05:00
James Seibel a6eeaa5b3e Closes #805 (Hide "Distant Horizons overloaded")
Also update chunks closest to the player first
2024-09-06 18:23:04 -05:00
James Seibel c462325ce6 Add BuilderB0y's getBlockState optimization 2024-09-05 07:50:25 -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
James Seibel fd89f569d0 Fix MC 1.17.1 not compiling 2024-09-01 21:16:14 -05:00
James Seibel eefc765823 Fix LODs flashing while moving 2024-09-01 21:10:32 -05:00
James Seibel ebccb2516b Cull beacons based on X/Z distance instead of 3D distance 2024-09-01 17:28:13 -05:00
James Seibel 8c62a40da9 Disable instanced rendering on Mac when Sodium is present
Closes !793 (Generic Rendering crashes with Sodium on M1 Mac)
2024-09-01 17:02:49 -05:00
James Seibel d56af5c38f Fix some beacon rendering/updating issues 2024-09-01 16:36:41 -05:00
James Seibel 39b1ec61ba Fix glass panes not affecting beacon colors 2024-09-01 15:04:27 -05:00
James Seibel cb613cf7df add disableUnchangedChunkCheck config 2024-08-31 22:11:29 -05:00
James Seibel 28e33b4c36 Fix MC 1.16+ compiling 2024-08-31 22:11:14 -05:00
James Seibel 855e6b8180 Update pos getters and remove (hopefully) unneeded imports 2024-08-31 21:57:53 -05:00
James Seibel d62161f529 Fix cloud color not matching MC 2024-08-31 21:56:32 -05:00
James Seibel 71d48411f1 Add DhBlockPosMutable and make the original immutable 2024-08-30 07:36:04 -05:00
James Seibel 731842e09c Fix DH beacon detection logic breaking the lighting engine 2024-08-29 19:54:44 -05:00
James Seibel 61169f87c0 Fix LODs not updating underground 2024-08-29 07:33:21 -05:00
James Seibel 9fb3b196d2 Add a quick DH cloud UI config 2024-08-28 07:16:33 -05:00
James Seibel 867b875cf9 Fix Lithium breaking world gen for MC 1.20.1 and older 2024-08-27 19:19:09 -05:00
James Seibel 3875c8c4ce Up version number 2.2.0 -> 2.2.1-dev 2024-08-20 19:16:28 -05:00
James Seibel 89b959d3f5 Up version number 2.1.3-dev -> 2.2.0 2024-08-20 17:45:42 -05:00
James Seibel d62e50d6f4 Fix Legacy GL causing fog to smear 2024-08-20 17:45:31 -05:00
James Seibel 16836a2b49 Move MixinChunkMap shared code to common 2024-08-18 14:46:49 -05:00
James Seibel f5651f26a5 Merge !65 (fix neo/forge chunk update events) 2024-08-18 14:31:24 -05:00
James Seibel 82ff59c857 Fix fog and SSAO being broken by some mods 2024-08-17 22:29:42 -05:00
James Seibel 8af61041f0 Remove 1.21 from CI build script
1.21.1 also covers 1.21
2024-08-16 17:28:36 -05:00
James Seibel 2a9136b56f Merge 1.21.1 and 1.21 2024-08-15 07:21:19 -05:00
James Seibel 64da6c811d revert temporary 1.17.1 breakage 2024-08-13 17:24:18 -05:00
James Seibel e6b93e0d92 forgot to close test preprocessor 2024-08-13 07:46:49 -05:00
James Seibel f874219a64 Wrong 1.17.1 perpreocessor used 2024-08-13 07:33:39 -05:00
James Seibel b4822740f4 temporarily break 1.17 compiling to test gitlab bages 2024-08-13 07:33:04 -05:00
James Seibel af205a50b4 Deprecate IDhApiWorldGenerator.isBusy(), task queuing is now handled internally 2024-08-12 22:20:14 -05:00
James Seibel 2f6eaf79bd Add optional DhApiChunk validation for world gen 2024-08-12 21:47:58 -05:00
James Seibel 625f1e700f Fix MC 1.21 / 1.21.1 2024-08-12 21:05:15 -05:00
James Seibel 897d5b0b11 Change MC 1.21 -> 1.21.1 in CI version 2024-08-12 19:53:48 -05:00
James Seibel 95641e2f4e Allow adding empty lists to DhApiChunk 2024-08-11 22:01:29 -05:00
James Seibel cd856b86c7 Fix DhApiChunk setDataPoints failing for empty lists 2024-08-11 21:55:19 -05:00
James Seibel c00aa6d627 Add MC 1.21.1 2024-08-11 20:05:24 -05:00
James Seibel 398a3fb0bc Add alex's cave warning message 2024-08-11 09:55:12 -05:00
James Seibel e0fa638ad9 Fix beacons not enabling/disabling correctly 2024-08-09 07:26:20 -05:00
James Seibel 4e42cbd4ce Fix frustum culling when the screen is warped 2024-08-07 18:55:09 -05:00
James Seibel b6c6be77cd Fix beacons not being updated 2024-08-07 07:47:30 -05:00
James Seibel 0964293a72 Fix direct memory leak and remove config for GpuUpload 2024-08-07 07:30:01 -05:00
James Seibel c8b6141ce0 Improve LOD detail level detection and hole filling 2024-08-04 08:31:03 -05:00
James Seibel 948540369f Attempt to improve LOD building speed and reduce broken lighting on servers 2024-08-03 17:11:22 -05:00
James Seibel 363df0ad6f Fix MC 1.16/1.17 compiling 2024-08-03 11:38:02 -05:00
James Seibel a37e105434 Add (disabled) test API world generator 2024-08-03 09:52:06 -05:00
James Seibel aeea0c00c3 Allow DhApiChunk to accept top down or bottom up data point orders 2024-08-03 09:33:05 -05:00
James Seibel 137352674e Fix off by 1 error in Render data transformer 2024-08-02 18:30:59 -05:00
James Seibel 4734552954 Fix MC 1.16 compiling 2024-08-02 18:21:47 -05:00
James Seibel 879c2f1ec4 Fix out of bounds exception in Full Data Transformer 2024-08-02 17:56:33 -05:00
James Seibel 7dc9d2a352 Clean up faster world gen and fix even offset gen events 2024-08-02 08:25:32 -05:00
James Seibel cabc470ebd Temporary Test removing world gen boarder chunks 2024-08-01 07:44:46 -05:00
James Seibel 0bf1f493aa Change some world gen info logs to debug 2024-08-01 07:06:47 -05:00
James Seibel 705bd14ee4 Fix cave culling affecting floating islands and add LOD reload to some configs 2024-07-31 19:06:47 -05:00
James Seibel 155955e49b Mark Iris 1.7.4 and lower as incompatible (as recommended by IMS) 2024-07-30 17:13:54 -05:00
James Seibel c76a793b18 Remove deprecated methods and move method to StringUtil 2024-07-30 17:07:16 -05:00
James Seibel 50cc8501a0 Remove unused sodium and McRenderWrapper methods
Removed methods were originally used to cull LODs if MC had loaded chunks, however this turned out to be more trouble than it was worth and caused more problems than it solved.
2024-07-30 17:01:09 -05:00
James Seibel 209279e3e4 Merge branch 'distant-horizons-m2' 2024-07-30 16:06:39 -05:00
James Seibel 41239572a5 Fix presets only using "custom" after any value was changed 2024-07-30 15:47:52 -05:00
James Seibel 106ab47c3d Fix default logging debug to file 2024-07-29 20:40:54 -05:00
James Seibel a84f9b60e5 Fix rapidly changing dimensions causing the game to crash 2024-07-29 07:29:56 -05:00
James Seibel 4481e8634a Fix incorrect DhApiChunk create constructor parameter order (again) 2024-07-28 20:18:31 -05:00
James Seibel 3e432682fb fix incorrect positions being fed into biome color code 2024-07-28 09:34:15 -05:00
James Seibel 05569c03a4 Revert and Deprecate DhApiChunk and DhApiTerrainDataPoint constructors 2024-07-28 08:56:26 -05:00
James Seibel 2d567b84be Fix holes in LODs boarding different detail levels 2024-07-27 21:06:55 -05:00
James Seibel e2a378250f Fix LOD upload warning 2024-07-27 20:25:58 -05:00
James Seibel e2083a1836 Fix LODs flashing twice when changing configs 2024-07-27 20:11:49 -05:00
James Seibel 334946ab59 Potentially fix thread warnings in ClientBlockStateColorCache 2024-07-27 19:15:00 -05:00
James Seibel 8c9bb98125 Update IDhApiRenderProxy.clearRenderDataCache() to also clear cached block colors 2024-07-27 17:36:57 -05:00
James Seibel 726f0f3d3c Remove unused ServerBlockStateCache 2024-07-27 16:51:14 -05:00
James Seibel 50e5898692 Rename ClientBlockStateCache -> ClientBlockStateColorCache
And do some additional cleanup
2024-07-27 16:44:47 -05:00
James Seibel de05a5f674 Refactor and cleanup ClientBlockStateCache 2024-07-27 16:25:27 -05:00
James Seibel 31b57fae50 fix 1.16.5 compiling 2024-07-27 16:24:31 -05:00
James Seibel 2f686057f3 Fix ice/water vertical LOD lighting 2024-07-27 09:30:51 -05:00
James Seibel 132251341f Fix replay mod not showing LODs 2024-07-21 20:06:40 -05:00
James Seibel 2bac5f933a remove unused clientLevelWrapper.getGameDirectory() 2024-07-21 19:29:01 -05:00
James Seibel 2e565aa83a Improve cave culling and add config for ignored/cave blocks 2024-07-21 17:27:26 -05:00
James Seibel 4e9d0f4861 Fix ConfigEntry String value saving 2024-07-21 16:13:55 -05:00
James Seibel 7216b193e8 Fix API chunk world gen 2024-07-20 17:58:39 -05:00
James Seibel c33a5bf814 Add IDhApiWrapperFactory resourceLocation string methods for block/biomes 2024-07-20 11:21:01 -05:00
James Seibel 97756a5196 Add AbstractDhApiChunkWorldGenerator.generateApiChunk() 2024-07-20 10:45:55 -05:00
James Seibel 377f7d23e3 Remove render param from DhApiAfterRenderEvent 2024-07-14 09:31:03 -05:00
James Seibel 7005202384 Add a optional memory cache to the IDhApiTerrainDataRepo 2024-07-14 08:41:22 -05:00
James Seibel 99e8f57bac add missing genericRendering to IDhApiGraphicsConfig 2024-07-14 07:25:49 -05:00
James Seibel afddf4168e Change some chunk deserialization errors to warnings 2024-07-13 12:59:43 -05:00
James Seibel fbffdc0c9f Fix fog for Mac and remove near fog limitation 2024-07-13 12:17:30 -05:00
James Seibel e6d3647490 Increase default fog start distance 2024-07-13 08:14:33 -05:00
James Seibel 13363ff363 make clouds smaller and thinner 2024-07-12 21:47:16 -05:00
James Seibel 7f98e4b1eb Fix potential chunkWrapper null pointer 2024-07-12 21:31:12 -05:00
James Seibel 408460b0ae Fix missing imports for MC 1.19 and below 2024-07-12 20:31:36 -05:00
James Seibel b69ef5835d Fix repo connections not getting closed 2024-07-12 20:22:02 -05:00
James Seibel 0428fa0912 Clone API event parameters to reduce listener contamination 2024-07-12 19:22:25 -05:00
James Seibel 9f3124fa56 Add renderEventParam to generic rendering shader binding by IMS request 2024-07-12 17:27:32 -05:00
James Seibel fbbdab73c6 Attempt to fix lag spikes when right clicking blocks 2024-07-12 17:24:45 -05:00
James Seibel ee9441c521 Fix world gen not skipping already complete stages 2024-07-12 07:41:18 -05:00
James Seibel a9e0fd5d9b Add generic object setup/cleanup events 2024-07-12 07:16:06 -05:00
James Seibel 98464889ca Fix material typo 2 2024-07-11 22:51:47 -05:00
James Seibel eed5fd60c6 Fix material typo 2024-07-11 22:07:15 -05:00
James Seibel ac43cd5496 Add generic object materials 2024-07-11 18:13:07 -05:00
James Seibel 1f16a7c808 Fix generic rendering and add EDhApiBlockMaterial 2024-07-11 17:58:05 -05:00
James Seibel 39e4c70754 Add api for generic rendering config 2024-07-11 17:39:01 -05:00
James Seibel 82eb27af4c Add DhApiBeforeGenericObjectRenderEvent 2024-07-11 17:32:26 -05:00
IMS212 3aaab94b39 Support both Sodium 0.5 and 0.6 with reflection 2024-07-10 21:02:53 -07:00
James Seibel 07a0779ca4 Fix potential light map crashing and memory leak 2024-07-10 18:57:09 -05:00
James Seibel 2adba02a38 Add "IP Only" to multiplayer tooltip 2024-07-10 07:45:12 -05:00
James Seibel 9dd76db3fc Fix generic rendering at extreme distances 2024-07-10 07:37:18 -05:00
James Seibel 97dacf2429 Add toggleable logging for GL Buffer garbage collection
Will need to be tested by someone who is experiencing issue #718, so far I've been unable to reproduce anything meaningful.
2024-07-09 17:40:27 -05:00
James Seibel 1c189e162a fix sub MC 1.20.1 compiling 2024-07-09 16:39:29 -05:00
James Seibel f7a0fff869 Move IBlockStateWrapper constants into LodUtil 2024-07-09 16:39:04 -05:00
James Seibel 2f985d0926 Add beacon colors 2024-07-09 07:33:30 -05:00
James Seibel 2a3c544fba Increase cloud rendering performance 2024-07-08 19:56:29 -05:00
James Seibel 09d133b994 Add generic rendering localization 2024-07-08 07:45:03 -05:00
James Seibel 26a4223ecf Fix double unloading beacons 2024-07-07 19:54:25 -05:00
James Seibel e2943fdcaf Fix beacons un-rendering when unloading LODs 2024-07-07 19:45:47 -05:00
James Seibel f1053251b4 Add missing generic rendering config options 2024-07-07 18:13:58 -05:00
James Seibel be1dcaf43c Add cloud rendering 2024-07-07 18:03:11 -05:00
James Seibel a899d988fc Fix concurrent modification for GenericObjectRenderer 2024-07-04 21:43:15 -05:00
James Seibel 06b5b2c514 Fix potential null pointer in auto updater 2024-07-04 17:37:30 -05:00
James Seibel 864a19b79f Remove useless IServerLevelWrapper.tryGetClientLevelWrapper() 2024-07-04 16:31:04 -05:00
James Seibel 8974323406 Fix Api client level not containing the generic renderer 2024-07-04 16:15:51 -05:00
James Seibel 46c9e0103a Improve world gen timeout warning message 2024-07-04 16:01:33 -05:00
James Seibel 02203466ed Move generic rendering to the level API 2024-07-03 22:38:14 -05:00
James Seibel 87b22ea1cc Add a config to use pre-existing lighting 2024-07-03 20:30:56 -05:00
James Seibel d26327a930 fix max chunk Y position for empty chunks 2024-07-03 19:14:47 -05:00
James Seibel 469d2bdcb7 Add improved beacon logic 2024-07-02 17:51:26 -05:00
James Seibel 5516603a0c Add temporary proof-of-concept beacon rendering 2024-06-30 18:08:55 -05:00
James Seibel b737adc3da Up API version 2.1.0 -> 3.0.0 2024-06-30 16:36:49 -05:00
James Seibel f3a8afeee3 Up version 2.1.2 -> 2.1.3-dev 2024-06-25 19:25:35 -05:00
James Seibel a4501f86e9 Update coreSubProjects 2024-06-25 19:24:13 -05:00
208 changed files with 8477 additions and 3814 deletions
+1 -1
View File
@@ -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
+5
View File
@@ -26,6 +26,11 @@ Merged/
# Folder created by the buildAll scripts # Folder created by the buildAll scripts
buildAllJars/ buildAllJars/
relocate_natives/.venv/
relocate_natives/__pycache__/
relocate_natives/apple-codesign/
relocate_natives/cache/
# file from notepad++ # file from notepad++
*.bak *.bak
+21 -26
View File
@@ -17,47 +17,42 @@ variables:
# These can be extended so code is a bit less duplicated # These can be extended so code is a bit less duplicated
.build_java: .build_java:
#image: eclipse-temurin:17 #image: eclipse-temurin:17
before_script:
- apt-get update
- apt-get install python3 python3-pip python-is-python3 python3-venv -y --no-install-recommends
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", "1.20.6", "1.21"] - MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21.1", "1.21.3", "1.21.4"]
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/;
- ./gradlew build -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/; - ./gradlew build -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
- ./gradlew mergeJars -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/; - ./gradlew mergeJars -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
- cp ./fabric/build/libs/* ./forge/build/libs/* ./neoforge/build/libs/* ./Merged/* . || true
artifacts: artifacts:
name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}" name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths: paths:
- Merged/*.jar - ./*.jar
- quilt/build/libs/*.jar
- fabric/build/libs/*.jar
- forge/build/libs/*.jar
- neoforge/build/libs/*.jar
exclude: exclude:
# TODO: There is a lot of duplicate stuff here, try to maybe make it smaller - ./*-all.jar
- fabric/build/libs/*-all.jar - ./*-dev.jar
- fabric/build/libs/*-dev.jar - ./*-sources.jar
- fabric/build/libs/*-sources.jar
- quilt/build/libs/*-all.jar
- quilt/build/libs/*-dev.jar
- quilt/build/libs/*-sources.jar
- forge/build/libs/*-all.jar
- forge/build/libs/*-dev.jar
- forge/build/libs/*-sources.jar
- neoforge/build/libs/*-all.jar
- neoforge/build/libs/*-dev.jar
- neoforge/build/libs/*-sources.jar
expire_in: 14 days expire_in: 14 days
when: always when: always
extends: .build_java extends: .build_java
@@ -72,15 +67,15 @@ api:
# this also runs unit tests # this also runs unit tests
- ./gradlew api:build --gradle-user-home cache/; - ./gradlew api:build --gradle-user-home cache/;
- ./gradlew api:addSourcesToCompiledJar --gradle-user-home cache/; - ./gradlew api:addSourcesToCompiledJar --gradle-user-home cache/;
- cp ./coreSubProjects/api/build/libs/merged/* .
artifacts: artifacts:
name: "Api_NightlyBuild-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}" name: "NightlyBuild_Api-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths: paths:
- coreSubProjects/api/build/libs/merged/*.jar - ./*.jar
# can be uncommented if we don't want a jar with the source code
# - coreSubProjects/api/build/libs/*.jar
exclude: exclude:
- coreSubProjects/api/build/libs/merged/*-all.jar - ./*-all.jar
- coreSubProjects/api/build/libs/merged/*-sources.jar - ./*-dev.jar
- ./*-sources.jar
expire_in: 1 day expire_in: 1 day
when: always when: always
extends: .build_java extends: .build_java
+3 -3
View File
@@ -6,13 +6,13 @@ Or click the checkbox once the issue has been created.
--> -->
1. [ ] Check the FAQ to see if your issue has already been reported and has a solution: 1. [ ] Check the FAQ to see if your issue has already been reported and has a solution:
[Problems-and-solutions](https://gitlab.com/jeseibel/distant-horizons/-/wikis/2-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions) [Problems-and-solutions](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
2. [ ] Make sure you are not using any mods on the incompatible list: 2. [ ] Make sure you are not using any mods on the incompatible list:
[Mod support FAQ](https://gitlab.com/jeseibel/distant-horizons/-/wikis/2-frequently-asked-questions/4-mod-support/Mod-Support) [Mod support FAQ](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/4-mod-support/Mod-Support)
3. [ ] Check the existing issues to verify that your bug hasn't already been submitted: 3. [ ] Check the existing issues to verify that your bug hasn't already been submitted:
[Issues](https://gitlab.com/jeseibel/distant-horizons/-/issues/) [Issues](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues)
4. [ ] Upload Minecraft's crash report and/or log. \ 4. [ ] Upload Minecraft's crash report and/or log. \
Minecraft crash reports are located in: `.minecraft/crash-reports` \ Minecraft crash reports are located in: `.minecraft/crash-reports` \
+1 -1
View File
@@ -1,4 +1,4 @@
- [ ] Check the existing [feature requests](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=updated_desc&state=opened&label_name%5B%5D=Feature) to verify that your feature hasn't already been suggested. - [ ] Check the existing [feature requests](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues?sort=updated_desc&state=opened&label_name%5B%5D=Feature) to verify that your feature hasn't already been suggested.
1. **Describe the feature**: 1. **Describe the feature**:
@@ -1,3 +1,3 @@
1. Check the existing [improvement requests](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=updated_desc&state=all&label_name%5B%5D=Improvement) to verify that your improvement hasn't already been suggested. 1. Check the existing [improvement requests](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues?sort=updated_desc&state=all&label_name%5B%5D=Improvement) to verify that your improvement hasn't already been suggested.
2. **Describe the improvement**: 2. **Describe the improvement**:
+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>
+24
View File
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [build]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<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="build" />
</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="distant-horizons [clean]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<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="clean" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [core:build]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<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="core:build" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [fabric:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<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="fabric:runClient" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [forge:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<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="distant-horizons [mergeJars]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<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="mergeJars" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
@@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [neoforge:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<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="distant-horizons [test]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<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="test" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
+1
View File
@@ -0,0 +1 @@
Distant Horizons logos © 2024 by Pankakes are licensed under CC BY-SA 4.0
+3 -3
View File
@@ -1,4 +1,4 @@
# <img src="https://gitlab.com/jeseibel/distant-horizons-core/-/raw/main/_Misc%20Files/logo%20files/LOD%20logo%20flat%20-%20with%20boarder.png" width="32"> Distant Horizons # <img src="https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/_Misc%20Files/logo%20files/new/SVG/Distant-Horizons.svg" height="128px">
_See farther without turning your game into a slide show._ _See farther without turning your game into a slide show._
<br> <br>
@@ -131,14 +131,14 @@ Prerequisites:
From the File Explorer: From the File Explorer:
1. Download and extract the project zip 1. Download and extract the project zip
2. Download the core from https://gitlab.com/jeseibel/distant-horizons-core and extract into a folder called `coreSubProjects` 2. Download the core from https://gitlab.com/distant-horizons-team/distant-horizons-core and extract into a folder called `coreSubProjects`
3. Open a terminal emulator in the project folder (On Windows you can type `cmd` in the title bar) 3. Open a terminal emulator in the project folder (On Windows you can type `cmd` in the title bar)
4. Run the commands: `./gradlew assemble` (You may need to use a `.\` on Windows) 4. Run the commands: `./gradlew assemble` (You may need to use a `.\` on Windows)
5. Merge the jars with `./gradlew mergeJars` 5. Merge the jars with `./gradlew mergeJars`
6. The compiled jar file will be in the folder `Merged` 6. The compiled jar file will be in the folder `Merged`
From the command line: From the command line:
1. `git clone --recurse-submodules https://gitlab.com/jeseibel/distant-horizons.git` 1. `git clone --recurse-submodules https://gitlab.com/distant-horizons-team/distant-horizons.git`
2. `cd distant-horizons` 2. `cd distant-horizons`
3. `./gradlew assemble` 3. `./gradlew assemble`
4. `./gradlew mergeJars` 4. `./gradlew mergeJars`
+134 -15
View File
@@ -1,3 +1,11 @@
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
import org.apache.tools.zip.ZipEntry
import javax.annotation.Nonnull
import org.apache.tools.zip.ZipOutputStream
plugins { plugins {
id "java" id "java"
@@ -11,7 +19,7 @@ plugins {
id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha" id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha"
// Architectury is used here only as a replacement for forge's own loom // Architectury is used here only as a replacement for forge's own loom
id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false id "dev.architectury.loom" version "1.7-SNAPSHOT" apply false
} }
@@ -66,8 +74,11 @@ writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm") rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm")
// Forgix settings (used for merging jars) // Forgix settings (used for merging jars)
forgix { forgix {
String loaderHyphenSeparatedList = ((String)gradle.builds_for).replaceAll(",", "-");
group = "com.seibel.distanthorizons" group = "com.seibel.distanthorizons"
mergedJarName = "DistantHorizons-${rootProject.versionStr}.jar" mergedJarName = "DistantHorizons-${loaderHyphenSeparatedList}-${rootProject.versionStr}.jar"
if (findProject(":forge")) if (findProject(":forge"))
forge { forge {
@@ -93,6 +104,83 @@ forgix {
removeDuplicate "com.seibel.distanthorizons" removeDuplicate "com.seibel.distanthorizons"
} }
class NativeTransformer implements Transformer {
private boolean enabled = false
private final HashMap<String, String> replacements = new HashMap()
private final HashMap<String, byte[]> rewrittenFiles = new HashMap()
private var nativeRelocator
public File rootDir
NativeTransformer() {
try {
int exitCode = Runtime.getRuntime().exec(new String[]{"python", "--version"}).waitFor()
if (exitCode == 0) {
enabled = true
}
} catch (IOException e) {
println(e)
}
}
void relocateNative(String target, String replacement) {
if (replacement.length() > target.length()) {
throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}")
}
replacements.put(target, replacement)
}
void before(Closure closure) {
if (enabled)
closure.run()
}
@Override
boolean canTransformResource(@Nonnull FileTreeElement element) {
return enabled && replacements.keySet().stream().anyMatch {
element.name.startsWith(it as String)
}
}
@Override
void transform(@Nonnull TransformerContext context) {
println("Transforming $context.path...")
byte[] content = context.is.readAllBytes()
if (nativeRelocator == null) {
nativeRelocator = new NativeRelocator(rootDir.toPath().resolve("relocate_natives"))
}
try {
Map.Entry<String, String> pathReplacement = replacements.entrySet().stream().filter {
context.path.startsWith(it.key as String)
}.findFirst().orElseThrow()
String path = context.path.replace(pathReplacement.key as String, pathReplacement.value as String)
content = nativeRelocator.processBinary(path, content, replacements)
rewrittenFiles.put(path, content)
}
catch (Throwable e) {
throw new GradleException("Failed to relocate", e)
}
}
@Override
boolean hasTransformedResource() { return !rewrittenFiles.isEmpty() }
@Override
void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {
for (Map.Entry<String, byte[]> rewrittenFile : rewrittenFiles.entrySet()) {
os.putNextEntry(new ZipEntry(rewrittenFile.key))
os.write(rewrittenFile.value)
}
}
}
subprojects { p -> subprojects { p ->
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")" // Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")"
// Useful later on so we dont have duplicated code // Useful later on so we dont have duplicated code
@@ -159,6 +247,7 @@ subprojects { p ->
if (findProject(":neoforge")) if (findProject(":neoforge"))
developmentNeoForge.extendsFrom coreProjects developmentNeoForge.extendsFrom coreProjects
// TODO remove unused fabricLike
if (findProject(":fabricLike") && p != project(":fabricLike")) { if (findProject(":fabricLike") && p != project(":fabricLike")) {
// Shadow fabricLike // Shadow fabricLike
fabricLike fabricLike
@@ -179,12 +268,21 @@ subprojects { p ->
if (isMinecraftSubProject) { if (isMinecraftSubProject) {
annotationProcessor("systems.manifold:manifold-preprocessor:${rootProject.manifold_version}") annotationProcessor("systems.manifold:manifold-preprocessor:${rootProject.manifold_version}")
} }
// Log4j // Log4j
// TODO: Change to shadowMe later to work in the standalone jar if (p == project(":core"))
// We cannot do this now as it would break Quilt {
implementation("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}") // the standalone core jar needs logging shaded otherwise it won't run
implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}") forgeShadowMe("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
forgeShadowMe("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
}
else
{
// When running in MC, MC already includes logging
implementation("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
}
// JOML // JOML
if (project.hasProperty("embed_joml") && embed_joml == "true") if (project.hasProperty("embed_joml") && embed_joml == "true")
@@ -218,8 +316,7 @@ subprojects { p ->
// forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}") // forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}")
// Netty // Netty
// Breaks 1.16.5 implementation("io.netty:netty-buffer:${rootProject.netty_version}")
//forgeShadowMe("io.netty:netty-all:${rootProject.netty_version}")
// Remember, for lwjgl dependencies that arent included in Minecraft, you need to also need to add it to the ShadowJar thing // Remember, for lwjgl dependencies that arent included in Minecraft, you need to also need to add it to the ShadowJar thing
forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") { forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") {
@@ -292,9 +389,26 @@ subprojects { p ->
// Compression (LZ4) // Compression (LZ4)
relocate "net.jpountz", "${librariesLocation}.jpountz" relocate "net.jpountz", "${librariesLocation}.jpountz"
// Logging
relocate "org.slf4j", "${librariesLocation}.slf4j"
// Sqlite Database // Sqlite Database
//At the moment, there is a bug in this library which doesnt allow it to be relocated // librariesLocation isn't used because it's too long for replacing paths in native libraries
// relocate "org.sqlite", "${librariesLocation}.sqlite" // Allowing strings larger than the original string would require shifting the entire binary's contents
transform(NativeTransformer) {
rootDir = project.rootDir
before {
relocate "org.sqlite", "dh_sqlite", {
exclude "org/sqlite/native/**"
}
relocate "jdbc:sqlite", "jdbc:dh_sqlite"
}
relocateNative "org/sqlite", "dh_sqlite"
relocateNative "org_sqlite", "dh_1sqlite"
}
// JOML // JOML
if (project.hasProperty("embed_joml") && embed_joml == "true") if (project.hasProperty("embed_joml") && embed_joml == "true")
@@ -307,7 +421,8 @@ subprojects { p ->
// relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg" // relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg"
// Netty // Netty
relocate "io.netty", "${librariesLocation}.netty" // Don't relocate, it causes problems with using MC's FriendlyByteBufs
// relocate "io.netty", "${librariesLocation}.netty"
mergeServiceFiles() mergeServiceFiles()
} }
@@ -386,7 +501,7 @@ subprojects { p ->
info_build_source : infoBuildSource, info_build_source : infoBuildSource,
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
@@ -434,7 +549,8 @@ subprojects { p ->
attributes( attributes(
'Implementation-Title': rootProject.mod_name, 'Implementation-Title': rootProject.mod_name,
'Implementation-Version': rootProject.mod_version, 'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain' // When changing the main of the jar change this line 'Multi-Release': true, // needed for logging in the standalone core jar
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain', // When changing the main of the jar change this line
) )
} }
} }
@@ -489,6 +605,9 @@ allprojects { p ->
repositories { repositories {
// Mojang overrides (added to fix downloading the wrong LWJGL libs on M1 Mac's and potentially other arm64 based machines)
maven { url "https://libraries.minecraft.net/" }
// The central repo // The central repo
mavenCentral() mavenCentral()
@@ -496,6 +615,7 @@ allprojects { p ->
maven { url "https://repo.enonic.com/public/" } maven { url "https://repo.enonic.com/public/" }
// For parchment mappings // For parchment mappings
// versions can be found here: https://ldtteam.jfrog.io/ui/native/parchmentmc-public/org/parchmentmc/data/
maven { url "https://maven.parchmentmc.org" } maven { url "https://maven.parchmentmc.org" }
// For Architectury API // For Architectury API
@@ -617,7 +737,6 @@ allprojects { p ->
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
if (isMinecraftSubProject) { if (isMinecraftSubProject) {
options.release = rootProject.java_version as Integer options.release = rootProject.java_version as Integer
options.compilerArgs += ["-Xplugin:Manifold"]
} else { } else {
options.release = 8; // Core & Api should use Java 8 no matter what options.release = 8; // Core & Api should use Java 8 no matter what
//options.release = rootProject.java_version as Integer // But if you want to test some stuff, then this can be enabled //options.release = rootProject.java_version as Integer // But if you want to test some stuff, then this can be enabled
+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
+4 -3
View File
@@ -5,6 +5,7 @@
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 buildAllJars mkdir buildAllJars
del buildAllJars/*
@rem Loop trough everything in the version properties folder @rem Loop trough everything in the version properties folder
for %%f in (versionProperties\*) do ( for %%f in (versionProperties\*) do (
@@ -13,11 +14,11 @@ for %%f in (versionProperties\*) do (
@rem Clean out the folders, build it, and merge it @rem Clean out the folders, build it, and merge it
echo ==================== Cleaning workspace to build !version! ==================== echo ==================== Cleaning workspace to build !version! ====================
call .\gradlew.bat clean -PmcVer="!version!" --no-daemon call .\gradlew.bat clean -PmcVer="!version!"
echo ==================== Building !version! ==================== echo ==================== Building !version! ====================
call .\gradlew.bat build -PmcVer="!version!" --no-daemon call .\gradlew.bat build -PmcVer="!version!"
echo ==================== Merging !version! ==================== echo ==================== Merging !version! ====================
call .\gradlew.bat mergeJars -PmcVer="!version!" --no-daemon call .\gradlew.bat mergeJars -PmcVer="!version!"
echo ==================== Moving jar ==================== echo ==================== Moving jar ====================
move Merged\*.jar buildAllJars\ move Merged\*.jar buildAllJars\
) )
+207
View File
@@ -0,0 +1,207 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
class NativeRelocator
{
private final Path rootDirectory;
private final Path cacheRoot;
/**
* Initializes the NativeRelocator by preparing the environment if necessary.
* Executes the appropriate preparation script based on the OS.
*
* @throws Exception if the preparation script fails or an unsupported OS is detected.
*/
NativeRelocator(Path rootDirectory) throws Exception
{
this.rootDirectory = rootDirectory;
this.cacheRoot = this.rootDirectory.resolve("cache");
if (this.rootDirectory.resolve(".venv").toFile().exists())
{
return;
}
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.directory(this.rootDirectory.toFile());
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win"))
{
processBuilder.command("powershell", "./prepare.ps1");
}
else if (os.contains("nix") || os.contains("nux") || os.contains("mac"))
{
processBuilder.command("./prepare.sh");
}
else
{
throw new IllegalStateException("Unsupported operating system: " + os);
}
Process process = processBuilder.start();
CompletableFuture<Void> outputFuture = readOutputStreams(process);
int exitCode = process.waitFor();
outputFuture.get();
if (exitCode != 0)
{
throw new Exception("Prepare failed: " + exitCode);
}
}
/**
* Reads and prints the output and error streams of a process asynchronously.
*
* @param process The process whose streams should be read.
* @return A CompletableFuture that completes once all output has been processed.
*/
private static CompletableFuture<Void> readOutputStreams(Process process)
{
return CompletableFuture.runAsync(() -> {
try
{
while (process.isAlive() || process.getInputStream().available() > 0 || process.getErrorStream().available() > 0)
{
if (process.getInputStream().available() > 0)
{
byte[] data = new byte[process.getInputStream().available()];
//noinspection ResultOfMethodCallIgnored
process.getInputStream().read(data);
System.out.write(data);
}
if (process.getErrorStream().available() > 0)
{
byte[] data = new byte[process.getErrorStream().available()];
//noinspection ResultOfMethodCallIgnored
process.getErrorStream().read(data);
System.err.write(data);
}
//noinspection BusyWait
Thread.sleep(100);
}
}
catch (Throwable ignored)
{
}
});
}
/**
* Replaces occurrences of a target string in a byte array, ensuring null termination.
*
* @param byteArray The byte array where replacements should occur.
* @param target The string to replace.
* @param replacement The replacement string (must not be longer than the target).
* @throws IllegalArgumentException if the replacement is longer than the target.
*/
private void replaceInNullTerminatedStrings(byte[] byteArray, String target, String replacement)
{
if (target.length() < replacement.length())
{
throw new IllegalArgumentException("Replacement must be the same length or shorter than the target.");
}
byte[] targetBytes = target.getBytes(StandardCharsets.US_ASCII);
byte[] replacementBytes = replacement.getBytes(StandardCharsets.US_ASCII);
byte nullByte = 0;
for (int endPos = 0; endPos < byteArray.length - targetBytes.length - 1; endPos++)
{
int startPos = endPos;
int targetPos = 0;
while (targetPos < targetBytes.length && byteArray[endPos] == targetBytes[targetPos])
{
targetPos++;
endPos++;
}
if (targetPos == targetBytes.length)
{
System.arraycopy(replacementBytes, 0, byteArray, startPos, replacementBytes.length);
startPos = startPos + replacementBytes.length;
while (byteArray[endPos] != nullByte)
{
byteArray[startPos] = byteArray[endPos];
endPos++;
startPos++;
}
byteArray[startPos] = nullByte;
}
}
}
/**
* Runs an external script to fix a modified binary and returns the processed content.
*
* @param outputFilePath Path to store the processed binary.
* @param content The original binary content.
* @return The modified binary content.
* @throws Exception if the process execution fails.
*/
public byte[] fixModifiedBinary(Path outputFilePath, byte[] content) throws Exception
{
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.directory(this.rootDirectory.toFile());
processBuilder.command(
this.rootDirectory.resolve(".venv/Scripts").toFile().exists()
? this.rootDirectory.resolve(".venv/Scripts/python.exe").toString()
: this.rootDirectory.resolve(".venv/bin/python").toString(),
"./fix_modified_binary.py",
outputFilePath.toString()
);
Process process = processBuilder.start();
CompletableFuture<Void> outputFuture = readOutputStreams(process);
process.getOutputStream().write(content);
process.getOutputStream().close();
int exitCode = process.waitFor();
outputFuture.get();
if (exitCode != 0)
{
throw new Exception("Process failed: " + exitCode);
}
return Files.readAllBytes(outputFilePath);
}
/**
* Processes a binary file, applying string replacements and fixing modifications.
*
* @param outputPath The output file path relative to the cache directory.
* @param content The binary content to process.
* @param replacements A map of string replacements to apply.
* @return The modified binary content.
* @throws Exception if processing fails.
*/
public byte[] processBinary(String outputPath, byte[] content, Map<String, String> replacements) throws Exception
{
Path outputFilePath = this.cacheRoot.resolve(outputPath);
//noinspection ResultOfMethodCallIgnored
outputFilePath.getParent().toFile().mkdirs();
if (outputFilePath.toFile().exists())
{
return Files.readAllBytes(outputFilePath);
}
for (Map.Entry<String, String> replacement : replacements.entrySet())
{
this.replaceInNullTerminatedStrings(content, replacement.getKey(), replacement.getValue());
}
return this.fixModifiedBinary(outputFilePath, content);
}
}
+2
View File
@@ -4,6 +4,8 @@ buildscript {
configurations.configureEach { configurations.configureEach {
resolutionStrategy { resolutionStrategy {
force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82' force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82'
// newer versions can be found by going to the link:
// https://repo.spongepowered.org/#browse/browse:maven-public:org%2Fspongepowered%2Fvanillagradle%2F0.2.1-SNAPSHOT
} }
} }
} }
@@ -3,8 +3,10 @@ package com.seibel.distanthorizons.common;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
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.commands.CommandInitializer;
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.MinecraftServerWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase; import com.seibel.distanthorizons.core.config.ConfigBase;
@@ -12,6 +14,7 @@ import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPreset
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.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
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;
@@ -34,7 +37,7 @@ public abstract class AbstractModInitializer
{ {
protected static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); protected static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private CommandDispatcher<CommandSourceStack> commandDispatcher; private CommandInitializer commandInitializer;
@@ -63,22 +66,23 @@ public abstract class AbstractModInitializer
{ {
DependencySetup.createClientBindings(); DependencySetup.createClientBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME); LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " client.");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup(); this.startup();
this.printModInfo(true); this.logBuildInfo();
this.createClientProxy().registerEvents(); this.createClientProxy().registerEvents();
this.createServerProxy(false).registerEvents(); this.createServerProxy(false).registerEvents();
this.initializeModCompat(); this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " Initialized"); LOGGER.info(ModInfo.READABLE_NAME + " client Initialized.");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
// Client uses config for auto-updater, so it's initialized here instead of post-init stage // Client uses config for auto-updater, so it's initialized here instead of post-init stage
this.initConfig(); this.initConfig();
logModIncompatibilityWarnings(); // needs to be called after config loading
this.subscribeClientStartedEvent(this::postInit); this.subscribeClientStartedEvent(this::postInit);
} }
@@ -87,11 +91,11 @@ public abstract class AbstractModInitializer
{ {
DependencySetup.createServerBindings(); DependencySetup.createServerBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME); LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " server.");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup(); this.startup();
this.printModInfo(false); this.logBuildInfo();
// 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,18 +104,22 @@ public abstract class AbstractModInitializer
this.createServerProxy(true).registerEvents(); this.createServerProxy(true).registerEvents();
LOGGER.info(ModInfo.READABLE_NAME + " Initialized"); this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized.");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandDispatcher = dispatcher; }); this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer = new CommandInitializer(dispatcher); });
this.subscribeServerStartingEvent(server -> this.subscribeServerStartingEvent(server ->
{ {
MinecraftDedicatedServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server; MinecraftServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server;
this.initConfig(); this.initConfig();
this.postInit(); this.postInit();
this.initCommands(); this.commandInitializer.initCommands();
this.checkForUpdates();
LOGGER.info("Dedicated server initialized at " + server.getServerDirectory()); LOGGER.info("Dedicated server initialized at " + server.getServerDirectory());
}); });
@@ -130,13 +138,13 @@ public abstract class AbstractModInitializer
this.createInitialBindings(); this.createInitialBindings();
} }
private void printModInfo(boolean printGitInfo) private void logBuildInfo()
{ {
LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION); LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
if (printGitInfo) // if the build is stable the branch/commit/etc shouldn't be needed
if (ModInfo.IS_DEV_BUILD)
{ {
// Useful for dev builds
LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch); LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch);
LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit); LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit);
LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source); LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source);
@@ -155,10 +163,24 @@ public abstract class AbstractModInitializer
private void initConfig() private void initConfig()
{ {
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, 2); ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, ModInfo.CONFIG_FILE_VERSION);
Config.completeDelayedSetup(); Config.completeDelayedSetup();
} }
private void checkForUpdates()
{
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get())
{
if (Config.Client.Advanced.AutoUpdater.enableSilentUpdates.get())
{
LOGGER.info("Silent updates are not allowed for dedicated servers; force disabling.");
Config.Client.Advanced.AutoUpdater.enableSilentUpdates.set(false);
}
SelfUpdater.onStart();
}
}
private void postInit() private void postInit()
{ {
LOGGER.info("Post-Initializing Mod"); LOGGER.info("Post-Initializing Mod");
@@ -166,7 +188,101 @@ public abstract class AbstractModInitializer
LOGGER.info("Mod Post-Initialized"); LOGGER.info("Mod Post-Initialized");
} }
private void initCommands() { /* currently unimplemented */ }
//==================================//
// mod partial compatibility checks //
//==================================//
/**
* Some mods will work with a few tweaks
* or will partially work but have some known issues we can't solve.
* This method will log (and display to chat if enabled)
* these warnings and potential fixes.
*/
private static void logModIncompatibilityWarnings()
{
boolean showChatWarnings = Config.Common.Logging.Warning.showModCompatibilityWarningsOnStartup.get();
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
String startingString = "Partially Incompatible Distant Horizons mod detected: ";
// Alex's caves
if (modChecker.isModLoaded("alexscaves"))
{
// There've been a few reports about this mod breaking DH at a few different points in time
// the fixes for said breakage changes depending on the version so unfortunately
// all we can do is log a warning so the user can handle it.
if (showChatWarnings)
{
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);
}
LOGGER.warn(startingString + "[Alex's Caves] may require some config changes in order to render Distant Horizons correctly.");
}
// William Wythers' Overhauled Overworld (WWOO)
if (modChecker.isModLoaded("wwoo"))
{
// WWOO has a bug with it's world gen that can't be fixed by DH or WWOO
// (at least that is what James learned after talking with WWOO)
// WWOO will cause grid lines to appear in the world when DH generates the chunks
// this might be due to how WWOO uses features for everything when generating
// and said features don't always get to the edge of said chunks.
String wwooWarning = "LODs generated by DH may have grid lines between sections. Disabling either WWOO or DH's distant generator will fix the problem.";
if (showChatWarnings)
{
String message =
// orange text
"\u00A76" + "Distant Horizons: WWOO detected." + "\u00A7r\n" +
wwooWarning;
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
LOGGER.warn(startingString + "[WWOO] "+ wwooWarning);
}
// Chunky
boolean chunkyPresent = false;
try
{
Class.forName("org.popcraft.chunky.api.ChunkyAPI");
chunkyPresent = true;
}
catch (ClassNotFoundException ignore) { }
if (chunkyPresent)
{
// Chunky can generate chunks faster than DH can process them,
// causing holes in the LODs.
// Generally it's better and faster to use DH's world generator.
String chunkyWarning = "Chunky can cause DH LODs to have holes " +
"since Chunky can generate chunks faster than DH can process them. \n" +
"Using DH's distant generator instead of chunky or increasing DH's CPU thread count can resolve the issue.";
if (showChatWarnings)
{
String message =
// orange text
"\u00A76" + "Distant Horizons: Chunky detected." + "\u00A7r\n" +
chunkyWarning;
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
LOGGER.warn(startingString + "[Chunky] "+ chunkyWarning);
}
}
@@ -0,0 +1,112 @@
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.IncompatibleMessageInternalEvent;
import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorInternalEvent;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
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 java.io.IOException;
import java.util.Objects;
public abstract class AbstractPluginPacketSender implements IPluginPacketSender
{
private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
() -> Config.Common.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 sendToClient(IServerPlayerWrapper serverPlayer, AbstractNetworkMessage message)
{
this.sendToClient((ServerPlayer) serverPlayer.getWrappedMcObject(), message);
}
public abstract void sendToClient(ServerPlayer serverPlayer, AbstractNetworkMessage message);
@Override
public abstract void sendToServer(AbstractNetworkMessage message);
public static AbstractNetworkMessage decodeMessage(FriendlyByteBuf in)
{
AbstractNetworkMessage message = null;
try
{
in.markReaderIndex();
int protocolVersion = in.readShort();
if (protocolVersion != ModInfo.PROTOCOL_VERSION)
{
return new IncompatibleMessageInternalEvent(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 ProtocolErrorInternalEvent(e, message, true);
}
finally
{
// Prevent connection crashing if not entire buffer has been read
in.readerIndex(in.writerIndex());
}
}
public static void encodeMessage(FriendlyByteBuf out, AbstractNetworkMessage 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 ProtocolErrorInternalEvent(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,36 @@
package com.seibel.distanthorizons.common;
#if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
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 AbstractNetworkMessage 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,102 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import net.minecraft.commands.CommandSourceStack;
#if MC_VER >= MC_1_19_2
import net.minecraft.network.chat.Component;
import java.util.Objects;
#else // < 1.19.2
import net.minecraft.network.chat.TranslatableComponent;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
#endif
/**
* Abstract class providing common functionality for DH's commands.
*/
public abstract class AbstractCommand
{
public abstract LiteralArgumentBuilder<CommandSourceStack> buildCommand();
/**
* Sends a success response to the player with the given text.
*
* @param commandContext The command context to send the response to.
* @param text The text to display in the success message.
* @return 1, indicating that the command was successful.
*/
protected int sendSuccessResponse(CommandContext<CommandSourceStack> commandContext, String text, boolean notifyAdmins)
{
#if MC_VER >= MC_1_20_1
commandContext.getSource().sendSuccess(() -> Component.literal(text), notifyAdmins);
#elif MC_VER >= MC_1_19_2
commandContext.getSource().sendSuccess(Component.literal(text), notifyAdmins);
#else
commandContext.getSource().sendSuccess(new TranslatableComponent(text), notifyAdmins);
#endif
return 1;
}
/**
* Sends a failure response to the player with the given text.
*
* @param commandContext The command context to send the response to.
* @param text The text to display in the failure message.
* @return 1, indicating that the command was successful.
*/
protected int sendFailureResponse(CommandContext<CommandSourceStack> commandContext, String text)
{
#if MC_VER >= MC_1_20_1
commandContext.getSource().sendFailure(Component.literal(text));
#elif MC_VER >= MC_1_19_2
commandContext.getSource().sendFailure(Component.literal(text));
#else
commandContext.getSource().sendFailure(new TranslatableComponent(text));
#endif
return 1;
}
/**
* Gets the server player from a command context.
*
* @param commandContext The command context to get the server player from.
* @return The server player wrapper for the player who sent the command.
*/
protected IServerPlayerWrapper getSourcePlayer(CommandContext<CommandSourceStack> commandContext) #if MC_VER < MC_1_19_2 throws CommandSyntaxException #endif
{
#if MC_VER >= MC_1_19_2
return ServerPlayerWrapper.getWrapper(Objects.requireNonNull(commandContext.getSource().getPlayer()));
#else
return ServerPlayerWrapper.getWrapper(commandContext.getSource().getPlayerOrException());
#endif
}
/**
* Checks if the source of a command is a player.
*
* @param source The source of the command to check.
* @return True if the source is a player, false otherwise.
*/
protected boolean isPlayerSource(CommandSourceStack source)
{
#if MC_VER >= MC_1_19_2
return source.isPlayer();
#else
try
{
source.getPlayerOrException();
return true;
}
catch (CommandSyntaxException e)
{
return false;
}
#endif
}
}
@@ -0,0 +1,49 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE;
import static net.minecraft.commands.Commands.literal;
/**
* Initializes commands of the mod.
*/
public class CommandInitializer
{
private final CommandDispatcher<CommandSourceStack> commandDispatcher;
/**
* Constructs a new instance of this class.
*
* @param commandDispatcher The dispatcher to use for registering commands.
*/
public CommandInitializer(CommandDispatcher<CommandSourceStack> commandDispatcher)
{
this.commandDispatcher = commandDispatcher;
}
/**
* Initializes all available commands.
*/
public void initCommands()
{
LiteralArgumentBuilder<CommandSourceStack> builder = literal("dh")
.requires(source -> source.hasPermission(4));
builder.then(new ConfigCommand().buildCommand());
builder.then(new DebugCommand().buildCommand());
builder.then(new PregenCommand().buildCommand());
if (DEBUG_CODEC_CRASH_MESSAGE)
{
builder.then(new CrashCommand().buildCommand());
}
this.commandDispatcher.register(builder);
}
}
@@ -0,0 +1,154 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.arguments.*;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.types.AbstractConfigType;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import net.minecraft.commands.CommandSourceStack;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.ToIntBiFunction;
import static com.mojang.brigadier.arguments.DoubleArgumentType.doubleArg;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
/**
* Command for managing config.
*/
public class ConfigCommand extends AbstractCommand
{
private static final List<CommandArgumentData<?>> commandArguments = Arrays.asList(
new CommandArgumentData<>(Integer.class, configEntry -> integer(configEntry.getMin(), configEntry.getMax()), IntegerArgumentType::getInteger),
new CommandArgumentData<>(Double.class, configEntry -> doubleArg(configEntry.getMin(), configEntry.getMax()), DoubleArgumentType::getDouble),
new CommandArgumentData<>(Boolean.class, BoolArgumentType::bool, BoolArgumentType::getBool),
new CommandArgumentData<>(String.class, StringArgumentType::string, StringArgumentType::getString)
);
/**
* Builds a command tree.
*/
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
{
LiteralArgumentBuilder<CommandSourceStack> builder = literal("config");
HashSet<String> addedCommands = new HashSet<>();
for (AbstractConfigType<?, ?> type : ConfigBase.INSTANCE.entries)
{
// Skip non-config entries
if (!(type instanceof ConfigEntry))
{
continue;
}
//noinspection PatternVariableCanBeUsed
ConfigEntry configEntry = (ConfigEntry) type;
if (configEntry.getChatCommandName() == null)
{
continue;
}
if (!addedCommands.add(configEntry.getChatCommandName()))
{
throw new IllegalStateException("Duplicate command name: " + configEntry.getChatCommandName());
}
LiteralArgumentBuilder<CommandSourceStack> subcommand = literal(configEntry.getChatCommandName())
.executes(commandContext -> this.sendSuccessResponse(commandContext,
"\n" +
"Description of §l" + configEntry.getChatCommandName() + "§r:\n" +
"§o" + configEntry.getComment().trim() + "§r\n" +
"§7Config file name: §f" + configEntry.name + "§7, category: §f" + configEntry.category + "\n" +
"\n" +
"Current value of " + configEntry.getChatCommandName() + " is §n" + configEntry.get() + "§r",
false
));
ToIntBiFunction<CommandContext<CommandSourceStack>, Object> updateConfigValue = (commandContext, value) -> {
configEntry.set(value);
return this.sendSuccessResponse(commandContext, "Changed the value of [" + configEntry.getChatCommandName() + "] to [" + value + "]", true);
};
// Enum type needs a special case since enums aren't represented by existing argument type
// and need literals for each individual value
if (Enum.class.isAssignableFrom(configEntry.getType()))
{
for (Object choice : configEntry.getType().getEnumConstants())
{
subcommand.then(
literal(choice.toString())
.executes(c -> updateConfigValue.applyAsInt(c, choice))
);
}
}
else
{
boolean setterAdded = false;
for (CommandArgumentData<?> commandArgumentData : commandArguments)
{
if (!commandArgumentData.argumentClass.isAssignableFrom(configEntry.getType()))
{
continue;
}
subcommand.then(argument("value", commandArgumentData.getArgumentType(configEntry))
.executes(c -> updateConfigValue.applyAsInt(c, commandArgumentData.getValue(c, "value"))));
setterAdded = true;
break;
}
if (!setterAdded)
{
throw new RuntimeException("Config type of " + type.getName() + " is not supported: " + configEntry.getType().getSimpleName());
}
}
builder.then(subcommand);
}
return builder;
}
private static class CommandArgumentData<T>
{
public final Class<T> argumentClass;
public final Function<ConfigEntry<T>, ArgumentType<T>> argumentTypeFunction;
private final BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter;
public CommandArgumentData(Class<T> argumentClass, Supplier<ArgumentType<T>> argumentTypeSupplier, BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter)
{
this(argumentClass, configEntry -> argumentTypeSupplier.get(), valueGetter);
}
public CommandArgumentData(Class<T> argumentClass, Function<ConfigEntry<T>, ArgumentType<T>> argumentTypeFunction, BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter)
{
this.argumentClass = argumentClass;
this.argumentTypeFunction = argumentTypeFunction;
this.valueGetter = valueGetter;
}
public ArgumentType<T> getArgumentType(ConfigEntry<T> configEntry)
{
return this.argumentTypeFunction.apply(configEntry);
}
public T getValue(CommandContext<CommandSourceStack> commandContext, String argumentName)
{
return this.valueGetter.apply(commandContext, argumentName);
}
}
}
@@ -0,0 +1,44 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage;
import net.minecraft.commands.CommandSourceStack;
import static net.minecraft.commands.Commands.literal;
public class CrashCommand extends AbstractCommand
{
@Override
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
{
return literal("crash")
.requires(this::isPlayerSource)
.then(literal("encode")
.executes(c -> {
assert SharedApi.getIDhServerWorld() != null;
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager()
.getConnectedPlayer(this.getSourcePlayer(c));
if (serverPlayerState != null)
{
serverPlayerState.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.ENCODE));
}
return 1;
}))
.then(literal("decode")
.executes(c -> {
assert SharedApi.getIDhServerWorld() != null;
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager()
.getConnectedPlayer(this.getSourcePlayer(c));
if (serverPlayerState != null)
{
serverPlayerState.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.DECODE));
}
return 1;
}));
}
}
@@ -0,0 +1,25 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import net.minecraft.commands.CommandSourceStack;
import java.util.ArrayList;
import java.util.List;
import static net.minecraft.commands.Commands.literal;
public class DebugCommand extends AbstractCommand
{
@Override
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
{
return literal("debug")
.executes(c -> {
List<String> lines = new ArrayList<>();
F3Screen.addStringToDisplay(lines);
return this.sendSuccessResponse(c, String.join("\n", lines), false);
});
}
}
@@ -0,0 +1,107 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.generation.PregenManager;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.DimensionArgument;
import net.minecraft.commands.arguments.coordinates.ColumnPosArgument;
import net.minecraft.server.level.ColumnPos;
import net.minecraft.server.level.ServerLevel;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
public class PregenCommand extends AbstractCommand
{
private final PregenManager pregenManager = new PregenManager();
@Override
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
{
LiteralArgumentBuilder<CommandSourceStack> statusCommand = literal("status")
.executes(this::pregenStatus);
LiteralArgumentBuilder<CommandSourceStack> startCommand = literal("start")
.then(argument("dimension", DimensionArgument.dimension())
.then(argument("origin", ColumnPosArgument.columnPos())
.then(argument("chunkRadius", integer(32))
.executes(this::pregenStart))));
LiteralArgumentBuilder<CommandSourceStack> stopCommand = literal("stop")
.executes(this::pregenStop);
return literal("pregen")
.then(statusCommand)
.then(startCommand)
.then(stopCommand);
}
private int pregenStatus(CommandContext<CommandSourceStack> c)
{
String statusString = this.pregenManager.getStatusString();
//noinspection ReplaceNullCheck
if (statusString != null)
{
return this.sendSuccessResponse(c, statusString, false);
}
else
{
return this.sendSuccessResponse(c, "Pregen is not running", false);
}
}
private int pregenStart(CommandContext<CommandSourceStack> c) throws CommandSyntaxException
{
this.sendSuccessResponse(c, "Starting pregen. Progress will be in the server console.", true);
ServerLevel level = DimensionArgument.getDimension(c, "dimension");
ColumnPos origin = ColumnPosArgument.getColumnPos(c, "origin");
int chunkRadius = getInteger(c, "chunkRadius");
CompletableFuture<Void> future = this.pregenManager.startPregen(
ServerLevelWrapper.getWrapper(level),
new DhBlockPos2D(#if MC_VER >= MC_1_19_2 origin.x(), origin.z() #else origin.x, origin.z #endif),
chunkRadius
);
future.whenComplete((result, throwable) -> {
if (throwable instanceof CancellationException)
{
this.sendSuccessResponse(c, "Pregen is cancelled", true);
return;
}
else if (throwable != null)
{
this.sendFailureResponse(c, "Pregen failed: " + throwable.getMessage() + "\n Check the logs for more details.");
return;
}
this.sendSuccessResponse(c, "Pregen is complete", true);
});
return 1;
}
private int pregenStop(CommandContext<CommandSourceStack> c)
{
CompletableFuture<Void> runningPregen = this.pregenManager.getRunningPregen();
if (runningPregen == null)
{
return this.sendFailureResponse(c, "Pregen is not running");
}
runningPregen.cancel(true);
return 1;
}
}
@@ -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, ServerLevelWrapper.getWrapper(level)),
ServerLevelWrapper.getWrapper(level)
);
}
}
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -22,7 +22,8 @@ package com.seibel.distanthorizons.common.wrappers;
import com.seibel.distanthorizons.common.wrappers.gui.ClassicConfigGUI; import com.seibel.distanthorizons.common.wrappers.gui.ClassicConfigGUI;
import com.seibel.distanthorizons.common.wrappers.gui.LangWrapper; import com.seibel.distanthorizons.common.wrappers.gui.LangWrapper;
import com.seibel.distanthorizons.common.wrappers.level.KeyedClientLevelManager; import com.seibel.distanthorizons.common.wrappers.level.KeyedClientLevelManager;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftDedicatedServerWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui; import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
@@ -32,6 +33,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
@@ -60,7 +62,7 @@ public class DependencySetup
//@Environment(EnvType.SERVER) //@Environment(EnvType.SERVER)
public static void createServerBindings() public static void createServerBindings()
{ {
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftDedicatedServerWrapper.INSTANCE); SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftServerWrapper.INSTANCE);
} }
//@Environment(EnvType.CLIENT) //@Environment(EnvType.CLIENT)
@@ -69,6 +71,7 @@ public class DependencySetup
SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE); SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftClientWrapper.INSTANCE); SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE); SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftGLWrapper.class, MinecraftGLWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE); SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE);
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -25,7 +25,12 @@ public class DependencySetupDoneCheck
{ {
// TODO move to DependencySetup // TODO move to DependencySetup
public static boolean isDone = false; public static boolean isDone = false;
// TODO why is this here and what is its purpose? /**
* This is used so we can override some MC logic when running
* in DH's world generator.
* Specifically so we can redirect threads to run on DH threads instead
* of MC threads.
*/
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; }); public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; });
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -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));
}
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -38,24 +38,6 @@ public class VersionConstants implements IVersionConstants
} }
@Override
public int getMinimumWorldHeight()
{
return 0;
}
@Override
public int getWorldGenerationCountPerThread()
{
return 1;
}
@Override
public boolean isVanillaRenderedChunkSquare()
{
return false;
}
@Override @Override
public String getMinecraftVersion() public String getMinecraftVersion()
{ {
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -55,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
{ {
@@ -82,6 +79,27 @@ 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 @Override
@@ -104,6 +122,13 @@ public class WrapperFactory implements IWrapperFactory
@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(); }
/** /**
@@ -130,7 +155,7 @@ public class WrapperFactory implements IWrapperFactory
} }
} }
#if MC_VER <= MC_1_21 //#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
@@ -149,43 +174,22 @@ public class WrapperFactory implements IWrapperFactory
} }
// the level is needed for the DH level wrapper... // the level is needed for the DH level wrapper...
Level level = (Level) objectArray[1]; Level level = (Level) objectArray[1];
// ...the LevelReader is needed for chunk lighting
LevelReader lightSource = level;
// 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, levelWrapper);
} }
// incorrect number of parameters from the API // incorrect number of parameters from the API
else else
{ {
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,
@@ -195,16 +199,13 @@ public class WrapperFactory implements IWrapperFactory
{ {
String[] expectedClassNames; String[] expectedClassNames;
#if MC_VER <= MC_1_21 //#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);
} }
@@ -222,7 +223,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;
@@ -243,7 +244,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_21 #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));
@@ -251,9 +252,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
} }
/** /**
@@ -266,11 +264,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_21
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);
@@ -281,13 +276,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_21 //#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));
@@ -299,10 +294,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,
@@ -314,11 +306,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_21
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);
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.common.wrappers.block;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@@ -226,8 +227,10 @@ public class BiomeWrapper implements IBiomeWrapper
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome); resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2 #elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value()); resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
#else #elif MC_VER < MC_1_21_3
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value()); resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
#else
resourceLocation = registryAccess.lookupOrThrow(Registries.BIOME).getKey(this.biome.value());
#endif #endif
if (resourceLocation == null) if (resourceLocation == null)
@@ -293,7 +296,7 @@ public class BiomeWrapper implements IBiomeWrapper
ResourceLocation resourceLocation; ResourceLocation resourceLocation;
try try
{ {
#if MC_VER < MC_1_21 #if MC_VER < MC_1_21_1
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1)); resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
#else #else
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1)); resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
@@ -318,10 +321,24 @@ public class BiomeWrapper implements IBiomeWrapper
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation); Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (unwrappedBiome != null); success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome); Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#else #elif MC_VER < MC_1_21_3
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation); Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
success = (unwrappedBiome != null); success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome); Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#else
Holder<Biome> biome;
Optional<Holder.Reference<Biome>> optionalBiomeHolder = registryAccess.lookupOrThrow(Registries.BIOME).get(resourceLocation);
if (optionalBiomeHolder.isPresent())
{
Biome unwrappedBiome = optionalBiomeHolder.get().value();
success = (unwrappedBiome != null);
biome = new Holder.Direct<>(unwrappedBiome);
}
else
{
success = false;
biome = null;
}
#endif #endif
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -19,22 +19,32 @@
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;
import org.jetbrains.annotations.Nullable;
#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;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@@ -46,11 +56,11 @@ 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;
import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.core.Holder;
#endif #endif
public class BlockStateWrapper implements IBlockStateWrapper public class BlockStateWrapper implements IBlockStateWrapper
@@ -72,28 +82,34 @@ public class BlockStateWrapper implements IBlockStateWrapper
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> BROKEN_RESOURCE_LOCATIONS = new HashSet<>();
// properties // // properties //
@Nullable
public final BlockState blockState; public final BlockState blockState;
/** technically final, but since it requires a method call to generate it can't be marked as such */ /** technically final, but since it requires a method call to generate it can't be marked as such */
private String serialString; private String serialString;
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;
@@ -121,21 +137,93 @@ public class BlockStateWrapper implements IBlockStateWrapper
} }
} }
/**
* Can be faster than {@link BlockStateWrapper#fromBlockState(BlockState, ILevelWrapper)}
* in cases where the same block state is expected to be referenced multiple times.
*/
public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper, IBlockStateWrapper guess)
{
BlockState guessBlockState = (guess == null || guess.isAir()) ? null : (BlockState) guess.getWrappedMcObject();
BlockState inputBlockState = (blockState == null || blockState.isAir()) ? null : blockState;
if (guess instanceof BlockStateWrapper
&& guessBlockState == inputBlockState)
{
return (BlockStateWrapper) guess;
}
else
{
return fromBlockState(blockState, levelWrapper);
}
}
private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper) private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper)
{ {
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.
@@ -149,37 +237,104 @@ public class BlockStateWrapper implements IBlockStateWrapper
return rendererIgnoredBlocks; return rendererIgnoredBlocks;
} }
HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredBlocks = getBlockWrappers(Config.Client.Advanced.Graphics.Culling.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.Graphics.Culling.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;
} }
@@ -198,27 +353,48 @@ public class BlockStateWrapper implements IBlockStateWrapper
} }
// get block properties (default to the values used by air)
boolean canOcclude = false;
boolean propagatesSkyLightDown = true;
if (this.blockState != null)
{
canOcclude = this.blockState.canOcclude();
#if MC_VER < MC_1_21_3
propagatesSkyLightDown = this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
#else
propagatesSkyLightDown = this.blockState.propagatesSkylightDown();
#endif
}
// this method isn't perfect, but works well enough for our use case // this method isn't perfect, but works well enough for our use case
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() && !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 (propagatesSkyLightDown && !canOcclude)
{ {
opacity = FULLY_TRANSPARENT; // probably glass or some other fully transparent block
// !canOcclude is required to ignore stairs and slabs since
// propagateSkyLightDown is true for them, but they're solid and don't actually let light through
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;
} }
@@ -264,6 +440,11 @@ public class BlockStateWrapper implements IBlockStateWrapper
@Override @Override
public boolean isSolid() public boolean isSolid()
{ {
if (this.isAir())
{
return false;
}
#if MC_VER < MC_1_20_1 #if MC_VER < MC_1_20_1
return this.blockState.getMaterial().isSolid(); return this.blockState.getMaterial().isSolid();
#else #else
@@ -287,7 +468,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(); }
@@ -318,8 +511,10 @@ public class BlockStateWrapper implements IBlockStateWrapper
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock()); resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2 #elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock()); resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
#else #elif MC_VER < MC_1_21_3
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock()); resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
#else
resourceLocation = registryAccess.lookupOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
#endif #endif
@@ -380,7 +575,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
ResourceLocation resourceLocation; ResourceLocation resourceLocation;
try try
{ {
#if MC_VER < MC_1_21 #if MC_VER < MC_1_21_1
resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1)); resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
#else #else
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1)); resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
@@ -398,9 +593,8 @@ public class BlockStateWrapper implements IBlockStateWrapper
{ {
#if MC_VER > MC_1_17_1 #if MC_VER > MC_1_17_1
// use the given level if possible, otherwise try using the currently loaded one LodUtil.assertTrue(levelWrapper != null && levelWrapper.getWrappedMcObject() != null);
Level level = (levelWrapper != null ? (Level) levelWrapper.getWrappedMcObject() : null); Level level = (Level)levelWrapper.getWrappedMcObject();
level = (level == null ? Minecraft.getInstance().level : level);
#endif #endif
Block block; Block block;
@@ -409,18 +603,22 @@ public class BlockStateWrapper implements IBlockStateWrapper
#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
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation); block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
#else #elif MC_VER < MC_1_21_3
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation); block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
#else
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
Optional<Holder.Reference<Block>> optionalBlockHolder = registryAccess.lookupOrThrow(Registries.BLOCK).get(resourceLocation);
block = optionalBlockHolder.isPresent() ? optionalBlockHolder.get().value() : null;
#endif #endif
if (block == null) if (block == null)
{ {
// shouldn't normally happen, but here to make the compiler happy // shouldn't normally happen, but here to make the compiler happy
if (!BrokenResourceLocations.contains(resourceLocation)) if (!BROKEN_RESOURCE_LOCATIONS.contains(resourceLocation))
{ {
BrokenResourceLocations.add(resourceLocation); BROKEN_RESOURCE_LOCATIONS.add(resourceLocation);
LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost."); LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
} }
@@ -450,9 +648,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
if (blockStatePropertiesString != null) if (blockStatePropertiesString != null)
{ {
// we should have found a blockstate, but didn't // we should have found a blockstate, but didn't
if (!BrokenResourceLocations.contains(resourceLocation)) if (!BROKEN_RESOURCE_LOCATIONS.contains(resourceLocation))
{ {
BrokenResourceLocations.add(resourceLocation); BROKEN_RESOURCE_LOCATIONS.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 for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
} }
} }
@@ -512,11 +710,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;
} }
@@ -529,15 +727,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")
@@ -546,7 +744,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
@@ -558,11 +756,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")
@@ -572,7 +770,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
@@ -581,37 +779,37 @@ public class BlockStateWrapper implements IBlockStateWrapper
|| this.blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE || this.blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE
|| serialString.contains("deepslate") ) || serialString.contains("deepslate") )
{ {
return IrisBlockMaterial.DEEPSLATE; return EDhApiBlockMaterial.DEEPSLATE;
} }
#endif #endif
else if (this.serialString.contains("snow")) else if (this.serialString.contains("snow"))
{ {
return IrisBlockMaterial.SNOW; return EDhApiBlockMaterial.SNOW;
} }
else if (serialString.contains("sand")) else if (serialString.contains("sand"))
{ {
return IrisBlockMaterial.SAND; return EDhApiBlockMaterial.SAND;
} }
else if (serialString.contains("terracotta")) else if (serialString.contains("terracotta"))
{ {
return IrisBlockMaterial.TERRACOTTA; return EDhApiBlockMaterial.TERRACOTTA;
} }
else if (this.blockState.is(BlockTags.BASE_STONE_NETHER)) else if (this.blockState.is(BlockTags.BASE_STONE_NETHER))
{ {
return IrisBlockMaterial.NETHER_STONE; return EDhApiBlockMaterial.NETHER_STONE;
} }
else if (serialString.contains("stone") else if (serialString.contains("stone")
|| serialString.contains("ore")) || serialString.contains("ore"))
{ {
return IrisBlockMaterial.STONE; return EDhApiBlockMaterial.STONE;
} }
else if (this.blockState.getLightEmission() > 0) else if (this.blockState.getLightEmission() > 0)
{ {
return IrisBlockMaterial.ILLUMINATED; return EDhApiBlockMaterial.ILLUMINATED;
} }
else else
{ {
return IrisBlockMaterial.UNKOWN; return EDhApiBlockMaterial.UNKNOWN;
} }
} }
@@ -0,0 +1,485 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 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.argbToInt(0, 255, 255, 255);
}
else
{
// determine the average color
tempColor = ColorUtil.argbToInt(
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.argbToInt(255, 182, 0, 182))
{
//make it not render at all
tempColor = ColorUtil.argbToInt(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;
}
}
}
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -19,9 +19,15 @@
package com.seibel.distanthorizons.common.wrappers.block; package com.seibel.distanthorizons.common.wrappers.block;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
#if MC_VER < MC_1_17_1
#elif MC_VER < MC_1_21_3
#else
import com.seibel.distanthorizons.core.util.ColorUtil;
import net.minecraft.client.renderer.texture.SpriteContents;
#endif
/** /**
* For wrapping/utilizing around TextureAtlasSprite * For wrapping/utilizing around TextureAtlasSprite
* *
@@ -29,15 +35,6 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite;
*/ */
public class TextureAtlasSpriteWrapper public class TextureAtlasSpriteWrapper
{ {
/**
* This code is from Minecraft Forge
* Which is licensed under the terms of GNU Lesser General Public License
* as published by the Free Software Foundation version 2.1
* of the License.
*
* The code has been modified to use TextureAtlasSprite
*/
public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y) public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y)
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
@@ -51,13 +48,29 @@ public class TextureAtlasSpriteWrapper
y += sprite.animatedTexture.getFrameY(frameIndex) * sprite.height; y += sprite.animatedTexture.getFrameY(frameIndex) * sprite.height;
} }
return sprite.mainImage[0].getPixelRGBA(x, y); return sprite.mainImage[0].getPixelRGBA(x, y);
#else #elif MC_VER < MC_1_21_3
if (sprite.contents().animatedTexture != null) if (sprite.contents().animatedTexture != null)
{ {
x += sprite.contents().animatedTexture.getFrameX(frameIndex) * sprite.contents().width(); x += sprite.contents().animatedTexture.getFrameX(frameIndex) * sprite.contents().width();
y += sprite.contents().animatedTexture.getFrameY(frameIndex) * sprite.contents().width(); y += sprite.contents().animatedTexture.getFrameY(frameIndex) * sprite.contents().width();
} }
return sprite.contents().originalImage.getPixelRGBA(x, y); return sprite.contents().originalImage.getPixelRGBA(x, y);
#else
SpriteContents content = sprite.contents(); // don't close, otherwise MC will be corrupted and you won't be able to re-access the texture
if (content.animatedTexture != null)
{
x += content.animatedTexture.getFrameX(frameIndex) * content.width();
y += content.animatedTexture.getFrameY(frameIndex) * content.width();
}
int abgr = content.originalImage.getPixel(x, y);
// re-pack the color so we can access it normally
int a = (abgr & 0xFF000000) >>> 24;
int b = (abgr & 0x00FF0000) >>> 16;
int g = (abgr & 0x0000FF00) >>> 8;
int r = (abgr & 0x000000FF);
return ColorUtil.argbToInt(a, r, g, b);
#endif #endif
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -42,15 +42,24 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
{ {
LevelReader parent; LevelReader parent;
public TintGetterOverrideFast(LevelReader parent)
{
this.parent = parent; //=============//
} // constructor //
//=============//
public TintGetterOverrideFast(LevelReader parent) { this.parent = parent; }
//=========//
// methods //
//=========//
private Biome _getBiome(BlockPos pos) private Biome _getBiome(BlockPos pos)
{ {
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
return parent.getBiome(pos).value(); return this.parent.getBiome(pos).value();
#else #else
return parent.getBiome(pos); return parent.getBiome(pos);
#endif #endif
@@ -63,169 +72,123 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); } public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
@Override @Override
public LevelLightEngine getLightEngine() public LevelLightEngine getLightEngine() { return this.parent.getLightEngine(); }
{
return parent.getLightEngine();
}
@Override @Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return this.parent.getBrightness(lightLayer, blockPos); }
{
return parent.getBrightness(lightLayer, blockPos);
}
@Override @Override
public int getRawBrightness(BlockPos blockPos, int i) public int getRawBrightness(BlockPos blockPos, int i) { return this.parent.getRawBrightness(blockPos, i); }
{
return parent.getRawBrightness(blockPos, i);
}
@Override @Override
public boolean canSeeSky(BlockPos blockPos) public boolean canSeeSky(BlockPos blockPos) { return this.parent.canSeeSky(blockPos); }
{
return parent.canSeeSky(blockPos);
}
@Override @Override
@Nullable @Nullable
public BlockEntity getBlockEntity(BlockPos blockPos) public BlockEntity getBlockEntity(BlockPos blockPos) { return this.parent.getBlockEntity(blockPos); }
{
return parent.getBlockEntity(blockPos);
}
@Override @Override
public BlockState getBlockState(BlockPos blockPos) public BlockState getBlockState(BlockPos blockPos) { return this.parent.getBlockState(blockPos); }
{
return parent.getBlockState(blockPos);
}
@Override @Override
public FluidState getFluidState(BlockPos blockPos) public FluidState getFluidState(BlockPos blockPos) { return this.parent.getFluidState(blockPos); }
{
return parent.getFluidState(blockPos);
}
@Override @Override
public int getLightEmission(BlockPos blockPos) public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); }
{
return parent.getLightEmission(blockPos);
}
#if MC_VER < MC_1_21_3
@Override @Override
public int getMaxLightLevel() public int getMaxLightLevel() { return parent.getMaxLightLevel(); }
{ #else
return parent.getMaxLightLevel(); #endif
}
@Override @Override
public Stream<BlockState> getBlockStates(AABB aABB) public Stream<BlockState> getBlockStates(AABB aABB)
{ { return this.parent.getBlockStates(aABB); }
return parent.getBlockStates(aABB);
}
@Override @Override
public BlockHitResult clip(ClipContext clipContext) public BlockHitResult clip(ClipContext clipContext)
{ { return this.parent.clip(clipContext); }
return parent.clip(clipContext);
}
@Override @Override
@Nullable @Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState) public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
{ { return this.parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState); }
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override @Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier) public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier)
{ { return this.parent.getBlockFloorHeight(voxelShape, supplier); }
return parent.getBlockFloorHeight(voxelShape, supplier);
}
@Override @Override
public double getBlockFloorHeight(BlockPos blockPos) public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); }
{
return parent.getBlockFloorHeight(blockPos);
}
#if MC_VER < MC_1_21_3
@Override @Override
public int getMaxBuildHeight() public int getMaxBuildHeight() { return this.parent.getMaxBuildHeight(); }
{ #else
return parent.getMaxBuildHeight(); @Override
} public int getMaxY() { return this.parent.getMaxY(); }
#endif
//==============//
// post MC 1.17 //
//==============//
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
@Override @Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType) public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType)
{ { return this.parent.getBlockEntity(blockPos, blockEntityType); }
return parent.getBlockEntity(blockPos, blockEntityType);
}
@Override @Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext) public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext)
{ { return this.parent.isBlockInLine(clipBlockStateContext); }
return parent.isBlockInLine(clipBlockStateContext);
}
@Override @Override
public int getHeight() public int getHeight() { return this.parent.getHeight(); }
{
return parent.getHeight(); #if MC_VER < MC_1_21_3
} @Override
public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); }
#else
@Override
public int getMinY() { return this.parent.getMinY(); }
#endif
@Override @Override
public int getMinBuildHeight() public int getSectionsCount() { return this.parent.getSectionsCount(); }
{
return parent.getMinBuildHeight(); #if MC_VER < MC_1_21_3
} @Override
public int getMinSection() { return this.parent.getMinSection(); }
#else
@Override
public int getMinSectionY() { return BlockAndTintGetter.super.getMinSectionY(); }
#endif
#if MC_VER < MC_1_21_3
@Override
public int getMaxSection() { return this.parent.getMaxSection(); }
#else
@Override
public int getMaxSectionY() { return this.parent.getMaxSectionY(); }
#endif
@Override @Override
public int getSectionsCount() public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); }
{
return parent.getSectionsCount();
}
@Override @Override
public int getMinSection() public boolean isOutsideBuildHeight(int i) { return this.parent.isOutsideBuildHeight(i); }
{
return parent.getMinSection();
}
@Override @Override
public int getMaxSection() public int getSectionIndex(int i) { return this.parent.getSectionIndex(i); }
{
return parent.getMaxSection();
}
@Override @Override
public boolean isOutsideBuildHeight(BlockPos blockPos) public int getSectionIndexFromSectionY(int i) { return this.parent.getSectionIndexFromSectionY(i); }
{
return parent.isOutsideBuildHeight(blockPos);
}
@Override @Override
public boolean isOutsideBuildHeight(int i) public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
{
return parent.isOutsideBuildHeight(i);
}
@Override
public int getSectionIndex(int i)
{
return parent.getSectionIndex(i);
}
@Override
public int getSectionIndexFromSectionY(int i)
{
return parent.getSectionIndexFromSectionY(i);
}
@Override
public int getSectionYFromSectionIndex(int i)
{
return parent.getSectionYFromSectionIndex(i);
}
#endif #endif
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.common.wrappers.block; package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.util.LodUtil;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Cursor3D; import net.minecraft.core.Cursor3D;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@@ -44,16 +45,28 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
LevelReader parent; LevelReader parent;
public int smoothingRange; public int smoothingRange;
//=============//
// constructor //
//=============//
public TintGetterOverrideSmooth(LevelReader parent, int smoothingRange) public TintGetterOverrideSmooth(LevelReader parent, int smoothingRange)
{ {
this.parent = parent; this.parent = parent;
this.smoothingRange = smoothingRange; this.smoothingRange = smoothingRange;
} }
//=========//
// methods //
//=========//
private Biome _getBiome(BlockPos pos) private Biome _getBiome(BlockPos pos)
{ {
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
return parent.getBiome(pos).value(); return this.parent.getBiome(pos).value();
#else #else
return parent.getBiome(pos); return parent.getBiome(pos);
#endif #endif
@@ -113,8 +126,11 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
@Override @Override
public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); } public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); }
#if MC_VER < MC_1_21_3
@Override @Override
public int getMaxLightLevel() { return this.parent.getMaxLightLevel(); } public int getMaxLightLevel() { return this.parent.getMaxLightLevel(); }
#else
#endif
@Override @Override
public Stream<BlockState> getBlockStates(AABB aABB) { return this.parent.getBlockStates(aABB); } public Stream<BlockState> getBlockStates(AABB aABB) { return this.parent.getBlockStates(aABB); }
@@ -135,8 +151,13 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
@Override @Override
public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); } public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); }
#if MC_VER < MC_1_21_3
@Override @Override
public int getMaxBuildHeight() { return this.parent.getMaxBuildHeight(); } public int getMaxBuildHeight() { return this.parent.getMaxBuildHeight(); }
#else
@Override
public int getMaxY() { return this.parent.getMaxY(); }
#endif
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
@Override @Override
@@ -148,17 +169,32 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
@Override @Override
public int getHeight() { return this.parent.getHeight(); } public int getHeight() { return this.parent.getHeight(); }
#if MC_VER < MC_1_21_3
@Override @Override
public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); } public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); }
#else
@Override
public int getMinY() { return this.parent.getMinY(); }
#endif
@Override @Override
public int getSectionsCount() { return this.parent.getSectionsCount(); } public int getSectionsCount() { return this.parent.getSectionsCount(); }
#if MC_VER < MC_1_21_3
@Override @Override
public int getMinSection() { return this.parent.getMinSection(); } public int getMinSection() { return this.parent.getMinSection(); }
#else
@Override
public int getMinSectionY() { return BlockAndTintGetter.super.getMinSectionY(); }
#endif
#if MC_VER < MC_1_21_3
@Override @Override
public int getMaxSection() { return this.parent.getMaxSection(); } public int getMaxSection() { return this.parent.getMaxSection(); }
#else
@Override
public int getMaxSectionY() { return this.parent.getMaxSectionY(); }
#endif
@Override @Override
public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); } public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -132,17 +132,27 @@ public class TintWithoutLevelOverrider implements BlockAndTintGetter
} }
//==============//
// post MC 1.17 //
//==============//
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
@Override @Override
public int getHeight() public int getHeight()
{ { throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); }
throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
} #if MC_VER < MC_1_21_3
@Override @Override
public int getMinBuildHeight() public int getMinBuildHeight()
{ { throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); }
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); #else
} @Override
public int getMinY()
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelOverrider. Object is for tinting only."); }
#endif
#endif #endif
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -39,11 +39,24 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
final BiomeWrapper biome; final BiomeWrapper biome;
public int smoothingRange; public int smoothingRange;
//=============//
// constructor //
//=============//
public TintWithoutLevelSmoothOverrider(BiomeWrapper biome, int smoothingRange) public TintWithoutLevelSmoothOverrider(BiomeWrapper biome, int smoothingRange)
{ {
this.biome = biome; this.biome = biome;
this.smoothingRange = smoothingRange; this.smoothingRange = smoothingRange;
} }
//=========//
// methods //
//=========//
@Override @Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{ {
@@ -57,7 +70,7 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
return biome; return biome;
#endif #endif
} }
//
// public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver) // public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
// { // {
// int i = smoothingRange; // int i = smoothingRange;
@@ -89,44 +102,43 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
@Override @Override
public float getShade(Direction direction, boolean shade) public float getShade(Direction direction, boolean shade)
{ { throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override @Override
public LevelLightEngine getLightEngine() public LevelLightEngine getLightEngine()
{ { throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelSmoothOverrider. 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(BlockPos pos)
{ { throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelSmoothOverrider. 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 BlockState getBlockState(BlockPos pos)
{ { throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelSmoothOverrider. 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 FluidState getFluidState(BlockPos pos)
{ { throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
//==============//
// post MC 1.17 //
//==============//
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
@Override @Override
public int getHeight() public int getHeight()
{ { throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
} #if MC_VER < MC_1_21_3
@Override @Override
public int getMinBuildHeight() public int getMinBuildHeight()
{ { throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); #else
} @Override
public int getMinY()
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
#endif
#endif #endif
} }
@@ -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,412 +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 IClientLevelWrapper levelWrapper;
public final BlockState blockState;
public final LevelReader level;
public final BlockPos pos;
public ClientBlockStateCache(BlockState blockState, IClientLevelWrapper samplingLevel, DhBlockPos samplingPos)
{
this.blockState = blockState;
this.levelWrapper = samplingLevel;
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
this.pos = McObjectConverter.Convert(samplingPos);
this.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, 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;
}
}
}
@@ -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);
}
}
}
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -16,34 +16,31 @@
* You should have received a copy of the GNU Lesser General Public License * 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/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.seibel.distanthorizons.common.wrappers.chunk; 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.misc.MutableBlockPosWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
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 net.minecraft.client.multiplayer.ClientChunkCache;
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.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk; 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;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
import net.minecraft.core.QuartPos; import net.minecraft.core.QuartPos;
@@ -67,8 +64,6 @@ import net.minecraft.world.level.chunk.LevelChunkSection;
#if MC_VER >= MC_1_20_1 #if MC_VER >= MC_1_20_1
import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.core.SectionPos;
#endif #endif
#if MC_VER <= MC_1_20_4 #if MC_VER <= MC_1_20_4
@@ -84,41 +79,28 @@ public class ChunkWrapper implements IChunkWrapper
/** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */ /** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos()); private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos());
private static final ThreadLocal<MutableBlockPosWrapper> MUTABLE_BLOCK_POS_WRAPPER_REF = ThreadLocal.withInitial(() -> new MutableBlockPosWrapper());
private final ChunkAccess chunk; private final ChunkAccess chunk;
private final DhChunkPos chunkPos; private final DhChunkPos chunkPos;
private final LevelReader lightSource;
private final ILevelWrapper wrappedLevel; private final ILevelWrapper wrappedLevel;
private boolean isDhLightCorrect = false; private boolean isDhBlockLightCorrect = false;
/** only used when connected to a dedicated server */ private boolean isDhSkyLightCorrect = false;
private boolean isMcClientLightingCorrect = false;
private ChunkLightStorage blockLightStorage; private ChunkLightStorage blockLightStorage;
private ChunkLightStorage skyLightStorage; private ChunkLightStorage skyLightStorage;
private ArrayList<DhBlockPos> blockLightPosList = null; private ArrayList<DhBlockPos> blockLightPosList = null;
private boolean useDhLighting;
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; /** will be null if we are using MC heightmaps */
private final int[][] solidHeightMap;
/** /** will be null if we are using MC heightmaps */
* Due to vanilla `isClientLightReady()` not being designed for use by a non-render thread, it may return 'true' private final int[][] lightBlockingHeightMap;
* before the light engine has ticked, (right after all light changes is marked by the engine to be processed).
* To fix this, on client-only mode, we mixin-redirect the `isClientLightReady()` so that after the call, it will
* trigger a synchronous update of this flag here on all chunks that are wrapped. <br><br>
*
* Note: Using a static weak hash map to store the chunks that need to be updated, as instance of chunk wrapper
* can be duplicated, with same chunk instance. And the data stored here are all temporary, and thus will not be
* visible when a chunk is re-wrapped later. <br>
* (Also, thread safety done via a reader writer lock)
*/
private static final ConcurrentLinkedQueue<ChunkWrapper> chunksNeedingClientLightUpdating = new ConcurrentLinkedQueue<>();
@@ -126,21 +108,25 @@ public class ChunkWrapper implements IChunkWrapper
// constructor // // constructor //
//=============// //=============//
public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource, ILevelWrapper wrappedLevel) public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel)
{ {
this.chunk = chunk; this.chunk = chunk;
this.lightSource = lightSource;
this.wrappedLevel = wrappedLevel; this.wrappedLevel = wrappedLevel;
this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z); this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z);
// TODO is this the best way to differentiate between when we are generating chunks and when MC gave us a chunk? // use DH heightmaps if requested
boolean isDhGeneratedChunk = (this.lightSource.getClass() == DhLitWorldGenRegion.class); if (Config.Common.LodBuilding.recalculateChunkHeightmaps.get())
// MC loaded chunks should get their lighting from MC, DH generated chunks should get their lighting from DH {
this.useDhLighting = isDhGeneratedChunk; this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
// FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the relative position validator
this.recalculateDhHeightMapsIfNeeded();
chunksNeedingClientLightUpdating.add(this); }
else
{
this.solidHeightMap = null;
this.lightBlockingHeightMap = null;
}
} }
@@ -161,19 +147,29 @@ public class ChunkWrapper implements IChunkWrapper
} }
@Override @Override
public int getMinBuildHeight() { return getMinBuildHeight(this.chunk); } public int getInclusiveMinBuildHeight() { return getInclusiveMinBuildHeight(this.chunk); }
public static int getMinBuildHeight(ChunkAccess chunk) public static int getInclusiveMinBuildHeight(ChunkAccess chunk)
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
return 0; return 0;
#else #elif MC_VER < MC_1_21_3
return chunk.getMinBuildHeight(); return chunk.getMinBuildHeight();
#else
return chunk.getMinY();
#endif #endif
} }
@Override @Override
public int getMaxBuildHeight() { return getMaxBuildHeight(this.chunk); } public int getExclusiveMaxBuildHeight() { return getExclusiveMaxBuildHeight(this.chunk); }
public static int getMaxBuildHeight(ChunkAccess chunk) { return chunk.getMaxBuildHeight(); } public static int getExclusiveMaxBuildHeight(ChunkAccess chunk)
{
#if MC_VER < MC_1_21_3
return chunk.getMaxBuildHeight();
#else
// +1 since Minecraft made the max value inclusive
return chunk.getMaxY() + 1;
#endif
}
@Override @Override
public int getMinNonEmptyHeight() public int getMinNonEmptyHeight()
@@ -185,7 +181,7 @@ public class ChunkWrapper implements IChunkWrapper
// default if every section is empty or missing // default if every section is empty or missing
this.minNonEmptyHeight = this.getMinBuildHeight(); this.minNonEmptyHeight = this.getInclusiveMinBuildHeight();
// determine the lowest empty section (bottom up) // determine the lowest empty section (bottom up)
LevelChunkSection[] sections = this.chunk.getSections(); LevelChunkSection[] sections = this.chunk.getSections();
@@ -217,12 +213,15 @@ public class ChunkWrapper implements IChunkWrapper
// default if every section is empty or missing // default if every section is empty or missing
this.maxNonEmptyHeight = this.getMaxBuildHeight(); this.maxNonEmptyHeight = this.getExclusiveMaxBuildHeight();
// determine the highest empty section (top down) // determine the highest empty section (top down)
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;
@@ -230,7 +229,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;
} }
} }
@@ -247,22 +246,92 @@ 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.getInclusiveMinBuildHeight(); }
/** Will only run if the config says the MC heightmaps shouldn't be trusted. */
public void recalculateDhHeightMapsIfNeeded()
{ {
// convert from an index to a block coordinate // re-calculate the min/max heights for consistency (during world gen these may be wrong)
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 this.minNonEmptyHeight = Integer.MIN_VALUE;
return this.chunk.getSections()[index].bottomBlockY(); this.maxNonEmptyHeight = Integer.MAX_VALUE;
#else
return this.chunk.getSectionYFromSectionIndex(index) * 16;
#endif // recalculate heightmaps if needed
if (this.solidHeightMap != null)
{
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
{
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
{
int minInclusiveBuildHeight = this.getMinNonEmptyHeight();
// if no blocks are found the height map will be at the bottom of the world
int solidHeight = minInclusiveBuildHeight;
int lightBlockingHeight = minInclusiveBuildHeight;
int y = this.getMaxNonEmptyHeight(); //this.getExclusiveMaxBuildHeight();
IBlockStateWrapper block = this.getBlockState(x, y, z);
while (// go down until we reach the minimum build height
y > minInclusiveBuildHeight
// keep going until we find both height map values
&& (solidHeight == minInclusiveBuildHeight || lightBlockingHeight == minInclusiveBuildHeight))
{
// is this block solid?
if (solidHeight == minInclusiveBuildHeight
&& block.isSolid())
{
solidHeight = y;
}
// is this block light blocking?
if (lightBlockingHeight == minInclusiveBuildHeight
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
{
lightBlockingHeight = y;
}
// get the next block down
y--;
block = this.getBlockState(x, y, z);
}
this.solidHeightMap[x][z] = solidHeight;
this.lightBlockingHeightMap[x][z] = lightBlockingHeight;
}
}
}
} }
@Override
public int getSolidHeightMapValue(int xRel, int zRel)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(xRel, zRel);
// will be null if we want to use MC heightmaps
if (this.solidHeightMap == null)
{
return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel);
}
else
{
return this.solidHeightMap[xRel][zRel];
}
}
@Override @Override
public int getSolidHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel); } public int getLightBlockingHeightMapValue(int xRel, int zRel)
{
@Override this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(xRel, zRel);
public int getLightBlockingHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel); }
if (this.lightBlockingHeightMap == null)
{
return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel);
}
else
{
return this.lightBlockingHeightMap[xRel][zRel];
}
}
@Override @Override
@@ -299,18 +368,134 @@ public class ChunkWrapper implements IChunkWrapper
blockPos.setY(relY); blockPos.setY(relY);
blockPos.setZ(relZ); blockPos.setZ(relZ);
// TODO copy into pooled array, this isn't thread safe and can cause MC to throw errors if the chunk is loaded
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel); return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
} }
@Override
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ, IMutableBlockPosWrapper mcBlockPos, IBlockStateWrapper guess)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
BlockPos.MutableBlockPos pos = (BlockPos.MutableBlockPos)mcBlockPos.getWrappedMcObject();
pos.setX(relX);
pos.setY(relY);
pos.setZ(relZ);
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(pos), this.wrappedLevel, guess);
}
/**
// Commented out experimental LevelChunkSection cloning logic to fix extremely rare concurrency modification issue
// James has only ever seen a report relating to LevelSection concurrent modification once,
// the issue can cause DH lighting/LOD building to fail due to the chunk being modified on the server.
// James has only heard of this issue once, so it isn't a high priority issue.
// And from James' quick look at a few different MC versions it appears the LevelChunkSection object changes quite drastically between MC versions,
// meaning any cloning logic would have to either be a new wrapper or very MC version dependent, either way a lot of additional work.
// Due to the large time cost and extremely rare nature of the issue, this logic is commented out unless this issue pops up again in the future.
// instance variable to hold the cloned sections
private final LevelChunkSection[] levelChunkSections;
// new constructor logic to clone the sections
public constructor(...)
{
// other constructor logic //
LevelChunkSection[] sections = this.chunk.getSections();
this.levelChunkSections = new LevelChunkSection[sections.length];
for (int i = 0; i < sections.length; i++)
{
LevelChunkSection section = sections[i];
if (section != null)
{
// TODO implement section cloning for older MC versions, only 1.21.4 MC (and maybe other semi recent versions) have a clean way to handle this
// TODO we probably want a wrapper object instead
#if MC_VER < MC_1_21_4
this.levelChunkSections[i] = section;
#else
this.levelChunkSections[i] = section.copy();
#endif
}
}
}
// replacement getters
@Override
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
return this.getBlockStateInternal(relX, relY, relZ, null);
}
@Override
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ, IMutableBlockPosWrapper mcBlockPos, IBlockStateWrapper guess)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
return this.getBlockStateInternal(relX, relY, relZ, guess);
}
// internal getter logic
private IBlockStateWrapper getBlockStateInternal(int relX, int y, int relZ, @Nullable IBlockStateWrapper guess)
{
try
{
// attempt to get the section for this position
int i = (y - this.getInclusiveMinBuildHeight()) / 16;
if (i >= 0 && i < this.levelChunkSections.length)
{
LevelChunkSection section = this.levelChunkSections[i];
if (!section.hasOnlyAir())
{
if (guess != null)
{
return BlockStateWrapper.fromBlockState(section.getBlockState(relX & 15, y & 15, relZ & 15), this.wrappedLevel, guess);
}
else
{
return BlockStateWrapper.fromBlockState(section.getBlockState(relX & 15, y & 15, relZ & 15), this.wrappedLevel);
}
}
}
return BlockStateWrapper.AIR;
}
catch (Exception e)
{
return BlockStateWrapper.AIR;
}
}
*/
@Override
public IMutableBlockPosWrapper getMutableBlockPosWrapper() { return MUTABLE_BLOCK_POS_WRAPPER_REF.get(); }
@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 void trySetStatus(ChunkStatus status) { trySetStatus(this.getChunk(), status); }
/** does nothing if the chunk object doesn't support setting it's status */
public static void trySetStatus(ChunkAccess chunk, ChunkStatus status)
{
if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(status);
#else
((ProtoChunk) chunk).setPersistedStatus(status);
#endif
}
}
public ChunkStatus getStatus() { return getStatus(this.getChunk()); } public ChunkStatus getStatus() { return getStatus(this.getChunk()); }
public static ChunkStatus getStatus(ChunkAccess chunk) public static ChunkStatus getStatus(ChunkAccess chunk)
{ {
#if MC_VER < MC_1_21 #if MC_VER < MC_1_21_1
return chunk.getStatus(); return chunk.getStatus();
#else #else
return chunk.getPersistedStatus(); return chunk.getPersistedStatus();
@@ -332,45 +517,15 @@ public class ChunkWrapper implements IChunkWrapper
// lighting // // lighting //
//==========// //==========//
@Override @Override
public void setIsDhLightCorrect(boolean isDhLightCorrect) { this.isDhLightCorrect = isDhLightCorrect; } public void setIsDhSkyLightCorrect(boolean isDhLightCorrect) { this.isDhSkyLightCorrect = isDhLightCorrect; }
@Override
public void setIsDhBlockLightCorrect(boolean isDhLightCorrect) { this.isDhBlockLightCorrect = isDhLightCorrect; }
@Override @Override
public void setUseDhLighting(boolean useDhLighting) { this.useDhLighting = useDhLighting; } public boolean isDhBlockLightingCorrect() { return this.isDhBlockLightCorrect; }
@Override @Override
public boolean isLightCorrect() public boolean isDhSkyLightCorrect() { return this.isDhSkyLightCorrect; }
{
if (this.useDhLighting)
{
return this.isDhLightCorrect;
}
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
return false; // MC's lighting engine doesn't work consistently enough to trust for 1.16 or 1.17
#else
if (this.chunk instanceof LevelChunk)
{
LevelChunk levelChunk = (LevelChunk) this.chunk;
if (levelChunk.getLevel() instanceof ClientLevel)
{
// connected to a server
return this.isMcClientLightingCorrect;
}
else
{
// in a single player world
return this.chunk.isLightCorrect() && levelChunk.loaded;
}
}
else
{
// called when in a single player world and the chunk is a proto chunk (in world gen, and not active)
return this.chunk.isLightCorrect();
}
#endif
}
@Override @Override
@@ -395,6 +550,8 @@ public class ChunkWrapper implements IChunkWrapper
return this.blockLightStorage; return this.blockLightStorage;
} }
public void setBlockLightStorage(ChunkLightStorage lightStorage) { this.blockLightStorage = lightStorage; } public void setBlockLightStorage(ChunkLightStorage lightStorage) { this.blockLightStorage = lightStorage; }
@Override
public void clearDhBlockLighting() { this.getBlockLightStorage().clear(); }
@Override @Override
@@ -409,6 +566,8 @@ 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()
{ {
@@ -421,50 +580,12 @@ public class ChunkWrapper implements IChunkWrapper
public void setSkyLightStorage(ChunkLightStorage lightStorage) { this.skyLightStorage = lightStorage; } public void setSkyLightStorage(ChunkLightStorage lightStorage) { this.skyLightStorage = lightStorage; }
@Override
public int getBlockLight(int relX, int y, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
// use the full lighting engine when the chunks are within render distance or the config requests it
if (this.useDhLighting)
{
// DH lighting method
return this.getBlockLightStorage().get(relX, y, relZ);
}
else
{
// note: this returns 0 if the chunk is unload
// MC lighting method
return this.lightSource.getBrightness(LightLayer.BLOCK, new BlockPos(relX + this.getMinBlockX(), y, relZ + this.getMinBlockZ()));
}
}
@Override
public int getSkyLight(int relX, int y, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
// use the full lighting engine when the chunks are within render distance or the config requests it
if (this.useDhLighting)
{
// DH lighting method
return this.getSkyLightStorage().get(relX, y, relZ);
}
else
{
// MC lighting method
return this.lightSource.getBrightness(LightLayer.SKY, new BlockPos(relX + this.getMinBlockX(), y, relZ + this.getMinBlockZ()));
}
}
/** /**
* FIXME synchronized is necessary for a rare issue where this method is called from two separate threads at the same time * FIXME synchronized is necessary for a rare issue where this method is called from two separate threads at the same time
* before the list has finished populating. * 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)
@@ -480,7 +601,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
} }
@@ -488,95 +615,12 @@ public class ChunkWrapper implements IChunkWrapper
return this.blockLightPosList; return this.blockLightPosList;
} }
public static void syncedUpdateClientLightStatus()
{
#if MC_VER < MC_1_18_2
// TODO: Check what to do in 1.18.1 and older
// since we don't currently handle this list,
// clear it to prevent memory leaks
chunksNeedingClientLightUpdating.clear();
#else
// update the chunks client lighting
ChunkWrapper chunkWrapper = chunksNeedingClientLightUpdating.poll();
while (chunkWrapper != null)
{
chunkWrapper.updateIsClientLightingCorrect();
chunkWrapper = chunksNeedingClientLightUpdating.poll();
}
#endif
}
/** Should be called after client light updates are triggered. */
private void updateIsClientLightingCorrect()
{
if (this.chunk instanceof LevelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel)
{
LevelChunk levelChunk = (LevelChunk) this.chunk;
ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource();
this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null &&
#if MC_VER <= MC_1_17_1
levelChunk.isLightCorrect();
#elif MC_VER < MC_1_20_1
levelChunk.isClientLightReady();
#else
checkLightSectionsOnChunk(levelChunk, levelChunk.getLevel().getLightEngine());
#endif
}
}
#if MC_VER >= MC_1_20_1
private static boolean checkLightSectionsOnChunk(LevelChunk chunk, LevelLightEngine engine)
{
LevelChunkSection[] sections = chunk.getSections();
int minY = chunk.getMinSection();
int maxY = chunk.getMaxSection();
for (int y = minY; y < maxY; ++y)
{
LevelChunkSection section = sections[chunk.getSectionIndexFromSectionY(y)];
if (section.hasOnlyAir()) continue;
if (!engine.lightOnInSection(SectionPos.of(chunk.getPos(), y)))
{
return false;
}
}
return true;
}
#endif
//===============// //===============//
// other methods // // other methods //
//===============// //===============//
@Override
public boolean doNearbyChunksExist()
{
if (this.lightSource instanceof DhLitWorldGenRegion)
{
return true;
}
for (int dx = -1; dx <= 1; dx++)
{
for (int dz = -1; dz <= 1; dz++)
{
if (dx == 0 && dz == 0)
{
continue;
}
else if (this.lightSource.getChunk(dx + this.chunk.getPos().x, dz + this.chunk.getPos().z, ChunkStatus.BIOMES, false) == null)
{
return false;
}
}
}
return true;
}
@Override @Override
public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; } public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; }
@@ -16,7 +16,6 @@ import java.util.regex.Pattern;
// Logger (for debug stuff) // Logger (for debug stuff)
import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui; import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui;
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
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.types.*; import com.seibel.distanthorizons.core.config.types.*;
@@ -119,14 +118,18 @@ public class ClassicConfigGUI
*/ */
private static void textField(AbstractConfigType info, Function<String, Number> func, Pattern pattern, boolean cast) private static void textField(AbstractConfigType info, Function<String, Number> func, Pattern pattern, boolean cast)
{ {
boolean isNumber = pattern != null;
((EntryInfo) info.guiValue).widget = (BiFunction<EditBox, Button, Predicate<String>>) (editBox, button) -> stringValue -> ((EntryInfo) info.guiValue).widget = (BiFunction<EditBox, Button, Predicate<String>>) (editBox, button) -> stringValue ->
{ {
boolean isNumber = (pattern != null);
stringValue = stringValue.trim(); stringValue = stringValue.trim();
if (!(stringValue.isEmpty() || !isNumber || pattern.matcher(stringValue).matches())) if (!(stringValue.isEmpty() || !isNumber || pattern.matcher(stringValue).matches()))
{
return false; return false;
}
Number value = 0;
Number value = info.typeIsFloatingPointNumber() ? 0.0 : 0; // different default values are needed so implicit casting works correctly (if not done casting from 0 (an int) to a double will cause an exception)
((EntryInfo) info.guiValue).error = null; ((EntryInfo) info.guiValue).error = null;
if (isNumber && !stringValue.isEmpty() && !stringValue.equals("-") && !stringValue.equals(".")) if (isNumber && !stringValue.isEmpty() && !stringValue.equals("-") && !stringValue.equals("."))
{ {
@@ -143,34 +146,41 @@ public class ClassicConfigGUI
switch (isValid) switch (isValid)
{ {
case 0: case 0:
((EntryInfo) info.guiValue).error = null; break; ((EntryInfo) info.guiValue).error = null;
break;
case -1: case -1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry) info).getMin())); break; ((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry) info).getMin()));
break;
case 1: case 1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry) info).getMax())); break; ((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry) info).getMax()));
break;
case 2: case 2:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid")); break; ((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid"));
break;
} }
} }
((EntryInfo) info.guiValue).tempValue = stringValue; ((EntryInfo) info.guiValue).tempValue = stringValue;
editBox.setTextColor(((ConfigEntry) info).isValid(value) == 0 ? 0xFFFFFFFF : 0xFFFF7777); editBox.setTextColor(((ConfigEntry) info).isValid(value) == 0 ? 0xFFFFFFFF : 0xFFFF7777); // white and red
// 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;
}; };
@@ -256,7 +266,7 @@ public class ClassicConfigGUI
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
0, 0,
#if MC_VER < MC_1_21 #if MC_VER < MC_1_21_1
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else #else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"), ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
@@ -307,10 +317,13 @@ public class ClassicConfigGUI
} }
catch (Exception e) catch (Exception e)
{ {
System.out.println("ERROR: Failed to show [" + info.getNameWCategory() + "]"); String message = "ERROR: Failed to show [\" + info.getNameWCategory() + \"], error: ["+e.getMessage()+"]";
if (info.get() != null) if (info.get() != null)
System.out.print(" with the value [" + info.get() + "] with type [" + info.getType() + "]"); {
e.printStackTrace(); message += " with the value [" + info.get() + "] with type [" + info.getType() + "]";
}
LOGGER.error(message, e);
} }
} }
@@ -384,9 +397,9 @@ public class ClassicConfigGUI
this.list.addButton(null, null, null, name); this.list.addButton(null, null, null, name);
return; return;
} }
if (ConfigLinkedEntry.class.isAssignableFrom(info.getClass())) if (ConfigUiLinkedEntry.class.isAssignableFrom(info.getClass()))
{ {
this.addMenuItem(((ConfigLinkedEntry) info).get()); this.addMenuItem(((ConfigUiLinkedEntry) info).get());
return; return;
} }
@@ -435,15 +448,17 @@ public class ClassicConfigGUI
} }
// A quick fix for tooltips on linked entries // A quick fix for tooltips on linked entries
AbstractConfigType newInfo = ConfigLinkedEntry.class.isAssignableFrom(info.getClass()) ? AbstractConfigType newInfo = ConfigUiLinkedEntry.class.isAssignableFrom(info.getClass()) ?
((ConfigLinkedEntry) info).get() : ((ConfigUiLinkedEntry) info).get() :
info; info;
Component name = Translatable(this.translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.getName()); Component name = Translatable(this.translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.getName());
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<>();
@@ -467,47 +482,47 @@ public class ClassicConfigGUI
private static void initEntry(AbstractConfigType info, String translationPrefix) private static void initEntry(AbstractConfigType configType, String translationPrefix)
{ {
info.guiValue = new EntryInfo(); configType.guiValue = new EntryInfo();
Class<?> fieldClass = info.getType(); Class<?> fieldClass = configType.getType();
if (ConfigEntry.class.isAssignableFrom(info.getClass())) if (ConfigEntry.class.isAssignableFrom(configType.getClass()))
{ {
if (fieldClass == Integer.class) if (fieldClass == Integer.class)
{ {
// For int // For int
textField(info, Integer::parseInt, INTEGER_ONLY_REGEX, true); textField(configType, Integer::parseInt, INTEGER_ONLY_REGEX, true);
} }
else if (fieldClass == Double.class) else if (fieldClass == Double.class)
{ {
// For double // For double
textField(info, Double::parseDouble, DECIMAL_ONLY_REGEX, false); textField(configType, Double::parseDouble, DECIMAL_ONLY_REGEX, false);
} }
else if (fieldClass == String.class || fieldClass == List.class) else if (fieldClass == String.class || fieldClass == List.class)
{ {
// For string or list // For string or list
textField(info, String::length, null, true); textField(configType, String::length, null, true);
} }
else if (fieldClass == Boolean.class) else if (fieldClass == Boolean.class)
{ {
// For boolean // For boolean
Function<Object, Component> func = value -> Translatable("distanthorizons.general."+((Boolean) value ? "true" : "false")).withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED); Function<Object, Component> func = value -> Translatable("distanthorizons.general."+((Boolean) value ? "true" : "false")).withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
((EntryInfo) info.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> { ((EntryInfo) configType.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
((ConfigEntry) info).uiSetWithoutSaving(!(Boolean) info.get()); ((ConfigEntry) configType).uiSetWithoutSaving(!(Boolean) configType.get());
button.setMessage(func.apply(info.get())); button.setMessage(func.apply(configType.get()));
}, func); }, func);
} }
else if (fieldClass.isEnum()) else if (fieldClass.isEnum())
{ {
// For enum // For enum
List<?> values = Arrays.asList(info.getType().getEnumConstants()); List<?> values = Arrays.asList(configType.getType().getEnumConstants());
Function<Object, Component> func = value -> Translatable(translationPrefix + "enum." + fieldClass.getSimpleName() + "." + info.get().toString()); Function<Object, Component> func = value -> Translatable(translationPrefix + "enum." + fieldClass.getSimpleName() + "." + configType.get().toString());
((EntryInfo) info.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> { ((EntryInfo) configType.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
// get the currently selected enum and enum index // get the currently selected enum and enum index
int startingIndex = values.indexOf(info.get()); int startingIndex = values.indexOf(configType.get());
Enum<?> enumValue = (Enum<?>) values.get(startingIndex); Enum<?> enumValue = (Enum<?>) values.get(startingIndex);
// search for the next enum that is selectable // search for the next enum that is selectable
@@ -535,12 +550,12 @@ public class ClassicConfigGUI
} }
((ConfigEntry<Enum<?>>) info).uiSetWithoutSaving(enumValue); ((ConfigEntry<Enum<?>>) configType).uiSetWithoutSaving(enumValue);
button.setMessage(func.apply(info.get())); button.setMessage(func.apply(configType.get()));
}, func); }, func);
} }
} }
else if (ConfigCategory.class.isAssignableFrom(info.getClass())) else if (ConfigCategory.class.isAssignableFrom(configType.getClass()))
{ {
// if (!info.info.getName().equals("")) // if (!info.info.getName().equals(""))
// info.name = new TranslatableComponent(info.info.getName()); // info.name = new TranslatableComponent(info.info.getName());
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -19,31 +19,37 @@
package com.seibel.distanthorizons.common.wrappers.gui; package com.seibel.distanthorizons.common.wrappers.gui;
/**
* Creates a button with a texture on it (and a background) that works with all mc versions
*
* @author coolGi
* @version 2023-10-03
*/
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.gui.components.Button;
#endif
#if MC_VER < MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
#else
import net.minecraft.client.gui.GuiGraphics;
#endif #endif
#if MC_VER < MC_1_17_1
import net.minecraft.client.gui.components.ImageButton;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
#elif MC_VER < MC_1_20_1
import net.minecraft.client.gui.components.ImageButton;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.renderer.GameRenderer;
#elif MC_VER < MC_1_20_2
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.client.gui.GuiGraphics;
#else
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.RenderType;
#endif
/**
* Creates a button with a texture on it (and a background) that works with all mc versions
*
* @author coolGi
* @version 2023-10-03
*/
#if MC_VER < MC_1_20_2 #if MC_VER < MC_1_20_2
public class TexturedButtonWidget extends ImageButton public class TexturedButtonWidget extends ImageButton
#else #else
@@ -57,20 +63,21 @@ public class TexturedButtonWidget extends Button
private final int v; private final int v;
private final int hoveredVOffset; private final int hoveredVOffset;
private final ResourceLocation texture; private final ResourceLocation textureResourceLocation;
private final int textureWidth; private final int textureWidth;
private final int textureHeight; private final int textureHeight;
#endif #endif
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, Component text) { public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text)
this(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text, true); {
this(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text, true);
} }
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground) public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground)
{ {
#if MC_VER < MC_1_20_2 #if MC_VER < MC_1_20_2
super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text); super(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text);
#else #else
// We don't pass on the text option as otherwise it will render (we normally pass it for narration) // We don't pass on the text option as otherwise it will render (we normally pass it for narration)
// TODO: Find a fix for it // TODO: Find a fix for it
@@ -80,7 +87,7 @@ public class TexturedButtonWidget extends Button
this.v = v; this.v = v;
this.hoveredVOffset = hoveredVOffset; this.hoveredVOffset = hoveredVOffset;
this.texture = texture; this.textureResourceLocation = textureResourceLocation;
this.textureWidth = textureWidth; this.textureWidth = textureWidth;
this.textureHeight = textureHeight; this.textureHeight = textureHeight;
@@ -92,7 +99,8 @@ public class TexturedButtonWidget extends Button
#if MC_VER < MC_1_20_2 #if MC_VER < MC_1_20_2
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
@Override @Override
public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta) { public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta)
{
if (this.renderBackground) // Renders the background of the button if (this.renderBackground) // Renders the background of the button
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
@@ -119,6 +127,7 @@ public class TexturedButtonWidget extends Button
super.renderButton(matrices, mouseX, mouseY, delta); super.renderButton(matrices, mouseX, mouseY, delta);
} }
#else #else
#if MC_VER < MC_1_20_1 #if MC_VER < MC_1_20_1
@Override @Override
@@ -154,24 +163,49 @@ public class TexturedButtonWidget extends Button
super.renderWidget(matrices, mouseX, mouseY, delta); super.renderWidget(matrices, mouseX, mouseY, delta);
} }
#endif #endif
#else #else
@Override @Override
public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta) public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta)
{ {
if (this.renderBackground) if (this.renderBackground)
{ {
//RenderSystem.enableBlend(); #if MC_VER < MC_1_21_3
//RenderSystem.enableDepthTest();
matrices.blitSprite(SPRITES.get(this.active, this.isHoveredOrFocused()), this.getX(), this.getY(), this.getWidth(), this.getHeight()); matrices.blitSprite(SPRITES.get(this.active, this.isHoveredOrFocused()), this.getX(), this.getY(), this.getWidth(), this.getHeight());
#else
matrices.blitSprite(
RenderType::guiTextured,
SPRITES.get(this.active, this.isHoveredOrFocused()),
this.getX(), this.getY(),
this.getWidth(), this.getHeight());
#endif
} }
// Renders the sprite // Renders the sprite
int i = 0; int i = 0;
if (!this.active) i = 2; if (!this.active)
else if (this.isHovered) i = 1; {
i = 2;
}
else if (this.isHovered)
{
i = 1;
}
matrices.blit(this.texture, this.getX(), this.getY(), this.u, this.v + (this.hoveredVOffset * i), this.width, this.height, this.textureWidth, this.textureHeight); #if MC_VER < MC_1_21_3
matrices.blit(this.textureResourceLocation, this.getX(), this.getY(), this.u, this.v + (this.hoveredVOffset * i), this.width, this.height, this.textureWidth, this.textureHeight);
#else
matrices.blit(
RenderType::guiTextured,
this.textureResourceLocation,
this.getX(), this.getY(),
this.u, this.v + (this.hoveredVOffset * i),
this.width, this.height,
this.textureWidth, this.textureHeight);
#endif
} }
#endif #endif
} }
@@ -1,8 +1,8 @@
package com.seibel.distanthorizons.common.wrappers.gui.updater; package com.seibel.distanthorizons.common.wrappers.gui.updater;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen; import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
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.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.jar.installer.MarkdownFormatter; import com.seibel.distanthorizons.core.jar.installer.MarkdownFormatter;
@@ -14,6 +14,7 @@ import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import org.apache.logging.log4j.Logger;
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.narration.NarratableEntry;
@@ -21,6 +22,7 @@ import net.minecraft.client.gui.narration.NarratableEntry;
#if MC_VER < MC_1_20_1 #if MC_VER < MC_1_20_1
import net.minecraft.client.gui.GuiComponent; import net.minecraft.client.gui.GuiComponent;
import com.mojang.blaze3d.vertex.PoseStack;
#else #else
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
#endif #endif
@@ -39,6 +41,9 @@ import java.util.*;
// TODO: Make this // TODO: Make this
public class ChangelogScreen extends DhScreen public class ChangelogScreen extends DhScreen
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private Screen parent; private Screen parent;
private String versionID; private String versionID;
private List<String> changelog; private List<String> changelog;
@@ -51,24 +56,32 @@ public class ChangelogScreen extends DhScreen
this(parent, null); this(parent, null);
if (!ModrinthGetter.initted) // Make sure the modrinth stuff is initted if (!ModrinthGetter.initted) // Make sure the modrinth stuff is initted
{
ModrinthGetter.init(); ModrinthGetter.init();
}
if (!ModrinthGetter.initted) // If its not initted, then this isnt usable if (!ModrinthGetter.initted) // If its not initted, then this isnt usable
{
return; return;
}
if (!ModrinthGetter.mcVersions.contains(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion())) if (!ModrinthGetter.mcVersions.contains(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()))
{
return; return;
}
String versionID = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()); String versionID = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
if (versionID == null) if (versionID == null)
{
return; return;
}
try try
{ {
setupChangelog(versionID); this.setupChangelog(versionID);
usable = true; this.usable = true;
} }
catch (Exception e) catch (Exception e)
{ {
e.printStackTrace(); LOGGER.error("failed to setup changelog, error: ["+e.getMessage()+"].", e);
} }
} }
@@ -80,11 +93,13 @@ public class ChangelogScreen extends DhScreen
if (versionID == null) if (versionID == null)
{
return; return;
}
try try
{ {
setupChangelog(versionID); this.setupChangelog(versionID);
usable = true; this.usable = true;
} }
catch (Exception e) catch (Exception e)
{ {
@@ -101,11 +116,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)
{ {
@@ -123,8 +144,10 @@ public class ChangelogScreen extends DhScreen
protected void init() protected void init()
{ {
super.init(); super.init();
if (!usable) if (!this.usable)
{
return; return;
}
this.addBtn( // Close this.addBtn( // Close
@@ -135,9 +158,9 @@ public class ChangelogScreen extends DhScreen
this.changelogArea = new TextArea(this.minecraft, this.width * 2, this.height, 32, 32, 10); this.changelogArea = new TextArea(this.minecraft, this.width * 2, this.height, 32, 32, 10);
for (int i = 0; i < changelog.size(); i++) for (int i = 0; i < this.changelog.size(); i++)
{ {
this.changelogArea.addButton(TextOrLiteral(changelog.get(i))); this.changelogArea.addButton(TextOrLiteral(this.changelog.get(i)));
// drawString(matrices, this.font, changelog.get(i), this.width / 2 - 175, this.height / 2 - 100 + i*10, 0xFFFFFF); // drawString(matrices, this.font, changelog.get(i), this.width / 2 - 175, this.height / 2 - 100 + i*10, 0xFFFFFF);
} }
@@ -155,29 +178,41 @@ public class ChangelogScreen extends DhScreen
#else #else
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#endif #endif
if (!usable) if (!this.usable)
{
return; return;
}
int maxScroll;
#if MC_VER <= MC_1_21_3
maxScroll = this.changelogArea.getMaxScroll();
#else
maxScroll = this.changelogArea.maxScrollAmount();
#endif
// Set the scroll position to the mouse height relative to the screen // Set the scroll position to the mouse height relative to the screen
// This is a bit of a hack as we cannot scroll on this area // This is a bit of a hack as we cannot scroll on this area
double scrollAmount = ((double) mouseY) / ((double) this.height) * 1.1 * this.changelogArea.getMaxScroll(); double scrollAmount = ((double) mouseY) / ((double) this.height) * 1.1 * maxScroll;
#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
this.changelogArea.setScrollAmount(scrollAmount); this.changelogArea.setScrollAmount(scrollAmount);
#else #elif MC_VER <= MC_1_21_3
this.changelogArea.scrollAmount = scrollAmount; this.changelogArea.scrollAmount = scrollAmount;
#else
this.changelogArea.setScrollAmount(scrollAmount);
#endif #endif
// render order matters, otherwise on 1.20.6+ the blurred background will render on top of the text // 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 this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
DhDrawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title this.DhDrawCenteredString(matrices, this.font, this.title, this.width / 2, 15, 0xFFFFFF); // Render title
} }
@Override @Override
public void onClose() public void onClose()
{ {
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Goto the parent screen
} }
public static class TextArea extends ContainerObjectSelectionList<ButtonEntry> public static class TextArea extends ContainerObjectSelectionList<ButtonEntry>
@@ -192,7 +227,7 @@ public class ChangelogScreen extends DhScreen
super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing); super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing);
#endif #endif
this.centerListVertically = false; this.centerListVertically = false;
textRenderer = minecraftClient.font; this.textRenderer = minecraftClient.font;
} }
public void addButton(Component text) public void addButton(Component text)
@@ -234,20 +269,20 @@ public class ChangelogScreen extends DhScreen
@Override @Override
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
{ {
matrices.drawString(textRenderer, text, 12, y + 5, 0xFFFFFF); matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF);
} }
#endif #endif
@Override @Override
public List<? extends GuiEventListener> children() public List<? extends GuiEventListener> children()
{ {
return children; return this.children;
} }
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
@Override @Override
public List<? extends NarratableEntry> narratables() public List<? extends NarratableEntry> narratables()
{ {
return children; return this.children;
} }
#endif #endif
} }
@@ -4,6 +4,7 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen; import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget; import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
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.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter; import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
@@ -15,6 +16,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
#endif #endif
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import org.apache.logging.log4j.Logger;
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*; import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
@@ -29,6 +31,9 @@ import java.util.*;
// and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192 // and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192
public class UpdateModScreen extends DhScreen public class UpdateModScreen extends DhScreen
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private Screen parent; private Screen parent;
private String newVersionID; private String newVersionID;
@@ -36,7 +41,7 @@ public class UpdateModScreen extends DhScreen
private String nextVer; private String nextVer;
public UpdateModScreen(Screen parent, String newVersionID) public UpdateModScreen(Screen parent, String newVersionID) throws IllegalArgumentException
{ {
super(Translatable(ModInfo.ID + ".updater.title")); super(Translatable(ModInfo.ID + ".updater.title"));
this.parent = parent; this.parent = parent;
@@ -54,6 +59,13 @@ public class UpdateModScreen extends DhScreen
this.currentVer = ModJarInfo.Git_Commit.substring(0,7); this.currentVer = ModJarInfo.Git_Commit.substring(0,7);
this.nextVer = this.newVersionID.substring(0,7); this.nextVer = this.newVersionID.substring(0,7);
} }
// done to prevent trying to update to "null"
// (this can happen if no versions are available to check/download from modrinth/gitlab)
if (this.nextVer == null)
{
throw new IllegalArgumentException("No new version found with the ID ["+newVersionID+"].");
}
} }
@Override @Override
@@ -69,19 +81,19 @@ public class UpdateModScreen extends DhScreen
// Logo image // Logo image
this.addBtn(new TexturedButtonWidget( this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen // Where the button is on the screen
this.width / 2 - 65, this.height / 2 - 110, this.width / 2 - 95, this.height / 2 - 110,
// Width and height of the button // Width and height of the button
130, 65, 195, 65,
// Offset // Offset
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
0, 0,
#if MC_VER < MC_1_21 #if MC_VER < MC_1_21_1
new ResourceLocation(ModInfo.ID, "logo.png"), new ResourceLocation(ModInfo.ID, "logo.png"),
#else #else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"), ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
#endif #endif
130, 65, 195, 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)
@@ -93,7 +105,7 @@ public class UpdateModScreen extends DhScreen
} }
catch (Exception e) catch (Exception e)
{ {
e.printStackTrace(); LOGGER.error("Failed to setup update mod screen, error: ["+e.getMessage()+"].", e);
} }
if (!ModInfo.IS_DEV_BUILD) if (!ModInfo.IS_DEV_BUILD)
@@ -107,7 +119,7 @@ public class UpdateModScreen extends DhScreen
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
0, 0,
#if MC_VER < MC_1_21 #if MC_VER < MC_1_21_1
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else #else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"), ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
@@ -1,21 +1,22 @@
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;
/** Allows to keep level manager enabled between loading different keyed levels */
private boolean enabled = false;
//=============// //=============//
@@ -31,24 +32,24 @@ public class KeyedClientLevelManager implements IKeyedClientLevelManager
//======================// //======================//
@Override @Override
public void setServerKeyedLevel(IServerKeyedClientLevel clientLevel) { this.overrideWrapper = clientLevel; } @Nullable
@Override public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; }
public IServerKeyedClientLevel getOverrideWrapper() { return this.overrideWrapper; }
@Override @Override
public IServerKeyedClientLevel getServerKeyedLevel(ILevelWrapper level, String serverLevelKey) public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String levelKey)
{ {
Objects.requireNonNull(level); IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevel((ClientLevel) clientLevel.getWrappedMcObject(), levelKey);
Objects.requireNonNull(serverLevelKey); this.serverKeyedLevel = keyedLevel;
return new ServerKeyedClientLevel((ClientLevel) level.getWrappedMcObject(), serverLevelKey); this.enabled = true;
return keyedLevel;
} }
@Override @Override
public void setUseOverrideWrapper(boolean useOverrideWrapper) { this.useOverrideWrapper = useOverrideWrapper; } public void clearKeyedLevel() { this.serverKeyedLevel = null; }
@Override @Override
public boolean getUseOverrideWrapper() { return this.useOverrideWrapper; } public boolean isEnabled() { return this.enabled; }
@Override
public void disable() { this.enabled = false; }
} }
@@ -2,6 +2,8 @@ package com.seibel.distanthorizons.common.wrappers.level;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServerKeyedClientLevel public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServerKeyedClientLevel
@@ -9,13 +11,20 @@ public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServe
/** A unique identifier (generally the level's name) for differentiating multiverse levels */ /** A unique identifier (generally the level's name) for differentiating multiverse levels */
private final String serverLevelKey; private final String serverLevelKey;
public ServerKeyedClientLevel(ClientLevel level, String serverLevelKey) public ServerKeyedClientLevel(ClientLevel level, String serverLevelKey)
{ {
super(level); super(level);
this.serverLevelKey = serverLevelKey; this.serverLevelKey = serverLevelKey;
} }
@Override @Override
public String getServerLevelKey() { return this.serverLevelKey; } public String getServerLevelKey() { return this.serverLevelKey; }
@Override
public String getDhIdentifier() { return this.getServerLevelKey(); }
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
import java.io.File; import java.io.File;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
@@ -31,6 +32,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,37 +40,43 @@ 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
import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TextComponent;
#endif #endif
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
#if MC_VER < MC_1_21_3
#else
import net.minecraft.util.profiling.Profiler;
#endif
/** /**
* 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 +107,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;
}
@@ -113,15 +118,15 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override @Override
public float getShade(EDhDirection lodDirection) public float getShade(EDhDirection lodDirection)
{ {
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading.get(); EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.Quality.lodShading.get();
switch (lodShading) switch (lodShading)
{ {
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,94 +155,129 @@ 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
} public String getUsername() { return MINECRAFT.getUser().getName(); }
@Override @Override
public DhBlockPos getPlayerBlockPos() public DhBlockPos getPlayerBlockPos()
{ {
BlockPos playerPos = getPlayer().blockPosition(); LocalPlayer player = this.getPlayer();
if (player == null)
{
return new DhBlockPos(0, 0, 0);
}
BlockPos playerPos = player.blockPosition();
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ()); return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
} }
@Override @Override
public DhChunkPos getPlayerChunkPos() public DhChunkPos getPlayerChunkPos()
{ {
LocalPlayer player = this.getPlayer();
if (player == null)
{
return new DhChunkPos(0, 0);
}
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
ChunkPos playerPos = new ChunkPos(getPlayer().blockPosition()); ChunkPos playerPos = new ChunkPos(player.blockPosition());
#else #else
ChunkPos playerPos = getPlayer().chunkPosition(); ChunkPos playerPos = player.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() { return this.getWrappedClientLevel(false); }
@Override
@Nullable
public IClientLevelWrapper getWrappedClientLevel(boolean bypassLevelKeyManager)
{ {
if (this.mc.level == null) ClientLevel level = MINECRAFT.level;
if (level == null)
{ {
return null; return null;
} }
return ClientLevelWrapper.getWrapperIgnoringOverride(this.mc.level); return ClientLevelWrapper.getWrapper(level, bypassLevelKeyManager);
}
/** Please move over to getInstallationDirectory() */
@Deprecated
@Override
public File getGameDirectory()
{
return getInstallationDirectory();
} }
@Override @Override
public IProfilerWrapper getProfiler() public IProfilerWrapper getProfiler()
{ {
if (profilerWrapper == null) ProfilerFiller profiler;
profilerWrapper = new ProfilerWrapper(mc.getProfiler()); #if MC_VER < MC_1_21_3
else if (mc.getProfiler() != profilerWrapper.profiler) profiler = MINECRAFT.getProfiler();
profilerWrapper.profiler = mc.getProfiler(); #else
return profilerWrapper; profiler = Profiler.get();
#endif
if (this.profilerWrapper == null)
{
this.profilerWrapper = new ProfilerWrapper(profiler);
}
else if (profiler != this.profilerWrapper.profiler)
{
this.profilerWrapper.profiler = profiler;
}
return this.profilerWrapper;
} }
/** Returns all worlds available to the server */ /** Returns all worlds available to the server */
@@ -246,7 +286,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 +300,32 @@ 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.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
#endif
}
@Override
public void sendOverlayMessage(String string)
{
LocalPlayer player = this.getPlayer();
if (player == null)
{
return;
}
#if MC_VER < MC_1_19_2
player.displayClientMessage(new TextComponent(string), /*isOverlay*/true);
#else
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/true);
#endif #endif
} }
@@ -290,24 +350,29 @@ 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 int getPlayerCount()
{
// can be null if the server hasn't finished booting up yet
if (MINECRAFT.getSingleplayerServer() == null)
{
return 1;
}
else
{
return MINECRAFT.getSingleplayerServer().getPlayerCount();
}
}
} }
@@ -0,0 +1,237 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.minecraft;
import com.mojang.blaze3d.platform.GlStateManager;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL32;
/**
* <b>Why does DH often call GL methods twice? </b><br>
* Once using the base {@link GL32} function and a second time using
* Minecraft's {@link GlStateManager}?<br><br>
*
* <b>Answer: </b><br>
* Compatibility and robustness<br>
* In general all MC rendering should go through MC's {@link GlStateManager},
* however that isn't always the case.
* So, to prevent issues if a mod (or MC itself) calls a direct GL function
* instead of the {@link GlStateManager} wrapper, we need to be sure about what the actual
* set value is (whether setting or getting) and that MC knows what DH has done.
* This way whether a mod (or MC) is using the {@link GlStateManager} or direct GL calls,
* they should always have the correct value for anything DH has modified.
* <br><br>
* This may slow down some low end GPUs that are driver limited,
* however James would rather have slow correct rendering vs fast broken rendering.
*/
public class MinecraftGLWrapper implements IMinecraftGLWrapper
{
public static final MinecraftGLWrapper INSTANCE = new MinecraftGLWrapper();
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/*
private static final StencilState STENCIL;
*/
// scissor //
/** @see GL32#GL_SCISSOR_TEST */
@Override
public void enableScissorTest()
{
GL32.glEnable(GL32.GL_SCISSOR_TEST);
GlStateManager._enableScissorTest();
}
/** @see GL32#GL_SCISSOR_TEST */
@Override
public void disableScissorTest()
{
GL32.glDisable(GL32.GL_SCISSOR_TEST);
GlStateManager._disableScissorTest();
}
// stencil //
//
// /** @see GL32#GL_SCISSOR_TEST */
// public void enableScissorTest() { GlStateManager._stencilFunc(); }
// /** @see GL32#GL_SCISSOR_TEST */
// public void disableScissorTest() { GlStateManager._disableScissorTest(); }
// depth //
/** @see GL32#GL_DEPTH_TEST */
@Override
public void enableDepthTest()
{
GL32.glEnable(GL32.GL_DEPTH_TEST);
GlStateManager._enableDepthTest();
}
/** @see GL32#GL_DEPTH_TEST */
@Override
public void disableDepthTest()
{
GL32.glDisable(GL32.GL_DEPTH_TEST);
GlStateManager._disableDepthTest();
}
/** @see GL32#glDepthFunc(int) */
@Override
public void glDepthFunc(int func)
{
GL32.glDepthFunc(func);
GlStateManager._depthFunc(func);
}
/** @see GL32#glDepthMask(boolean) */
@Override
public void enableDepthMask()
{
GL32.glDepthMask(true);
GlStateManager._depthMask(true);
}
/** @see GL32#glDepthMask(boolean) */
@Override
public void disableDepthMask()
{
GL32.glDepthMask(false);
GlStateManager._depthMask(false);
}
// blending //
/** @see GL32#GL_BLEND */
@Override
public void enableBlend()
{
GL32.glEnable(GL32.GL_BLEND);
GlStateManager._enableBlend();
}
/** @see GL32#GL_BLEND */
@Override
public void disableBlend()
{
GL32.glDisable(GL32.GL_BLEND);
GlStateManager._disableBlend();
}
/** @see GL32#glBlendFunc */
@Override
public void glBlendFunc(int sfactor, int dfactor)
{
GL32.glBlendFunc(sfactor, dfactor);
GlStateManager._blendFunc(sfactor, dfactor);
}
/** @see GL32#glBlendFuncSeparate */
@Override
public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha)
{
GL32.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
GlStateManager._blendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
}
// frame buffers //
/** @see GL32#glBindFramebuffer */
@Override
public void glBindFramebuffer(int target, int framebuffer)
{
GL32.glBindFramebuffer(target, framebuffer);
GlStateManager._glBindFramebuffer(target, framebuffer);
}
// buffers //
/** @see GL32#glGenBuffers() */
@Override
public int glGenBuffers()
{ return GlStateManager._glGenBuffers(); }
/** @see GL32#glDeleteBuffers(int) */
@Override
public void glDeleteBuffers(int buffer)
{ GlStateManager._glDeleteBuffers(buffer); }
// culling //
/** @see GL32#GL_CULL_FACE */
@Override
public void enableFaceCulling()
{
GL32.glEnable(GL32.GL_CULL_FACE);
GlStateManager._enableCull();
}
/** @see GL32#GL_CULL_FACE */
@Override
public void disableFaceCulling()
{
GL32.glDisable(GL32.GL_CULL_FACE);
GlStateManager._disableCull();
}
// textures //
/** @see GL32#glGenTextures() */
@Override
public int glGenTextures() { return GlStateManager._genTexture(); }
/** @see GL32#glDeleteTextures(int) */
@Override
public void glDeleteTextures(int texture) { GlStateManager._deleteTexture(texture); }
/** @see GL32#glActiveTexture(int) */
@Override
public void glActiveTexture(int textureId)
{
GL32.glActiveTexture(textureId);
GlStateManager._activeTexture(textureId);
}
@Override
public int getActiveTexture() { return GL32.glGetInteger(GL32.GL_ACTIVE_TEXTURE); }
/**
* Always binds to {@link GL32#GL_TEXTURE_2D}
* @see GL32#glBindTexture(int, int)
*/
@Override
public void glBindTexture(int texture)
{
GL32.glBindTexture(GL32.GL_TEXTURE_2D, texture);
GlStateManager._bindTexture(texture);
}
}
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -21,54 +21,43 @@ 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.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
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.util.ColorUtil;
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;
#endif #endif
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
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;
@@ -78,10 +67,9 @@ 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; import org.joml.Vector4f;
/** /**
@@ -123,14 +111,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()
@@ -151,59 +131,36 @@ 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)
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
float[] colorValues = new float[4]; float[] colorValues = new float[4];
GL15.glGetFloatv(GL15.GL_FOG_COLOR, colorValues); GL15.glGetFloatv(GL15.GL_FOG_COLOR, colorValues);
#else return new Color(
Math.max(0f, Math.min(colorValues[0], 1f)), // r
Math.max(0f, Math.min(colorValues[1], 1f)), // g
Math.max(0f, Math.min(colorValues[2], 1f)), // b
Math.max(0f, Math.min(colorValues[3], 1f)) // a
);
#elif MC_VER < MC_1_21_3
FogRenderer.setupColor(MC.gameRenderer.getMainCamera(), partialTicks, MC.level, 1, MC.gameRenderer.getDarkenWorldAmount(partialTicks)); FogRenderer.setupColor(MC.gameRenderer.getMainCamera(), partialTicks, MC.level, 1, MC.gameRenderer.getDarkenWorldAmount(partialTicks));
float[] colorValues = RenderSystem.getShaderFogColor(); float[] colorValues = RenderSystem.getShaderFogColor();
#endif
return new Color( return new Color(
Math.max(0f, Math.min(colorValues[0], 1f)), Math.max(0f, Math.min(colorValues[0], 1f)), // r
Math.max(0f, Math.min(colorValues[1], 1f)), Math.max(0f, Math.min(colorValues[1], 1f)), // g
Math.max(0f, Math.min(colorValues[2], 1f)), Math.max(0f, Math.min(colorValues[2], 1f)), // b
Math.max(0f, Math.min(colorValues[3], 1f)) Math.max(0f, Math.min(colorValues[3], 1f)) // a
); );
#else
Vector4f colorValues = FogRenderer.computeFogColor(MC.gameRenderer.getMainCamera(), partialTicks, MC.level, 1, MC.gameRenderer.getDarkenWorldAmount(partialTicks));
return new Color(
Math.max(0f, Math.min(colorValues.x, 1f)), // r
Math.max(0f, Math.min(colorValues.y, 1f)), // g
Math.max(0f, Math.min(colorValues.z, 1f)), // b
Math.max(0f, Math.min(colorValues.w, 1f)) // a
);
#endif
} }
// getSpecialFogColor() is the same as getFogColor() // getSpecialFogColor() is the same as getFogColor()
@@ -213,18 +170,24 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
if (MC.level.dimensionType().hasSkyLight()) if (MC.level.dimensionType().hasSkyLight())
{ {
float frameTime; float frameTime;
#if MC_VER < MC_1_21 #if MC_VER < MC_1_21_1
frameTime = MC.getFrameTime(); frameTime = MC.getFrameTime();
#else #elif MC_VER < MC_1_21_3
frameTime = MC.getTimer().getRealtimeDeltaTicks(); frameTime = MC.getTimer().getRealtimeDeltaTicks();
#else
frameTime = MC.deltaTracker.getGameTimeDeltaTicks();
#endif #endif
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), frameTime); Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), frameTime);
#else
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
#endif
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z); return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
#elif MC_VER < MC_1_21_3
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
#else
int argbColorInt = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);;
return ColorUtil.toColorObjARGB(argbColorInt); // TODO MC changed color formats
#endif
} }
else else
{ {
@@ -304,6 +267,8 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override @Override
public int getDepthTextureId() { return this.getRenderTarget().getDepthTextureId(); } public int getDepthTextureId() { return this.getRenderTarget().getDepthTextureId(); }
@Override
public int getColorTextureId() { return this.getRenderTarget().getColorTextureId(); }
@Override @Override
public int getTargetFrameBufferViewportWidth() public int getTargetFrameBufferViewportWidth()
@@ -317,77 +282,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()); }
@@ -408,6 +302,10 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
#endif #endif
} }
/**
* It's better to use {@link MinecraftRenderWrapper#setLightmapId(int, IClientLevelWrapper)} if possible,
* however old MC versions don't support it.
*/
public void updateLightmap(NativeImage lightPixels, IClientLevelWrapper level) public void updateLightmap(NativeImage lightPixels, IClientLevelWrapper level)
{ {
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same // Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
@@ -415,8 +313,18 @@ 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();
LightMapWrapper wrapper = this.lightmapByDimensionType.compute(dimensionType, (dimType, oldWrapper) -> new LightMapWrapper()); LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
wrapper.uploadLightmap(lightPixels); wrapper.uploadLightmap(lightPixels);
} }
public void setLightmapId(int tetxureId, IClientLevelWrapper level)
{
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
// object for the same MC level and/or the same hash,
// so this will have to do for now
IDimensionTypeWrapper dimensionType = level.getDimensionType();
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
wrapper.setLightmapId(tetxureId);
}
} }
@@ -6,26 +6,47 @@ import net.minecraft.server.dedicated.DedicatedServer;
import java.io.File; import java.io.File;
//@Environment(EnvType.SERVER) //@Environment(EnvType.SERVER)
public class MinecraftDedicatedServerWrapper implements IMinecraftSharedWrapper public class MinecraftServerWrapper implements IMinecraftSharedWrapper
{ {
public static final MinecraftDedicatedServerWrapper INSTANCE = new MinecraftDedicatedServerWrapper(); public static final MinecraftServerWrapper INSTANCE = new MinecraftServerWrapper();
private MinecraftDedicatedServerWrapper() { }
public DedicatedServer dedicatedServer = null; public DedicatedServer dedicatedServer = null;
//=============//
// constructor //
//=============//
private MinecraftServerWrapper() { }
//=========//
// methods //
//=========//
@Override @Override
public boolean isDedicatedServer() { return true; } public boolean isDedicatedServer() { return true; }
@Override @Override
public File getInstallationDirectory() public File getInstallationDirectory()
{ {
if (this.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 completed initialization!");
} }
#if MC_VER < MC_1_21 #if MC_VER < MC_1_21_1
return this.dedicatedServer.getServerDirectory(); return this.dedicatedServer.getServerDirectory();
#else #else
return this.dedicatedServer.getServerDirectory().toFile(); return this.dedicatedServer.getServerDirectory().toFile();
#endif #endif
} }
@Override
public int getPlayerCount()
{
return this.dedicatedServer.getPlayerCount();
}
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -31,31 +31,19 @@ public class ProfilerWrapper implements IProfilerWrapper
{ {
public ProfilerFiller profiler; public ProfilerFiller profiler;
public ProfilerWrapper(ProfilerFiller newProfiler) public ProfilerWrapper(ProfilerFiller newProfiler) { this.profiler = newProfiler; }
{
profiler = newProfiler;
}
/** starts a new section inside the currently running section */ /** starts a new section inside the currently running section */
@Override @Override
public void push(String newSection) public void push(String newSection) { this.profiler.push(newSection); }
{
profiler.push(newSection);
}
/** ends the currently running section and starts a new one */ /** ends the currently running section and starts a new one */
@Override @Override
public void popPush(String newSection) public void popPush(String newSection) { this.profiler.popPush(newSection); }
{
profiler.popPush(newSection);
}
/** ends the currently running section */ /** ends the currently running section */
@Override @Override
public void pop() public void pop() { this.profiler.pop(); }
{
profiler.pop();
}
} }
@@ -0,0 +1,15 @@
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();
#if MC_VER == MC_1_16_5
void distantHorizons$setDimensionChangeDestination(ServerLevel dimensionChangeDestination);
#endif
}
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -20,6 +20,8 @@
package com.seibel.distanthorizons.common.wrappers.misc; package com.seibel.distanthorizons.common.wrappers.misc;
import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
@@ -27,49 +29,67 @@ import java.nio.ByteBuffer;
public class LightMapWrapper implements ILightMapWrapper public class LightMapWrapper implements ILightMapWrapper
{ {
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private int textureId = 0; private int textureId = 0;
//==============// //==============//
// constructors // // constructors //
//==============// //==============//
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);
}
//==================//
//=========// // lightmap syncing //
// methods // //==================//
//=========//
public void uploadLightmap(NativeImage image) public void uploadLightmap(NativeImage image)
{ {
int currentBind = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); int currentTexture = GLMC.getActiveTexture();
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
if (this.textureId == 0) if (this.textureId == 0)
{ {
this.createLightmap(image); this.createLightmap(image);
} }
else
{
GLMC.glBindTexture(this.textureId);
}
image.upload(0, 0, 0, false); image.upload(0, 0, 0, false);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, currentBind); GLMC.glBindTexture(currentTexture);
} }
private void createLightmap(NativeImage image)
{
this.textureId = GLMC.glGenTextures();
GLMC.glBindTexture(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);
}
public void setLightmapId(int minecraftLightmapTetxureId)
{
// just use the MC texture ID
this.textureId = minecraftLightmapTetxureId;
}
//==============//
// lightmap use //
//==============//
@Override @Override
public void bind() public void bind()
{ {
GL32.glActiveTexture(GL32.GL_TEXTURE0); GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId); GLMC.glBindTexture(this.textureId);
} }
@Override @Override
public void unbind() { GL32.glBindTexture(GL32.GL_TEXTURE_2D, 0); } public void unbind() { GLMC.glBindTexture(0); }
} }
@@ -0,0 +1,30 @@
package com.seibel.distanthorizons.common.wrappers.misc;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
import net.minecraft.core.BlockPos;
public class MutableBlockPosWrapper implements IMutableBlockPosWrapper
{
public final BlockPos.MutableBlockPos pos;
//=============//
// constructor //
//=============//
public MutableBlockPosWrapper()
{
this.pos = new BlockPos.MutableBlockPos();
}
//===========//
// overrides //
//===========//
@Override
public Object getWrappedMcObject() { return this.pos; }
}
@@ -1,54 +1,115 @@
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 the 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;
public static ServerPlayerWrapper getWrapper(ServerPlayer serverPlayer)
{
return serverPlayerWrapperMap.computeIfAbsent(serverPlayer, ServerPlayerWrapper::new);
}
private ServerPlayerWrapper(ServerPlayer serverPlayer)
{
this.serverPlayer = serverPlayer;
}
public UUID getUUID() //=============//
{ // constructor //
return serverPlayer.getUUID(); //=============//
}
public static ServerPlayerWrapper getWrapper(ServerPlayer serverPlayer)
{ return serverPlayerWrapperMap.computeIfAbsent(serverPlayer.connection, ignored -> new ServerPlayerWrapper(serverPlayer.connection)); }
private ServerPlayerWrapper(ServerGamePacketListenerImpl connection) { this.connection = connection; }
//=========//
// getters //
//=========//
private ServerPlayer getServerPlayer() { return this.connection.player; }
@Override
public String getName() { return this.getServerPlayer().getName().getString(); }
@Override
public IServerLevelWrapper getLevel() public IServerLevelWrapper getLevel()
{ {
#if MC_VER < MC_1_20_1 ServerLevel level = ((IMixinServerPlayer) this.getServerPlayer()).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
#endif level = this.getServerPlayer().getLevel();
} #else
level = this.getServerPlayer().serverLevel();
public Object getWrappedMcObject() #endif
{ }
return serverPlayer;
return ServerLevelWrapper.getWrapper(level);
} }
@Override @Override
public String toString() public Vec3d getPosition()
{ {
return "Wrapped{" + serverPlayer.toString() + "}"; Vec3 position = this.getServerPlayer().position();
return new Vec3d(position.x, position.y, position.z);
} }
@Override
public int getViewDistance() { return this.getServerPlayer().server.getPlayerList().getViewDistance(); }
@Override
public SocketAddress getRemoteAddress()
{
#if MC_VER >= MC_1_19_4
return this.getServerPlayer().connection.getRemoteAddress();
#else // < 1.19.4
return this.getServerPlayer().connection.connection.getRemoteAddress();
#endif
}
//================//
// base overrides //
//================//
@Override
public Object getWrappedMcObject() { return this.getServerPlayer(); }
@Override
public String toString() { return "Wrapped{" + this.getServerPlayer() + "}"; }
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof ServerPlayerWrapper))
{
return false;
}
ServerPlayerWrapper that = (ServerPlayerWrapper) obj;
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,16 +19,19 @@ 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.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.File;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -38,17 +41,26 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
#if MC_VER < MC_1_21_3
#else
import com.seibel.distanthorizons.core.util.ColorUtil;
#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; private BiomeWrapper plainsBiomeWrapper;
@Deprecated // TODO circular references are bad
private IDhLevel parentDhLevel;
@@ -64,23 +76,28 @@ public class ClientLevelWrapper implements IClientLevelWrapper
// wrapper logic // // wrapper logic //
//===============// //===============//
public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level) { return getWrapper(level, false); }
@Nullable @Nullable
public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level) public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level, boolean bypassLevelKeyManager)
{ {
if (level == null) if (!bypassLevelKeyManager)
{ {
return null; if (level == null)
{
return null;
}
// 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;
}
} }
// used if the client is connected to a server that defines the currently loaded level return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new);
if (KEYED_CLIENT_LEVEL_MANAGER.getUseOverrideWrapper())
{
return KEYED_CLIENT_LEVEL_MANAGER.getOverrideWrapper();
}
return getWrapperIgnoringOverride(level);
} }
public static IClientLevelWrapper getWrapperIgnoringOverride(@NotNull ClientLevel level) { return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new); }
@Nullable @Nullable
@Override @Override
@@ -88,7 +105,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
@@ -108,7 +125,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
} }
catch (Exception e) catch (Exception e)
{ {
LOGGER.error("Failed to get server side wrapper for client level: " + level); LOGGER.error("Failed to get server side wrapper for client level: " + this.level);
return null; return null;
} }
} }
@@ -120,9 +137,13 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//====================// //====================//
@Override @Override
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState) public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockWrapper)
{ {
return this.blockMap.getColor(((BlockStateWrapper) blockState).blockState, (BiomeWrapper) biome, pos); ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent(
((BlockStateWrapper) blockWrapper).blockState,
(block) -> new ClientBlockStateColorCache(block, this));
return blockColorCache.getColor((BiomeWrapper) biome, pos);
} }
@Override @Override
@@ -142,9 +163,12 @@ 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 @Override
public IBiomeWrapper getPlainsBiomeWrapper() public IBiomeWrapper getPlainsBiomeWrapper()
{ {
@@ -168,6 +192,16 @@ public class ClientLevelWrapper implements IClientLevelWrapper
@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
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
@Override
public String getDhIdentifier() { return this.getHashedSeedEncoded() + "@" + this.getDimensionName(); }
@Override @Override
public EDhApiLevelType getLevelType() { return EDhApiLevelType.CLIENT_LEVEL; } public EDhApiLevelType getLevelType() { return EDhApiLevelType.CLIENT_LEVEL; }
@@ -180,33 +214,35 @@ 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()
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
return 0; return 0;
#else #elif MC_VER < MC_1_21_3
return this.level.getMinBuildHeight(); return this.level.getMinBuildHeight();
#else
return this.level.getMinY();
#endif #endif
} }
@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;
} }
return new ChunkWrapper(chunk, this.level, this); return new ChunkWrapper(chunk, this);
} }
@Override @Override
@@ -218,9 +254,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
@Override @Override
public IBlockStateWrapper getBlockState(DhBlockPos pos) public IBlockStateWrapper getBlockState(DhBlockPos pos)
{ { return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this); }
return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this);
}
@Override @Override
public IBiomeWrapper getBiome(DhBlockPos pos) { return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this); } public IBiomeWrapper getBiome(DhBlockPos pos) { return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this); }
@@ -229,7 +263,61 @@ 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;
}
@Override
public File getDhSaveFolder()
{
if (this.parentDhLevel == null)
{
return null;
}
return this.parentDhLevel.getSaveStructure().getSaveFolder(this);
}
//===================//
// 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)
{
#if MC_VER < MC_1_21_3
Vec3 colorVec3 = this.level.getCloudColor(tickDelta);
return new Color((float)colorVec3.x, (float)colorVec3.y, (float)colorVec3.z);
#else
int argbColor = this.level.getCloudColor(tickDelta);
return ColorUtil.toColorObjARGB(argbColor);
#endif
}
//================//
// base overrides //
//================//
@Override @Override
public String toString() public String toString()
@@ -239,7 +327,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.getDhIdentifier() + "}";
} }
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -28,65 +28,81 @@ import net.minecraft.world.level.dimension.DimensionType;
/** /**
* @author James Seibel * @author James Seibel
* @version 2022-9-16
*/ */
public class DimensionTypeWrapper implements IDimensionTypeWrapper public class DimensionTypeWrapper implements IDimensionTypeWrapper
{ {
private static final ConcurrentMap<DimensionType, DimensionTypeWrapper> dimensionTypeWrapperMap = new ConcurrentHashMap<>(); private static final ConcurrentMap<String, DimensionTypeWrapper> DIMENSION_WRAPPER_BY_NAME = new ConcurrentHashMap<>();
private final DimensionType dimensionType; private final DimensionType dimensionType;
public DimensionTypeWrapper(DimensionType dimensionType)
{
this.dimensionType = dimensionType; //=============//
} // Constructor //
//=============//
public DimensionTypeWrapper(DimensionType dimensionType) { this.dimensionType = dimensionType; }
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType) public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
{ {
//first we check if the biome has already been wrapped String dimName = getName(dimensionType);
if (dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
return dimensionTypeWrapperMap.get(dimensionType); // check if the dimension has already been wrapped
if (DIMENSION_WRAPPER_BY_NAME.containsKey(dimName)
&& DIMENSION_WRAPPER_BY_NAME.get(dimName) != null)
{
return DIMENSION_WRAPPER_BY_NAME.get(dimName);
}
//if it hasn't been created yet, we create it and save it in the map // create the missing wrapper
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType); DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper); DIMENSION_WRAPPER_BY_NAME.put(dimName, dimensionTypeWrapper);
//we return the newly created wrapper
return dimensionTypeWrapper; return dimensionTypeWrapper;
} }
public static void clearMap() public static void clearMap() { DIMENSION_WRAPPER_BY_NAME.clear(); }
{
dimensionTypeWrapperMap.clear();
}
//=================//
// wrapper methods //
//=================//
@Override @Override
public String getDimensionName() public String getName() { return getName(this.dimensionType); }
public static String getName(DimensionType dimensionType)
{ {
#if MC_VER <= MC_1_16_5
// effectsLocation() is marked as client only, so using the backing field directly
return dimensionType.effectsLocation.getPath();
#else
return dimensionType.effectsLocation().getPath(); return dimensionType.effectsLocation().getPath();
#endif
} }
@Override @Override
public boolean hasCeiling() public boolean hasCeiling() { return this.dimensionType.hasCeiling(); }
{
return dimensionType.hasCeiling();
}
@Override @Override
public boolean hasSkyLight() public boolean hasSkyLight() { return this.dimensionType.hasSkyLight(); }
{
return dimensionType.hasSkyLight();
}
@Override @Override
public Object getWrappedMcObject() public Object getWrappedMcObject() { return this.dimensionType; }
{
return this.dimensionType; // there's definitely a better way of doing this, but it should work well enough for now
} @Override
public boolean isTheEnd() { return this.getName().equalsIgnoreCase("the_end"); }
@Override
public double getCoordinateScale() { return this.dimensionType.coordinateScale(); }
//================//
// base overrides //
//================//
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj)
{ {
@@ -97,9 +113,10 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
else else
{ {
DimensionTypeWrapper other = (DimensionTypeWrapper) obj; DimensionTypeWrapper other = (DimensionTypeWrapper) obj;
return other.getDimensionName().equals(this.getDimensionName()); return other.getName().equals(this.getName());
} }
} }
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -23,19 +23,18 @@ 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;
@@ -48,19 +47,21 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
import org.apache.logging.log4j.Logger; #if MC_VER < MC_1_21_3
import org.jetbrains.annotations.Nullable; #else
import java.nio.file.Path;
#endif
import org.apache.logging.log4j.Logger;
/**
* @version 2022-9-16
*/
public class ServerLevelWrapper implements IServerLevelWrapper 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;
@@ -68,12 +69,10 @@ public class ServerLevelWrapper implements IServerLevelWrapper
// constructors // // constructors //
//==============// //==============//
public static ServerLevelWrapper getWrapper(ServerLevel level) { return LEVEL_WRAPPER_BY_SERVER_LEVEL.computeIfAbsent(level, ServerLevelWrapper::new); } public static ServerLevelWrapper getWrapper(ServerLevel level)
{ return LEVEL_WRAPPER_BY_SERVER_LEVEL.computeIfAbsent(level, ServerLevelWrapper::new); }
public ServerLevelWrapper(ServerLevel level) public ServerLevelWrapper(ServerLevel level) { this.level = level; }
{
this.level = level;
}
@@ -81,108 +80,141 @@ public class ServerLevelWrapper implements IServerLevelWrapper
// methods // // methods //
//=========// //=========//
@Nullable
@Override @Override
public IClientLevelWrapper tryGetClientLevelWrapper() public File getMcSaveFolder()
{ {
MinecraftClientWrapper client = MinecraftClientWrapper.INSTANCE; #if MC_VER < MC_1_21_3
if (client.mc.level == null) return this.level.getChunkSource().getDataStorage().dataFolder;
{ #else
return null; return this.level.getChunkSource().getDataStorage().dataFolder.toFile();
} #endif
return ClientLevelWrapper.getWrapper(client.mc.level);
} }
@Override @Override
public File getSaveFolder() public String getWorldFolderName()
{ {
return level.getChunkSource().getDataStorage().dataFolder; #if MC_VER >= MC_1_17_1
return this.level.getServer().getWorldScreenshotFile().get().getParent().getFileName().toString();
#else // <= 1.16.5
return this.level.getServer().getWorldScreenshotFile().getParentFile().getName();
#endif
} }
@Override @Override
public DimensionTypeWrapper getDimensionType() public DimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
{
return DimensionTypeWrapper.getDimensionTypeWrapper(level.dimensionType()); @Override
} public String getDimensionName() { return this.level.dimension().location().toString(); }
@Override
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
@Override
public String getDhIdentifier() { return this.getDimensionName(); }
@Override @Override
public EDhApiLevelType getLevelType() { return EDhApiLevelType.SERVER_LEVEL; } public EDhApiLevelType getLevelType() { return EDhApiLevelType.SERVER_LEVEL; }
public ServerLevel getLevel() public ServerLevel getLevel() { return this.level; }
{
return level;
}
@Override @Override
public boolean hasCeiling() public boolean hasCeiling() { return this.level.dimensionType().hasCeiling(); }
{
return level.dimensionType().hasCeiling();
}
@Override @Override
public boolean hasSkyLight() public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
{
return level.dimensionType().hasSkyLight();
}
@Override @Override
public int getHeight() public int getMaxHeight() { return this.level.getHeight(); }
{
return level.getHeight();
}
@Override @Override
public int getMinHeight() public int getMinHeight()
{ {
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
return 0; return 0;
#elif MC_VER < MC_1_21_3
return this.level.getMinBuildHeight();
#else #else
return level.getMinBuildHeight(); return this.level.getMinY();
#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);
} }
@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();
} }
@Override
public File getDhSaveFolder()
{
if (this.parentDhLevel == null)
{
return null;
}
return this.parentDhLevel.getSaveStructure().getSaveFolder(this);
}
//================//
// base overrides //
//================//
@Override
public String toString() { return "Wrapped{" + this.level.toString() + "@" + this.getDhIdentifier() + "}"; }
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -20,11 +20,10 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration; package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -43,8 +42,10 @@ 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 final EDhApiDistantGeneratorMode generatorMode;
public EventTimer timer = null; public EventTimer timer = null;
public long inQueueTime; public long inQueueTime;
public long timeoutTime = -1; public long timeoutTime = -1;
@@ -55,12 +56,13 @@ public final class GenerationEvent
public GenerationEvent( public GenerationEvent(
DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup, DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer) EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
{ {
this.inQueueTime = System.nanoTime(); this.inQueueTime = System.nanoTime();
this.id = generationFutureDebugIDs++; this.id = generationFutureDebugIDs++;
this.minPos = minPos; this.minPos = minPos;
this.size = size; this.size = size;
this.generatorMode = generatorMode;
this.targetGenerationStep = targetGenerationStep; this.targetGenerationStep = targetGenerationStep;
this.threadedParam = ThreadedParameters.getOrMake(generationGroup.params); this.threadedParam = ThreadedParameters.getOrMake(generationGroup.params);
this.resultConsumer = resultConsumer; this.resultConsumer = resultConsumer;
@@ -70,17 +72,11 @@ public final class GenerationEvent
public static GenerationEvent startEvent( public static GenerationEvent startEvent(
DhChunkPos minPos, int size, BatchGenerationEnvironment genEnvironment, DhChunkPos minPos, int size, BatchGenerationEnvironment genEnvironment,
EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer, EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
ExecutorService worldGeneratorThreadPool) ExecutorService worldGeneratorThreadPool)
{ {
if (size % 2 == 0) GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, generatorMode, target, resultConsumer);
{ generationEvent.future = CompletableFuture.supplyAsync(() ->
size += 1; // size must be odd for vanilla world gen regions to work
}
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer);
generationEvent.future = CompletableFuture.runAsync(() ->
{ {
long runStartTime = System.nanoTime(); long runStartTime = System.nanoTime();
generationEvent.timeoutTime = runStartTime; generationEvent.timeoutTime = runStartTime;
@@ -88,21 +84,75 @@ public final class GenerationEvent
generationEvent.timer = new EventTimer("setup"); generationEvent.timer = new EventTimer("setup");
BatchGenerationEnvironment.isDistantGeneratorThread.set(true); BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
try try
{ {
//LOGGER.info("generating [{}]", event.minPos); genEnvironment.generateLodFromListAsync(generationEvent, (runnable) ->
genEnvironment.generateLodFromList(generationEvent); {
worldGeneratorThreadPool.execute(() ->
{
boolean alreadyMarked = BatchGenerationEnvironment.isCurrentThreadDistantGeneratorThread();
if (!alreadyMarked)
{
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
}
try
{
runnable.run();
}
catch (Throwable throwable)
{
handleWorldGenThrowable(generationEvent, throwable);
}
finally
{
if (!alreadyMarked)
{
BatchGenerationEnvironment.isDistantGeneratorThread.set(false);
}
}
});
});
} }
catch (InterruptedException ignored) catch (Throwable initialThrowable)
{ {
handleWorldGenThrowable(generationEvent, initialThrowable);
} }
finally finally
{ {
BatchGenerationEnvironment.isDistantGeneratorThread.remove(); BatchGenerationEnvironment.isDistantGeneratorThread.remove();
} }
return null;
}, worldGeneratorThreadPool); }, worldGeneratorThreadPool);
return generationEvent; return generationEvent;
} }
/** There's probably a better way to handle this, but it'll work for now */
private static void handleWorldGenThrowable(GenerationEvent generationEvent, Throwable initialThrowable)
{
Throwable throwable = initialThrowable;
while (throwable instanceof CompletionException)
{
throwable = throwable.getCause();
}
if (throwable instanceof InterruptedException
|| throwable instanceof UncheckedInterruptedException
|| throwable instanceof RejectedExecutionException)
{
// these exceptions can be ignored, generally they just mean
// the thread is busy so it'll need to try again later.
// FIXME this should cause the world gen task to be re-queued so we can try again later
// however, currently it can cause large gaps in the world gen instead.
// These gaps will generate correctly if the level is reloaded and the world gen is re-queued,
// however this is makes it look like the generator isn't working or skipped something.
}
else
{
generationEvent.future.completeExceptionally(throwable);
}
}
public boolean isComplete() { return this.future.isDone(); } public boolean isComplete() { return this.future.isDone(); }
@@ -126,21 +176,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();
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -50,23 +50,26 @@ import net.minecraft.world.level.storage.WorldData;
public final class GlobalParameters public final class GlobalParameters
{ {
public final ChunkGenerator generator; public final ChunkGenerator generator;
#if MC_VER < MC_1_19_2
public final StructureManager structures;
#else
public final StructureTemplateManager structures;
public final RandomState randomState;
#endif
#if MC_VER < MC_1_19_4
public final WorldGenSettings worldGenSettings;
#else
public final WorldOptions worldOptions;
#endif
public final IDhServerLevel lodLevel; public final IDhServerLevel lodLevel;
public final ServerLevel level; public final ServerLevel level;
public final Registry<Biome> biomes; public final Registry<Biome> biomes;
public final RegistryAccess registry; public final RegistryAccess registry;
public final long worldSeed; public final long worldSeed;
public final DataFixer fixerUpper; public final DataFixer fixerUpper;
#if MC_VER < MC_1_19_2
public final StructureManager structures;
#else
public final StructureTemplateManager structures;
public final RandomState randomState;
#endif
#if MC_VER < MC_1_19_4
public final WorldGenSettings worldGenSettings;
#else
public final WorldOptions worldOptions;
#endif
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
public final BiomeManager biomeManager; public final BiomeManager biomeManager;
public final ChunkScanAccess chunkScanner; // FIXME: Figure out if this is actually needed public final ChunkScanAccess chunkScanner; // FIXME: Figure out if this is actually needed
@@ -76,29 +79,34 @@ public final class GlobalParameters
{ {
this.lodLevel = lodLevel; this.lodLevel = lodLevel;
level = ((ServerLevelWrapper) lodLevel.getServerLevelWrapper()).getWrappedMcObject(); this.level = ((ServerLevelWrapper) lodLevel.getServerLevelWrapper()).getWrappedMcObject();
MinecraftServer server = level.getServer(); MinecraftServer server = this.level.getServer();
WorldData worldData = server.getWorldData(); WorldData worldData = server.getWorldData();
registry = server.registryAccess(); this.registry = server.registryAccess();
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
worldGenSettings = worldData.worldGenSettings(); this.worldGenSettings = worldData.worldGenSettings();
biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY); this.biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY);
worldSeed = worldGenSettings.seed(); this.worldSeed = worldGenSettings.seed();
#elif MC_VER < MC_1_21_3
this.worldOptions = worldData.worldGenOptions();
this.biomes = registry.registryOrThrow(Registries.BIOME);
this.worldSeed = worldOptions.seed();
#else #else
worldOptions = worldData.worldGenOptions(); this.worldOptions = worldData.worldGenOptions();
biomes = registry.registryOrThrow(Registries.BIOME); this.biomes = this.registry.lookupOrThrow(Registries.BIOME);
worldSeed = worldOptions.seed(); this.worldSeed = this.worldOptions.seed();
#endif #endif
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
biomeManager = new BiomeManager(level, BiomeManager.obfuscateSeed(worldSeed)); this.biomeManager = new BiomeManager(this.level, BiomeManager.obfuscateSeed(this.worldSeed));
chunkScanner = level.getChunkSource().chunkScanner(); this.chunkScanner = this.level.getChunkSource().chunkScanner();
#endif #endif
structures = server.getStructureManager(); this.structures = server.getStructureManager();
generator = level.getChunkSource().getGenerator(); this.generator = this.level.getChunkSource().getGenerator();
fixerUpper = server.getFixerUpper(); this.fixerUpper = server.getFixerUpper();
#if MC_VER >= MC_1_19_2 #if MC_VER >= MC_1_19_2
randomState = level.getChunkSource().randomState(); this.randomState = this.level.getChunkSource().randomState();
#endif #endif
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU GPL v3 License. * licensed under the GNU GPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -19,7 +19,6 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject; package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.google.common.collect.Maps;
import com.mojang.serialization.Codec; import com.mojang.serialization.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.chunk.ChunkWrapper;
@@ -27,21 +26,14 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGeneratio
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.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.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import it.unimi.dsi.fastutil.shorts.ShortList;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
#if MC_VER >= MC_1_19_4 #if MC_VER >= MC_1_19_4
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
@@ -50,7 +42,6 @@ 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.nbt.Tag;
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.*;
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
@@ -59,20 +50,22 @@ 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.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.*; import net.minecraft.world.level.chunk.*;
#if MC_VER < MC_1_21_3
import net.minecraft.world.level.chunk.storage.ChunkSerializer; import net.minecraft.world.level.chunk.storage.ChunkSerializer;
#else
#endif
import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.Heightmap;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.BlendingData; import net.minecraft.world.level.levelgen.blending.BlendingData;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.StructureFeature; import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif #endif
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext;
import net.minecraft.world.ticks.LevelChunkTicks; import net.minecraft.world.ticks.LevelChunkTicks;
#endif #endif
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
#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;
#endif #endif
@@ -85,7 +78,7 @@ import net.minecraft.world.level.material.Fluids;
#if MC_VER == MC_1_20_6 #if MC_VER == MC_1_20_6
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType; import net.minecraft.world.level.chunk.status.ChunkType;
#elif MC_VER == MC_1_21 #elif MC_VER >= MC_1_21_1
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType; import net.minecraft.world.level.chunk.status.ChunkType;
#endif #endif
@@ -111,6 +104,8 @@ public class ChunkLoader
private static boolean lightingSectionErrorLogged = false; private static boolean lightingSectionErrorLogged = false;
private static final ConcurrentHashMap<String, Object> LOGGED_ERROR_MESSAGE_MAP = new ConcurrentHashMap<>();
//============// //============//
@@ -128,7 +123,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))
@@ -238,22 +233,27 @@ public class ChunkLoader
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY); Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#else #elif MC_VER < MC_1_21_3
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME); Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#else
Registry<Biome> biomes = level.registryAccess().lookupOrThrow(Registries.BIOME);
#endif #endif
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec( Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS)); biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec( Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_21_3
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS)); biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#else #else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW( Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS)); biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#endif #endif
#endif #endif
int i = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[i]; LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex];
boolean isLightOn = chunkData.getBoolean("isLightOn"); boolean isLightOn = chunkData.getBoolean("isLightOn");
boolean hasSkyLight = level.dimensionType().hasSkyLight(); boolean hasSkyLight = level.dimensionType().hasSkyLight();
@@ -288,28 +288,44 @@ public class ChunkLoader
#endif #endif
blockStateContainer = tagSection.contains("block_states", 10) blockStateContainer = tagSection.contains("block_states", 10)
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)) ? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
#if MC_VER < MC_1_20_6 #if MC_VER < MC_1_20_6
.getOrThrow(false, LOGGER::error) .getOrThrow(false, (message) -> logWarningOnce(message))
#else #else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null)) .getOrThrow((message) -> logErrorAndReturnException(message))
#endif #endif
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); : new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
biomeContainer = tagSection.contains("biomes", 10) biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error) ? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, (message) -> logWarningOnce(message))
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); : new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else #else
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, i, (String) string)) if (tagSection.contains("biomes", 10))
{
biomeContainer =
biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes"))
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
#if MC_VER < MC_1_20_6 #if MC_VER < MC_1_20_6
.getOrThrow(false, LOGGER::error) .getOrThrow(false, (message) -> logWarningOnce(message));
#else #else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null)) .getOrThrow((message) -> logErrorAndReturnException(message));
#endif #endif
: new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); }
else
{
biomeContainer = new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(),
#if MC_VER < MC_1_21_3
biomes.getHolderOrThrow(Biomes.PLAINS),
#else
biomes.getOrThrow(Biomes.PLAINS),
#endif
PalettedContainer.Strategy.SECTION_BIOMES);
}
#endif #endif
#if MC_VER < MC_1_20_1 #if MC_VER < MC_1_20_1
@@ -325,7 +341,7 @@ public class ChunkLoader
} }
private static private static
#if MC_VER < MC_1_20_6 ChunkStatus.ChunkType #if MC_VER < MC_1_20_6 ChunkStatus.ChunkType
#elif MC_VER < MC_1_21 ChunkType #elif MC_VER < MC_1_21_1 ChunkType
#else ChunkType #endif #else ChunkType #endif
readChunkType(CompoundTag tagLevel) readChunkType(CompoundTag tagLevel)
{ {
@@ -353,12 +369,16 @@ public class ChunkLoader
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData) private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
{ {
ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9); ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
for (int n = 0; n < tagPostProcessings.size(); ++n) for (int i = 0; i < tagPostProcessings.size(); ++i)
{ {
ListTag listTag3 = tagPostProcessings.getList(n); ListTag listTag3 = tagPostProcessings.getList(i);
for (int o = 0; o < listTag3.size(); ++o) for (int j = 0; j < listTag3.size(); ++j)
{ {
chunk.addPackedPostProcess(listTag3.getShort(o), n); #if MC_VER < MC_1_21_3
chunk.addPackedPostProcess(listTag3.getShort(j), i);
#else
chunk.addPackedPostProcess(ShortList.of(listTag3.getShort(j)), i);
#endif
} }
} }
} }
@@ -370,7 +390,12 @@ public class ChunkLoader
{ {
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data")); Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null);
#if MC_VER < MC_1_21_3
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logWarningOnce(message)).orElse(null);
#else
blendingData = BlendingData.unpack(BlendingData.Packed.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logWarningOnce(message)).orElse(null));
#endif
} }
return blendingData; return blendingData;
} }
@@ -392,7 +417,7 @@ public class ChunkLoader
return null; return null;
#else #else
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getMinBuildHeight(chunk), ChunkWrapper.getMaxBuildHeight(chunk)); CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getInclusiveMinBuildHeight(chunk), ChunkWrapper.getExclusiveMaxBuildHeight(chunk));
ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage; ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage;
ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage; ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage;
@@ -471,7 +496,7 @@ public class ChunkLoader
skyLight = LodUtil.MAX_MC_LIGHT; skyLight = LodUtil.MAX_MC_LIGHT;
} }
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getMinBuildHeight(chunk); int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getInclusiveMinBuildHeight(chunk);
blockLightStorage.set(relX, y, relZ, blockLight); blockLightStorage.set(relX, y, relZ, blockLight);
skyLightStorage.set(relX, y, relZ, skyLight); skyLightStorage.set(relX, y, relZ, skyLight);
} }
@@ -495,10 +520,60 @@ public class ChunkLoader
} }
} }
private static void logErrors(ChunkPos chunkPos, int i, String string)
//=========//
// logging //
//=========//
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{ {
LOGGER.error("Distant Horizons: Recoverable errors when loading section [" + chunkPos.x + ", " + i + ", " + chunkPos.z + "]: " + string); LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
return newMessage;
});
} }
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
return newMessage;
});
}
private static void logWarningOnce(String message) { logWarningOnce(message, null); }
private static void logWarningOnce(String message, Exception e)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.",
e);
return newMessage;
});
}
private static RuntimeException logErrorAndReturnException(String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
return newMessage;
});
// Currently we want to ignore these errors, if returning null is a problem, we can change this later
return null; //new RuntimeException(message);
}
@@ -1,23 +1,28 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject; package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
#if MC_VER >= MC_1_21 #if MC_VER >= MC_1_21_1
import net.minecraft.server.level.GenerationChunkHolder; import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import java.util.concurrent.CompletableFuture;
public class DhGenerationChunkHolder extends GenerationChunkHolder public class DhGenerationChunkHolder extends GenerationChunkHolder
{ {
public DhGenerationChunkHolder(ChunkPos pos) public DhGenerationChunkHolder(ChunkPos pos) { super(pos); }
{
super(pos);
}
@Override @Override
public int getTicketLevel() { return 0; } public int getTicketLevel() { return 0; }
@Override @Override
public int getQueueLevel() { return 0; } public int getQueueLevel() { return 0; }
#if MC_VER < MC_1_21_3
#else
@Override
protected void addSaveDependency(CompletableFuture<?> completableFuture) { }
#endif
} }
#endif #endif
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -20,7 +20,6 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject; package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.EnumSet;
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.chunk.ChunkWrapper;
@@ -63,12 +62,19 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.*; import net.minecraft.world.level.chunk.status.*;
#endif #endif
#if MC_VER == MC_1_21 #if MC_VER >= MC_1_21_1
import net.minecraft.util.StaticCache2D; import net.minecraft.util.StaticCache2D;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import net.minecraft.server.level.GenerationChunkHolder; import net.minecraft.server.level.GenerationChunkHolder;
#endif #endif
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.ticks.BlackholeTickAccess;
import net.minecraft.world.ticks.LevelTickAccess;
#endif
public class DhLitWorldGenRegion extends WorldGenRegion public class DhLitWorldGenRegion extends WorldGenRegion
{ {
@@ -77,8 +83,9 @@ public class DhLitWorldGenRegion extends WorldGenRegion
private static ChunkStatus debugTriggeredForStatus = null; private static ChunkStatus debugTriggeredForStatus = null;
public final ServerLevel serverLevel;
public final DummyLightEngine lightEngine; public final DummyLightEngine lightEngine;
public final BatchGenerationEnvironment.IEmptyChunkGeneratorFunc generator; public final BatchGenerationEnvironment.IEmptyChunkRetrievalFunc generator;
public final int writeRadius; public final int writeRadius;
public final int size; public final int size;
@@ -123,11 +130,11 @@ public class DhLitWorldGenRegion extends WorldGenRegion
ChunkAccess centerChunk, 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.IEmptyChunkGeneratorFunc generator) BatchGenerationEnvironment.IEmptyChunkRetrievalFunc generator)
{ {
#if MC_VER == MC_1_16_5 #if MC_VER == MC_1_16_5
super(serverLevel, chunkList); super(serverLevel, chunkList);
#elif MC_VER < MC_1_21 #elif MC_VER < MC_1_21_1
super(serverLevel, chunkList, chunkStatus, writeRadius); super(serverLevel, chunkList, chunkStatus, writeRadius);
#else #else
super(serverLevel, super(serverLevel,
@@ -140,9 +147,10 @@ public class DhLitWorldGenRegion extends WorldGenRegion
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), writeRadius, (WorldGenContext var1, ChunkStep var2, StaticCache2D<GenerationChunkHolder> var3, ChunkAccess var4) -> null),
centerChunk); centerChunk);
#endif #endif
this.firstPos = chunkList.get(0).getPos(); this.firstPos = chunkList.get(0).getPos();
this.serverLevel = serverLevel;
this.generator = generator; this.generator = generator;
this.lightEngine = lightEngine; this.lightEngine = lightEngine;
this.writeRadius = writeRadius; this.writeRadius = writeRadius;
@@ -171,7 +179,18 @@ public class DhLitWorldGenRegion extends WorldGenRegion
if (center.isUpgrading()) if (center.isUpgrading())
{ {
LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration(); LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration();
if (blockPos.getY() < levelHeightAccessor.getMinBuildHeight() || blockPos.getY() >= levelHeightAccessor.getMaxBuildHeight())
int minY;
int maxY;
#if MC_VER < MC_1_21_3
minY = levelHeightAccessor.getMinBuildHeight();
maxY = levelHeightAccessor.getMaxBuildHeight();
#else
minY = levelHeightAccessor.getMinY();
maxY = levelHeightAccessor.getMaxY();
#endif
if (blockPos.getY() < minY || blockPos.getY() >= maxY)
{ {
return false; return false;
} }
@@ -181,6 +200,22 @@ public class DhLitWorldGenRegion extends WorldGenRegion
} }
#endif #endif
#if MC_VER >= MC_1_18_2
@Override
@NotNull
public LevelTickAccess<Block> getBlockTicks()
{
// DH world gen doesn't need ticking, so return the BlackholeTickAccess list (which causes all ticks to be ignored).
// If this isn't done the server may attempt to tick chunks outside the vanilla render distance,
// which can throw warnings or cause other issues
return BlackholeTickAccess.emptyLevelList();
}
@Override
@NotNull
public LevelTickAccess<Fluid> getFluidTicks() { return BlackholeTickAccess.emptyLevelList(); }
#endif
// TODO Check this // TODO Check this
// @Override // @Override
// public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos, // public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos,
@@ -195,7 +230,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
ChunkAccess chunkAccess = this.getChunk(blockPos); ChunkAccess chunkAccess = this.getChunk(blockPos);
if (chunkAccess instanceof LevelChunk) if (chunkAccess instanceof LevelChunk)
return true; return true;
chunkAccess.setBlockState(blockPos, blockState, false); chunkAccess.setBlockState(blockPos, blockState, /*isBlockMoving*/false);
// This is for post ticking for water on gen and stuff like that. Not enabled // This is for post ticking for water on gen and stuff like that. Not enabled
// for now. // for now.
// if (blockState.hasPostProcess(this, blockPos)) // if (blockState.hasPostProcess(this, blockPos))
@@ -235,6 +270,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; }
@@ -324,7 +375,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
if (chunk == null) if (chunk == null)
{ {
// chunk isn't in memory, generate a new one // chunk isn't in memory, generate a new one
chunk = this.generator.generate(chunkX, chunkZ); chunk = this.generator.getChunk(chunkX, chunkZ);
if (chunk == null) if (chunk == null)
{ {
throw new NullPointerException("The provided generator should not return null!"); throw new NullPointerException("The provided generator should not return null!");
@@ -375,14 +426,10 @@ public class DhLitWorldGenRegion extends WorldGenRegion
/** Overriding allows us to use our own lighting engine */ /** Overriding allows us to use our own lighting engine */
@Override @Override
public boolean canSeeSky(@NotNull BlockPos blockPos) public boolean canSeeSky(@NotNull BlockPos blockPos)
{ { return (this.getBrightness(LightLayer.SKY, blockPos) >= LodUtil.MAX_MC_LIGHT); }
return (this.getBrightness(LightLayer.SKY, blockPos) >= this.getMaxLightLevel());
}
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver) public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
{ { return this.calculateBlockTint(blockPos, colorResolver); }
return this.calculateBlockTint(blockPos, colorResolver);
}
private Biome _getBiome(BlockPos pos) private Biome _getBiome(BlockPos pos)
{ {
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -22,11 +22,11 @@ import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
#endif #endif
/** /**
* @deprecated should be replaced with net.minecraft.world.level.chunk.storage.IOWorker to * Shouldn't be used when the C2ME mod is present,
* prevent potential file corruption and issues with the C2ME mod. * otherwise there may be potential file corruption.
* Generally this would be done via (MC ServerLevel) level.getChunkSource().chunkMap.worker#loadAsync() * When C2ME is present use (via MC ServerLevel) level.getChunkSource().chunkMap.worker#loadAsync()
* instead.
*/ */
@Deprecated
public class RegionFileStorageExternalCache implements AutoCloseable public class RegionFileStorageExternalCache implements AutoCloseable
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -46,22 +46,11 @@ public class RegionFileStorageExternalCache implements AutoCloseable
static class RegionFileCache
{
public final long pos;
public final RegionFile file;
public RegionFileCache(long pos, RegionFile file)
{
this.pos = pos;
this.file = file;
}
}
private final ConcurrentLinkedQueue<RegionFileCache> regionFileCache = new ConcurrentLinkedQueue<>();
public ConcurrentLinkedQueue<RegionFileCache> regionFileCache = new ConcurrentLinkedQueue<>();
public RegionFileStorageExternalCache(RegionFileStorage storage) { this.storage = storage; } public RegionFileStorageExternalCache(RegionFileStorage storage) { this.storage = storage; }
@@ -150,7 +139,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
if (retryCount >= maxRetryCount) if (retryCount >= maxRetryCount)
{ {
BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at " + pos + "."); BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at [" + pos + "].");
} }
@@ -237,4 +226,22 @@ public class RegionFileStorageExternalCache implements AutoCloseable
} }
//================//
// helper classes //
//================//
private static class RegionFileCache
{
public final long pos;
public final RegionFile file;
public RegionFileCache(long pos, RegionFile file)
{
this.pos = pos;
this.file = file;
}
}
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -61,35 +61,57 @@ public final class StepBiomes
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (!chunkWrapper.getStatus().isOrAfter(STATUS)) if (chunkWrapper.getStatus().isOrAfter(STATUS))
{ {
#if MC_VER < MC_1_21 // this chunk has already generated this step
((ProtoChunk) chunk).setStatus(STATUS); continue;
#else }
((ProtoChunk) chunk).setPersistedStatus(STATUS); else if (chunk instanceof ProtoChunk)
#endif {
chunkWrapper.trySetStatus(STATUS);
chunksToDo.add(chunk); chunksToDo.add(chunk);
} }
} }
for (ChunkAccess chunk : chunksToDo) for (ChunkAccess chunk : chunksToDo)
{ {
// System.out.println("StepBiomes: "+chunk.getPos());
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
this.environment.params.generator.createBiomes(this.environment.params.biomes, chunk); this.environment.params.generator.createBiomes(this.environment.params.biomes, chunk);
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, Blender.of(worldGenRegion), chunk = this.environment.confirmFutureWasRunSynchronously(
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); this.environment.params.generator.createBiomes(
this.environment.params.biomes,
Runnable::run,
Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk)
);
#elif MC_VER < MC_1_19_4 #elif MC_VER < MC_1_19_4
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion), chunk = this.environment.confirmFutureWasRunSynchronously(
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); this.environment.params.generator.createBiomes(
#elif MC_VER < MC_1_21 this.environment.params.biomes,
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion), Runnable::run,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk)
);
#elif MC_VER < MC_1_21_1
chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes(
Runnable::run,
this.environment.params.randomState,
Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk)
);
#else #else
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.randomState, Blender.of(worldGenRegion), chunk = this.environment.confirmFutureWasRunSynchronously(
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); this.environment.params.generator.createBiomes(
this.environment.params.randomState,
Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk)
);
#endif #endif
} }
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -59,14 +59,14 @@ public final class StepFeatures
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (!chunkWrapper.getStatus().isOrAfter(STATUS) if (chunkWrapper.getStatus().isOrAfter(STATUS))
&& chunk instanceof ProtoChunk)
{ {
#if MC_VER < MC_1_21 // this chunk has already generated this step
((ProtoChunk) chunk).setStatus(STATUS); continue;
#else }
((ProtoChunk) chunk).setPersistedStatus(STATUS); else if (chunk instanceof ProtoChunk)
#endif {
chunkWrapper.trySetStatus(STATUS);
} }
@@ -76,7 +76,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));
} }
@@ -87,7 +87,6 @@ public final class StepFeatures
#endif #endif
Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter()); Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter());
BatchGenerationEnvironment.clearDistantGenerationMixinData();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -67,32 +67,42 @@ public final class StepNoise
{ {
continue; continue;
} }
chunkWrapper.trySetStatus(STATUS);
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk); chunksToDo.add(chunk);
} }
for (ChunkAccess chunk : chunksToDo) for (ChunkAccess chunk : chunksToDo)
{ {
// System.out.println("StepNoise: "+chunk.getPos());
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
this.environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk); this.environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk);
#elif MC_VER < MC_1_18_2 #elif MC_VER < MC_1_18_2
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, chunk = this.environment.confirmFutureWasRunSynchronously(
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); this.environment.params.generator.fillFromNoise(
Runnable::run,
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk));
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), chunk = this.environment.confirmFutureWasRunSynchronously(
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); this.environment.params.generator.fillFromNoise(
#elif MC_VER < MC_1_21 Runnable::run,
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk));
#elif MC_VER < MC_1_21_1
chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise(
Runnable::run,
Blender.of(worldGenRegion),
this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk));
#else #else
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Blender.of(worldGenRegion), this.environment.params.randomState, chunk = this.environment.confirmFutureWasRunSynchronously(
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); this.environment.params.generator.fillFromNoise(
Blender.of(worldGenRegion),
this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk));
#endif #endif
UncheckedInterruptedException.throwIfInterrupted(); UncheckedInterruptedException.throwIfInterrupted();
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -59,14 +59,14 @@ public final class StepStructureReference
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (!chunkWrapper.getStatus().isOrAfter(STATUS)) if (chunkWrapper.getStatus().isOrAfter(STATUS))
{ {
#if MC_VER < MC_1_21 // this chunk has already generated this step
((ProtoChunk) chunk).setStatus(STATUS); continue;
#else }
((ProtoChunk) chunk).setPersistedStatus(STATUS); else if (chunk instanceof ProtoChunk)
#endif {
chunksToDo.add(chunk); chunkWrapper.trySetStatus(STATUS);
} }
} }
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -28,7 +28,9 @@ 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 com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.WorldGenRegion; import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.ProtoChunk;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -76,24 +78,25 @@ public final class StepStructureStart
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (!chunkWrapper.getStatus().isOrAfter(STATUS)) if (chunkWrapper.getStatus().isOrAfter(STATUS))
{ {
#if MC_VER < MC_1_21 // this chunk has already generated this step
((ProtoChunk) chunk).setStatus(STATUS); continue;
#else }
((ProtoChunk) chunk).setPersistedStatus(STATUS); else if (chunk instanceof ProtoChunk)
#endif {
chunksToDo.add(chunk); chunkWrapper.trySetStatus(STATUS);
} }
} }
#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)
@@ -109,15 +112,20 @@ public final class StepStructureStart
STRUCTURE_PLACEMENT_LOCK.lock(); STRUCTURE_PLACEMENT_LOCK.lock();
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
environment.params.generator.createStructures(environment.params.registry, tParams.structFeat, chunk, environment.params.structures, this.environment.params.generator.createStructures(this.environment.params.registry, tParams.structFeat, chunk, this.environment.params.structures,
environment.params.worldSeed); this.environment.params.worldSeed);
#elif MC_VER < MC_1_19_4 #elif MC_VER < MC_1_19_4
environment.params.generator.createStructures(environment.params.registry, environment.params.randomState, tParams.structFeat, chunk, environment.params.structures, this.environment.params.generator.createStructures(this.environment.params.registry, this.environment.params.randomState, tParams.structFeat, chunk, this.environment.params.structures,
environment.params.worldSeed); this.environment.params.worldSeed);
#elif MC_VER <= MC_1_21_3
this.environment.params.generator.createStructures(this.environment.params.registry,
this.environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, this.environment.params.structures);
#else #else
environment.params.generator.createStructures(environment.params.registry, this.environment.params.generator.createStructures(this.environment.params.registry,
environment.params.level.getChunkSource().getGeneratorState(), this.environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, environment.params.structures); tParams.structFeat, chunk, this.environment.params.structures,
this.environment.params.level.dimension());
#endif #endif
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
@@ -2,7 +2,7 @@
* This file is part of the Distant Horizons mod * This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License. * licensed under the GNU LGPL v3 License.
* *
* Copyright (C) 2020-2023 James Seibel * Copyright (C) 2020 James Seibel
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by * it under the terms of the GNU Lesser General Public License as published by
@@ -58,14 +58,14 @@ public final class StepSurface
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (!chunkWrapper.getStatus().isOrAfter(STATUS)) if (chunkWrapper.getStatus().isOrAfter(STATUS))
{ {
#if MC_VER < MC_1_21 // this chunk has already generated this step
((ProtoChunk) chunk).setStatus(STATUS); continue;
#else }
((ProtoChunk) chunk).setPersistedStatus(STATUS); else if (chunk instanceof ProtoChunk)
#endif {
chunkWrapper.trySetStatus(STATUS);
chunksToDo.add(chunk); chunksToDo.add(chunk);
} }
} }
@@ -2,6 +2,8 @@ accessWidener v1 named
# used when determining where to save files to # used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File; accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering # used when rendering
accessible field com/mojang/blaze3d/vertex/VertexBuffer vertexCount I accessible field com/mojang/blaze3d/vertex/VertexBuffer vertexCount I
@@ -31,6 +33,9 @@ accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecr
#accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder; #accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder;
#accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doCreateBiomes (Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)V #accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doCreateBiomes (Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)V
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
# lod generation from save file # lod generation from save file
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop; accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
@@ -47,4 +52,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
@@ -1,8 +1,9 @@
accessWidener v1 named accessWidener v1 named
# used when determining where to save files to # used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File; accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering # used when rendering
accessible field com/mojang/blaze3d/vertex/VertexBuffer indexCount I accessible field com/mojang/blaze3d/vertex/VertexBuffer indexCount I
@@ -31,6 +32,11 @@ accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecr
# accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder; # accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder;
#accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doCreateBiomes (Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)V #accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doCreateBiomes (Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)V
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file # lod generation from save file
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop; accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
@@ -2,6 +2,8 @@ accessWidener v1 named
# used when determining where to save files to # used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File; accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering # used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
@@ -15,6 +17,11 @@ accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chu
# world generation # world generation
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file # 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/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
@@ -2,6 +2,8 @@ accessWidener v1 named
# used when determining where to save files to # used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File; accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering # used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
@@ -15,6 +17,11 @@ accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chu
# world generation # world generation
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file # 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/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
@@ -2,6 +2,8 @@ accessWidener v1 named
# used when determining where to save files to # used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File; accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering # used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
@@ -15,6 +17,11 @@ accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chu
# world generation # world generation
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file # 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/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
@@ -2,6 +2,8 @@ accessWidener v1 named
# used when determining where to save files to # used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File; accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel iss
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering # used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
@@ -17,6 +19,11 @@ accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chu
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage; accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage;
accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file # 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/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;

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