Compare commits

...

677 Commits

Author SHA1 Message Date
James Seibel 71e6b9b58e remove unused getFov() 2026-04-05 17:17:28 -05:00
Ran-Mewo 1ec342928f fix api tests 2026-03-30 17:06:05 +11:00
James Seibel d45a1379bd Fix running fabric via gradle/IDE 2026-03-29 16:04:37 -05:00
Ran-Mewo ecb3dce963 wao 2026-03-29 19:23:25 +11:00
James Seibel 2b8cddd424 disable thread pausing when render tasks exist 2026-03-27 07:00:38 -05:00
James Seibel 13895fec51 start cleaning up config 2026-03-24 19:37:52 -05:00
James Seibel 88c7245be6 change EDhApiRendererMode DEBUG -> DEBUG_TRIANGLE 2026-03-24 19:36:57 -05:00
James Seibel 6aad156a32 Fix Iris using the wrong far clip plane
https://github.com/IrisShaders/Iris/issues/2534
2026-03-24 07:16:31 -05:00
James Seibel 94535a213e LodRequestModule TODO cleanup 2026-03-22 21:40:32 -05:00
James Seibel 63d6d42356 ignore already closed repo close calls 2026-03-22 21:36:31 -05:00
James Seibel acecbede8e fix phantom log when there is sufficient memory 2026-03-21 16:57:38 -05:00
James Seibel e2a8953e4c minor cloud render cleanup 2026-03-21 16:35:33 -05:00
James Seibel 32eae23963 stage VBO/IBO upload and allow global IBO 2026-03-21 15:33:36 -05:00
James Seibel 480c3b3ec5 Drastically improve frame stability 2026-03-21 15:20:28 -05:00
James Seibel 6c0736a2a0 minor renderer refactoring 2026-03-19 07:28:41 -05:00
James Seibel c89abd414b Add render task profiling 2026-03-18 07:45:42 -05:00
James Seibel da2454b249 Add the ability to cull lilly pads, seaGrass, etc. 2026-03-15 20:53:21 -05:00
James Seibel 5933ef8245 Fix MC complaining about GL shader file names 2026-03-15 16:52:03 -05:00
James Seibel b9984c7723 remove unneeded chunk update warnings 2026-03-15 16:46:40 -05:00
James Seibel 2de50475f1 reduce the max time for render thread tasks 2026-03-15 16:19:33 -05:00
James Seibel bb838328a7 Show RenderApi config in UI 2026-03-15 16:15:34 -05:00
James Seibel 1980e64b2f Fix vertex buffer count calculation 2026-03-15 14:55:49 -05:00
James Seibel c3df26f5cb Disable Mac initial startup delay 2026-03-15 14:54:06 -05:00
James Seibel 17df533fa6 Improve warning logs for chunkUpdateManager 2026-03-14 15:55:38 -05:00
James Seibel 2393bdd8e8 clean up todo comments 2026-03-14 15:33:23 -05:00
James Seibel 082b364f52 default renderable box group to clean 2026-03-14 15:25:44 -05:00
James Seibel 8a39610b8c move beacon render enabling to LodQuadTree
Fixes beacons not always showing/hiding correctly
2026-03-14 14:32:19 -05:00
James Seibel aa53835772 fix generic rendering not uploading in some cases 2026-03-14 14:30:58 -05:00
James Seibel cd5a3ce52b Try fix concurrency issue with render closing 2026-03-14 10:18:40 -05:00
James Seibel 38e104a9fc Render task handler name tracking 2026-03-14 09:20:01 -05:00
James Seibel e27cee1f71 SharedApi set world lock 2026-03-14 09:19:41 -05:00
James Seibel 15d1d78954 Fix render task auto cleanup timer 2026-03-14 09:18:28 -05:00
s809 e2421c97ed Stop pregen on server shutdown 2026-03-13 00:47:44 +05:00
s809 8b72920652 Show real pregen speed quicker 2026-03-13 00:01:01 +05:00
s809 212031a05f Fix world generation not running with 0 players connected 2026-03-12 22:48:27 +05:00
s809 804293e291 Replace fix with debug wireframe stub 2026-03-12 22:38:37 +05:00
s809 cfdd464f30 Revert "Fix server not loading levels"
This reverts commit 4c9d703e15.
2026-03-12 21:12:04 +05:00
s809 4c9d703e15 Fix server not loading levels 2026-03-12 19:20:26 +05:00
James Seibel 0eba376e70 up Dh version 2.4.6 -> 3.0.0 2026-03-10 19:07:39 -05:00
James Seibel 89e6355d73 Merge branch 'blazeRender' 2026-03-10 19:06:58 -05:00
James Seibel 31b6d2dd05 Fix debug wireframes not rendering 2026-03-10 19:06:02 -05:00
James Seibel d75b65e6e7 hide render API config from UI 2026-03-10 18:44:48 -05:00
James Seibel 1bbe41c068 move GL shaders into the correct folder 2026-03-10 17:56:05 -05:00
James Seibel e426fc2380 Move lightmap wrapper methods into common 2026-03-10 17:33:55 -05:00
James Seibel 4e908b5b15 Make render interfaces consistent 2026-03-10 17:20:19 -05:00
James Seibel b51ab3d9cd clear up render task logging 2026-03-10 17:03:13 -05:00
James Seibel 9e7d0a1538 method rename 2026-03-10 16:58:57 -05:00
James Seibel 5b0bf59f00 fix iris rendering 2026-03-10 16:44:32 -05:00
James Seibel 0362d89173 Separate out some rendering logic 2026-03-10 14:50:53 -05:00
James Seibel 1b0f93db07 Changes for blaze/gl rendering config 2026-03-10 11:40:36 -05:00
James Seibel 559bad5676 Add rendering API definition 2026-03-09 20:12:40 -05:00
James Seibel f145444fd4 fix race condition in SharedApi setup 2026-03-09 20:07:31 -05:00
James Seibel 10b08ca116 Fix ClientApi profiler adding an incorrect layer 2026-03-09 19:14:21 -05:00
James Seibel 1dd244e889 Rename and reorganize render pass interfaces 2026-03-09 18:59:19 -05:00
James Seibel 2ea3d645e8 remove deprication warnings 2026-03-09 17:39:58 -05:00
James Seibel 49e34d78a5 update debug wireframe renderer 2026-03-09 17:35:24 -05:00
James Seibel 17cdb0f745 re-add some core rendering handlers 2026-03-09 16:35:17 -05:00
James Seibel 67b2467bee change where vertex size is found 2026-03-09 16:34:25 -05:00
James Seibel 82c832a4af Add RenderThreadTaskHandler 2026-03-09 16:30:45 -05:00
James Seibel c84dbfceaf update some imports 2026-03-09 16:26:48 -05:00
James Seibel 27b66940be Start moving OpenGL rendering to common 1 2026-03-09 16:22:55 -05:00
James Seibel 8240101a46 javadoc 2026-03-09 13:58:29 -05:00
James Seibel e1a932cf38 Only upload unique LOD uniforms once 2026-03-09 13:51:44 -05:00
James Seibel 39dd1c8509 renderable box cleanup 2026-03-09 12:28:49 -05:00
James Seibel fee0aadcbe MC -> blaze renaming 2026-03-09 12:19:53 -05:00
James Seibel a8c15d22c3 rename vbo containers 2026-03-09 11:16:50 -05:00
James Seibel bd833ba510 move blaze shader files 2026-03-09 09:57:03 -05:00
James Seibel a5a9a62e89 Fix ssao application 2026-03-08 21:14:48 -05:00
James Seibel e790bfb7e8 merge apply shaders 2026-03-07 14:32:02 -06:00
James Seibel 0f539f3a6f start separating out uniform logic 2026-03-05 17:32:19 -06:00
James Seibel d3f28f064b fix chunk update queue count flipped 2026-03-05 07:23:13 -06:00
James Seibel 40149bc1d1 fix chunk update queue count flipped 2026-03-05 07:23:02 -06:00
James Seibel 40703db763 uncomment deferred rendering 2026-03-05 07:22:22 -06:00
James Seibel 2a554395f7 debug rendering 2026-03-04 18:07:49 -06:00
James Seibel c3dce412fa Add far fade 2026-03-04 07:38:49 -06:00
James Seibel 225c2df8df add fog 2026-03-03 07:47:58 -06:00
James Seibel 84c212a780 add SSAO 2026-03-02 07:45:25 -06:00
James Seibel 3d9ae5f088 generic object rendering 2026-03-01 19:32:54 -06:00
James Seibel 200e089a33 re-add lighting 2026-02-28 12:13:34 -06:00
James Seibel 3f509be195 remove unneeded partial ticks from old vanilla fade 2026-02-28 11:34:48 -06:00
James Seibel 1a2c0b0be1 re-add vanilla fading 2026-02-28 11:34:39 -06:00
James Seibel c6a4355718 minor render cleanup 2026-02-28 08:13:26 -06:00
James Seibel 3e80961d18 Rough initial LOD renderering 2026-02-26 16:55:54 -06:00
James Seibel 9f483ee07a Fix fade test shader 2026-02-24 22:03:43 -06:00
James Seibel d1ba402f4d start of vanilla fade logic 2026-02-24 09:57:03 -06:00
James Seibel 87cead607f move test shader folder 2026-02-24 07:02:05 -06:00
James Seibel 1b2992f1dc proof-of-concept test renderer 2026-02-23 12:31:21 -06:00
James Seibel ed0e94ccb7 Fix chunks applying to the wrong dimension 2026-02-18 22:05:23 -06:00
James Seibel 364f8f7afc improve transformer comment 2026-02-18 07:08:44 -06:00
James Seibel 962f20460c Fix dark LODs with false tintWithAvoidedBlocks 2026-02-17 07:47:30 -06:00
James Seibel ce17ac71c6 Remove buffer mapping 2026-02-17 07:16:42 -06:00
James Seibel 5adb9afa0a always disable instanced rendering on mac 2026-02-16 07:16:32 -06:00
James Seibel 3c80c11888 comment on a partially broken unit test 2026-02-14 22:34:17 -06:00
James Seibel 0714697e05 Fix beacon update locks 2026-02-14 22:01:25 -06:00
James Seibel df52f41b87 Fix beacons not reloading (and improve logic) 2026-02-14 21:32:30 -06:00
James Seibel 66a1d0296e maybe fix deprecated warn in OptifineAcessor 2026-02-14 21:31:51 -06:00
James Seibel c827817a94 ClientLevelModule cleanup 2026-02-14 11:10:35 -06:00
James Seibel 9895676b63 Increase startup timeout for MAC 2026-02-14 08:32:44 -06:00
James Seibel 5d5c94e652 Force enable fog if MC is rendering fog
done to fix underwater/blindness rendering
2026-02-14 08:24:22 -06:00
James Seibel 0a568571d9 Add logs when changing vanilla settings 2026-02-13 07:49:31 -06:00
James Seibel 291c0470ad auto disable fancy graphics if enabled 2026-02-13 07:35:50 -06:00
James Seibel 2f4587579f Change quadElementBuffer to only use DATA 2026-02-13 07:35:38 -06:00
James Seibel 2d70388587 Maybe fix Mac crashing with sodium on world start? 2026-02-12 07:27:11 -06:00
James Seibel a758c0bb3f regions to initalizer 2026-02-12 07:13:05 -06:00
James Seibel dd6aed273d Hopefully fix a rare concurrency issue in buffer Builder 2026-02-11 07:45:42 -06:00
James Seibel e2d663ee34 remove some unneeded concurrency checks in render setup 2026-02-11 07:34:55 -06:00
James Seibel dfc920d9bb Fix a harmless error message 2026-02-11 07:15:49 -06:00
James Seibel c91631809e re-add chat warning if G1GC is used 2026-02-11 07:09:33 -06:00
James Seibel 457bbebbdd Reduce memory allocation slightly during LOD loading 2026-02-10 07:32:13 -06:00
James Seibel 4d3242a370 improve concurrency handling in LOD render loading 2026-02-09 07:44:03 -06:00
James Seibel f0d71027f1 Fix loosing some thread pool tasks 2026-02-09 07:43:53 -06:00
James Seibel a98d9239e6 document Vbo.getVertexCount() 2 2026-02-08 21:37:33 -06:00
James Seibel 365edd48f8 document Vbo.getVertexCount() 2026-02-08 21:37:17 -06:00
James Seibel 9f22e3f2b3 Improve render load error handling 2026-02-08 21:06:01 -06:00
James Seibel 78f83197d7 Hide non-rendering levels in the F3 screen 2026-02-08 20:42:32 -06:00
James Seibel 42ae79b76c remove deprecated OS enum (replaced by EPlatform) 2026-02-08 20:12:26 -06:00
James Seibel 1178ef0706 Remove unused ID mappings after data update
Requires re-downsampling all LODs
2026-02-08 19:56:24 -06:00
James Seibel a506d2ef1f FullDataSource regions 2026-02-08 19:55:09 -06:00
James Seibel 92d9e631a7 Fix update propagator only re-queueing once queue empty 2026-02-08 19:53:03 -06:00
James Seibel 8afe388eb6 DataIdMap to string 2026-02-08 19:52:32 -06:00
James Seibel df1dae70c2 FullDataPointUtil reformat 2026-02-08 19:52:23 -06:00
James Seibel 777ed9215a datasource v1 mapping decode cleanup 2026-02-08 17:25:27 -06:00
James Seibel 25f4bde825 delayed full data cache regions 2026-02-07 18:37:21 -06:00
James Seibel fbb1beb359 ConfigEntry tostring for debugging 2026-02-07 18:15:12 -06:00
James Seibel f0852235b2 LodQuadTree cleanup and todo remove 2026-02-07 18:13:27 -06:00
James Seibel 4faa82e895 DhServerLevel use base shouldDoWorldGen()
@pshsh @s809 do you know why DhServerLevel overrode AbstractDhServerLevel?
In a quick test it appears using the super method worked just fine.
2026-02-07 17:59:08 -06:00
James Seibel 7201df1eff retrieval queue cleanup 2026-02-07 17:57:03 -06:00
James Seibel b6b13843eb shader constructor cleanup 2026-02-07 17:46:30 -06:00
James Seibel 10208d4dfa fix vanilla fade order 2026-02-07 17:00:30 -06:00
James Seibel d04e156dd1 Move some world gen queue limiting into the LodQuadTree 2026-02-07 16:37:56 -06:00
James Seibel 19412f80c5 cleanup cloud render handler 2026-02-07 16:13:33 -06:00
James Seibel dea3557546 Add colors to the F3 screen 2026-02-07 14:14:13 -06:00
James Seibel a3c72dbcbc clientApi remove finished todo comments 2026-02-07 13:28:22 -06:00
James Seibel dffad16d27 Clean up ColumnArrayView 2026-02-07 13:20:06 -06:00
James Seibel 0a3756eb9d Remove swing UI classes 2026-02-07 13:19:44 -06:00
James Seibel 821acaa0b9 MovableGridRingList cleanup 2026-02-07 12:05:18 -06:00
James Seibel 0fc5c55712 ArrayGridList pos object merge 2026-02-07 11:55:16 -06:00
James Seibel c13d04ff2b vanillaFade shader cleanup 2026-02-07 11:54:44 -06:00
James Seibel c9925b0b6c full data point ID map regions 2026-02-07 11:00:51 -06:00
James Seibel 725602e61f handle extra sql db closed 2026-02-07 10:18:40 -06:00
James Seibel bdeeb4c93b remove TODO for LWJGL versioning 2026-02-07 10:01:05 -06:00
James Seibel b3185c00d6 Replace DhLodPos with DhSectionPos 2026-02-07 08:40:17 -06:00
James Seibel ce20d3da32 debug render cleanup 2026-02-07 08:31:51 -06:00
James Seibel 69653336bf Remove most SSAO configs
No one seems to need or use them so we can just hard-code in the values we know look good.
2026-02-05 21:58:30 -06:00
James Seibel 062f11df21 Change graphics configs from doubles to floats 2026-02-05 21:57:17 -06:00
James Seibel 9e68d11608 re-add ignored blocks to config UI 2026-02-05 21:37:37 -06:00
James Seibel 3d349cb292 Only allow rendering clouds in config set dimensions 2026-02-05 21:37:29 -06:00
James Seibel d13bd9c4d3 clean up QuadTree unit tests 2026-02-05 21:12:25 -06:00
James Seibel 4a256683ab comments and region for update prop threading 2026-02-05 18:01:21 -06:00
James Seibel 7325d57e88 Make GLState AutoClosable for more robustness 2026-02-05 17:36:40 -06:00
James Seibel 7348cb1c37 Remove incomplete standalone GUI and comment incomplete DB parsing 2026-02-05 17:29:19 -06:00
James Seibel 007a3bdefa remove legacy logo SVG
Still available in the core repo's misc folder
2026-02-05 17:11:29 -06:00
James Seibel 6d95eb1099 fix magic numbers for lightmap binding 2026-02-05 17:11:09 -06:00
James Seibel 86e21cdaf6 remove unused VertexFormats 2026-02-05 17:02:15 -06:00
James Seibel d0bb03288d Fix fog falloff config not being used 2026-02-05 16:57:39 -06:00
James Seibel be3e618aca shader reformatting 2026-02-05 16:49:30 -06:00
James Seibel 35d3614c88 add a TODO to GenFullDataSourceProvider 2026-02-05 07:40:47 -06:00
James Seibel ba32af2c58 improve priority task picker task counting 2026-02-05 07:40:34 -06:00
James Seibel 2a681ef8e6 Pool ZStd decompress contexts 2026-02-05 07:40:11 -06:00
James Seibel 24684b7760 Fix render loading queuing incorrectly 2026-02-05 07:39:58 -06:00
James Seibel d3d4312ff6 Handle cave block culling more generically 2026-02-04 07:47:54 -06:00
James Seibel 6086a8a957 null check to AbstractConfigBase 2026-02-04 07:47:18 -06:00
James Seibel cd2530ca88 cleanup TODO comments 2026-02-04 07:47:08 -06:00
James Seibel af0e5699e2 Fix !1088 (API config.getApiValue() not returning null) 2026-02-04 07:19:50 -06:00
James Seibel d0e14ac408 LZMA cache commenting 2026-02-03 21:32:16 -06:00
James Seibel 0636712f80 Fix a rare world gen phantom closing issue 2026-02-03 21:32:10 -06:00
James Seibel 9bfd2812a6 fix near clip plane flickering 2026-02-03 21:30:16 -06:00
James Seibel 5f12c9e1f3 rename normal.vert -> quadApply.vert 2026-02-03 20:50:45 -06:00
James Seibel f8f0e3f4b9 move phantom pool into a new namespace 2026-02-03 20:48:07 -06:00
James Seibel ac36ce4560 refactoring and TODO cleanup 2026-02-03 20:47:06 -06:00
James Seibel f2612f39c0 cleanup world gen queue todo comments 2026-02-03 20:35:15 -06:00
James Seibel e7e7bc866d Clean up RenderUtil near clip methods 2026-02-03 20:34:54 -06:00
James Seibel 39eed05d83 Fix Debug renderer on newer MC versions 2026-02-03 19:59:12 -06:00
James Seibel b0624c8714 up the api version 5.1.0 -> 6.0.0 2026-02-03 07:05:50 -06:00
James Seibel 40351d1894 Require a data cache for API Repo methods 2026-02-03 07:05:13 -06:00
James Seibel a488478b1d null check in level loading 2026-02-02 07:18:00 -06:00
James Seibel c4b49ef308 Remove MC Texture LodBias config 2026-02-02 07:17:49 -06:00
James Seibel b511ab4fb3 Add RenderWrapper.getPartialTickTime() 2026-02-02 07:09:06 -06:00
James Seibel bbf69c7911 Make generic object updating async 2026-01-31 17:33:03 -06:00
James Seibel 6de41cd384 LodRenderSection remove unused code 2026-01-31 16:01:34 -06:00
James Seibel 7e7ccf1f38 unindent RenberableBoxGroup 2026-01-31 12:50:16 -06:00
James Seibel 5a6aa00ae7 RenberableBoxGroup regions 2026-01-31 12:49:58 -06:00
James Seibel 9c71540928 clean up bufferQuad arg names 2 2026-01-31 12:49:49 -06:00
James Seibel ff1859de8d clean up BufferQuad arg names 2026-01-31 12:42:03 -06:00
James Seibel 00be9a3c4f Handle MC running at 0 FPS 2026-01-31 10:22:23 -06:00
James Seibel dfed2c8966 GLproxy regions 2026-01-31 09:16:44 -06:00
James Seibel b20aebb09c generic renderer regions 2026-01-31 09:15:22 -06:00
James Seibel 9cbe98f81b clean up more TODO comments 2026-01-30 07:49:24 -06:00
James Seibel a9bfc3fbe1 Clean up more TODOs 2026-01-29 07:48:10 -06:00
James Seibel c30e410132 Clean up some API TODOs 2026-01-29 07:14:30 -06:00
James Seibel 11cd36cdec Fix TODO in DependencyInjector 2026-01-28 07:20:05 -06:00
James Seibel 59e35ffad3 Fix beacons not rendering 2026-01-27 20:50:53 -06:00
James Seibel 7b26c0baeb Fix paused executors not running tasks 2026-01-27 20:49:54 -06:00
James Seibel 60e53ff20b remove debug log 2026-01-27 20:49:42 -06:00
James Seibel 0f756a370b Queue gen tasks outside the tree update thread 2026-01-27 20:06:25 -06:00
James Seibel f0f525ef79 Fix ZStd decompressions streams being to long 2026-01-27 20:05:50 -06:00
James Seibel 59a8e31507 Reduce GC pressure when loading LODs from disk 2026-01-27 18:21:08 -06:00
James Seibel 6d3288ec42 LodRenderSection remove rate limit and improve task ordering 2026-01-26 20:29:55 -06:00
James Seibel 4e979b1f00 LodQuadBuilder regions 2026-01-26 06:52:45 -06:00
James Seibel 396545340e Move Thread Pool stat string method 2026-01-26 06:52:07 -06:00
James Seibel 87bfbf1afe Add a bunch of TODO comments 2026-01-26 06:51:05 -06:00
James Seibel 4346a2e803 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2026-01-24 13:39:00 -06:00
James Seibel 6c228c1a17 Move Quad tree related objects 2026-01-24 13:38:02 -06:00
James Seibel d90361af59 Change LOD loading to start at lowest detail 2026-01-24 13:37:23 -06:00
s809 a0f06e4451 Merge branch 'feature/split-generation-toggles' 2026-01-18 22:39:43 +05:00
s809 2a4bfef7a6 Add correct descriptions 2026-01-18 22:38:01 +05:00
James Seibel 37d08b6dfa pause world gen when moving quickly 2026-01-17 17:03:43 -06:00
James Seibel eadf19405e add changes from DhRenderState 2026-01-17 16:15:46 -06:00
James Seibel f0757296f8 Add dynamic overdraw distance based on camera speed 2026-01-17 16:12:06 -06:00
James Seibel 3e29b361e6 Improve clientApi code regions 2026-01-17 10:46:45 -06:00
James Seibel 7b6fd03d78 Change render wrapper get Texture error returns 2026-01-17 09:56:21 -06:00
s809 7be65a2258 Split off server generation into a separate toggle 2026-01-17 01:43:05 +05:00
s809 1a540cf2bc Make sure payload chunk is readable 2026-01-14 22:17:46 +05:00
James Seibel 20fc2efb46 Improve concurrent iterating in QuadTree 2026-01-10 17:03:43 -06:00
James Seibel d8beba2498 minor cleanup in LodBufferContainer cleanup 2026-01-10 17:02:56 -06:00
James Seibel 9f0cb5a394 Add forge specific icon/logo
Done to fix a forge limitation where logos can't contain a file pathhttps://github.com/MinecraftForge/MinecraftForge/issues/7348
2026-01-10 11:56:08 -06:00
James Seibel df63401d11 DB updater use correct classloader 2026-01-10 08:21:09 -06:00
James Seibel db95951ade minor reformat and comment 2026-01-10 08:20:44 -06:00
s809 1e020f93a6 Reapply "Run plugin messages on a DH thread"
This reverts commit ff3145336d.
2026-01-09 20:29:23 +05:00
James Seibel 7aee6dfb44 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2026-01-07 07:50:25 -06:00
James Seibel 546a51a295 expand distant beacon beams for visiblity 2026-01-07 07:50:22 -06:00
James Seibel ec7e791e9f Change EMinecraftColor -> MinecraftTextFormat
No need for an enum when all the values are strings
2026-01-06 07:10:40 -06:00
James Seibel d60dec3d82 Merge branch 'main' into 'main'
Fix typo in high vanilla render distance warning

See merge request distant-horizons-team/distant-horizons-core!94
2026-01-05 13:00:17 +00:00
s809 89a80103f0 Wrong message target 2026-01-04 20:04:30 +05:00
s809 8e14a7223c Add a chat message for incompatible messages 2026-01-04 19:36:24 +05:00
meanwhile131 7cf1e901f5 Fix typo in high vanilla render distance warning 2026-01-01 15:06:30 +04:00
James Seibel ba923fa829 Fix neoforge thread causing resource loading to fail 2025-12-26 14:13:27 -06:00
s809 505dbe2f62 Replace the failure state with future exceptions 2025-12-27 00:51:30 +05:00
James Seibel 48c5828e8f up version number 2.4.5 -> 2.4.6-dev 2025-12-24 22:41:27 -06:00
James Seibel eb2317934f up version number 2.4.4 -> 2.4.5 2025-12-24 22:06:53 -06:00
James Seibel 60537cda1b Replace MC color code strings with an enum 2025-12-24 22:04:50 -06:00
James Seibel 508ff2b776 Fix null pointer in ChunkUpdateQueueManager 2025-12-24 21:53:39 -06:00
James Seibel 7c4ac2bd7e remove dev from version number 2025-12-23 22:55:40 -06:00
James Seibel 8c13c2cf47 Fix toggling world gen not recreating queue 2025-12-23 22:55:40 -06:00
James Seibel 802019ff72 up DH api version 5.0.0 -> 5.1.0 2025-12-23 20:01:06 -06:00
James Seibel 141890556c Revert "remove deprecated getHeight() from DhApiLevelWrapper"
This reverts commit 50bdb73a52.
2025-12-23 19:56:28 -06:00
James Seibel 353838db41 add experimental option to ignore rendering dimensions by name 2025-12-23 12:22:00 -06:00
James Seibel f1547477c9 add clientLevelWrapper to DhApiRenderParam 2025-12-23 12:20:42 -06:00
James Seibel 535a645a84 minor internal API cleanup 2025-12-23 12:19:14 -06:00
James Seibel 2dc7f02b32 Remove experimental option onlyLoadCenterLods
option is now merged into main
2025-12-23 12:18:28 -06:00
James Seibel 50bdb73a52 remove deprecated getHeight() from DhApiLevelWrapper
use getMaxHeight() instead
2025-12-23 12:06:15 -06:00
James Seibel 53e6c95432 commenting DhTerrainShaderProgram 2025-12-23 08:57:51 -06:00
James Seibel 36f0029e45 Fix earth curvature shader compiling 2025-12-23 08:47:44 -06:00
s809 5067e970a2 Use another method to create a buffer 2025-12-23 12:50:02 +05:00
James Seibel 167ca94e69 Remove deprecated disableVanillaFog config 2025-12-22 20:31:24 -06:00
James Seibel 8d94b86bfd Hide network config changes by default 2025-12-22 14:51:29 -06:00
James Seibel a29567430e Net only log changed config values 2025-12-22 14:37:34 -06:00
James Seibel fb2dae48e2 re-enable remote timestamp getting 2025-12-22 14:21:12 -06:00
James Seibel 948b4bfd9c comment out debug log 2025-12-22 14:17:57 -06:00
James Seibel ca44256ca9 disable full data debug phantom array stacks 2025-12-22 14:17:47 -06:00
James Seibel a29b6a5aab remove unnecessary config appearance check 2025-12-22 14:17:13 -06:00
James Seibel 868254ccc8 try fixing rare leak in delayed data source cache
Didn't fix the problem, but shouldn't hurt
2025-12-22 14:16:34 -06:00
James Seibel 195fde8d73 quad tree spilt request cleanup 2025-12-22 13:58:26 -06:00
James Seibel ce7b9b94b6 fix/improve world gen/retrieval error handling 2025-12-22 13:58:26 -06:00
James Seibel 1f0c2e286a fix network splitting requests 2025-12-22 13:58:26 -06:00
James Seibel f79fd5e06f error handling in AbstactDhLevel chunk update 2025-12-22 13:58:26 -06:00
James Seibel 47c1d3955f failed attmpt to fix leaks
Breaks split world gen requests
2025-12-22 13:57:49 -06:00
James Seibel 2c5f5a3d4c minor refactors 2025-12-22 09:46:21 -06:00
James Seibel 81c533051e close errored data sources in full data provider 2025-12-22 08:35:15 -06:00
James Seibel 5cbe5ecfd8 Fix dis/re-enabling world gen queuing 2025-12-21 19:44:48 -06:00
James Seibel d4b4d28c9f Fix null error log in Data source provider 2025-12-21 08:53:27 -06:00
James Seibel b8e653b5f7 Fix phantom checkout not updating stack trace 2025-12-21 08:52:44 -06:00
James Seibel 80fea09598 Fix concurrency error in LodQuadTree 2025-12-21 08:52:25 -06:00
James Seibel 1d4f914a9f Merge branch 'worldGenRefactor' 2025-12-20 10:53:39 -06:00
James Seibel bf92dea2eb reduce stuttering at the cost of lighting quality 2025-12-20 10:52:51 -06:00
s809 2dd675b8da Handle generated LOD updates outside the render thread 2025-12-20 15:22:26 +05:00
s809 ff3145336d Revert "Run plugin messages on a DH thread"
This reverts commit 280181c91e.
2025-12-20 14:32:39 +05:00
James Seibel 280181c91e Run plugin messages on a DH thread 2025-12-19 16:54:29 -06:00
James Seibel 60232e713b refactor world gen queue 2025-12-19 16:54:07 -06:00
James Seibel 55d9030954 Remove extra particle for world gen 2025-12-18 10:20:01 -06:00
James Seibel 452bd75f5d remove chunkWrapper.isStillValid() 2025-12-18 10:18:07 -06:00
James Seibel 72be1e2602 Remove LodRenderSection.isFullyGenerated() 2025-12-18 10:17:36 -06:00
James Seibel 1c30213aca up version number 2.4.3 -> 2.4.4-dev 2025-12-18 10:04:41 -06:00
James Seibel e9a044308f remove dev from version number 2025-12-18 09:35:07 -06:00
James Seibel 1aabc0c792 remove chunkWrapper.isStillValid() 2025-12-18 09:35:02 -06:00
James Seibel 4a1513ed65 fix compiling 2025-12-17 22:41:22 -06:00
James Seibel 6d98c9cb84 start world gen refactoring 2025-12-17 22:39:23 -06:00
James Seibel b1b0642fbe LodRenderSection commenting/regions 2025-12-17 09:32:12 -06:00
James Seibel eecb28d11f Fix GLProxy error in multiplayer
Make some GLProxy methods static to prevent setup order issues
2025-12-17 09:02:07 -06:00
James Seibel 90564f2537 fix javadoc in LevelWrapper 2025-12-16 16:39:03 -06:00
James Seibel ded0b979cf Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2025-12-16 14:45:57 -06:00
James Seibel ed9cc5485c Add SSAO fade out distance 2025-12-16 14:45:53 -06:00
s809 cbd5974657 Fix packet handle errors not showing on F3 screen 2025-12-17 00:15:55 +05:00
James Seibel 0e5fba58ab minor shader program refactor 2025-12-16 09:13:22 -06:00
James Seibel 2943e63382 slight light engine optimization 2025-12-15 14:37:15 -06:00
James Seibel 30564aade7 up version number 2.4.2 -> 2.4.3-dev 2025-12-15 10:17:28 -06:00
James Seibel aabb90ada6 remove dev from version number 2025-12-15 09:00:15 -06:00
James Seibel 963a8dc53f comment LevelWrapper getDimensionName() 2025-12-15 08:55:40 -06:00
James Seibel aa6d69385b Move GC warning into the log 2025-12-15 08:44:06 -06:00
James Seibel f42c9cf8fb Improve initial library check error handling 2025-12-14 22:29:08 -06:00
James Seibel 92e0011c8d Fix auto update success dialog 2025-12-14 21:50:56 -06:00
James Seibel c20d95a7c7 improve spacing for self updater version log 2025-12-14 21:21:45 -06:00
James Seibel 353aa1ed2c maybe improve ZStd version check 2025-12-14 21:20:42 -06:00
James Seibel 5aa43ebcc8 hide LODs when underwater 2025-12-14 17:22:35 -06:00
James Seibel b6145461b6 add note to ignored block CSV 2025-12-14 17:02:53 -06:00
James Seibel 478e431076 up version number 2.4.1 -> 2.4.2-dev 2025-12-14 17:00:34 -06:00
James Seibel 6feb7f1b42 remove dev from version number 2025-12-14 13:46:04 -06:00
James Seibel 016fc66293 Print a warning if G1GC is used
G1GC is known to cause stuttering
2025-12-13 16:46:59 -06:00
James Seibel 6d3e30d425 add Zstd decompress lib check in initalizer 2025-12-13 15:48:05 -06:00
James Seibel 5be5c5a5bc replace client ticks with a timer
Prevents DH loading issues when MC ticks are paused
2025-12-13 11:19:33 -06:00
James Seibel ed5aeb8951 minor texture setup reformatting 2025-12-13 10:43:01 -06:00
James Seibel 7f0ddadf26 up version number 2.4.0 -> 2.4.1-dev 2025-12-13 10:20:44 -06:00
James Seibel a2c61ed278 up version number 2.3.7 -> 2.4.0 2025-12-13 10:19:50 -06:00
James Seibel 99eb4ac8a1 Fix infinite loop in DhSectionPos 2025-12-13 09:10:12 -06:00
James Seibel c75902d9d6 debug particle cleanup 2025-12-13 08:50:15 -06:00
James Seibel 1743949ba5 fix GeneratedFullDataSourceProvider not adding update listener 2025-12-13 08:49:45 -06:00
James Seibel a74a37a0e8 world gen queue refactoring 2025-12-13 08:49:31 -06:00
James Seibel 4ed7941288 fix missing localization 2025-12-12 07:45:12 -06:00
James Seibel ec59a5f754 comment cleanup and enum renaming for API use 2025-12-11 07:35:37 -06:00
James Seibel 895e04b7cc Remove unused wrapper functions and refactor 2025-12-10 18:50:35 -06:00
James Seibel 8f0930fa02 Allow world gen limits on singleplayer 2025-12-10 07:09:29 -06:00
James Seibel c1c4328fa5 rename API getSoftCache -> createSoftCahe 2025-12-09 20:57:27 -06:00
James Seibel 91240e4f7a disable mip-mapping on textures
necessary to fix MC 1.21.11 rendering
2025-12-09 20:57:09 -06:00
James Seibel 17c61a97cc revert long windows filepath char 2025-12-09 07:21:40 -06:00
James Seibel b78b852ffb Merge branch 'batchGenRefactor' 2025-12-09 07:16:18 -06:00
James Seibel 26d4220967 Add logging/messaging for corrupted DB files 2025-12-09 07:12:33 -06:00
James Seibel 5edc73cc03 enable long file paths for the config file 2025-12-06 12:28:22 -06:00
James Seibel 6fcfc9379e Fix repo unit tests 2025-12-06 12:27:53 -06:00
James Seibel 149fbccfa5 Merge branch 'batchGenRefactor' 2025-12-06 12:19:17 -06:00
James Seibel 5ca754d2ac Fix world gen progress config resetting on reboot 2025-12-06 09:18:34 -06:00
James Seibel f13744e858 Add thread pool priority setting
Setting this to 1 higher than C2ME can reduce issues with Chunky overwhelming DH.
2025-12-05 07:35:16 -06:00
James Seibel 64ac218003 Improve empty LOD debugging slightly 2025-12-05 07:28:57 -06:00
James Seibel 385bd326cf minor world gen related refactoring 2025-12-04 07:39:09 -06:00
James Seibel 4e9559f230 enable long file paths on windows for the DB 2025-12-02 07:07:17 -06:00
James Seibel 6ea864ef6b TEST 2025-11-29 09:59:33 -06:00
James Seibel 4e96728c25 maybe fix concurrency error during world gen shutdown 2025-11-28 16:29:47 -06:00
James Seibel 1c44ef7f0c minor reformatting 2025-11-28 16:23:36 -06:00
James Seibel 227d0d09ba fix getDataPointAtBlockPos() relative Y 2025-11-28 15:53:47 -06:00
James Seibel d7ba3fa724 fix LOD only mode when transparency is disabled 2025-11-28 15:53:38 -06:00
James Seibel 7e46adf469 add the ability to ignore update chunk pos 2025-11-28 10:48:42 -06:00
James Seibel f43e2fa441 don't render thick snow layers 2025-11-28 09:39:03 -06:00
James Seibel f9819d3d46 fix vanilla fading for MC versions before 1.21.5 2025-11-28 08:42:20 -06:00
James Seibel 19b23bea5f add slow world gen warning config 2025-11-27 09:59:16 -06:00
James Seibel d1c0f7ebb4 Update .editorconfig 2025-11-26 13:55:33 -06:00
James Seibel 5a4ddafbbb Z_std_stream localization 2025-11-26 13:52:17 -06:00
James Seibel 7c40d96f2e DhApiTerrainDataPoint to string 2025-11-26 13:52:07 -06:00
James Seibel b535be16c0 auto merge API world gen data
done to reduce memory use with broken API world generators
2025-11-26 13:51:58 -06:00
James Seibel 22f5608f9a hide the compressor config option 2025-11-24 14:31:42 -06:00
James Seibel a498422843 stream cleanup 3 2025-11-24 14:30:17 -06:00
James Seibel bfd6efb4a4 handle ZStd streams 2025-11-24 14:28:06 -06:00
James Seibel c8c9df3a34 data stream cleanup 2025-11-24 14:15:23 -06:00
James Seibel 3349e5b898 clean up DhDataInputStream 2025-11-24 13:51:48 -06:00
James Seibel ed7511ff6a proof-of-concept block Zstd compression 2025-11-24 12:40:49 -06:00
James Seibel 8516e8f9ab re-enable varint unit tests 2025-11-24 12:38:34 -06:00
James Seibel 47a4d1535f minor variable refactoring 2025-11-22 11:01:53 -06:00
James Seibel 33a55dc7cd Delete EventTimer.java 2025-11-22 09:30:00 -06:00
James Seibel 1b4f9e8942 minor throw/this cleanup 2025-11-22 09:24:31 -06:00
James Seibel 2537c4a259 Rename IBatchGeneratorEvnWrapper 2025-11-22 08:16:30 -06:00
James Seibel b74b6e8068 minor RollingAverage refactor 2025-11-22 08:16:11 -06:00
James Seibel 25979d6a76 Move some exception logic into ExceptionUtil 2025-11-21 06:59:03 -06:00
James Seibel 3f287388d5 re-add biome blending to API config options 2025-11-18 07:42:43 -06:00
James Seibel 72d2ba6aae comment out phantom buffer cleanup log 2025-11-18 07:32:58 -06:00
James Seibel 611ed4e24a add mod note in memory low message 2025-11-18 07:32:48 -06:00
James Seibel eac7a38e73 hopefully reduce the chance of downsampling holes 2025-11-18 07:32:18 -06:00
James Seibel afd7da7763 Optimize full data update processing 2025-11-18 07:16:50 -06:00
James Seibel ff7abb6a18 Fix rendering when Iris isn't installed 2025-11-16 16:11:40 -06:00
James Seibel ca3f5da5de Add unit test for data source merging speed 2025-11-16 15:30:16 -06:00
James Seibel 69012ab7e6 rename and cleanup data source update methods 2025-11-16 15:29:13 -06:00
James Seibel e5e502b4f8 Remove unused/broken FullData LevelMinY 2025-11-15 19:09:16 -06:00
James Seibel 42dc0903de Fix shaders when far clip fading is active 2025-11-15 18:20:47 -06:00
James Seibel 4b20637e47 Fix WorldGen after restarting generation 2025-11-15 12:07:53 -06:00
James Seibel 3257ae8480 replace server tick/world gen tick with a timer 2025-11-15 09:47:15 -06:00
James Seibel a6ddc561a0 up protocol version 12 -> 13 2025-11-15 09:42:38 -06:00
James Seibel 7c82c9eb7b add adj data to DTO en/decoding 2025-11-15 09:42:20 -06:00
James Seibel 3c62e18502 Fix gitlab getter Long/Int cast 2025-11-15 07:55:56 -06:00
James Seibel eea5198fb6 Merge branch 'adjData' 2025-11-14 07:46:37 -06:00
James Seibel b82a59ecbc Speed up shutdown and reduce logging 2025-11-14 07:46:02 -06:00
James Seibel 6bfcf36687 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2025-11-13 07:19:19 -06:00
James Seibel 6fe0df7d0f Don't duplicate adjacent data 2025-11-13 07:18:09 -06:00
James Seibel b9746381eb Add varint encoding for full data
Closes Merge !93
Thanks Ryan Hitchman!
2025-11-12 07:21:54 -06:00
s809 91dffa3c3e Prevent auto-pause while pregen is running 2025-11-11 23:48:13 +05:00
James Seibel 6eb24ecde1 re-add GPU upload config including "none" 2025-11-10 07:33:03 -06:00
James Seibel 767753c004 add logging to infinite repo unit test 2025-11-10 06:56:24 -06:00
James Seibel 97442f8833 Fix config min/max validation default setup 2025-11-08 19:11:56 -06:00
James Seibel 62359e3dde remove LOD load pref logging 2025-11-08 19:08:30 -06:00
James Seibel b5199cfa87 Optimize ColumnBox building 2025-11-08 18:08:02 -06:00
James Seibel f0acc73c56 Add compass Index to Edirection 2025-11-08 17:48:30 -06:00
James Seibel f9dfc38bf1 Separate BlockBiomeWrapperPair from FullDataPointIdMap 2025-11-08 17:47:50 -06:00
James Seibel 5c5d39738e minor reformating 2025-11-08 17:44:08 -06:00
James Seibel 27fb629c22 default unsafe UI values to config option 2025-11-08 17:41:07 -06:00
James Seibel c374bf7ca8 test 2025-11-08 08:14:03 -06:00
James Seibel 7e04b12e37 Optimize PrefRecorder slightly 2025-11-07 07:41:59 -06:00
James Seibel 67637dbf10 detail level renaming 2025-11-06 21:50:43 -06:00
James Seibel 6456651d27 Handle non-adjacent data conversion 2025-11-06 21:28:25 -06:00
James Seibel 9343854b4a Clean up data source getters 2025-11-06 07:42:58 -06:00
James Seibel 5fd8ed840f Add adjacent data to FullDataDTO for faster loading 2025-11-06 07:35:23 -06:00
James Seibel 4d4d8fd8e9 Split up full data source provider into multiple classes 2025-11-04 07:46:06 -06:00
James Seibel bf05965015 remove IDataSource 2025-11-02 07:20:07 -06:00
James Seibel 47569f2b3c minor dataSourceHandler refactor 2025-11-01 16:33:07 -04:00
James Seibel 0567195f73 minor datasource renaming 2025-11-01 16:27:54 -04:00
James Seibel e355366ffc Clean up EDhDirection 2025-11-01 09:06:53 -04:00
James Seibel 3681d50eb2 minor comment cleanup 2025-11-01 08:42:25 -04:00
James Seibel e0c143881f Fix compression mode javadoc 2025-11-01 08:34:02 -04:00
James Seibel 2a49fdee7f Add experimental loading option and perfRecorder 2025-10-28 07:46:53 -05:00
James Seibel f39e06b6dc remove unused interrupt check 2025-10-28 07:36:28 -05:00
James Seibel 0d5c454dd4 remove unused ColumnQuadView methods 2025-10-28 07:24:24 -05:00
James Seibel 1b447fdc98 Fix logger builder doubling DH name 2025-10-28 07:23:58 -05:00
James Seibel d84ba05380 minor style reformatting 2025-10-27 06:52:36 -05:00
James Seibel 3e7f160fcd Merge Fade apply shaders 2025-10-25 11:54:32 -05:00
James Seibel dcaf334828 use same fade apply frag shader 2025-10-25 11:39:27 -05:00
James Seibel 789306ccff Add far clip fading 2025-10-25 11:06:19 -05:00
James Seibel e33fa3cb5e Rename fade renderer -> Vanilla Fade renderer 2025-10-25 09:36:11 -05:00
James Seibel 8f99117066 Fix iris not setting face culling in the MC state manager 2025-10-25 08:38:29 -05:00
James Seibel 2136c0fe83 framebuffer name consistency fix 2025-10-25 08:37:11 -05:00
James Seibel 7a6cffe19d Move getKeyedLevelDimensionName() to implementation 2025-10-23 07:17:46 -05:00
James Seibel 06bef93c82 run occlusion culling whenever saving a LOD
Also run culling for every column in an LOD, which improves compression by about 20%
- Thanks Scaevolus
2025-10-22 07:25:04 -05:00
James Seibel 939e45ce62 minor RenderBufferHandler optimization and bugfix 2025-10-19 16:40:57 -05:00
James Seibel 7f958269e4 Fix not reloading LODs on horizontal quality change 2025-10-19 16:16:35 -05:00
James Seibel 07e3091d13 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2025-10-19 16:06:24 -05:00
James Seibel f7ece2b02e Clean up LodRendering logic 2025-10-19 16:06:00 -05:00
s809 bd796c2ce0 Fix handling of empty server keys 2025-10-19 22:58:07 +05:00
s809 4e6be35da9 Merge branch 'feature/server-keys' 2025-10-19 22:57:50 +05:00
James Seibel 0e0e1e1b0f Make LodRenderer a singleton 2025-10-18 11:42:36 -05:00
James Seibel f4ab101403 Dh and level wrapper refactoring and commenting 2025-10-17 07:21:16 -05:00
James Seibel 0902d3f0f5 merge loggers and add logger builder 2025-10-15 17:37:08 -05:00
James Seibel 75c2758fd5 up version number 2.3.6 -> 2.3.7 2025-10-13 18:03:19 -05:00
James Seibel 9ddd917f3b remove dev from version number 2025-10-13 16:32:29 -05:00
James Seibel c5945b1254 remove unused EConfigEntryPerformance
It is a cool idea, but one that unfortunately never got implemented
2025-10-13 07:46:53 -05:00
James Seibel 9060579615 Fix world gen progress ui button 2025-10-13 07:42:46 -05:00
James Seibel 8f0217185f Improve config gui object casting 2025-10-13 07:33:27 -05:00
James Seibel 656971b0b9 typo fixing for major config refactoring 2025-10-12 21:10:18 -05:00
James Seibel 5fa3a11024 major config backend refactoring 2025-10-12 20:56:15 -05:00
James Seibel ed3d00bfce up version number 2.3.5 -> 2.3.6 2025-10-11 20:55:36 -05:00
James Seibel ba2681d7b2 remove dev from version number 2025-10-11 20:54:00 -05:00
James Seibel 168570f21f minor lodRenderer refactor 2025-10-11 18:40:32 -05:00
James Seibel b3928d3b1f rename renderFade -> renderFadeTransparent 2025-10-11 11:14:03 -05:00
James Seibel 57aec6092c comment out delayed save cache test to improve build speed 2025-10-10 07:00:32 -05:00
James Seibel 278f4b1642 move more logic into a global RenderState 2025-10-10 06:58:47 -05:00
James Seibel 26d0b5c571 disable world gen progress display by default 2025-10-09 20:12:31 -05:00
James Seibel 3cb8bbeaa7 Fix some tasks being dropped 2025-10-09 20:12:22 -05:00
James Seibel 009cfdce93 Fix VANILLA_CHUNKS API world gen 2025-10-08 17:27:04 -05:00
James Seibel 463565384b Re-add biome blending 2025-10-05 16:23:09 -05:00
James Seibel aed5bb4163 Separate DH pool threads and new executor "Render Loader"
Having separate threads for each task behind the scenes allows for easier performance monitoring vs having a single threadpool that handles everything.
2025-10-04 20:10:10 -05:00
James Seibel bd517e54cf remove duplicate "thread" name in ticker threads 2025-10-04 19:54:19 -05:00
James Seibel b323b7e52d rename uniforms in SSAO shader 2025-10-04 13:45:18 -05:00
James Seibel 32b3eac589 add nullable attributes to world getters 2025-10-04 10:48:34 -05:00
James Seibel 569a5442a9 fix a potential null pointer on world shutdown 2025-10-04 10:26:53 -05:00
James Seibel 25213cae39 Fix noise texture only applying changes on level change 2025-10-04 10:26:34 -05:00
James Seibel 82bb5ef64e fix typo in far falloff 2025-10-03 06:58:04 -05:00
James Seibel a8748471df Handle null pointer on server shutdown 2025-10-02 20:29:42 -05:00
James Seibel 721124b886 Write custom timeout logic for DelayedDataSourceCache
This should make the code a bit more transparent vs using the CacheBuilder, plus hopefully resolve a concurrent writing issue that causes monoliths
2025-10-02 20:29:26 -05:00
James Seibel 85e52301d6 typo in ApiEventInjector 2025-10-02 18:08:47 -05:00
James Seibel 08ede3351d Add DhApiChunkProcessingEvent 2025-10-02 18:03:27 -05:00
James Seibel 9690c898b0 handle null pointer on server shutdown 2025-10-02 07:33:05 -05:00
James Seibel 328336bd29 Allow unbinding Dependencies
TODO replacing may be a better way to handle it
2025-10-02 07:32:58 -05:00
James Seibel 75f0061d97 remove unused ServerPlayerWrapper methods 2025-10-02 07:07:31 -05:00
James Seibel be87c79b1b Handle a few rendering setup edge cases 2025-10-02 07:07:22 -05:00
James Seibel 12a885aa6e Manually close compression streams to try reducing GC reliance 2025-09-29 17:21:01 -05:00
James Seibel d33be490a7 cull LOD rendering on the quad tree 2025-09-29 07:28:03 -05:00
James Seibel cb654f2429 replace IConfigEntry apiValuePresent -> apiIsOverriding 2025-09-28 16:16:31 -05:00
James Seibel 2705cb679e minor config handler refactoring 2025-09-28 16:14:22 -05:00
James Seibel 372fcedc7c add IConfigEntry.apiValuePresent 2025-09-27 20:58:15 -05:00
James Seibel 25e909203d prep for Config UI refactoring 2025-09-27 20:55:37 -05:00
s809 b312582ce4 Add global bandwidth limit setting 2025-09-26 21:45:10 +05:00
James Seibel 73324c71ec Force Mac upload method to DATA
Maybe will help with crashing/memory corruption?
Data is the most basic upload method in GL so Mac should be able to support it a lot better than BUFFER_STORAGE.
2025-09-24 07:23:14 -05:00
James Seibel 0cdb5cf0ec Remove Mac state validation option 2025-09-24 07:13:51 -05:00
James Seibel cbfb1625bc add extra logic to proof-of-concept java swing UI 2025-09-21 21:28:56 -05:00
James Seibel 25e69d03ba Make config lang test return empty string if up to date 2025-09-21 21:28:36 -05:00
James Seibel 9564f02283 maybe fix freebsd OS crashing 2025-09-20 22:40:53 -05:00
James Seibel 9e7378be63 Merge branch 'merge-bedrock' 2025-09-20 16:16:36 -05:00
James Seibel 2495c38dc2 Merge branch 'merge-bedrock' 2025-09-20 15:23:34 -05:00
James Seibel 17fcdb428c finish glproxy comment 2025-09-20 15:14:28 -05:00
James Seibel 944e4f9cb4 Add experimental option to maybe help with Mac crashing 2025-09-20 15:10:54 -05:00
James Seibel 7c0b746220 re-add notnull anotation to ClientPluginChannelApi 2025-09-20 14:21:29 -05:00
Fabian Maurer b4cb390333 Use correct Supplier interface (1.7.10)
It works on modern since
com.google.common.base.Supplier implements
java.util.function.Supplier
but that is not guaranteed
2025-09-19 14:16:36 +02:00
Fabian Maurer 15cda35434 Remove dependency on org.checkerframework (1.7.10) 2025-09-19 14:16:36 +02:00
Fabian Maurer 361d251aa2 Replace isLessSpecificThan with helper function (for 1.7.10) 2025-09-19 14:16:36 +02:00
Fabian Maurer a565e7d906 User older netty functions (1.7.10) 2025-09-19 14:16:36 +02:00
James Seibel 57bbb12b39 Fix "CUSTOM" quality preset when Iris is present 2025-09-16 07:44:18 -05:00
James Seibel df17c1cc1b include world gen chunk/sec rate in progress log 2025-09-14 08:18:37 -05:00
James Seibel a4f7aad306 change world gen progress message to reduce confusion 2025-09-14 08:18:14 -05:00
James Seibel 1b2c1a59f9 Improve world gen task queue speed slightly 2025-09-13 17:59:39 -05:00
James Seibel f0bcf88b35 cache a few repo sql strings 2025-09-13 17:06:33 -05:00
James Seibel 5dbda75c0b add a unit test for SQL update performance testing 2025-09-13 17:01:40 -05:00
James Seibel 5caa945925 remove sea level from level wrapper 2025-09-11 07:07:21 -05:00
James Seibel 6bdfee3636 remove unexplored terrain rendering 2025-09-11 07:06:15 -05:00
James Seibel 1ec536b7df Add unexplored ocean for overworld 2025-09-10 07:46:21 -05:00
James Seibel 9ffda4d43e ColumnRenderSource doesn't need to be a IDataSource 2025-09-07 16:15:26 -05:00
James Seibel 670ec28b6f improve lod load time slightly
done by caching the ClientLevelWrapper used to determine block colors
2025-09-07 16:15:05 -05:00
James Seibel 771814af98 Fix typo in config 2025-09-06 22:10:24 -05:00
James Seibel 90f1d38233 make unexplored fog slightly lighter 2025-09-06 11:59:44 -05:00
James Seibel 54a4f380bd change world gen wireframe height to match unexplored fog 2025-09-06 11:59:44 -05:00
James Seibel bab421c381 add a config for unexplored fog 2025-09-06 11:59:44 -05:00
James Seibel 9c285c17a9 lower unexplored fog slightly 2025-09-06 11:59:44 -05:00
James Seibel 470a9ce8f1 Close #1036 (LODs reloading twice on config change)
Also clean up config event handling
2025-09-06 11:59:44 -05:00
James Seibel d6b79f8b06 fix concurrency issue during unexplored fog setup 2025-09-06 11:59:44 -05:00
James Seibel 71f1dce956 Add unexplored fog 2025-09-06 11:59:44 -05:00
James Seibel 9857eb337f Add remove(obj) and remove(index) to RenderableBoxGroup 2025-09-06 11:59:44 -05:00
James Seibel bced9938f3 Add unexplored fog proof of concept 2025-09-06 11:59:25 -05:00
James Seibel 7f46257e1a add TODO to testRenderer 2025-09-06 09:33:49 -05:00
James Seibel 5f8b566486 improve generic obj render perf logging 2025-09-06 09:33:29 -05:00
James Seibel 9fe2a3fa7b minor dontMergeColoredColumns reformat and comment 2025-09-06 08:53:09 -05:00
James Seibel eb6750bb8d Merge branch 'dontMergeColoredColumns' 2025-09-06 08:38:58 -05:00
James Seibel e86487ab9d Fix LOD-only rendering mode 2025-09-06 08:38:34 -05:00
James Seibel 5423b49f3d Merge !83 (Improve Chunk Update Queue) 2025-09-05 22:23:25 -05:00
James Seibel a2c6f906fa update compression unit test file path 2025-09-05 07:10:49 -05:00
Fabian Maurer d51474a64a Don't merge blocks that get colored by blocks above into columns 2025-09-04 17:56:54 +02:00
James Seibel 5b41c7d48a add (native) ZStd compression as default compressor 2025-09-03 07:39:58 -05:00
s809 034ec7d656 Bump protocol version 2025-08-16 21:01:45 +05:00
s809 fb5e15a2f1 Add a server keys feature 2025-08-16 20:59:28 +05:00
s809 674fc30e77 Replace pooled buffers with unpooled 2025-08-07 17:55:22 +05:00
s809 a05bd307f9 Reduce network logging by default 2025-07-27 23:21:13 +05:00
James Seibel d78a50ce49 up version number 2.3.4 -> 2.3.5 2025-07-19 14:59:14 -05:00
James Seibel 013eab9268 add space to self updater warning log 2025-07-19 14:57:22 -05:00
James Seibel 435cbde238 remove dev from version number 2025-07-19 14:56:53 -05:00
s809 d7040bad13 Load level on player add if missing 2025-07-19 19:05:52 +05:00
James Seibel a588070ce1 up version number 2.3.3 -> 2.3.4 2025-07-12 09:35:05 -05:00
James Seibel d156772438 remove dev from the version number 2025-07-12 09:34:18 -05:00
James Seibel de7ae41769 Fix API config renderingEnabled() changing the user value
Fixes #1083
2025-07-12 08:16:35 -05:00
James Seibel 618ad1938b full data DTO close data source if corrupted 2025-07-10 22:24:33 -05:00
James Seibel 5b10263f82 minor format cleanup 2025-07-10 07:27:23 -05:00
James Seibel 34f914c52f Mark a unit test as deprecated
Done to suppress warnings in compiler log
2025-07-10 07:26:41 -05:00
James Seibel 67b766c674 Fix monoliths when connected to a server 2025-07-10 07:26:14 -05:00
James Seibel a3e7469203 Fixes !1078 (lag due to beacon updating on server) 2025-07-09 07:28:25 -05:00
James Seibel 4ecaa6a9a1 Potentially fix an issue with AMD GPU shader compiling
Fix from Cortex and the Canvas mod
2025-07-08 07:22:36 -05:00
James Seibel 24f9dadc58 Disable vanilla fading when shaders are active 2025-07-07 07:49:36 -05:00
s809 b3ebaffa85 Disable enableAdaptiveTransferSpeed bby default 2025-07-01 22:03:12 +05:00
James Seibel b7ac1909d6 Fix config UI changes not always saving 2025-07-01 07:45:26 -05:00
James Seibel 32c3118afa comment out Z_STD compression 2025-06-30 06:54:27 -05:00
James Seibel 3a525f53f0 Rename world gen mode "Internal Server" -> "Full - Save Chunks" 2025-06-28 13:57:58 -05:00
James Seibel f3947312c1 Re-Add Z_STD compression for testing 2025-06-28 11:37:06 -05:00
James Seibel 839ea1e778 increase ram amount for unit tests
needed for compression tests
2025-06-28 11:36:17 -05:00
James Seibel d2becd2c03 Fix rare fade error when restarting the LodRenderer 2025-06-28 10:45:36 -05:00
James Seibel 7d87347199 Fix multiplayer null pointer 2025-06-28 09:22:23 -05:00
James Seibel f4117751c9 Fix world-gen progress not showing in release builds 2025-06-27 07:29:31 -05:00
James Seibel a8a085f296 Move RenderState to core 2025-06-26 07:50:53 -05:00
James Seibel 317319593e rename renderDeferredLods -> renderDeferredLodsForShaders 2025-06-25 07:47:08 -05:00
James Seibel 4633f90a03 Add null handling to ServerPlayerStateManager.handlePluginMessage() 2025-06-25 07:45:43 -05:00
James Seibel 5802bbb3f3 keep cave culling for medium quality preset
This may be changed back at some point in the future, but depending on the usecase (IE vanilla survival) cave culling is generally better than not having it, and if people see weirdness they can probably guess that increasing the quality preset may fix it.
2025-06-23 07:23:26 -05:00
James Seibel e93d5b90f1 Disable cave culling for medium quality and higher 2025-06-17 07:15:28 -05:00
James Seibel 9be56607a5 Reduce stuttering with fast world gen 2025-06-14 16:17:28 -05:00
James Seibel 91743bf742 Add Api Before/After Text Create events
Deprecate DhApiColorDepthTextureCreatedEvent since it is less obvious when it fires
2025-06-09 07:50:21 -05:00
James Seibel d40d293f54 Fix hash collisions in FullDataPointIdMap 2025-06-06 07:43:38 -05:00
James Seibel a075e60e3e Fix GLMC.glDeleteTextures() calls 2025-06-04 07:07:39 -05:00
s809 d72c7c3695 Check LOD timestamps in file handler threads 2025-06-03 23:41:47 +05:00
Ran 309fa07664 Merge branch 'fix_max_y' into 'main'
Fix max Y validation

See merge request distant-horizons-team/distant-horizons-core!85
2025-05-18 00:32:51 +00:00
Stewart Borle 0a017567c4 Fix max Y validation 2025-05-18 00:32:51 +00:00
James Seibel e01261da5c Remove line ending from editorconfig
Done to fix some issues with some devs on linux
2025-05-17 11:47:00 -05:00
James Seibel a0879d07c5 json indent 2 -> 4
for consistency
2025-05-17 11:25:18 -05:00
Ran bbb15263f2 Fix gradle versioning 2025-05-03 11:21:05 +10:00
s809 5ca3563c66 Bump protocol version 2025-05-03 00:08:03 +05:00
s809 30256a2779 Send scaled generation bounds coordinates 2025-05-03 00:08:03 +05:00
Ran 4b4f10f5e6 Fix gradle versioning for core application 2025-05-02 12:44:25 +10:00
s809 ad995544f7 Use bytesReceived instead of decreasing multiplicatively 2025-04-20 23:59:34 +05:00
s809 d521e931f4 Change data send tick rate 4 -> 20 2025-04-20 18:26:07 +05:00
s809 dd30a8274a Add a config entry and refactor 2025-04-20 18:25:27 +05:00
s809 3ca5efadc9 Adaptive data transfer speed 2025-04-20 03:02:18 +05:00
Ran 09174c2d2a Improve LodDataBuilder.java
- Use bitwise modulo
- Don't compute certain things 256 times when they can be computed once.
- Removed expressions that are always false
- Improved comments
2025-04-11 11:24:16 +10:00
James Seibel e079b28e77 maybe break n-sized rendering but fix LOD loading getting stuck 2025-04-07 06:56:53 -05:00
James Seibel 136124a703 up version number 2.3.2 -> 2.3.3 2025-04-05 09:11:19 -05:00
James Seibel 3ed50e5134 remove dev from version number 2025-04-05 09:10:01 -05:00
James Seibel b5e3e6867c Improve DH world gen progress message 2025-04-02 07:25:14 -05:00
James Seibel 3e04342148 Add FIXME comments to Lod and Fade renderers 2025-04-02 07:24:38 -05:00
James Seibel 6699b568df Fix memory leaks due to un-closed thread pools and worlds
How did it take this long to realize the DhWorld objects were never being closed?
2025-03-30 17:30:57 -05:00
James Seibel 53bee4ad42 Remove unused code in LodRenderer 2025-03-30 16:55:01 -05:00
James Seibel 5d5e462221 Fix the sun/moon and stars not rendering 2025-03-30 16:49:58 -05:00
James Seibel d9b924cfed Fix beacon beams now going through some blocks 2025-03-30 15:23:19 -05:00
James Seibel 8bd70d593c Fix flashing on MC 1.21.5 in non-overworld dimensions 2025-03-30 14:36:51 -05:00
James Seibel 5597044604 don't log InterruptedException during threadPool shutdown 2025-03-29 20:11:31 -05:00
James Seibel 5d7c043d06 Fix fog for MC 1.16.5 2025-03-29 19:22:51 -05:00
James Seibel 4aac61b37f hide repo double close warnings in release 2025-03-29 15:39:45 -05:00
James Seibel 22460fa1f5 Fix duplicate world gen due to short memoization time
Reverts 276f2adf00
2025-03-29 15:30:28 -05:00
James Seibel 2d127c7d98 Fix an infinite loop in the lighting engine
Not sure how I didn't catch this until MC 1.21.5
2025-03-29 15:29:34 -05:00
James Seibel 91e17c420a Fix SSAO applying to sky 2025-03-29 10:31:48 -05:00
James Seibel 93f5a85cb5 Fix MC 1.21.5 rendering and bright glass on sky 2025-03-29 10:31:34 -05:00
James Seibel b275971486 re-add stencil to GL state
shouldn't be needed, but just in case
2025-03-29 09:52:41 -05:00
James Seibel 1234ff4d28 up version number 2.3.1 -> 2.3.2 2025-03-25 07:17:27 -05:00
James Seibel f9bd7e2daf remove dev from version number 2025-03-25 07:16:43 -05:00
James Seibel 8ec4e235eb Add config to only log GL errors once 2025-03-19 22:02:53 -05:00
James Seibel b8a59d0ef6 Attempt to fix Linux complaining about glIsFramebuffer() 2025-03-19 18:34:02 -05:00
James Seibel e500143781 Potentially fix GL errors when accessing the default FBO on Linux 2025-03-19 17:00:30 -05:00
James Seibel 406468b54c Fix restoring textures to the default FBO 2025-03-18 20:18:13 -05:00
James Seibel 6857300ae2 Add stack tracing to GL error logging 2025-03-18 18:10:00 -05:00
James Seibel 6775ee23c3 fix passing in the wrong flags to glBufferStorage()
Might Resolve #964 and #950
2025-03-18 07:43:20 -05:00
James Seibel 44645943e2 Fix terrain API repo failing if no cache was provided 2025-03-18 07:42:31 -05:00
James Seibel f385c4a56b Fix changing graphics settings on world load via API 2025-03-16 14:29:56 -05:00
James Seibel 0cf5e6d594 Fix GL error logging 2025-03-14 10:17:52 -05:00
James Seibel 7b5b8da0d2 decrease gen message timeout 5 -> 2 seconds
done to make it consistently appear
2025-03-13 21:17:01 -05:00
James Seibel 851f2ccd06 Add additional error checking/handling to Shader compiling 2025-03-13 21:12:29 -05:00
James Seibel 6c40389c07 up version number 2.3.0-b -> 2.3.1-b-dev 2025-03-08 08:11:01 -06:00
James Seibel fada9e4cf6 Fix repo leak unit test failing in release 2025-03-08 08:10:43 -06:00
James Seibel 06198fdbb8 Revert "temporarily disable sqlite tests for release"
This reverts commit ebc1114a51.
2025-03-06 07:43:14 -06:00
James Seibel 3158eed5a3 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2025-03-06 07:42:32 -06:00
James Seibel e701c0e5ea remove dev from version number 2025-03-06 07:40:49 -06:00
James Seibel ebc1114a51 temporarily disable sqlite tests for release 2025-03-06 07:40:24 -06:00
s809 d2ff4a5806 Add some debugging info for DTOs 2025-03-02 20:08:45 +05:00
s809 eb8563482e Replace chunk counts with speed in pregen 2025-02-27 21:08:27 +05:00
s809 b53c33e454 Make generation info text a bit clearer 2025-02-27 21:08:02 +05:00
s809 2483671e5e Should be division instead of multiplication 2025-02-26 23:16:58 +05:00
s809 cc4733b052 Offset generation bounds by teleportation scale 2025-02-26 22:13:38 +05:00
James Seibel 34e5463718 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2025-02-25 07:25:49 -06:00
James Seibel 53011a13be duct tape fix to reduce chance of LOD uploading requiring MC reboot 2025-02-25 07:25:46 -06:00
s809 1c579675a2 Remember split section responses temporarily 2025-02-22 20:55:10 +05:00
James Seibel 69a4e6b27e Add TODO about why LODs sometimes fail to load 2025-02-22 08:55:41 -06:00
s809 b05eb78f3a Fix foreground thread sometimes blocking server shutdown 2025-02-19 21:17:38 +05:00
s809 83fabe3ee8 Show section numbers in pregen 2025-02-19 20:37:26 +05:00
s809 fdfab2b3a8 Use another method for enforcing non nsized generation 2025-02-19 20:15:34 +05:00
James Seibel 45c67d057a Fix IDhApiConfigValue.clearValue() failing for some deprecated functions 2025-02-17 21:16:22 -06:00
James Seibel c296795280 Fix DB leaks in FullDataV2Repo 2025-02-16 20:07:00 -06:00
James Seibel 2deb24ec1e Add javadocs to ClientWrapper getPlayer Pos methods 2025-02-16 19:53:44 -06:00
James Seibel 5ab7a3030a Fix DB leaks in FullDataV2Repo 2025-02-16 19:52:48 -06:00
James Seibel 1af4d23c14 improve DB leak tracking exception handling 2025-02-16 19:52:37 -06:00
James Seibel 977204abf0 Add DB leak tracking 2025-02-16 19:34:13 -06:00
James Seibel 276f2adf00 Revert 10 minute memoization for world gen
I thought this was only an issue for N-sized generation, but in testing found it to still be an issue for max-detail retrieval as well.
This will have to be looked into more another time
2025-02-15 11:56:21 -06:00
James Seibel 1b3c9e1a89 Fix beacon culling with auto overdraw prevention 2025-02-15 11:12:46 -06:00
James Seibel 6fbe0a9e72 Add missing cave blocks for cave culling 2025-02-15 11:06:43 -06:00
James Seibel 11a2b8bf5b Add TODO to PriorityTaskPicker about VisualVM 2025-02-15 11:06:30 -06:00
James Seibel 99f2d2f844 Add TODO comment about Immersive Portals only rendering 1 level 2025-02-14 07:48:05 -06:00
s809 a5c029203c Invert generateOnlyInHighestDetail and rename to enableNSizedGeneration 2025-02-11 22:08:25 +05:00
James Seibel 84015e4a40 Put N-sized generation and upsampling behind experimental configs 2025-02-11 07:47:24 -06:00
James Seibel 08f63470a5 Fix auto updater failing for nightly builds 2025-02-10 07:46:43 -06:00
James Seibel f2404b6455 remove unneeded IVersionConstant methods 2025-02-08 11:39:18 -06:00
James Seibel f20231ccbc fix rare null pointer in sharedApi 2025-02-08 11:38:36 -06:00
s809 3a94bbe804 Reduce queue size back to improve responsiveness 2025-02-07 23:23:15 +05:00
s809 15f1754922 Improve ordering of reading positions to update a bit 2025-02-07 23:21:41 +05:00
s809 28448941e1 Keep update propagation queue filled 2025-02-07 23:20:24 +05:00
James Seibel 18c29b9810 Attempt to fix threadpool shutdown rejection exception 2025-02-07 07:25:57 -06:00
James Seibel fa66cefbe2 Add comments to LodRenderSection memoized gen positions
also increase timeout from 15 sec -> 10 minutes
 - done to test if memoization is actually needed
2025-02-07 07:14:50 -06:00
James Seibel f7dc46cb55 Increase full data update task count to reduce down time 2025-02-06 20:10:30 -06:00
James Seibel 5cebee3be4 Flush world gen memory cache when full 2025-02-06 20:08:28 -06:00
s809 532ac8fe01 Fix incorrect distance being used in update propagation SQL and reduce queue size 2025-02-07 01:05:14 +05:00
James Seibel 8385eeb62c Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2025-02-05 17:31:26 -06:00
James Seibel 95db6885e7 add error logging to FullDataRequestHandler 2025-02-05 17:31:13 -06:00
James Seibel 10a3840373 Fix empty data sources when moving in multiplayer or with N-sized world gen
Increases Protocol version 9 -> 10
2025-02-05 17:30:59 -06:00
James Seibel cedaaa8a2e replace a few implicit datasource V2 repo statement setters 2025-02-04 19:29:39 -06:00
James Seibel 2c7f11c722 Improve DhApiBeforeRenderEvent javadocs 2025-02-03 20:30:28 -06:00
James Seibel 4fbda8f02b Fix render enabled config getting set by world gen progress config 2025-02-02 19:52:31 -06:00
James Seibel b0bd536248 Fix compiling with missing "E" 2025-02-02 15:52:17 -06:00
s809 a3ed0012e3 Balance tasks in thread pool using elapsed time instead of priorities 2025-02-02 20:30:35 +05:00
s809 9952481d77 Do not request already fulfilled sections again until some time passes 2025-02-02 20:30:35 +05:00
s809 5e137ee10d Auto-move old save data to new location 2025-02-02 20:30:35 +05:00
James Seibel f02ea68b6f Add missing Enum prefix to RequestResult -> ERequestResult 2025-02-01 16:08:15 -06:00
s809 1041e0a4dd Remove generationProgressDisableMessageDisplayTimeInSeconds from server config command 2025-02-01 19:38:43 +05:00
s809 6fb862ecfe Add GUI description for generateOnlyInHighestDetail 2025-02-01 19:38:25 +05:00
s809 1f8013c1cf Use generateOnlyInHighestDetail client-side 2025-02-01 18:30:07 +05:00
s809 157d72d8dc Decrease delay between missing generation rechecks 2025-01-31 14:54:18 +05:00
James Seibel 2c077f5224 Fix a null pointer in the chunk update queue 2025-01-30 20:13:53 -06:00
s809 6e5bd02ae0 Fix beacon beams flickering 2025-01-30 22:30:02 +05:00
s809 a7578b2a72 Process chunks only once with real-time updates enabled 2025-01-30 21:47:31 +05:00
s809 041cf4e0d4 Fix nightly self-updater after moving jars into zip root 2025-01-30 18:11:34 +05:00
s809 bb1154b036 Revert "Improve chunk processing throughput"
This reverts commit dd3903f66e.
2025-01-28 20:05:59 +05:00
James Seibel 9c9c90e786 Improve world gen import hiding message 2025-01-26 17:47:28 -06:00
James Seibel 3dbd05a4ae minor beacon beam height merge cleanup 2025-01-26 17:41:46 -06:00
James Seibel 042a0b6853 Merge branch 'distant-horizons-core-main' 2025-01-26 17:40:15 -06:00
James Seibel 39c621b8d9 Remove locks from LodRenderSection uploading 2025-01-26 17:12:39 -06:00
James Seibel dd3903f66e Improve chunk processing throughput 2025-01-26 17:05:37 -06:00
James Seibel 2d1859c77d change low memory warning to 4GB or more 2025-01-26 16:53:11 -06:00
James Seibel d62a801c43 Reduce locking in SharedApi.UpdateChunkPosManager 2025-01-25 10:10:10 -06:00
James Seibel cb40336fda Increase rolling average window for world gen queue
This should reduce fluctuations a bit
2025-01-24 21:53:13 -06:00
James Seibel 766c831af0 fix recalculate heightmap breaking stairs, slabs, and glass 2025-01-24 07:22:11 -06:00
s809 736df9f848 Check if session is ready before ignoring local chunks 2025-01-24 11:20:37 +05:00
James Seibel a347caafed Fix holes when moving with N-sized world gen/server side support 2025-01-23 19:44:58 -06:00
James Seibel 2d5902df28 Fix data source leaks for custom world generators 2025-01-23 19:38:01 -06:00
James Seibel 29e496757a Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2025-01-23 19:16:33 -06:00
James Seibel 7cf05ed31d Fix rare concurrency error on world gen shutdown 2025-01-23 19:15:55 -06:00
James Seibel e7eb8e24ae Speed up PhantomArrayListPool for large checkouts 2025-01-23 19:15:42 -06:00
s809 cdca7723a7 Ignore local chunks if realtime updates are enabled 2025-01-23 23:21:13 +05:00
s809 e0a0ba5222 Fix full data source being released too early 2025-01-23 22:06:20 +05:00
James Seibel 0f88c7c231 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2025-01-22 21:31:20 -06:00
James Seibel d9911f64b9 Simplify Full data hash logic to speed up saving 2025-01-22 21:31:07 -06:00
s809 8bddd6d503 Fix column order check breaking on tiny columns 2 2025-01-23 00:18:33 +05:00
s809 9b261f6472 Fix column order check breaking on tiny columns 2025-01-23 00:15:12 +05:00
James Seibel 00559b5d34 Remove unneeded locks and speed up FullDataId Entry retrieval 2025-01-22 07:16:04 -06:00
James Seibel 9cae54a079 Show instructions to disable world gen progress message for short time 2025-01-21 07:49:30 -06:00
James Seibel 363ec76450 fix isClosedException name 2025-01-21 07:07:33 -06:00
s809 ebd00df388 Fix task splitting causing generation of already generated sections 2025-01-21 17:26:54 +05:00
James Seibel 13882f44ce minor LodRenderSection rename 2025-01-20 21:51:04 -06:00
James Seibel fce1fa3f41 Fix cached RenderSource closing while in use 2025-01-20 21:50:33 -06:00
James Seibel fab8191ddd remove unneeded wal flush logic 2025-01-20 07:39:04 -06:00
James Seibel 582541d240 handle additional DB closed message on DTO get 2025-01-20 07:38:40 -06:00
s809 f609dcb468 Merge remote-tracking branch 'origin/main' 2025-01-20 11:00:13 +05:00
s809 a69936ca69 Merge branch 'feature/generation-bounds' 2025-01-20 10:59:11 +05:00
James Seibel 8c81c867b6 merge 2025-01-19 17:42:45 -06:00
James Seibel 995f80d553 Fix beacons disappearing and not updating correctly 2025-01-19 17:42:14 -06:00
s809 08f36b4371 Lower the log level of rate limit hits 2025-01-18 17:02:35 +05:00
Jan Trummer 9dcc7e1ad2 Replace set with setMinDefaultMax 2025-01-11 16:05:58 +01:00
Jan Trummer 72139f1f59 Add config to set max beacon render height 2025-01-11 13:38:44 +01:00
460 changed files with 26187 additions and 26163 deletions
+2 -3
View File
@@ -4,14 +4,13 @@
[*]
charset = utf-8
end_of_line = crlf
indent_size = 4
indent_style = space
insert_final_newline = false
max_line_length = 1000
tab_width = 4
trim_trailing_whitespace = false
ij_continuation_indent_size = 8
ij_continuation_indent_size = 4
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = true
@@ -537,7 +536,7 @@ ij_groovy_wrap_chain_calls_after_dot = false
ij_groovy_wrap_long_lines = false
[{*.har,*.json,*.png.mcmeta,mcmod.info,pack.mcmeta}]
indent_size = 2
indent_size = 4
ij_json_array_wrapping = split_into_lines
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
+20 -4
View File
@@ -2,10 +2,26 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
id "java"
id "com.github.johnrengelman.shadow" version '8.1.1' apply false
id "com.gradleup.shadow"
}
repositories {
mavenCentral()
}
tasks.withType(JavaCompile).configureEach {
options.release = 8
options.encoding = "UTF-8"
}
configurations {
testImplementation.extendsFrom compileOnly
}
dependencies {
compileOnly "org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}"
testImplementation "junit:junit:4.13"
}
shadowJar {
// required for basic shadowJar setup
@@ -21,7 +37,7 @@ task addSourcesToCompiledJar(type: ShadowJar) {
doFirst {
System.out.println("Adding source files from: \n" +
"[" + sourceJarPath + "] to compiled API jar: \n" +
"[" + shadowJar.archivePath + "]")
"[" + shadowJar.archiveFile.get().asFile + "]")
// Validate the input JAR file
if (!secondJarFile.exists()) {
@@ -42,7 +58,7 @@ task addSourcesToCompiledJar(type: ShadowJar) {
}
// set the jars to merge
from shadowJar.archivePath
from shadowJar.archiveFile.get().asFile
from secondJarFile
// alternative method to Include the source files in the combined JAR
@@ -39,10 +39,6 @@ package com.seibel.distanthorizons.api.enums;
*/
public enum EDhApiDetailLevel
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/**
* detail level: 0 <Br>
* width in Blocks: 1
@@ -28,10 +28,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/
public enum EDhApiBlocksToAvoid
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
NONE(false),
NON_COLLIDING(true);
@@ -22,7 +22,9 @@ package com.seibel.distanthorizons.api.enums.config;
/**
* UNCOMPRESSED <br>
* LZ4 <br>
* XZ <br><br>
* Z_STD <br>
* Z_STD_STREAM <br>
* LZMA2 <br><br>
*
* Note: speed and compression ratios are examples
* and should only be used for estimated comparisons.
@@ -32,15 +34,11 @@ package com.seibel.distanthorizons.api.enums.config;
*/
public enum EDhApiDataCompressionMode
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/**
* Should only be used internally and for unit testing. <br><br>
*
* Read Speed: 1.64 MS / DTO <br>
* Write Speed: 12.44 MS / DTO <br>
* Read Speed: 6.09 MS / DTO <br>
* Write Speed: 6.01 MS / DTO <br>
* Compression ratio: 1.0 <br>
*/
@DisallowSelectingViaConfigGui
@@ -49,28 +47,42 @@ public enum EDhApiDataCompressionMode
/**
* Extremely fast (often faster than uncompressed), but generally poor compression. <br><br>
*
* Read Speed: 1.85 MS / DTO <br>
* Write Speed: 9.46 MS / DTO <br>
* Compression ratio: 0.3638 <br>
* Read Speed: 3.25 MS / DTO <br>
* Write Speed: 5.99 MS / DTO <br>
* Compression ratio: 0.4513 <br>
*/
LZ4(1),
/*
* Decent speed and good compression. <br><br>
/**
* Great speed and good compression. <br><br>
*
* Read Speed: 11.78 MS / DTO <br>
* Write Speed: 16.76 MS / DTO <br>
* Compression ratio: 0.2199 <br>
* Read Speed: 2.1 MS / DTO <br>
* Write Speed: 4.9 MS / DTO <br>
* Compression ratio: 0.2606 <br>
*/
//@Deprecated
//Z_STD(2),
Z_STD_BLOCK(4),
/**
* Similar to {@link EDhApiDataCompressionMode#Z_STD_BLOCK}
* except slower. <br><br>
*
* This option is only provided for legacy support when processing old databases. <br><br>
*
* Read Speed: 9.31 MS / DTO <br>
* Write Speed: 15.13 MS / DTO <br>
* Compression ratio: 0.2606 <br>
*/
@Deprecated
@DisallowSelectingViaConfigGui
Z_STD_STREAM(2),
/**
* Extremely slow, but very good compression. <br><br>
*
* Read Speed: 12.25 MS / DTO <br>
* Write Speed: 490.07 MS / DTO <br>
* Compression ratio: 0.1242 <br>
* Extremely slow, but very good compression. <br>
* Often causes whole computer stuttering due to memory bandwidth saturation. <br><br>
*
* Read Speed: 13.29 MS / DTO <br>
* Write Speed: 70.95 MS / DTO <br>
* Compression ratio: 0.2068 <br>
*/
LZMA2(3);
@@ -35,25 +35,12 @@ public enum EDhApiGpuUploadMethod
/** Picks the best option based on the GPU the user has. */
AUTO(false, false),
// commented out since it isn't currently in use
//BUFFER_STORAGE_MAPPING(true, true),
/** Fast rendering, no stuttering. */
BUFFER_STORAGE(false, true),
/** Fast rendering but may stutter when uploading. */
SUB_DATA(false, false),
/**
* May end up storing buffers in System memory. <br>
* Fast rending if in GPU memory, slow if in system memory, <br>
* but won't stutter when uploading.
*
* @deprecated not currently supported
*/
@Deprecated
BUFFER_MAPPING(true, false),
/** Fast rendering but may stutter when uploading. */
DATA(false, false);
@@ -29,10 +29,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/
public enum EDhApiGrassSideRendering
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
AS_GRASS,
FADE_TO_DIRT,
AS_DIRT;
@@ -31,11 +31,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/
public enum EDhApiHorizontalQuality
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
// Note: any quadraticBase less than 2.0f has issues with DetailDistanceUtil, and will always return the lowest detail level.
// So for now we are limiting the lowest value to 2.0
// LOWEST was originally 1.0f and LOW was 1.5f
@@ -29,10 +29,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/
public enum EDhApiLodShading
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/**
* Uses Minecraft's shading for LODs. <Br>
* This means if Minecraft's shading is disabled DH's shading will be as well.
@@ -0,0 +1,53 @@
/*
* 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.api.enums.config;
import org.apache.logging.log4j.Level;
/**
* ALL
* DEBUG
* INFO
* WARN
* ERROR
* DISABLED
*
* @since API 5.0.0
* @version 2024-4-6
*/
public enum EDhApiLoggerLevel
{
// ordered from most to least broad
ALL(Level.ALL),
DEBUG(Level.DEBUG),
INFO(Level.INFO),
WARN(Level.WARN),
ERROR(Level.ERROR),
DISABLED(Level.OFF),
;
public final Level level;
EDhApiLoggerLevel(Level level)
{ this.level = level; }
}
@@ -1,54 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.api.enums.config;
import org.apache.logging.log4j.Level;
/**
* @since API 2.0.0
* @version 2024-4-6
*/
public enum EDhApiLoggerMode
{
DISABLED(Level.OFF, Level.OFF),
LOG_ALL_TO_FILE(Level.ALL, Level.OFF),
LOG_ERROR_TO_CHAT(Level.ALL, Level.ERROR),
LOG_WARNING_TO_CHAT(Level.ALL, Level.WARN),
LOG_INFO_TO_CHAT(Level.ALL, Level.INFO),
LOG_DEBUG_TO_CHAT(Level.ALL, Level.DEBUG),
LOG_ALL_TO_CHAT(Level.ALL, Level.ALL),
LOG_ERROR_TO_CHAT_AND_FILE(Level.ERROR, Level.ERROR),
LOG_WARNING_TO_CHAT_AND_FILE(Level.WARN, Level.WARN),
LOG_INFO_TO_CHAT_AND_FILE(Level.INFO, Level.INFO),
LOG_DEBUG_TO_CHAT_AND_FILE(Level.DEBUG, Level.DEBUG),
LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE(Level.INFO, Level.WARN),
LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE(Level.INFO, Level.ERROR),
;
public final Level levelForFile;
public final Level levelForChat;
EDhApiLoggerMode(Level levelForFile, Level levelForChat)
{
this.levelForFile = levelForFile;
this.levelForChat = levelForChat;
}
}
@@ -64,10 +64,7 @@ public enum EDhApiMaxHorizontalResolution
/** How wide each LOD DataPoint is */
public final int dataPointWidth;
/**
* This is the same as detailLevel in LodQuadTreeNode,
* lowest is 0 highest is 9
*/
/** This is the same as detailLevel in LodQuadTreeNode */
public final byte detailLevel;
/* Start/End X/Z give the block positions
@@ -33,10 +33,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/
public enum EDhApiMcRenderingFadeMode
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/**
* No fading is done, there will be a pronounced border between
* Minecraft and Distant Horizons. <br>
@@ -0,0 +1,19 @@
package com.seibel.distanthorizons.api.enums.config;
import com.seibel.distanthorizons.coreapi.ModInfo;
/**
* AUTO, <br>
* OPEN_GL, <br>
* BLAZE_3D, <br><br>
*
* @since API 6.0.0
* @version 2026-3-10
*/
public enum EDhApiRenderApi
{
AUTO,
OPEN_GL,
BLAZE_3D;
}
@@ -35,11 +35,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/
public enum EDhApiServerFolderNameMode
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** Only use the server name */
NAME_ONLY,
@@ -34,11 +34,6 @@ package com.seibel.distanthorizons.api.enums.config;
@Deprecated // not currently in use, if the config this enum represents is re-implemented, the deprecated flag can be removed
public enum EDhApiVanillaOverdraw
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/**
* Don't draw LODs where a minecraft chunk could be.
* Use Overdraw Offset to tweak the border thickness.
@@ -53,7 +53,7 @@ public enum EDhApiVerticalQuality
public int calculateMaxVerticalData(byte dataDetail)
public int calculateMaxNumberOfVerticalSlicesAtDetailLevel(byte dataDetail)
{
// for detail levels lower than what the enum defines, use the lowest quality item
int index = MathUtil.clamp(0, dataDetail, this.maxVerticalData.length - 1);
@@ -28,10 +28,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/
public enum EDhApiWorldCompressionMode
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/**
* Every block/biome change is recorded in the database. <br>
* This is what DH 2.0 and 2.0.1 all used by default and will store a lot of data.
@@ -35,10 +35,6 @@ import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui
*/
public enum EDhApiQualityPreset
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
@DisallowSelectingViaConfigGui
CUSTOM,
@@ -34,10 +34,6 @@ import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui
*/
public enum EDhApiThreadPreset
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
@DisallowSelectingViaConfigGui
CUSTOM,
@@ -33,11 +33,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
*/
public enum EDhApiDebugRendering
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** LODs are rendered normally */
OFF,
@@ -48,11 +43,7 @@ public enum EDhApiDebugRendering
SHOW_BLOCK_MATERIAL,
/** Only draw overlapping LOD quads. */
SHOW_OVERLAPPING_QUADS,
/** LOD colors are based on renderSource flags. */
SHOW_RENDER_SOURCE_FLAG;
SHOW_OVERLAPPING_QUADS;
public static EDhApiDebugRendering next(EDhApiDebugRendering type)
{
@@ -65,7 +56,7 @@ public enum EDhApiDebugRendering
case SHOW_BLOCK_MATERIAL:
return SHOW_OVERLAPPING_QUADS;
case SHOW_OVERLAPPING_QUADS:
return SHOW_RENDER_SOURCE_FLAG;
return OFF;
default:
return OFF;
}
@@ -76,8 +67,6 @@ public enum EDhApiDebugRendering
switch (type)
{
case OFF:
return SHOW_RENDER_SOURCE_FLAG;
case SHOW_RENDER_SOURCE_FLAG:
return SHOW_OVERLAPPING_QUADS;
case SHOW_OVERLAPPING_QUADS:
return SHOW_DETAIL;
@@ -33,10 +33,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
@Deprecated
public enum EDhApiFogDrawMode
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/**
* Use whatever Fog setting Optifine is using.
* If Optifine isn't installed this defaults to {@link EDhApiFogDrawMode#FOG_ENABLED}.
@@ -30,11 +30,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
*/
public enum EDhApiFogFalloff
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
LINEAR(0),
EXPONENTIAL(1),
EXPONENTIAL_SQUARED(2);
@@ -33,11 +33,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
*/
public enum EDhApiHeightFogDirection
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
ABOVE_CAMERA (true, true, false),
BELOW_CAMERA (true, false, true),
ABOVE_AND_BELOW_CAMERA (true, true, true),
@@ -20,22 +20,17 @@
package com.seibel.distanthorizons.api.enums.rendering;
/**
* Default <br>
* Debug <br>
* Disabled <br>
* DEFAULT <br>
* DEBUG_TRIANGLE <br>
* DISABLED <br>
*
* @since API 2.0.0
* @version 2024-4-6
* @version 2026-03-23
*/
public enum EDhApiRendererMode
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
DEFAULT,
DEBUG,
DEBUG_TRIANGLE,
DISABLED;
@@ -45,8 +40,8 @@ public enum EDhApiRendererMode
switch (type)
{
case DEFAULT:
return DEBUG;
case DEBUG:
return DEBUG_TRIANGLE;
case DEBUG_TRIANGLE:
return DISABLED;
default:
return DEFAULT;
@@ -60,10 +55,10 @@ public enum EDhApiRendererMode
{
case DEFAULT:
return DISABLED;
case DEBUG:
case DEBUG_TRIANGLE:
return DEFAULT;
default:
return DEBUG;
return DEBUG_TRIANGLE;
}
}
@@ -29,11 +29,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
*/
public enum EDhApiTransparency
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
DISABLED(false, false),
FAKE(true, true),
COMPLETE(true, false);
@@ -34,11 +34,6 @@ package com.seibel.distanthorizons.api.enums.worldGeneration;
*/
public enum EDhApiDistantGeneratorMode
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** Don't generate any new terrain, just generate LODs for already generated chunks. */
PRE_EXISTING_ONLY((byte) 1),
@@ -31,11 +31,6 @@ package com.seibel.distanthorizons.api.enums.worldGeneration;
*/
public enum EDhApiDistantGeneratorProgressDisplayLocation
{
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
OVERLAY,
CHAT,
LOG,
@@ -20,6 +20,8 @@
package com.seibel.distanthorizons.api.enums.worldGeneration;
/**
* DOWN_SAMPLED, <br>
*
* EMPTY, <br>
* STRUCTURE_START, <br>
* STRUCTURE_REFERENCE, <br>
@@ -37,6 +39,14 @@ package com.seibel.distanthorizons.api.enums.worldGeneration;
*/
public enum EDhApiWorldGenerationStep
{
/**
* Only used when using N-sized world generators or server-side retrieval.
* This denotes that the given datasource was created using lower quality LOD data from above it in the quad tree. <br>
*
* This isn't a valid option for queuing world generation.
*/
DOWN_SAMPLED(-1, "down_sampled"),
EMPTY(0, "empty"),
STRUCTURE_START(1, "structure_start"),
STRUCTURE_REFERENCE(2, "structure_reference"),
@@ -23,10 +23,10 @@ import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
/**
* Distant Horizons' fog configuration. <br><br>
* Distant Horizons' SSAO configuration. <br><br>
*
* @author James Seibel
* @version 2022-9-6
* @version 2026-02-05
* @since API 1.0.0
*/
public interface IDhApiAmbientOcclusionConfig extends IDhApiConfigGroup
@@ -34,32 +34,4 @@ public interface IDhApiAmbientOcclusionConfig extends IDhApiConfigGroup
/** Determines if Ambient Occlusion is rendered */
IDhApiConfigValue<Boolean> enabled();
/**
* Determines how many points in space are sampled for the occlusion test.
* Higher numbers will improve quality and reduce banding, but will increase GPU load.
*/
IDhApiConfigValue<Integer> sampleCount();
/** Determines the radius Screen Space Ambient Occlusion is applied, measured in blocks. */
IDhApiConfigValue<Double> radius();
/** Determines how dark the Screen Space Ambient Occlusion effect will be. */
IDhApiConfigValue<Double> strength();
/** Increasing the value can reduce banding at the cost of reducing the strength of the effect. */
IDhApiConfigValue<Double> bias();
/**
* Determines how dark the occlusion shadows can be. <br>
* 0 = totally black at the corners <br>
* 1 = no shadow
*/
IDhApiConfigValue<Double> minLight();
/**
* The radius, measured in pixels, that blurring is calculated. <br>
* Higher numbers will reduce banding at the cost of GPU performance.
*/
IDhApiConfigValue<Integer> blurRadius();
}
@@ -44,7 +44,7 @@ public interface IDhApiFarFogConfig extends IDhApiConfigGroup
* 0.0 = fog starts at the camera <br>
* 1.0 = fog starts at the edge of the fake chunk render distance <br>
*/
IDhApiConfigValue<Double> farFogStartDistance();
IDhApiConfigValue<Float> farFogStartDistance();
/**
* Defines where the fog ends as a percent of the radius
@@ -54,18 +54,18 @@ public interface IDhApiFarFogConfig extends IDhApiConfigGroup
* 0.0 = fog ends at the camera <br>
* 1.0 = fog ends at the edge of the fake chunk render distance <br>
*/
IDhApiConfigValue<Double> farFogEndDistance();
IDhApiConfigValue<Float> farFogEndDistance();
/** Defines how opaque the fog is at its thinnest point. */
IDhApiConfigValue<Double> farFogMinThickness();
IDhApiConfigValue<Float> farFogMinThickness();
/** Defines how opaque the fog is at its thickest point. */
IDhApiConfigValue<Double> farFogMaxThickness();
IDhApiConfigValue<Float> farFogMaxThickness();
/** Defines how the fog changes in thickness. */
IDhApiConfigValue<EDhApiFogFalloff> farFogFalloff();
/** Defines the fog density. */
IDhApiConfigValue<Double> farFogDensity();
IDhApiConfigValue<Float> farFogDensity();
}
@@ -104,7 +104,7 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup
* 2 = blending of 5x5 <br>
* ... <br>
*/
// IDhApiConfigValue<Integer> getBiomeBlending();
IDhApiConfigValue<Integer> getBiomeBlending();
@@ -124,19 +124,19 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup
*
* @since API 2.0.0
*/
IDhApiConfigValue<Double> overdrawPreventionRadius();
IDhApiConfigValue<Float> overdrawPreventionRadius();
/**
* Modifies how bright fake chunks are. <br>
* This is done when generating the vertex data and is applied before any shaders.
*/
IDhApiConfigValue<Double> brightnessMultiplier();
IDhApiConfigValue<Float> brightnessMultiplier();
/**
* Modifies how saturated fake chunks are. <br>
* This is done when generating the vertex data and is applied before any shaders.
*/
IDhApiConfigValue<Double> saturationMultiplier();
IDhApiConfigValue<Float> saturationMultiplier();
/** Defines if Distant Horizons should attempt to cull fake chunk cave geometry. */
IDhApiConfigValue<Boolean> caveCullingEnabled();
@@ -150,12 +150,6 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup
/** If enabled vanilla chunk rendering is disabled and only fake chunks are rendered. */
IDhApiConfigValue<Boolean> lodOnlyMode();
/**
* Setting this to a non-zero number will modify vanilla Minecraft's LOD Bias,
* increasing how quickly its textures fade away.
*/
IDhApiConfigValue<Double> lodBias();
/**
* Determines how LODs should be shaded.
*
@@ -52,24 +52,24 @@ public interface IDhApiHeightFogConfig extends IDhApiConfigGroup
* Defines the height fog's base height if {@link IDhApiHeightFogConfig#heightFogDirection()}
* is set to use a specific height.
*/
IDhApiConfigValue<Double> heightFogBaseHeight();
IDhApiConfigValue<Float> heightFogBaseHeight();
/** Defines the height fog's starting height as a percent of the world height. */
IDhApiConfigValue<Double> heightFogStartingHeightPercent();
IDhApiConfigValue<Float> heightFogStartingHeightPercent();
/** Defines the height fog's ending height as a percent of the world height. */
IDhApiConfigValue<Double> heightFogEndingHeightPercent();
IDhApiConfigValue<Float> heightFogEndingHeightPercent();
/** Defines how opaque the height fog is at its thinnest point. */
IDhApiConfigValue<Double> heightFogMinThickness();
IDhApiConfigValue<Float> heightFogMinThickness();
/** Defines how opaque the height fog is at its thickest point. */
IDhApiConfigValue<Double> heightFogMaxThickness();
IDhApiConfigValue<Float> heightFogMaxThickness();
/** Defines how the height fog changes in thickness. */
IDhApiConfigValue<EDhApiFogFalloff> heightFogFalloff();
/** Defines the height fog's density. */
IDhApiConfigValue<Double> heightFogDensity();
IDhApiConfigValue<Float> heightFogDensity();
}
@@ -37,8 +37,8 @@ public interface IDhApiNoiseTextureConfig extends IDhApiConfigGroup
/** Defines how many steps of noise should be applied. */
IDhApiConfigValue<Integer> noiseSteps();
/** Defines how intense the noise will be. */
IDhApiConfigValue<Double> noiseIntensity();
/** Defines how intense the noise will be, between 0.0 and 1.0. */
IDhApiConfigValue<Float> noiseIntensity();
/**
* Defines how far should the noise texture render before it fades away. (in blocks) <br>
@@ -1,20 +1,26 @@
package com.seibel.distanthorizons.api.interfaces.data;
/**
* Can be used to drastically speed up repeat read operations in {@link IDhApiTerrainDataRepo}.
*
* Can be used to drastically speed up repeat read operations in {@link IDhApiTerrainDataRepo}. <br><br>
*
* Once you are done with this cache, closing it will free up any objects
* the cache is holding. This can reduce Garbage Collector overhead and reduce stuttering.
*
* @see IDhApiTerrainDataRepo
*
* @author James Seibel
* @version 2024-7-14
* @version 2026-1-29
* @since API 3.0.0
*/
public interface IDhApiTerrainDataCache
public interface IDhApiTerrainDataCache extends AutoCloseable
{
/**
* Removes any data that's currently stored in this cache.
* This cane be done to free up memory or invalidate
* the cache so fresh data can be pulled in.
* <br><br>
* This should be called before de-referencing this object
* so DH can handle any necessary cleanup for internal objects.
*/
void clear();
@@ -32,7 +32,7 @@ import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
* @see IDhApiTerrainDataCache
*
* @author James Seibel
* @version 2023-6-22
* @version 2026-02-03
* @since API 1.0.0
*/
public interface IDhApiTerrainDataRepo
@@ -42,24 +42,17 @@ public interface IDhApiTerrainDataRepo
// getters //
//=========//
/** @see IDhApiTerrainDataRepo#getSingleDataPointAtBlockPos(IDhApiLevelWrapper, int, int, int, IDhApiTerrainDataCache) */
default DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ) { return this.getSingleDataPointAtBlockPos(levelWrapper, blockPosX, blockPosY, blockPosZ, null); }
/**
* Returns the terrain datapoint at the given block position, at/or containing the given Y position.
* @since API 3.0.0
*/
DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, IDhApiTerrainDataCache dataCache);
/** @see IDhApiTerrainDataRepo#getColumnDataAtBlockPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */
default DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ) { return this.getColumnDataAtBlockPos(levelWrapper, blockPosX, blockPosZ, null); }
/**
* Returns every datapoint in the column located at the given block X and Z position top to bottom.
* @since API 3.0.0
*/
DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, IDhApiTerrainDataCache dataCache);
/** @see IDhApiTerrainDataRepo#getAllTerrainDataAtChunkPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */
default DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ) { return this.getAllTerrainDataAtChunkPos(levelWrapper, chunkPosX, chunkPosZ, null); }
/**
* Returns every datapoint in the given chunk's X and Z position. <br><br>
*
@@ -71,8 +64,6 @@ public interface IDhApiTerrainDataRepo
*/
DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, IDhApiTerrainDataCache dataCache);
/** @see IDhApiTerrainDataRepo#getAllTerrainDataAtRegionPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */
default DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ) { return this.getAllTerrainDataAtRegionPos(levelWrapper, regionPosX, regionPosZ, null); }
/**
* Returns every datapoint in the given region's X and Z position. <br><br>
*
@@ -84,8 +75,6 @@ public interface IDhApiTerrainDataRepo
*/
DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, IDhApiTerrainDataCache dataCache);
/** @see IDhApiTerrainDataRepo#getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper, byte, int, int, IDhApiTerrainDataCache) */
default DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ) { return this.getAllTerrainDataAtDetailLevelAndPos(levelWrapper, detailLevel, posX, posZ, null); }
/**
* Returns every datapoint in the column located at the given detail level and X/Z position. <br>
* This can be used to return terrain data for non-standard sizes (IE 2x2 blocks or 2x2 chunks).
@@ -101,21 +90,6 @@ public interface IDhApiTerrainDataRepo
/** @see IDhApiTerrainDataRepo#raycast(IDhApiLevelWrapper, double, double, double, float, float, float, int, IDhApiTerrainDataCache) */
default DhApiResult<DhApiRaycastResult> raycast(
IDhApiLevelWrapper levelWrapper,
double rayOriginX, double rayOriginY, double rayOriginZ,
float rayDirectionX, float rayDirectionY, float rayDirectionZ,
int maxRayBlockLength)
{
return this.raycast(
levelWrapper,
rayOriginX, rayOriginY, rayOriginZ,
rayDirectionX, rayDirectionY, rayDirectionZ,
maxRayBlockLength,
null);
}
/**
* Returns the datapoint and position of the LOD
* at the end of the given ray. <br><br>
@@ -161,9 +135,16 @@ public interface IDhApiTerrainDataRepo
//=========//
/**
* Creates a new cache you manage that can be used to speed up repeat
* read operations. <br>
* Without a cache each operation must: hit the backing database file,
* decompress it, and parse it; which is a fairly slow process. <br>
*
* @return a {@link IDhApiTerrainDataCache} backed by {@link java.lang.ref.SoftReference}'s.
* @since API 3.0.0
* @since API 5.0.0
*/
IDhApiTerrainDataCache getSoftCache();
IDhApiTerrainDataCache createSoftCache();
}
@@ -40,7 +40,7 @@ public interface IDhApiEventInjector extends IDependencyInjector<IDhApiEvent>
* @return true if the handler was unbound, false if the handler wasn't bound.
* @throws IllegalArgumentException if the implementation object doesn't implement the interface
*/
// Note to self: Don't try adding a generic type to IDhApiEvent, the constructor won't accept it
// Note to DH Devs: Don't try adding a generic type to IDhApiEvent, the constructor won't accept it
boolean unbind(Class<? extends IDhApiEvent> dependencyInterface, Class<? extends IDhApiEvent> dependencyClassToRemove) throws IllegalArgumentException;
@@ -169,7 +169,7 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
*
* After the {@link IDhApiWorldGenerator} has been generated, it should be passed into the
* resultConsumer's {@link Consumer#accept(Object)} method.
* Note: if air blocks aren't included in the with the {@link DhApiChunk} with proper lighting, lower detail levels will appear as black/unlit.
* Note: if air blocks aren't included in the {@link IDhApiFullDataSource} with proper lighting, lower detail levels will appear as black/unlit.
*
* @implNote the default implementation of this method throws an {@link UnsupportedOperationException},
* and must be overridden when {@link #getReturnType()} returns {@link EDhApiWorldGeneratorReturnType#API_CHUNKS}.
@@ -3,6 +3,7 @@ package com.seibel.distanthorizons.api.interfaces.render;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
import java.util.List;
@@ -18,7 +19,7 @@ import java.util.List;
* @version 2024-7-3
* @since API 3.0.0
*/
public interface IDhApiCustomRenderObjectFactory
public interface IDhApiCustomRenderObjectFactory extends IBindable
{
/**
* Creates a {@link IDhApiRenderableBoxGroup} from for the given {@link DhApiRenderableBox}
@@ -73,7 +73,7 @@ public interface IDhApiRenderableBoxGroup extends List<DhApiRenderableBox>
* This is a good place to change the origin or notify of any box changes.
*/
void setPreRenderFunc(Consumer<DhApiRenderParam> renderEventParam);
void setPostRenderFunc(Consumer<DhApiRenderParam> renderEventParam); // TODO name?
void setPostRenderFunc(Consumer<DhApiRenderParam> renderEventParam);
/**
* If a cube's color, position, or other property is changed this method
@@ -0,0 +1,48 @@
/*
* 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.api.methods.events.abstractEvents;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiTextureCreatedParam;
/**
* Called after Distant Horizons (re)creates
* the color and depth textures it renders to. <br>
*
* @author James Seibel
* @version 2025-6-9
* @since API 4.1.0
*/
public abstract class DhApiAfterColorDepthTextureCreatedEvent implements IDhApiEvent<DhApiTextureCreatedParam>
{
/** Fired before Distant Horizons creates. */
public abstract void onResize(DhApiEventParam<DhApiTextureCreatedParam> event);
//=========================//
// internal DH API methods //
//=========================//
@Override
public final void fireEvent(DhApiEventParam<DhApiTextureCreatedParam> event) { this.onResize(event); }
}
@@ -0,0 +1,49 @@
/*
* 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.api.methods.events.abstractEvents;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiTextureCreatedParam;
/**
* Called before Distant Horizons (re)creates
* the color and depth textures it renders to. <br>
*
* @author James Seibel
* @version 2025-6-9
* @since API 4.1.0
*/
public abstract class DhApiBeforeColorDepthTextureCreatedEvent implements IDhApiEvent<DhApiTextureCreatedParam>
{
/** Fired before Distant Horizons creates. */
public abstract void onResize(DhApiEventParam<DhApiTextureCreatedParam> event);
//=========================//
// internal DH API methods //
//=========================//
@Override
public final void fireEvent(DhApiEventParam<DhApiTextureCreatedParam> event) { this.onResize(event); }
}
@@ -25,11 +25,16 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp
/**
* Called before Distant Horizons starts rendering a frame. <br>
* Canceling the event will prevent DH from rendering that frame.
* Canceling the event will prevent DH from rendering that frame. <br> <br>
*
* This is called before DH starts modifying the GL state.
* If you want to inject into DH's rendering pass, use {@link DhApiBeforeRenderPassEvent} instead.
*
* @author James Seibel
* @version 2023-6-23
* @since API 1.0.0
*
* @see DhApiBeforeRenderPassEvent
*/
public abstract class DhApiBeforeRenderEvent implements IDhApiCancelableEvent<DhApiRenderParam>
{
@@ -0,0 +1,197 @@
/*
* 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.api.methods.events.abstractEvents;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.interfaces.factories.IDhApiWrapperFactory;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
/**
* Used to override which blocks may be stored in a given chunk.
* This can be used for X-ray prevention or to replace problematic mod blocks
* that don't fit into the {@link IDhApiBlockStateWrapper} format DH requires
* (IE modded blocks that use NBT data
* to determine their model and/or texture). <br/><br/>
*
* This event is fired for each block or biome change when DH is processing a chunk.
* A change happens when DH finds a different block or biome while walking through a chunk.
* For example with the block sequence:<br/>
* <code> stone -> stone -> air -> stone </code> <br/>
* This event would be fired for the first, third, and forth blocks in the sequence
* (IE the first stone, first air, and last stone respectively). <br/> <br/>
*
* The order DH will process blocks is undefined so a specific ordering shouldn't be relied upon for your logic to function. <br/> <br/>
*
* <b>Threading note:</b> this event may be called concurrently across multiple threads. <br/>
* <b>Performance note:</b> this event will be called very frequently, avoid expensive lookups or other slow operations if possible. <br/>
*
* @see DhApiLevelLoadEvent
* @see IDhApiWrapperFactory
*
* @author James Seibel
* @version 2025-09-29
* @since API 4.1.0
*/
public abstract class DhApiChunkProcessingEvent implements IDhApiEvent<DhApiChunkProcessingEvent.EventParam>
{
public abstract void blockOrBiomeChangedDuringChunkProcessing(DhApiEventParam<EventParam> event);
//=========================//
// internal DH API methods //
//=========================//
@Override
public final void fireEvent(DhApiEventParam<EventParam> event) { this.blockOrBiomeChangedDuringChunkProcessing(event); }
//==================//
// parameter object //
//==================//
public static class EventParam implements IDhApiEventParam
{
/** The saved level. */
public final IDhApiLevelWrapper levelWrapper;
/** the processed chunk's X pos in chunk coordinates */
public final int chunkX;
/** the processed chunk's Z pos in chunk coordinates */
public final int chunkZ;
public int relativeBlockPosX;
public int blockPosY;
public int relativeBlockPosZ;
public IDhApiBlockStateWrapper currentBlock;
public IDhApiBiomeWrapper currentBiome;
private IDhApiBlockStateWrapper newBlock;
private IDhApiBiomeWrapper newBiome;
//=============//
// constructor //
//=============//
public EventParam(IDhApiLevelWrapper newLevelWrapper, int chunkX, int chunkZ)
{
this.levelWrapper = newLevelWrapper;
this.chunkX = chunkX;
this.chunkZ = chunkZ;
}
/**
* Internal method use by Distant Horizons
* to set up this event.
*/
public void updateForPosition(
int relativeBlockPosX, int blockPosY, int relativeBlockPosZ,
IDhApiBlockStateWrapper currentBlock,
IDhApiBiomeWrapper currentBiome)
{
this.relativeBlockPosX = relativeBlockPosX;
this.blockPosY = blockPosY;
this.relativeBlockPosZ = relativeBlockPosZ;
this.newBlock = null;
this.newBiome = null;
this.currentBlock = currentBlock;
this.currentBiome = currentBiome;
}
//=================//
// getters/setters //
//=================//
/**
* Sets the {@link IDhApiBlockStateWrapper} that should be used at this event's current position in the chunk.
* If you don't want to modify the block at this event's current position,
* either don't call this method or pass in null. <br>
* Passing in null will remove the override, meaning the original block will be used. <br><br>
*
* A {@link IDhApiWrapperFactory} should be used to get the {@link IDhApiBlockStateWrapper} that's returned.
* Attempting to create your own {@link IDhApiBlockStateWrapper} will cause a {@link ClassCastException}. <br/> <br/>
*
* If multiple API users are listening to this event the override may already have been set.
* With that in mind it is recommended to check if an override has already been set via
* {@link EventParam#getBlockOverride()} to handle that occurrence. <br>
* Note that the order of API events firing is undefined so a specific order shouldn't be relied upon. <br><br>
*
* @see IDhApiWrapperFactory
*/
public void setBlockOverride(IDhApiBlockStateWrapper block) { this.newBlock = block; }
/**
* Returns the currently overriding block for this position.
* This will be null if no other API event has set the override.
*/
public IDhApiBlockStateWrapper getBlockOverride() { return this.newBlock; }
/**
* Sets the {@link IDhApiBiomeWrapper} that should be used at this event's current position in the chunk.
* If you don't want to modify the biome at this event's current position,
* either don't call this method or pass in null. <br>
* Passing in null will remove the override, meaning the original biome will be used. <br><br>
*
* A {@link IDhApiWrapperFactory} should be used to get the {@link IDhApiBiomeWrapper} that's returned.
* Attempting to create your own {@link IDhApiBiomeWrapper} will cause a {@link ClassCastException}. <br/> <br/>
*
* If multiple API users are listening to this event the override may already have been set.
* With that in mind it is recommended to check if an override has already been set via
* {@link EventParam#getBiomeOverride()} ()} to handle that occurrence. <br>
* Note that the order of API events firing is undefined so a specific order shouldn't be relied upon. <br><br>
*
* @see IDhApiWrapperFactory
*/
public void setBiomeOverride(IDhApiBiomeWrapper biome) { this.newBiome = biome; }
/**
* Returns the currently overriding biome for this position.
* This will be null if no other API event has set the override.
*/
public IDhApiBiomeWrapper getBiomeOverride() { return this.newBiome; }
/**
* Returns the same instance of this event.
* Copying this event isn't recommended due to
* how often it would be called per chunk, creating
* unnecessary garbage collector pressure.
*/
@Override
public EventParam copy() { return this; }
@Override
public boolean getCopyBeforeFire() { return false; }
}
}
@@ -22,15 +22,18 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiTextureCreatedParam;
/**
* Called whenever Distant Horizons (re)creates
* Called before Distant Horizons (re)creates
* the color and depth textures it renders to. <br>
*
* @author James Seibel
* @version 2024-3-2
* @since API 2.0.0
* @deprecated Replaced by {@link DhApiBeforeColorDepthTextureCreatedEvent} since this event's name isn't obvious when it fires.
*/
@Deprecated // internal notes: this method must be kept around due to Iris using it and we don't want to break old Iris support
public abstract class DhApiColorDepthTextureCreatedEvent implements IDhApiEvent<DhApiColorDepthTextureCreatedEvent.EventParam>
{
/** Fired before Distant Horizons creates. */
@@ -73,6 +76,15 @@ public abstract class DhApiColorDepthTextureCreatedEvent implements IDhApiEvent<
this.newHeight = newHeight;
}
public EventParam(DhApiTextureCreatedParam textureCreatedParam)
{
this.previousWidth = textureCreatedParam.previousWidth;
this.previousHeight = textureCreatedParam.previousHeight;
this.newWidth = textureCreatedParam.newWidth;
this.newHeight = textureCreatedParam.newHeight;
}
@Override
@@ -9,5 +9,23 @@ import com.seibel.distanthorizons.api.interfaces.util.IDhApiCopyable;
*/
public interface IDhApiEventParam extends IDhApiCopyable
{
/**
* Internal DH use. <br> <br>
*
* Most API events will clone their parameters
* before firing to prevent API implementors
* from modifying the properties causing
* any subsequent listeners to see the wrong data. <br><br>
*
* However, this can be overridden for API events that shouldn't
* be cloned before firing.
* Generally that would be done for performance reasons
* where an event may fire hundreds or thousands of times
* in quick succession or where the event parameter is needed
* internally by DH after firing.
*
* @since API 4.1.0
*/
default boolean getCopyBeforeFire() { return true; }
}
@@ -20,6 +20,8 @@
package com.seibel.distanthorizons.api.methods.events.sharedParameterObjects;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiWorldLoadEvent;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
@@ -27,7 +29,7 @@ import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
* Contains information relevant to Distant Horizons and Minecraft rendering.
*
* @author James Seibel
* @version 2024-1-31
* @version 2025-12-23
* @since API 1.0.0
*/
public class DhApiRenderParam implements IDhApiEventParam
@@ -38,7 +40,7 @@ public class DhApiRenderParam implements IDhApiEventParam
/** Indicates how far into this tick the frame is. */
public final float partialTicks;
/**
/**
* Indicates DH's near clip plane, measured in blocks.
* Note: this may change based on time, player speed, and other factors.
*/
@@ -58,25 +60,34 @@ public class DhApiRenderParam implements IDhApiEventParam
public final DhApiMat4f dhProjectionMatrix;
/** The model view matrix Distant Horizons is using to render this frame. */
public final DhApiMat4f dhModelViewMatrix;
/** combination of the MVM and projection matrices */
public final DhApiMat4f dhMvmProjMatrix;
public final int worldYOffset;
/**
* The level currently being rendered.
*
* @since API 5.1.0
*/
public final IDhApiLevelWrapper clientLevelWrapper;
//==============//
// constructors //
//==============//
public DhApiRenderParam(DhApiRenderParam parent)
{
this(
parent.renderPass,
parent.partialTicks,
parent.nearClipPlane, parent.farClipPlane,
parent.mcProjectionMatrix.copy(), parent.mcModelViewMatrix.copy(),
parent.dhProjectionMatrix.copy(), parent.dhModelViewMatrix.copy(),
parent.worldYOffset
parent.renderPass,
parent.partialTicks,
parent.nearClipPlane, parent.farClipPlane,
parent.mcProjectionMatrix.copy(), parent.mcModelViewMatrix.copy(),
parent.dhProjectionMatrix.copy(), parent.dhModelViewMatrix.copy(),
parent.worldYOffset,
parent.clientLevelWrapper
);
}
public DhApiRenderParam(
@@ -85,8 +96,9 @@ public class DhApiRenderParam implements IDhApiEventParam
float nearClipPlane, float farClipPlane,
DhApiMat4f newMcProjectionMatrix, DhApiMat4f newMcModelViewMatrix,
DhApiMat4f newDhProjectionMatrix, DhApiMat4f newDhModelViewMatrix,
int worldYOffset
)
int worldYOffset,
IDhApiLevelWrapper clientLevelWrapper
)
{
this.renderPass = renderPass;
@@ -101,7 +113,12 @@ public class DhApiRenderParam implements IDhApiEventParam
this.dhProjectionMatrix = newDhProjectionMatrix;
this.dhModelViewMatrix = newDhModelViewMatrix;
DhApiMat4f combinedMatrix = new DhApiMat4f(this.dhProjectionMatrix);
combinedMatrix.multiply(this.dhModelViewMatrix);
this.dhMvmProjMatrix = combinedMatrix;
this.worldYOffset = worldYOffset;
this.clientLevelWrapper = clientLevelWrapper;
}
@@ -111,10 +128,9 @@ public class DhApiRenderParam implements IDhApiEventParam
// base overrides //
//================//
@Override
public DhApiRenderParam copy()
{
return new DhApiRenderParam(this);
}
@Override
public DhApiRenderParam copy() { return new DhApiRenderParam(this); }
}
@@ -0,0 +1,68 @@
/*
* 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.api.methods.events.sharedParameterObjects;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
/**
* Contains information relevant to when Distant Horizons (re)creates
* depth/color textures for rendering.
*
* @author James Seibel
* @version 2025-6-9
* @since API 4.1.0
*/
public class DhApiTextureCreatedParam implements IDhApiEventParam
{
/** Measured in pixels */
public final int previousWidth;
/** Measured in pixels */
public final int previousHeight;
/** Measured in pixels */
public final int newWidth;
/** Measured in pixels */
public final int newHeight;
public DhApiTextureCreatedParam(
int previousWidth, int previousHeight,
int newWidth, int newHeight)
{
this.previousWidth = previousWidth;
this.previousHeight = previousHeight;
this.newWidth = newWidth;
this.newHeight = newHeight;
}
@Override
public DhApiTextureCreatedParam copy()
{
return new DhApiTextureCreatedParam(
this.previousWidth, this.previousHeight,
this.newWidth, this.newHeight
);
}
}
@@ -31,7 +31,7 @@ import java.util.List;
* Contains a list of {@link DhApiTerrainDataPoint} representing the blocks in a Minecraft chunk.
*
* @author Builderb0y, James Seibel
* @version 2024-7-21
* @version 2025-12-11
* @since API 2.0.0
*
* @see IDhApiWrapperFactory
@@ -54,27 +54,12 @@ public class DhApiChunk
// constructors //
//==============//
/**
* Deprecated due to the topYBlockPos and bottomYBlockPos variables being put in the wrong order.
* They should have been in bottom -> top order.
*
* @see DhApiChunk#create(int, int, int, int)
*/
@Deprecated
public DhApiChunk(int chunkPosX, int chunkPosZ, int topYBlockPos, int bottomYBlockPos)
{ this(chunkPosX, chunkPosZ, bottomYBlockPos, topYBlockPos, false); }
/**
* @since API 3.0.0
*/
/** @since API 3.0.0 */
public static DhApiChunk create(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos)
{ return new DhApiChunk(chunkPosX, chunkPosZ, bottomYBlockPos, topYBlockPos, false); }
{ return new DhApiChunk(chunkPosX, chunkPosZ, bottomYBlockPos, topYBlockPos); }
/**
* Only visible to internal DH methods
* @param ignoredParameter is only present to differentiate the two constructors and isn't actually used
*/
private DhApiChunk(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos, boolean ignoredParameter)
/** Only visible to internal DH methods */
private DhApiChunk(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos)
{
this.chunkPosX = chunkPosX;
this.chunkPosZ = chunkPosZ;
@@ -29,7 +29,7 @@ import java.util.ArrayList;
* Holds a single datapoint of terrain data.
*
* @author James Seibel
* @version 2024-7-20
* @version 2025-11-15
* @since API 1.0.0
*/
public class DhApiTerrainDataPoint
@@ -47,6 +47,10 @@ public class DhApiTerrainDataPoint
public final int blockLightLevel;
public final int skyLightLevel;
/**
* An unsigned block position of the bottom vertex for this LOD relative to the level's minimum height.
* Should be greater than or equal to 0.
*/
public final int bottomYBlockPos;
public final int topYBlockPos;
@@ -59,28 +63,7 @@ public class DhApiTerrainDataPoint
// constructors //
//==============//
/**
* Deprecated due to the topYBlockPos and bottomYBlockPos variables being put in the wrong order.
* They should have been in bottom -> top order.
*
* @see DhApiTerrainDataPoint#create(byte, int, int, int, int, IDhApiBlockStateWrapper, IDhApiBiomeWrapper)
*/
@Deprecated
public DhApiTerrainDataPoint(
byte detailLevel,
int blockLightLevel, int skyLightLevel,
int topYBlockPos, int bottomYBlockPos,
IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper)
{
this(detailLevel, blockLightLevel, skyLightLevel,
bottomYBlockPos, topYBlockPos,
blockStateWrapper, biomeWrapper,
false);
}
/**
* @since API 3.0.0
*/
/** @since API 3.0.0 */
public static DhApiTerrainDataPoint create(
byte detailLevel,
int blockLightLevel, int skyLightLevel,
@@ -91,20 +74,15 @@ public class DhApiTerrainDataPoint
return new DhApiTerrainDataPoint(
detailLevel, blockLightLevel, skyLightLevel,
bottomYBlockPos, topYBlockPos,
blockStateWrapper, biomeWrapper,
false);
blockStateWrapper, biomeWrapper);
}
/**
* Only visible to internal DH methods
* @param ignoredParameter is only present to differentiate the two constructors and isn't actually used
*/
/** Only visible to internal DH methods */
private DhApiTerrainDataPoint(
byte detailLevel,
int blockLightLevel, int skyLightLevel,
int bottomYBlockPos, int topYBlockPos,
IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper,
boolean ignoredParameter
IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper
)
{
this.detailLevel = detailLevel;
@@ -118,4 +96,24 @@ public class DhApiTerrainDataPoint
this.biomeWrapper = biomeWrapper;
}
//================//
// base overrides //
//================//
@Override
public String toString()
{
return "[Block:" + this.blockStateWrapper.getSerialString() +
",Biome:" + this.biomeWrapper.getName() +
",TopY:" + this.topYBlockPos +
",BottomY:" + this.bottomYBlockPos +
",BlockLight:" + this.blockLightLevel +
",SkyLight:" + this.skyLightLevel +
"]";
}
}
@@ -23,6 +23,7 @@ public class DhApiRenderableBox
public DhApiVec3d maxPos;
public Color color;
/** @see EDhApiBlockMaterial */
public byte material;
@@ -143,19 +143,23 @@ public class ApiEventInjector extends DependencyInjector<IDhApiEvent> implements
// attempt to clone the event input if possible
// this is done to reduce the likely hood that one event listener
// will make change the event parameter for other listeners
// this is done to reduce the likelihood that one event listener
// will change the event parameter for other listeners
T input = eventInput;
if (eventInput instanceof IDhApiEventParam)
{
try
IDhApiEventParam dhApiEventParam = (IDhApiEventParam) eventInput;
if (dhApiEventParam.getCopyBeforeFire())
{
//noinspection unchecked
input = (T) ((IDhApiEventParam) eventInput).copy();
}
catch (Exception e)
{
LOGGER.error("Unable to clone event parameter ["+eventInput.getClass().getSimpleName()+"], error: ["+e.getMessage()+"].", e);
try
{
//noinspection unchecked
input = (T) dhApiEventParam.copy();
}
catch (Exception e)
{
LOGGER.error("Unable to clone event parameter [" + eventInput.getClass().getSimpleName() + "], error: [" + e.getMessage() + "].", e);
}
}
}
@@ -43,11 +43,11 @@ public class DependencyInjector<BindableType extends IBindable> implements IDepe
protected final boolean allowDuplicateBindings;
public DependencyInjector(Class<BindableType> newBindableInterface)
{
this.bindableInterface = newBindableInterface;
this.allowDuplicateBindings = false;
}
//==============//
// constructors //
//==============//
//region
public DependencyInjector(Class<BindableType> newBindableInterface, boolean newAllowDuplicateBindings)
{
@@ -55,18 +55,30 @@ public class DependencyInjector<BindableType extends IBindable> implements IDepe
this.allowDuplicateBindings = newAllowDuplicateBindings;
}
//endregion
//=========//
// binding //
//=========//
//region
@Override
public void bind(Class<? extends BindableType> dependencyInterface, BindableType dependencyImplementation) throws IllegalStateException, IllegalArgumentException
{
// duplicate check if requested
if (this.dependencies.containsKey(dependencyInterface) && !this.allowDuplicateBindings)
if (this.dependencies.containsKey(dependencyInterface)
&& !this.allowDuplicateBindings)
{
throw new IllegalStateException("The dependency [" + dependencyInterface.getSimpleName() + "] has already been bound.");
}
if (dependencyImplementation == null)
{
throw new NullPointerException("Can't bind null to ["+dependencyInterface.getSimpleName()+"]");
}
// make sure the given dependency implements the necessary interfaces
boolean implementsInterface = this.checkIfClassImplements(dependencyImplementation.getClass(), dependencyInterface) ||
@@ -129,25 +141,84 @@ public class DependencyInjector<BindableType extends IBindable> implements IDepe
@Override
public boolean checkIfClassExtends(Class<?> classToTest, Class<?> extensionToLookFor) { return extensionToLookFor.isAssignableFrom(classToTest); }
//endregion
//===========//
// unbinding //
//===========//
//region
public void replaceBinding(Class<? extends BindableType> dependencyInterface, BindableType dependencyImplementation) throws IllegalStateException, IllegalArgumentException
{
this.unbindAll(dependencyInterface);
this.bind(dependencyInterface, dependencyImplementation);
}
public void unbindAll(Class<? extends BindableType> dependencyInterface) throws IllegalStateException, IllegalArgumentException
{
// remove the dependency if present
this.dependencies.remove(dependencyInterface);
}
public void unbind(Class<? extends BindableType> dependencyInterface, BindableType dependencyImplementation) throws IllegalStateException, IllegalArgumentException
{
// check if this object is bound
if (!this.dependencies.containsKey(dependencyInterface))
{
return;
}
// make sure the given dependency implements the necessary interfaces
boolean implementsInterface = this.checkIfClassImplements(dependencyImplementation.getClass(), dependencyInterface)
|| this.checkIfClassExtends(dependencyImplementation.getClass(), dependencyInterface);
boolean implementsBindable = this.checkIfClassImplements(dependencyImplementation.getClass(), this.bindableInterface);
// display any errors
if (!implementsInterface)
{
throw new IllegalArgumentException("The dependency [" + dependencyImplementation.getClass().getSimpleName() + "] doesn't implement or extend: [" + dependencyInterface.getSimpleName() + "].");
}
if (!implementsBindable)
{
throw new IllegalArgumentException("The dependency [" + dependencyImplementation.getClass().getSimpleName() + "] doesn't implement the interface: [" + IBindable.class.getSimpleName() + "].");
}
// make sure the hashSet has an array to hold the dependency
if (!this.dependencies.containsKey(dependencyInterface))
{
this.dependencies.put(dependencyInterface, new ArrayList<BindableType>());
}
// remove the dependency if present
this.dependencies.get(dependencyInterface).remove(dependencyImplementation);
this.dependencies.remove(dependencyInterface);
}
//endregion
//=========//
// getters //
//=========//
//region
@SuppressWarnings("unchecked")
@Override
public <T extends BindableType> T get(Class<T> interfaceClass) throws ClassCastException
{
return (T) this.getInternalLogic(interfaceClass, false).get(0);
}
{ return (T) this.getInternalLogic(interfaceClass, false).get(0); }
@Override
public <T extends BindableType> ArrayList<T> getAll(Class<T> interfaceClass) throws ClassCastException
{
return this.getInternalLogic(interfaceClass, false);
}
{ return this.getInternalLogic(interfaceClass, false); }
@Override
public <T extends BindableType> T get(Class<T> interfaceClass, boolean allowIncompleteDependencies) throws ClassCastException
{
return (T) this.getInternalLogic(interfaceClass, allowIncompleteDependencies).get(0);
}
{ return (T) this.getInternalLogic(interfaceClass, allowIncompleteDependencies).get(0); }
/**
* Always returns a list of size 1 or greater,
@@ -180,6 +251,7 @@ public class DependencyInjector<BindableType extends IBindable> implements IDepe
return emptyList;
}
//endregion
/** Removes all bound dependencies. */
@@ -31,26 +31,26 @@ public final class ModInfo
public static final String DEDICATED_SERVER_INITIAL_PATH = "dedicated_server_initial";
/** Incremented every time any packets are added, changed or removed, with a few exceptions. */
public static final int PROTOCOL_VERSION = 9;
public static final int PROTOCOL_VERSION = 13;
public static final String WRAPPER_PACKET_PATH = "message";
/** The internal mod name */
public static final String NAME = "DistantHorizons";
/** Human-readable version of NAME */
public static final String READABLE_NAME = "Distant Horizons";
public static final String VERSION = "2.3.0-b-dev";
public static final String VERSION = "3.0.0-b-dev";
/** Returns true if the current build is an unstable developer build, false otherwise. */
public static final boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev");
/** This version should only be updated when breaking changes are introduced to the DH API */
public static final int API_MAJOR_VERSION = 4;
public static final int API_MAJOR_VERSION = 6;
/** This version should be updated whenever new methods are added to the DH API */
public static final int API_MINOR_VERSION = 0;
/** This version should be updated whenever non-breaking fixes are added to the DH API */
public static final int API_PATCH_VERSION = 0;
/** If the config file has an older version it'll be re-created from scratch. */
public static final int CONFIG_FILE_VERSION = 3;
public static final int CONFIG_FILE_VERSION = 4;
/** All DH owned threads should start with this string to allow for easier debugging and profiling. */
public static final String THREAD_NAME_PREFIX = "DH-";
@@ -1,83 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.coreapi.interfaces.config;
import java.util.function.Consumer;
/**
* Use for making the config variables
*
* @author coolGi
* @version 2022-5-26
*/
public interface IConfigEntry<T>
{
/** Gets the default value of the option */
T getDefaultValue();
void setApiValue(T newApiValue);
T getApiValue();
/** Returns true if this config can be set via the API. */
boolean getAllowApiOverride();
void set(T newValue);
T get();
T getTrueValue();
/** Sets the value without saving */
void setWithoutSaving(T newValue);
/** Gets the min value */
T getMin();
/** Sets the min value */
void setMin(T newMin);
/** Gets the max value */
T getMax();
/** Sets the max value */
void setMax(T newMax);
/** Sets the min and max in 1 setter */
void setMinMax(T newMin, T newMax);
/** Gets the comment */
String getComment();
/** Sets the comment */
void setComment(String newComment);
/**
* Checks if the option is valid
*
* 0 == valid
* 2 == invalid
* 1 == number too high
* -1 == number too low
*/
byte isValid(); // TODO replace with an enum
/** Checks if a value is valid */
byte isValid(T value);
/** Is the value of this equal to another */
boolean equals(IConfigEntry<?> obj);
void addValueChangeListener(Consumer<T> onValueChangeFunc);
}
+52 -31
View File
@@ -1,18 +1,23 @@
plugins {
id "java"
id "com.github.johnrengelman.shadow" version '8.1.1' apply false // Set this to true if you're using the standalone Core project
id "com.gradleup.shadow"
}
apply plugin: "application"
repositories {
mavenCentral()
maven { url "https://repo.enonic.com/public/" }
}
application {
mainClass.set("com.seibel.distanthorizons.core.jar.JarMain")
tasks.withType(JavaCompile).configureEach {
options.release = 8
options.encoding = "UTF-8"
}
configurations {
shadowedArtifact // Used by DH to specify that we want to implement the shadowed core JAR file instead of the regular JAR file
shade
implementation.extendsFrom shade
testImplementation.extendsFrom compileOnly
}
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
@@ -20,36 +25,46 @@ OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePl
// Set the OS lwjgl is using to the current os
project.ext.lwjglNatives = "natives-" + os.toFamilyName()
dependencies { // All of these dependencies are in Vanilla Minecraft, but we need to depend on it as we arent importing Minecraft in the core
// Imports most of lwjgl's libraries (well, only the ones that we need)
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}") // TODO: Use Minecraft's version for lwjgl_version (which changes in nearly every version) instead of a hard defined version for all versions
dependencies {
// API project dependency
implementation project(":api")
// REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use
implementation "org.lwjgl:lwjgl"
implementation "org.lwjgl:lwjgl-assimp"
implementation "org.lwjgl:lwjgl-glfw"
implementation "org.lwjgl:lwjgl-openal"
implementation "org.lwjgl:lwjgl-opengl"
implementation "org.lwjgl:lwjgl-stb"
implementation "org.lwjgl:lwjgl-tinyfd"
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
// MC-provided libraries (available at runtime via Minecraft)
compileOnly platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
compileOnly "org.lwjgl:lwjgl"
compileOnly "org.lwjgl:lwjgl-assimp"
compileOnly "org.lwjgl:lwjgl-glfw"
compileOnly "org.lwjgl:lwjgl-stb"
compileOnly "org.lwjgl:lwjgl-tinyfd"
testRuntimeOnly platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
testRuntimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
testRuntimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
testRuntimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
testRuntimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
testRuntimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
// FIXME for some reason this line doesn't actually shade in the library
// shade "it.unimi.dsi:fastutil:${rootProject.fastutil_version}" // Add our own fastutil version
compileOnly("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
compileOnly("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
compileOnly("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
compileOnly("org.joml:joml:${rootProject.joml_version}")
compileOnly("io.netty:netty-buffer:${rootProject.netty_version}")
compileOnly("org.jetbrains:annotations:16.0.2")
compileOnly("com.google.code.findbugs:jsr305:3.0.2")
compileOnly("com.google.common:google-collect:0.5")
compileOnly("com.google.guava:guava:31.1-jre")
// DH's bundled libraries (shadowed + relocated in loader jars)
implementation("com.github.luben:zstd-jni:${rootProject.zstd_version}")
implementation("org.lz4:lz4-java:${rootProject.lz4_version}")
implementation("org.tukaani:xz:${rootProject.xz_version}")
implementation("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}")
implementation("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
implementation("com.electronwill.night-config:json:${rootProject.nightconfig_version}")
// Some other dependencies
implementation("org.jetbrains:annotations:16.0.2")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("com.google.common:google-collect:0.5")
implementation("com.google.guava:guava:31.1-jre")
// JUnit (core tests only)
compileOnly("junit:junit:4.13")
compileOnly("org.junit.jupiter:junit-jupiter:5.8.2")
compileOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
}
artifacts {
@@ -60,4 +75,10 @@ shadowJar {
def librariesLocation = "DistantHorizons.libraries"
// relocate "it.unimi.dsi.fastutil", "${librariesLocation}.unimi.dsi.fastutil"
mergeServiceFiles()
}
}
test {
// this is necessary specifically for the Compression tests since those
// need more than the default 512 MB of RAM
jvmArgs '-Xmx4096m'
}
@@ -19,43 +19,69 @@
package com.seibel.distanthorizons.core;
import com.github.luben.zstd.ZstdOutputStream;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderObjectFactory;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderEvent;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.eventHandlers.IgnoredDimensionCsvHandler;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.sql.DatabaseUpdater;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.world.DhApiWorldProxy;
import com.seibel.distanthorizons.core.api.external.methods.config.DhApiConfig;
import com.seibel.distanthorizons.core.api.external.methods.data.DhApiTerrainDataRepo;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import net.jpountz.lz4.LZ4FrameOutputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.sqlite.SQLiteJDBCLoader;
import org.sqlite.util.OSInfo;
import org.tukaani.xz.XZOutputStream;
import java.awt.*;
import java.io.File;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.List;
/** Handles first time Core setup. */
public class Initializer
{
private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME + "-" + Initializer.class.getSimpleName());
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
public static void init()
{
//============================//
// check referenced libraries //
//============================//
//region
LOGGER.info("Running library validation...");
// confirm that all referenced libraries are available to use
try
{
// if any library isn't present in the jar its class
// will throw an error (not an exception)
Class<?> fastCompressor = LZ4FrameOutputStream.class;
Class<?> smallCompressor = XZOutputStream.class;
Class<?> lz4Compressor = LZ4FrameOutputStream.class;
Class<?> zstdCompressor = ZstdOutputStream.class;
{
byte[] testCompressByteArray = new byte[1024];
for (int i = 0; i < testCompressByteArray.length; i++)
{
testCompressByteArray[i] = (byte) (i % 126);
}
byte[] compressedBytes = com.github.luben.zstd.Zstd.compress(testCompressByteArray);
com.github.luben.zstd.Zstd.decompress(compressedBytes);
}
Class<?> lzmaCompressor = XZOutputStream.class;
//Class<?> networking = ByteBuf.class;
Class<?> config = com.electronwill.nightconfig.core.Config.class;
Class<?> oldFastUtil = it.unimi.dsi.fastutil.longs.LongArrayList.class; // available in 8.2.1
@@ -63,30 +89,6 @@ public class Initializer
Class<?> sqliteJava = org.sqlite.SQLiteConnection.class;
Class<?> sqliteNative = org.sqlite.core.NativeDB.class;
//// maybe these lines are needed to shade SQLite, James isn't sure.
//// Although they never seemed to fail, which is a bit odd.
//try
//{
// // needed by Forge to load the Java database connection
// Class.forName("org.sqlite.JDBC");
// LOGGER.info("loaded normal SQLITE");
//}
//catch (ClassNotFoundException e)
//{
// LOGGER.warn("normal: " + e.getMessage(), e);
//}
//
//try
//{
// // needed by Forge to load the Java database connection
// Class.forName("DistantHorizons.libraries.sqlite.JDBC");
// LOGGER.info("loaded shaded SQLITE");
//}
//catch (ClassNotFoundException e)
//{
// LOGGER.warn("shaded: " + e.getMessage(), e);
//}
boolean sqliteLoaded = SQLiteJDBCLoader.initialize();
if (!sqliteLoaded)
{
@@ -95,12 +97,18 @@ public class Initializer
}
catch (Throwable e)
{
LOGGER.fatal("Critical programmer error: One or more libraries aren't present. Error: [" + e.getMessage() + "].", e);
// throwing here should crash the game, notifying the developer that something is wrong
throw new RuntimeException(e);
MC_CLIENT.crashMinecraft("Distant Horizons critical setup error: One or more libraries are either in-accessible, corrupted, or overwritten by another mod. Error: [" + e.getMessage() + "].", e);
}
// confirm the resource directory is present
//endregion
//==========================//
// check resource directory //
//==========================//
//region
try
{
int scriptCount = DatabaseUpdater.getAutoUpdateScriptCount();
@@ -111,10 +119,18 @@ public class Initializer
}
catch (Exception e)
{
LOGGER.fatal("Critical programmer error: Can't read SQL Scripts resource folder is either missing or malformed. Error: [" + e.getMessage() + "].");
throw new RuntimeException(e);
MC_CLIENT.crashMinecraft("Critical programmer error: Can't read SQL Scripts resource folder is either missing or malformed. Error: [" + e.getMessage() + "].", e);
}
//endregion
//===========================//
// Java AWT Headless setting //
//===========================//
//region
// This code has been disabled since it can cause Mac
// to lock up and refuse the load (there's a bug with Java.awt texture loading)
//if (MC_CLIENT != null)
@@ -131,18 +147,99 @@ public class Initializer
// }
//}
//endregion
//===================//
// API delayed setup //
//===================//
//region
// link Core's config to the API
DhApi.Delayed.configs = DhApiConfig.INSTANCE;
DhApi.Delayed.terrainRepo = DhApiTerrainDataRepo.INSTANCE;
DhApi.Delayed.worldProxy = DhApiWorldProxy.INSTANCE;
DhApi.Delayed.renderProxy = DhApiRenderProxy.INSTANCE;
DhApi.Delayed.customRenderObjectFactory = GenericRenderObjectFactory.INSTANCE;
DhApi.Delayed.wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
if (DhApi.Delayed.wrapperFactory == null)
{
LOGGER.error("Programmer Error: No ["+IWrapperFactory.class.getSimpleName()+"] assigned to the DhApi.");
MC_CLIENT.crashMinecraft("Programmer Error: No ["+IWrapperFactory.class.getSimpleName()+"] assigned to the DhApi.", new Exception());
}
DhApi.Delayed.customRenderObjectFactory = SingletonInjector.INSTANCE.get(IDhApiCustomRenderObjectFactory.class);
if (DhApi.Delayed.customRenderObjectFactory == null)
{
MC_CLIENT.crashMinecraft("Programmer Error: No ["+IDhApiCustomRenderObjectFactory.class.getSimpleName()+"] assigned to the DhApi.", new Exception());
}
DhApi.events.bind(DhApiBeforeRenderEvent.class, IgnoredDimensionCsvHandler.INSTANCE);
//endregion
//==============================//
// G1 Garbage collector warning //
//==============================//
//region
// log a warning if G1GC is being used
// (this garbage collector is known to cause stuttering)
{
boolean g1GcInUse = false;
StringBuilder garbageCollectorNames = new StringBuilder();
List<GarbageCollectorMXBean> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcMxBean : gcMxBeans)
{
if (!garbageCollectorNames.toString().isEmpty())
{
garbageCollectorNames.append(", ");
}
garbageCollectorNames.append(gcMxBean.getName());
// "G1 Young Generation" // "G1 Concurrent GC" // "G1 Old Generation"
if (gcMxBean.getName().toLowerCase().contains("g1 "))
{
g1GcInUse = true;
}
}
LOGGER.info("Garbage collectors: ["+garbageCollectorNames+"]");
if (g1GcInUse)
{
String warningMessageHeader = "Distant Horizons: G1 Garbage collector detected.";
String warningMessageBody =
"This can cause FPS stuttering. \n" +
"It's recommended to use a concurrent garbage collector \n" +
"like ZGC (Java 21+) or Shenandoah (Java 8 through 17) \n" +
"for a smoother experience."
;
if (Config.Common.Logging.Warning.logGarbageCollectorWarning.get())
{
LOGGER.warn(
warningMessageHeader + "\n" +
warningMessageBody +
"");
}
if (Config.Common.Logging.Warning.showGarbageCollectorWarning.get())
{
ClientApi.INSTANCE.showChatMessageNextFrame(
MinecraftTextFormat.ORANGE + warningMessageHeader + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
warningMessageBody +
"");
}
}
}
//endregion
}
}
@@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiAmbientOcclusionConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.Config;
public class DhApiAmbientOcclusionConfig implements IDhApiAmbientOcclusionConfig
@@ -35,30 +35,6 @@ public class DhApiAmbientOcclusionConfig implements IDhApiAmbientOcclusionConfig
@Override
public IDhApiConfigValue<Boolean> enabled()
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.Ssao.enableSsao); }
@Override
public IDhApiConfigValue<Integer> sampleCount()
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.Ssao.sampleCount); }
@Override
public IDhApiConfigValue<Double> radius()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Ssao.radius); }
@Override
public IDhApiConfigValue<Double> strength()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Ssao.strength); }
@Override
public IDhApiConfigValue<Double> bias()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Ssao.bias); }
@Override
public IDhApiConfigValue<Double> minLight()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Ssao.minLight); }
@Override
public IDhApiConfigValue<Integer> blurRadius()
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.Ssao.blurRadius); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.enableSsao); }
}
@@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiDebuggingConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
@@ -34,7 +34,7 @@ public class DhApiDebuggingConfig implements IDhApiDebuggingConfig
public IDhApiConfigValue<EDhApiDebugRendering> debugRendering()
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRendering); }
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRenderingColors); }
public IDhApiConfigValue<Boolean> debugKeybindings()
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.enableDebugKeybindings); }
@@ -22,7 +22,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFarFogConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.Config;
public class DhApiFarFogConfig implements IDhApiFarFogConfig
@@ -34,27 +34,27 @@ public class DhApiFarFogConfig implements IDhApiFarFogConfig
@Override
public IDhApiConfigValue<Double> farFogStartDistance()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.farFogStart); }
public IDhApiConfigValue<Float> farFogStartDistance()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.farFogStart); }
@Override
public IDhApiConfigValue<Double> farFogEndDistance()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.farFogEnd); }
public IDhApiConfigValue<Float> farFogEndDistance()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.farFogEnd); }
@Override
public IDhApiConfigValue<Double> farFogMinThickness()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.farFogMin); }
public IDhApiConfigValue<Float> farFogMinThickness()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.farFogMin); }
@Override
public IDhApiConfigValue<Double> farFogMaxThickness()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.farFogMax); }
public IDhApiConfigValue<Float> farFogMaxThickness()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.farFogMax); }
@Override
public IDhApiConfigValue<EDhApiFogFalloff> farFogFalloff()
{ return new DhApiConfigValue<EDhApiFogFalloff, EDhApiFogFalloff>(Config.Client.Advanced.Graphics.Fog.farFogFalloff); }
@Override
public IDhApiConfigValue<Double> farFogDensity()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.farFogDensity); }
public IDhApiConfigValue<Float> farFogDensity()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.farFogDensity); }
}
@@ -25,9 +25,10 @@ import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFarFogConfig;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFogConfig;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiHeightFogConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.coreapi.util.converters.ApiFogDrawModeConverter;
import com.seibel.distanthorizons.core.config.api.converters.ApiFogDrawModeConverter;
import com.seibel.distanthorizons.core.config.api.converters.InvertedBoolConverter;
public class DhApiFogConfig implements IDhApiFogConfig
{
@@ -67,7 +68,7 @@ public class DhApiFogConfig implements IDhApiFogConfig
@Override
@Deprecated
public IDhApiConfigValue<Boolean> disableVanillaFog()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.disableVanillaFog); }
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.enableVanillaFog, new InvertedBoolConverter()); }
@Override
public IDhApiConfigValue<Boolean> enableVanillaFog()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.enableVanillaFog); }
@@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiGenericRenderingConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.Config;
public class DhApiGenericRenderingConfig implements IDhApiGenericRenderingConfig
@@ -23,9 +23,10 @@ import com.seibel.distanthorizons.api.enums.config.*;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.*;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.api.converters.RenderModeEnabledConverter;
public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
{
@@ -60,7 +61,7 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
@Override
public IDhApiConfigValue<Boolean> renderingEnabled()
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.quickEnableRendering); }
{ return new DhApiConfigValue<EDhApiRendererMode, Boolean>(Config.Client.Advanced.Debugging.rendererMode, new RenderModeEnabledConverter()); }
@Override
public IDhApiConfigValue<EDhApiRendererMode> renderingMode()
@@ -96,10 +97,9 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
public IDhApiConfigValue<Boolean> tintWithAvoidedBlocks()
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks); }
// TODO re-implement
// @Override
// public IDhApiConfigValue<Integer> getBiomeBlending()
// { return new DhApiConfigValue<Integer, Integer>(Quality.lodBiomeBlending); }
@Override
public IDhApiConfigValue<Integer> getBiomeBlending()
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.Quality.lodBiomeBlending); }
@@ -108,16 +108,16 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
//===========================//
@Override
public IDhApiConfigValue<Double> overdrawPreventionRadius()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Culling.overdrawPrevention); }
public IDhApiConfigValue<Float> overdrawPreventionRadius()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Culling.overdrawPrevention); }
@Override
public IDhApiConfigValue<Double> brightnessMultiplier()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Quality.brightnessMultiplier); }
public IDhApiConfigValue<Float> brightnessMultiplier()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Quality.brightnessMultiplier); }
@Override
public IDhApiConfigValue<Double> saturationMultiplier()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Quality.saturationMultiplier); }
public IDhApiConfigValue<Float> saturationMultiplier()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Quality.saturationMultiplier); }
@Override
public IDhApiConfigValue<Boolean> caveCullingEnabled()
@@ -135,10 +135,6 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
public IDhApiConfigValue<Boolean> lodOnlyMode()
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.lodOnlyMode); }
@Override
public IDhApiConfigValue<Double> lodBias()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Quality.lodBias); }
@Override
public IDhApiConfigValue<EDhApiLodShading> lodShading()
{ return new DhApiConfigValue<EDhApiLodShading, EDhApiLodShading>(Config.Client.Advanced.Graphics.Quality.lodShading); }
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiHeightFogConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.Config;
public class DhApiHeightFogConfig implements IDhApiHeightFogConfig
@@ -44,31 +44,31 @@ public class DhApiHeightFogConfig implements IDhApiHeightFogConfig
{ return new DhApiConfigValue<EDhApiHeightFogDirection, EDhApiHeightFogDirection>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection); }
@Override
public IDhApiConfigValue<Double> heightFogBaseHeight()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight); }
public IDhApiConfigValue<Float> heightFogBaseHeight()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight); }
@Override
public IDhApiConfigValue<Double> heightFogStartingHeightPercent()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogStart); }
public IDhApiConfigValue<Float> heightFogStartingHeightPercent()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogStart); }
@Override
public IDhApiConfigValue<Double> heightFogEndingHeightPercent()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd); }
public IDhApiConfigValue<Float> heightFogEndingHeightPercent()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd); }
@Override
public IDhApiConfigValue<Double> heightFogMinThickness()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin); }
public IDhApiConfigValue<Float> heightFogMinThickness()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin); }
@Override
public IDhApiConfigValue<Double> heightFogMaxThickness()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax); }
public IDhApiConfigValue<Float> heightFogMaxThickness()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax); }
@Override
public IDhApiConfigValue<EDhApiFogFalloff> heightFogFalloff()
{ return new DhApiConfigValue<EDhApiFogFalloff, EDhApiFogFalloff>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff); }
@Override
public IDhApiConfigValue<Double> heightFogDensity()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity); }
public IDhApiConfigValue<Float> heightFogDensity()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity); }
}
@@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiMultiThreadingConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.Config;
public class DhApiMultiThreadingConfig implements IDhApiMultiThreadingConfig
@@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiMultiplayerConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.api.enums.config.EDhApiServerFolderNameMode;
@@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiNoiseTextureConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.Config;
public class DhApiNoiseTextureConfig implements IDhApiNoiseTextureConfig
@@ -41,8 +41,8 @@ public class DhApiNoiseTextureConfig implements IDhApiNoiseTextureConfig
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.NoiseTexture.noiseSteps); }
@Override
public IDhApiConfigValue<Double> noiseIntensity()
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity); }
public IDhApiConfigValue<Float> noiseIntensity()
{ return new DhApiConfigValue<Float, Float>(Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity); }
@Override
public IDhApiConfigValue<Integer> noiseDropoff()
@@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.common;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.both.IDhApiWorldGenerationConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.api.DhApiConfigValue;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.core.config.Config;
@@ -2,10 +2,10 @@ package com.seibel.distanthorizons.core.api.external.methods.data;
import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataCache;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.SoftReference;
@@ -13,15 +13,16 @@ import java.lang.ref.SoftReference;
public class DhApiTerrainDataCache implements IDhApiTerrainDataCache
{
private final Object modificationLock = new Object();
private Long2ReferenceOpenHashMap<SoftReference<FullDataSourceV2>> posToFullDataRef = new Long2ReferenceOpenHashMap<>();
private final Long2ReferenceOpenHashMap<SoftReference<FullDataSourceV2>> posToFullDataRef = new Long2ReferenceOpenHashMap<>();
private static final Logger LOGGER = LogManager.getLogger(DhApiTerrainDataCache.class.getSimpleName());
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
//==================//
// internal methods //
//==================//
//region
public void add(long pos, FullDataSourceV2 dataSource)
{
@@ -48,11 +49,14 @@ public class DhApiTerrainDataCache implements IDhApiTerrainDataCache
}
}
//endregion
//=============//
// API methods //
//=============//
//region
@Override
public void clear()
@@ -82,6 +86,23 @@ public class DhApiTerrainDataCache implements IDhApiTerrainDataCache
}
}
//endregion
//================//
// base overrides //
//================//
//region
@Override
public void close() { this.clear(); }
@Override
public String toString() { return "Size: " + this.posToFullDataRef.size(); }
//endregion
}
@@ -31,9 +31,10 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.pos.DhLodPos;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
import com.seibel.distanthorizons.core.util.DhApiTerrainDataPointUtil;
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
@@ -48,8 +49,7 @@ import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3i;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
@@ -64,7 +64,9 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
{
public static DhApiTerrainDataRepo INSTANCE = new DhApiTerrainDataRepo();
private static final Logger LOGGER = LogManager.getLogger(DhApiTerrainDataRepo.class.getSimpleName());
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final AbstractDebugWireframeRenderer DEBUG_RENDERER = SingletonInjector.INSTANCE.get(AbstractDebugWireframeRenderer.class);
// debugging values
private static volatile boolean debugThreadRunning = false;
@@ -89,30 +91,30 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
//================//
@Override
public DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, @Nullable IDhApiTerrainDataCache dataCache)
{ return getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, dataCache); }
public DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, IDhApiTerrainDataCache dataCache)
{ return getTerrainDataAtBlockYPos(levelWrapper, DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, dataCache); }
@Override
public DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, @Nullable IDhApiTerrainDataCache dataCache)
{ return getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, dataCache); }
public DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, IDhApiTerrainDataCache dataCache)
{ return getTerrainDataColumnArray(levelWrapper, DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, dataCache); }
@Override
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, @Nullable IDhApiTerrainDataCache dataCache)
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ), dataCache); }
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, IDhApiTerrainDataCache dataCache)
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, DhSectionPos.encode(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ), dataCache); }
@Override
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, @Nullable IDhApiTerrainDataCache dataCache)
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ), dataCache); }
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, IDhApiTerrainDataCache dataCache)
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, DhSectionPos.encode(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ), dataCache); }
@Override
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, @Nullable IDhApiTerrainDataCache dataCache)
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(detailLevel, posX, posZ), dataCache); }
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, IDhApiTerrainDataCache dataCache)
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, DhSectionPos.encode(detailLevel, posX, posZ), dataCache); }
// private getters //
/** Returns a single API terrain datapoint that contains the given Y block position */
private static DhApiResult<DhApiTerrainDataPoint> getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer blockYPos, @Nullable IDhApiTerrainDataCache dataCache)
private static DhApiResult<DhApiTerrainDataPoint> getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, long requestedColumnPos, Integer blockYPos, IDhApiTerrainDataCache dataCache)
{
DhApiResult<DhApiTerrainDataPoint[]> result = getTerrainDataColumnArray(levelWrapper, requestedColumnPos, blockYPos, dataCache);
if (result.success && result.payload.length > 0)
@@ -126,7 +128,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
}
/**
* Returns all the block columns represented by the given {@link DhLodPos}. <br>
* Returns all the block columns represented by the given {@link DhSectionPos}. <br>
* IE, A position with the detail level: <br>
* 0 (block): will return a 1x1 matrix of data. (don't do this, we have a specific method for that.) <br>
* 1 (2 blocks): will return a 2x2 matrix of data. <br>
@@ -135,11 +137,14 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
* will stop and return the in progress data if any errors are encountered.
*/
private static DhApiResult<DhApiTerrainDataPoint[][][]> getTerrainDataOverAreaForPositionDetailLevel(
IDhApiLevelWrapper levelWrapper, DhLodPos requestedAreaPos,
@Nullable IDhApiTerrainDataCache dataCache)
IDhApiLevelWrapper levelWrapper, long requestedAreaPos,
IDhApiTerrainDataCache dataCache)
{
DhLodPos startingBlockPos = requestedAreaPos.getCornerLodPos(LodUtil.BLOCK_DETAIL_LEVEL);
int widthOfAreaInBlocks = BitShiftUtil.powerOfTwo(requestedAreaPos.detailLevel);
byte requestedDetailLevel = DhSectionPos.getDetailLevel(requestedAreaPos);
long startingBlockPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL,
DhSectionPos.getX(requestedAreaPos) * BitShiftUtil.powerOfTwo(requestedDetailLevel - LodUtil.BLOCK_DETAIL_LEVEL),
DhSectionPos.getZ(requestedAreaPos) * BitShiftUtil.powerOfTwo(requestedDetailLevel - LodUtil.BLOCK_DETAIL_LEVEL));
int widthOfAreaInBlocks = BitShiftUtil.powerOfTwo(requestedDetailLevel);
DhApiTerrainDataPoint[][][] returnArray = new DhApiTerrainDataPoint[widthOfAreaInBlocks][widthOfAreaInBlocks][];
int dataColumnsReturned = 0;
@@ -149,7 +154,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
{
for (int z = 0; z < widthOfAreaInBlocks; z++)
{
DhLodPos blockColumnPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, startingBlockPos.x + x, startingBlockPos.z + z);
long blockColumnPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, DhSectionPos.getX(startingBlockPos) + x, DhSectionPos.getZ(startingBlockPos) + z);
DhApiResult<DhApiTerrainDataPoint[]> result = getTerrainDataColumnArray(levelWrapper, blockColumnPos, null, dataCache);
if (result.success)
{
@@ -175,8 +180,8 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
*/
private static DhApiResult<DhApiTerrainDataPoint[]> getTerrainDataColumnArray(
IDhApiLevelWrapper levelWrapper,
DhLodPos requestedColumnPos, Integer nullableBlockYPos,
@Nullable IDhApiTerrainDataCache apiDataCache)
long requestedColumnPos, Integer nullableBlockYPos,
IDhApiTerrainDataCache apiDataCache)
{
//============//
// validation //
@@ -197,10 +202,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
// require a data cache to prevent horrible performance (especially on ray-casts)
if (apiDataCache == null)
{
return DhApiResult.createFail("Missing [" + IDhApiTerrainDataCache.class.getSimpleName() + "], if a cache isn't provided your repo operations will be significantly slower.");
}
// the data cache must be our own implementation
if (!(apiDataCache instanceof DhApiTerrainDataCache))
{
// custom level wrappers aren't supported,
// the API user must get a level wrapper from our code somewhere
return DhApiResult.createFail("Unsupported [" + IDhApiTerrainDataCache.class.getSimpleName() + "] implementation, only the core class [" + DhApiTerrainDataCache.class.getSimpleName() + "] is a valid parameter.");
}
DhApiTerrainDataCache dataCache = (DhApiTerrainDataCache) apiDataCache;
@@ -213,12 +223,12 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
}
// get the detail levels for this request
byte requestedDetailLevel = requestedColumnPos.detailLevel;
byte requestedDetailLevel = DhSectionPos.getDetailLevel(requestedColumnPos);
byte sectionDetailLevel = (byte) (requestedDetailLevel + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
// get the positions for this request
long sectionPos = requestedColumnPos.getSectionPosWithSectionDetailLevel(sectionDetailLevel);
DhLodPos relativePos = requestedColumnPos.getDhSectionRelativePositionForDetailLevel();
long sectionPos = DhSectionPos.convertToDetailLevel(requestedColumnPos, sectionDetailLevel);
long relativePos = DhSectionPos.getDhSectionRelativePositionForDetailLevel(requestedColumnPos, DhSectionPos.getDetailLevel(requestedColumnPos));
@@ -226,10 +236,9 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
// get the data source //
//=====================//
FullDataSourceV2 dataSource = null;
try
{
FullDataSourceV2 dataSource = null;
// try using the cached data if possible
if (dataCache != null)
{
@@ -244,7 +253,12 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
{
return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "].");
}
dataCache.add(sectionPos, dataSource);
// save to the cache if present
if (dataCache != null)
{
dataCache.add(sectionPos, dataSource);
}
}
@@ -254,7 +268,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
//===============================//
FullDataPointIdMap mapping = dataSource.mapping;
LongArrayList dataColumn = dataSource.get(relativePos.x, relativePos.z);
LongArrayList dataColumn = dataSource.getColumnAtRelPos(DhSectionPos.getX(relativePos), DhSectionPos.getZ(relativePos));
if (dataColumn != null)
{
int dataColumnIndexCount = dataColumn.size();
@@ -273,7 +287,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
if (!getSpecificYCoordinate)
{
// if we aren't look for a specific datapoint, add each datapoint to the return array
returnArray[i] = DhApiTerrainDataPointUtil.createApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint);
returnArray[i] = DhApiTerrainDataPointUtil.createApiDatapoint(levelWrapper.getMinHeight(), mapping, requestedDetailLevel, dataPoint);
}
else
{
@@ -290,7 +304,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
if (bottomY <= requestedY && requestedY < topY) // blockPositions start from the bottom of the block, thus "<=" for bottomY, just "<" for topY
{
// this datapoint contains the requested block position, return it
DhApiTerrainDataPoint apiTerrainData = DhApiTerrainDataPointUtil.createApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint);
DhApiTerrainDataPoint apiTerrainData = DhApiTerrainDataPointUtil.createApiDatapoint(levelWrapper.getMinHeight(), mapping, requestedDetailLevel, dataPoint);
return DhApiResult.createSuccess(new DhApiTerrainDataPoint[]{apiTerrainData});
}
}
@@ -316,6 +330,14 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
LOGGER.error("Unexpected exception in getTerrainDataColumnArray. Error: [" + e.getMessage() + "]", e);
return DhApiResult.createFail("Unexpected exception: [" + e.getMessage() + "].");
}
finally
{
if (dataCache == null
&& dataSource != null)
{
dataSource.close();
}
}
}
@@ -333,7 +355,10 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
@Nullable
IDhApiTerrainDataCache dataCache)
{
return this.raycastLodData(levelWrapper, new Vec3d(rayOriginX, rayOriginY, rayOriginZ), new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ), maxRayBlockLength, dataCache);
return this.raycastLodData(levelWrapper,
new Vec3d(rayOriginX, rayOriginY, rayOriginZ),
new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ),
maxRayBlockLength, dataCache);
}
/**
@@ -351,8 +376,8 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
{
rayDirection.normalize();
int minBlockHeight = levelWrapper.getMinHeight();
int maxBlockHeight = levelWrapper.getMaxHeight();
int minLevelBlockHeight = levelWrapper.getMinHeight();
int maxLevelBlockHeight = levelWrapper.getMaxHeight();
@@ -368,7 +393,8 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
DhApiRaycastResult closetFoundDataPoint = null;
while (blockPos.y >= minBlockHeight && blockPos.y < maxBlockHeight
while (blockPos.y >= minLevelBlockHeight
&& blockPos.y < maxLevelBlockHeight
&& currentLength <= maxRayBlockLength)
{
// get the LOD columns around this position
@@ -391,7 +417,8 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
{
// does this LOD contain the given Y position?
Vec3i dataPointPos = new Vec3i(columnPos.x, dataPoint.bottomYBlockPos, columnPos.z);
if (exactPos.y >= dataPoint.bottomYBlockPos && exactPos.y <= dataPoint.topYBlockPos)
if (exactPos.y >= dataPoint.bottomYBlockPos
&& exactPos.y <= dataPoint.topYBlockPos)
{
if (closetFoundDataPoint == null)
{
@@ -491,7 +518,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
// this will throw a cast exception if the chunk object array isn't correct
IChunkWrapper chunk = SingletonInjector.INSTANCE.get(IWrapperFactory.class).createChunkWrapper(chunkObjectArray);
SharedApi.INSTANCE.applyChunkUpdate(chunk, dhLevel.getLevelWrapper(), true);
SharedApi.INSTANCE.applyChunkUpdate(chunk, dhLevel.getLevelWrapper());
return DhApiResult.createSuccess();
@@ -504,7 +531,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
//=============//
@Override
public IDhApiTerrainDataCache getSoftCache() { return new DhApiTerrainDataCache(); }
public IDhApiTerrainDataCache createSoftCache() { return new DhApiTerrainDataCache(); }
@@ -524,10 +551,10 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
Thread thread = new Thread(() -> {
try
{
DhApiResult<DhApiTerrainDataPoint> single = getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, debugDataCache);
DhApiResult<DhApiTerrainDataPoint[]> column = getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, debugDataCache);
DhApiResult<DhApiTerrainDataPoint> single = getTerrainDataAtBlockYPos(levelWrapper, DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, debugDataCache);
DhApiResult<DhApiTerrainDataPoint[]> column = getTerrainDataColumnArray(levelWrapper, DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, debugDataCache);
DhLodPos chunkPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ).convertToDetailLevel(LodUtil.CHUNK_DETAIL_LEVEL);
long chunkPos = DhSectionPos.encodeContaining(LodUtil.CHUNK_DETAIL_LEVEL, new DhChunkPos(blockPosX, blockPosZ));
DhApiResult<DhApiTerrainDataPoint[][][]> area = getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, chunkPos, debugDataCache);
@@ -560,12 +587,16 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
}
// draw raycast position
if (rayCast.success && rayCast.payload != null)
if (rayCast.success
&& rayCast.payload != null)
{
DebugRenderer.makeParticle(
new DebugRenderer.BoxParticle(
new DebugRenderer.Box(
DhSectionPos.encode((byte) 0, rayCast.payload.pos.x, rayCast.payload.pos.z), rayCast.payload.dataPoint.bottomYBlockPos, rayCast.payload.dataPoint.topYBlockPos, -0.1f, Color.RED),
DEBUG_RENDERER.makeParticle(
new AbstractDebugWireframeRenderer.BoxParticle(
new AbstractDebugWireframeRenderer.Box(
DhSectionPos.encode((byte) 0, rayCast.payload.pos.x, rayCast.payload.pos.z),
rayCast.payload.dataPoint.bottomYBlockPos,
rayCast.payload.dataPoint.topYBlockPos,
-0.1f, Color.RED),
1.0, 0f
)
);
@@ -23,17 +23,27 @@ import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.render.renderer.FadeRenderer;
import com.seibel.distanthorizons.core.render.RenderParams;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.render.renderer.*;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTestTriangleRenderer;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.network.session.NetworkSession;
@@ -42,30 +52,22 @@ import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger;
import com.seibel.distanthorizons.core.logging.SpamReducedLogger;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.renderer.TestRenderer;
import com.seibel.distanthorizons.core.util.RenderUtil;
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
import com.seibel.distanthorizons.core.world.DhClientServerWorld;
import com.seibel.distanthorizons.core.world.DhClientWorld;
import com.seibel.distanthorizons.core.world.IDhClientWorld;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW;
import java.io.File;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
/**
* This holds the methods that should be called
@@ -74,28 +76,39 @@ import java.util.concurrent.TimeUnit;
*/
public class ClientApi
{
private static final Logger LOGGER = LogManager.getLogger();
public static boolean prefLoggerEnabled = false;
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder().maxCountPerSecond(1).build();
public static final ClientApi INSTANCE = new ClientApi();
public static final TestRenderer TEST_RENDERER = new TestRenderer();
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
public static final long SPAM_LOGGER_FLUSH_NS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
/** this includes the is dev build message and low allocated memory warning */
private static final int MS_BETWEEN_STATIC_STARTUP_MESSAGES = 4_000;
/**
* This isn't the cleanest way of storing variables before passing them to the LOD renderer,
* but due to how mixins work and the inconsistency between MC versions,
* having a static object that stores a single frame's data
* is often the easiest solution. <br><br>
*
* Only downside is making sure each variable is populated before rendering.
*/
public static final DhRenderState RENDER_STATE = new DhRenderState();
/**
* 50ms = 20 FPS
* @link https://fpstoms.com/
* @see ClientApi#cameraSpeedRollingAverage
*/
private static final long MIN_MS_BETWEEN_SPEED_CHECKS = 50;
private boolean isDevBuildMessagePrinted = false;
private boolean lowMemoryWarningPrinted = false;
private boolean highVanillaRenderDistanceWarningPrinted = false;
/** when the last static */
private long lastStaticWarningMessageSentMsTime = 0L;
private final Queue<String> chatMessageQueueForNextFrame = new LinkedBlockingQueue<>();
@@ -103,8 +116,6 @@ public class ClientApi
public boolean rendererDisabledBecauseOfExceptions = false;
private long lastFlushNanoTime = 0;
private final ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent);
/** Delay loading the first level to give the server some time to respond with level to actually load */
@@ -116,8 +127,24 @@ public class ClientApi
/** Holds any chunks that were loaded before the {@link ClientApi#clientLevelLoadEvent(IClientLevelWrapper)} was fired. */
public final HashMap<Pair<IClientLevelWrapper, DhChunkPos>, IChunkWrapper> waitingChunkByClientLevelAndPos = new HashMap<>();
/** re-set every frame during the opaque rendering stage */
private boolean renderingCancelledForThisFrame;
/** publicly available so {@link F3Screen} can display the error */
@Nullable
public String lastRenderParamValidationMessage = null;
/**
* measured in blocks/second <br>
*
* The number of points tracked here is related
* to the rate at which we check for speed.
* So if the ms_between is changed the number of points
* tracked should also be to keep the ratio roughly the same.
* @see ClientApi#MIN_MS_BETWEEN_SPEED_CHECKS
*/
public RollingAverage cameraSpeedRollingAverage = new RollingAverage(40);
private Vec3d lastCameraPosForSpeedCheck = new Vec3d();
private long msSinceLastSpeedCheck = 0L;
@@ -132,6 +159,7 @@ public class ClientApi
//==============//
// world events //
//==============//
///region
/**
* May be fired slightly before or after the associated
@@ -157,17 +185,16 @@ public class ClientApi
if (Config.Common.Logging.Warning.showReplayWarningOnStartup.get())
{
MC_CLIENT.sendChatMessage("\u00A76" + "Distant Horizons: Replay detected." + "\u00A7r"); // gold color
MC_CLIENT.sendChatMessage(MinecraftTextFormat.ORANGE + "Distant Horizons: Replay detected." + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage("DH may behave strangely or have missing functionality.");
MC_CLIENT.sendChatMessage("In order to use pre-generated LODs, put your DH database(s) in:");
MC_CLIENT.sendChatMessage("\u00A77"+".Minecraft" + File.separator + ClientOnlySaveStructure.SERVER_DATA_FOLDER_NAME + File.separator + ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME + File.separator + "DIMENSION_NAME"+"\u00A7r"); // light gray color
MC_CLIENT.sendChatMessage("This can be disabled in DH's config under Advanced -> Logging.");
MC_CLIENT.sendChatMessage(MinecraftTextFormat.GRAY +".Minecraft" + File.separator + ClientOnlySaveStructure.SERVER_DATA_FOLDER_NAME + File.separator + ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME + File.separator + "DIMENSION_NAME"+ MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage("This message can be disabled in DH's config under Advanced -> Logging.");
MC_CLIENT.sendChatMessage("");
}
}
// firing after clientLevelLoadEvent
// TODO if level has prepped to load it should fire level load event
DhClientWorld world = new DhClientWorld();
SharedApi.setDhWorld(world);
@@ -210,11 +237,14 @@ public class ClientApi
this.waitingClientLevels.clear();
}
///endregion
//==============//
// level events //
//==============//
///region
public void clientLevelUnloadEvent(IClientLevelWrapper level)
{
@@ -231,7 +261,6 @@ public class ClientApi
if (world != null)
{
world.unloadLevel(level);
SharedApi.INSTANCE.clearQueuedChunkUpdates();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(level));
}
else
@@ -246,8 +275,15 @@ public class ClientApi
}
}
public void clientLevelLoadEvent(IClientLevelWrapper levelWrapper)
public void clientLevelLoadEvent(@Nullable IClientLevelWrapper levelWrapper)
{
// can happen if there was an issue during level load
if (levelWrapper == null)
{
return;
}
// wait a moment before loading the level to give the server a chance to handle the client's login request
if (MC_CLIENT.clientConnectedToDedicatedServer())
{
@@ -308,7 +344,7 @@ public class ClientApi
if (levelWrapper.equals(level))
{
IChunkWrapper chunkWrapper = this.waitingChunkByClientLevelAndPos.get(levelChunkPair);
SharedApi.INSTANCE.chunkLoadEvent(chunkWrapper, levelWrapper);
SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, levelWrapper);
keysToRemove.add(levelChunkPair);
}
}
@@ -320,54 +356,14 @@ public class ClientApi
}
}
//===============//
// render events //
//===============//
public void clientTickEvent()
{
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
profiler.push("DH-ClientTick");
try
{
boolean doFlush = System.nanoTime() - this.lastFlushNanoTime >= SPAM_LOGGER_FLUSH_NS;
if (doFlush)
{
this.lastFlushNanoTime = System.nanoTime();
SpamReducedLogger.flushAll();
}
ConfigBasedLogger.updateAll();
ConfigBasedSpamLogger.updateAll(doFlush);
IDhClientWorld clientWorld = SharedApi.getIDhClientWorld();
if (clientWorld != null)
{
clientWorld.clientTick();
// Ignore local world gen, as it's managed by server ticking
if (!(clientWorld instanceof DhClientServerWorld))
{
SharedApi.worldGenTick(clientWorld::worldGenTick);
}
}
}
catch (Exception e)
{
// handle errors here to prevent blowing up a mixin or API up stream
LOGGER.error("Unexpected error in ClientApi.clientTickEvent(), error: "+e.getMessage(), e);
}
profiler.pop();
}
///endregion
//============//
// networking //
//============//
///region
/**
* Forwards a decoded message into the registered handlers.
@@ -376,45 +372,143 @@ public class ClientApi
*/
public void pluginMessageReceived(@NotNull AbstractNetworkMessage message)
{
NetworkSession networkSession = this.pluginChannelApi.networkSession;
if (networkSession != null)
@Nullable ThreadPoolExecutor executor = ThreadPoolUtil.networkClientHandlerExecutor();
if (executor == null)
{
networkSession.tryHandleMessage(message);
LOGGER.warn("warn");
return;
}
try
{
executor.execute(() ->
{
NetworkSession networkSession = this.pluginChannelApi.networkSession;
if (networkSession != null)
{
networkSession.tryHandleMessage(message);
}
});
}
catch (RejectedExecutionException e)
{
LOGGER.warn("Plugin message executor rejected");
}
}
///endregion
//===========//
// rendering //
//===========//
/** Should be called before {@link ClientApi#renderDeferredLods} */
public void renderLods(IClientLevelWrapper levelWrapper, Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks)
{ this.renderLodLayer(levelWrapper, mcModelViewMatrix, mcProjectionMatrix, partialTicks, false); }
//===============//
// LOD rendering //
//===============//
///region
/** Should be called before {@link ClientApi#renderDeferredLodsForShaders} */
public void renderLods() { this.renderLodLayer(false); }
/**
* Only necessary when Shaders are in use.
* Should be called after {@link ClientApi#renderLods}
*/
public void renderDeferredLods(IClientLevelWrapper levelWrapper, Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks)
{ this.renderLodLayer(levelWrapper, mcModelViewMatrix, mcProjectionMatrix, partialTicks, true); }
public void renderDeferredLodsForShaders() { this.renderLodLayer(true); }
private void renderLodLayer(
IClientLevelWrapper levelWrapper, Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks,
boolean renderingDeferredLayer)
public static long firstRenderTimeMs = 0;
private void renderLodLayer(boolean renderingDeferredLayer)
{
// logging //
this.sendQueuedChatMessages();
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
profiler.pop(); // get out of "terrain"
profiler.push("DH-RenderLevel");
// render parameter setup //
//===========//
// debugging //
//===========//
//region
//DhApiTerrainDataRepo.asyncDebugMethod(
// RENDER_STATE.clientLevelWrapper,
// MC_CLIENT.getPlayerBlockPos().getX(),
// MC_CLIENT.getPlayerBlockPos().getY(),
// MC_CLIENT.getPlayerBlockPos().getZ()
//);
//endregion
//=====================//
// render thread tasks //
//=====================//
///region
// only run these tasks once per frame
if (!renderingDeferredLayer)
{
profiler.push("DH render thread tasks");
//===============//
// chat messages //
//===============//
this.sendQueuedChatMessages();
//======================//
// GL Proxy queued jobs //
//======================//
try
{
// these tasks always need to be called, regardless of whether the renderer is enabled or not to prevent memory leaks
RenderThreadTaskHandler.INSTANCE.runRenderThreadTasks();
}
catch (Exception e)
{
LOGGER.error("Unexpected issue running render thread tasks, error: [" + e.getMessage() + "].", e);
}
//==============//
// camera speed //
//==============//
long nowMs = System.currentTimeMillis();
if (this.msSinceLastSpeedCheck + MIN_MS_BETWEEN_SPEED_CHECKS < nowMs)
{
// calc time since last check
double secSinceLastCheck = (nowMs - this.msSinceLastSpeedCheck) / 1_000.0;
this.msSinceLastSpeedCheck = nowMs;
// get the distance traveled since last frame
Vec3d camPos = MC_RENDER.getCameraExactPosition();
double distanceInBlocks = camPos.getDistance(this.lastCameraPosForSpeedCheck);
double speed = distanceInBlocks / secSinceLastCheck;
// record new values for next check
this.cameraSpeedRollingAverage.add(speed);
this.lastCameraPosForSpeedCheck = camPos;
}
profiler.pop();
}
///endregion
//=================//
// parameter setup //
//=================//
///region
EDhApiRenderPass renderPass;
if (DhApiRenderProxy.INSTANCE.getDeferTransparentRendering())
@@ -433,67 +527,76 @@ public class ClientApi
renderPass = EDhApiRenderPass.OPAQUE_AND_TRANSPARENT;
}
DhApiRenderParam renderEventParam =
new DhApiRenderParam(
renderPass,
partialTicks,
RenderUtil.getNearClipPlaneDistanceInBlocks(partialTicks), RenderUtil.getFarClipPlaneDistanceInBlocks(),
mcProjectionMatrix, mcModelViewMatrix,
RenderUtil.createLodProjectionMatrix(mcProjectionMatrix, partialTicks), RenderUtil.createLodModelViewMatrix(mcModelViewMatrix),
levelWrapper.getMinHeight()
);
// A global render state variable is used since MC has split up their
// render prep and actual rendering into different threads/methods
// this is annoying since it's possible to start a render with only
// partially complete info, but there isn't a better option at the moment
RenderParams renderParams = new RenderParams(renderPass, RENDER_STATE);
///endregion
// render validation //
//============//
// validation //
//============//
///region
if (firstRenderTimeMs == 0)
{
firstRenderTimeMs = System.currentTimeMillis();
}
String validationMessage = renderParams.getValidationErrorMessage(firstRenderTimeMs);
if (validationMessage != null)
{
// store the error message so it can be seen on the F3 screen
this.lastRenderParamValidationMessage = validationMessage;
return;
}
else
{
this.lastRenderParamValidationMessage = null;
}
if (this.rendererDisabledBecauseOfExceptions)
{
// re-enable rendering if the user toggles DH rendering
if (!Config.Client.quickEnableRendering.get())
{
LOGGER.info("DH Renderer re-enabled after exception. Some rendering issues may occur. Please reboot Minecraft if you see any rendering issues.");
this.rendererDisabledBecauseOfExceptions = false;
Config.Client.quickEnableRendering.set(true);
}
return;
}
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DISABLED)
{
return;
}
///endregion
//===========//
// rendering //
//===========//
///region
try
{
// TODO write this message to the F3 menu so people can see when a different mod screws with the lightmap
String reasonLodsCannotRender = RenderUtil.shouldLodsRender(levelWrapper, renderEventParam);
if (reasonLodsCannotRender != null)
{
return;
}
IDhClientWorld dhClientWorld = SharedApi.getIDhClientWorld();
if (dhClientWorld == null)
{
return;
}
IDhClientLevel level = (IDhClientLevel) dhClientWorld.getLevel(levelWrapper);
if (level == null)
{
return;
}
if (this.rendererDisabledBecauseOfExceptions)
{
// re-enable rendering if the user toggles DH rendering
if (!Config.Client.quickEnableRendering.get())
{
LOGGER.info("DH Renderer re-enabled after exception. Some rendering issues may occur. Please reboot Minecraft if you see any rendering issues.");
this.rendererDisabledBecauseOfExceptions = false;
Config.Client.quickEnableRendering.set(true);
}
return;
}
// render pass //
if (!renderingDeferredLayer)
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
{
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
if (!renderingDeferredLayer)
{
this.renderingCancelledForThisFrame = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderEvent.class, renderEventParam);
if (!this.renderingCancelledForThisFrame)
boolean renderingCancelled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderEvent.class, renderParams);
if (!renderingCancelled)
{
level.render(renderEventParam, profiler);
LodRenderer.INSTANCE.render(renderParams, profiler);
}
if (!DhApi.Delayed.renderProxy.getDeferTransparentRendering())
@@ -501,25 +604,42 @@ public class ClientApi
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null);
}
}
else if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEBUG)
else
{
profiler.push("Render Debug");
ClientApi.TEST_RENDERER.render();
profiler.pop();
boolean renderingCancelled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDeferredRenderEvent.class, renderParams);
if (!renderingCancelled)
{
LodRenderer.INSTANCE.renderDeferred(renderParams, profiler);
}
if (DhApi.Delayed.renderProxy.getDeferTransparentRendering())
{
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null);
}
}
}
else
{
boolean renderingCancelled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDeferredRenderEvent.class, renderEventParam);
if (!renderingCancelled)
if (!renderingDeferredLayer)
{
level.renderDeferred(renderEventParam, profiler);
}
if (DhApi.Delayed.renderProxy.getDeferTransparentRendering())
{
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null);
IDhMetaRenderer metaRenderer = SingletonInjector.INSTANCE.get(IDhMetaRenderer.class);
IDhTestTriangleRenderer testRenderer = SingletonInjector.INSTANCE.get(IDhTestTriangleRenderer.class);
if (testRenderer != null
&& metaRenderer != null)
{
// meta renderer needed for render state/texture
// for setup on some APIs (IE openGL)
metaRenderer.runRenderPassSetup(renderParams);
testRenderer.render(renderParams);
metaRenderer.runRenderPassCleanup(renderParams);
}
else
{
RATE_LIMITED_LOGGER.warn("Unable to find singleton [" + IDhTestTriangleRenderer.class.getSimpleName() + "]");
}
}
}
}
@@ -528,61 +648,97 @@ public class ClientApi
this.rendererDisabledBecauseOfExceptions = true;
LOGGER.error("Unexpected Renderer error in render pass [" + renderPass + "]. Error: " + e.getMessage(), e);
MC_CLIENT.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons renderer has encountered an exception!");
MC_CLIENT.sendChatMessage("\u00A74Renderer disabled to try preventing GL state corruption.");
MC_CLIENT.sendChatMessage("\u00A74Toggle DH rendering via the config UI to re-activate DH rendering.");
MC_CLIENT.sendChatMessage("\u00A74Error: " + e);
}
finally
{
try
{
// these tasks always need to be called, regardless of whether the renderer is enabled or not to prevent memory leaks
GLProxy.getInstance().runRenderThreadTasks();
}
catch (Exception e)
{
LOGGER.error("Unexpected issue running render thread tasks.", e);
}
profiler.pop(); // end LOD
profiler.push("terrain"); // go back into "terrain"
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "" + MinecraftTextFormat.BOLD + "ERROR: Distant Horizons renderer has encountered an exception!" + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Renderer disabled to try preventing GL state corruption." + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Toggle DH rendering via the config UI to re-activate DH rendering." + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Error: " + MinecraftTextFormat.CLEAR_FORMATTING + e);
}
///endregion
profiler.pop(); // end LOD
}
/** should be called after DH and MC finish rendering so we can smooth the transition between the two */
public void renderFadeOpaque(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IClientLevelWrapper level)
///endregion
//================//
// fade rendering //
//================//
///region
/**
* The first fade pass.
* Called after MC finishes rendering the opaque passes.
*/
public void renderFadeOpaque()
{
// only fade when DH is rendering
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
IDhVanillaFadeRenderer fadeRenderer = SingletonInjector.INSTANCE.get(IDhVanillaFadeRenderer.class);
if (fadeRenderer == null)
{
if (Config.Client.Advanced.Graphics.Quality.vanillaFadeMode.get() == EDhApiMcRenderingFadeMode.DOUBLE_PASS)
{
FadeRenderer.INSTANCE.render(mcModelViewMatrix, mcProjectionMatrix, partialTicks, level);
}
return;
}
// only fade when DH is rendering
if (Config.Client.Advanced.Debugging.rendererMode.get() != EDhApiRendererMode.DISABLED
&&
(
// only fade when requested
Config.Client.Advanced.Graphics.Quality.vanillaFadeMode.get() == EDhApiMcRenderingFadeMode.DOUBLE_PASS
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
)
// don't fade when Iris shaders are active, otherwise the rendering can get weird
&& !DhApiRenderProxy.INSTANCE.getDeferTransparentRendering())
{
RenderParams renderParams = new RenderParams(EDhApiRenderPass.OPAQUE, RENDER_STATE);
fadeRenderer.render(renderParams);
}
}
/** should be called after DH and MC finish rendering so we can smooth the transition between the two */
public void renderFade(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IClientLevelWrapper level)
/**
* The second fade pass.
* Called after MC finishes rendering both opaque
* and transparent passes.
*/
public void renderFadeTransparent()
{
// only fade when DH is rendering
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
IDhVanillaFadeRenderer fadeRenderer = SingletonInjector.INSTANCE.get(IDhVanillaFadeRenderer.class);
if (fadeRenderer == null)
{
// fade if any level fading is active
if (Config.Client.Advanced.Graphics.Quality.vanillaFadeMode.get() != EDhApiMcRenderingFadeMode.NONE)
return;
}
// only fade when DH is rendering
if (Config.Client.Advanced.Debugging.rendererMode.get() != EDhApiRendererMode.DISABLED)
{
boolean renderFade =
(
// only fade when requested
Config.Client.Advanced.Graphics.Quality.vanillaFadeMode.get() != EDhApiMcRenderingFadeMode.NONE
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
)
// don't fade when Iris shaders are active, otherwise the rendering can get weird
&& !DhApiRenderProxy.INSTANCE.getDeferTransparentRendering();
if (renderFade)
{
FadeRenderer.INSTANCE.render(mcModelViewMatrix, mcProjectionMatrix, partialTicks, level);
RenderParams renderParams = new RenderParams(EDhApiRenderPass.TRANSPARENT, RENDER_STATE);
fadeRenderer.render(renderParams);
}
}
}
///endregion
//=================//
// DEBUG USE //
//=================//
//==========//
// keyboard //
//==========//
///region
/** Trigger once on key press, with CLIENT PLAYER. */
public void keyPressedEvent(int glfwKey)
@@ -594,23 +750,32 @@ public class ClientApi
}
if (glfwKey == GLFW.GLFW_KEY_F8)
{
Config.Client.Advanced.Debugging.debugRendering.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get()));
MC_CLIENT.sendChatMessage("F8: Set debug mode to " + Config.Client.Advanced.Debugging.debugRendering.get());
}
else if (glfwKey == GLFW.GLFW_KEY_F6)
if (glfwKey == GLFW.GLFW_KEY_F6)
{
Config.Client.Advanced.Debugging.rendererMode.set(EDhApiRendererMode.next(Config.Client.Advanced.Debugging.rendererMode.get()));
MC_CLIENT.sendChatMessage("F6: Set rendering to " + Config.Client.Advanced.Debugging.rendererMode.get());
}
else if (glfwKey == GLFW.GLFW_KEY_P)
else if (glfwKey == GLFW.GLFW_KEY_F7)
{
prefLoggerEnabled = !prefLoggerEnabled;
MC_CLIENT.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
Config.Client.Advanced.Debugging.lodOnlyMode.set(!Config.Client.Advanced.Debugging.lodOnlyMode.get());
MC_CLIENT.sendChatMessage("F7: Set LOD only mode to " + Config.Client.Advanced.Debugging.lodOnlyMode.get());
}
else if (glfwKey == GLFW.GLFW_KEY_F8)
{
Config.Client.Advanced.Debugging.debugRenderingColors.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRenderingColors.get()));
MC_CLIENT.sendChatMessage("F8: Set debug mode to " + Config.Client.Advanced.Debugging.debugRenderingColors.get());
}
}
///endregion
//======//
// chat //
//======//
///region
private void sendQueuedChatMessages()
{
// this includes if the current build is a dev build
@@ -652,15 +817,16 @@ public class ClientApi
private void detectAndSendBootTimeWarnings()
{
// dev build
if (ModInfo.IS_DEV_BUILD && !this.isDevBuildMessagePrinted && MC_CLIENT.playerExists())
if (ModInfo.IS_DEV_BUILD
&& !this.isDevBuildMessagePrinted
&& MC_CLIENT.playerExists())
{
this.isDevBuildMessagePrinted = true;
this.lastStaticWarningMessageSentMsTime = System.currentTimeMillis();
// remind the user that this is a development build
String message =
// green text
"\u00A72" + "Distant Horizons: nightly/unstable build, version: [" + ModInfo.VERSION+"]." + "\u00A7r\n" +
MinecraftTextFormat.DARK_GREEN + "Distant Horizons: nightly/unstable build, version: [" + ModInfo.VERSION+"]." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"Issues may occur with this version.\n" +
"Here be dragons!\n";
MC_CLIENT.sendChatMessage(message);
@@ -669,7 +835,8 @@ public class ClientApi
// memory
if (this.staticStartupMessageSentRecently()) return;
if (!this.lowMemoryWarningPrinted && Config.Common.Logging.Warning.showLowMemoryWarningOnStartup.get())
if (!this.lowMemoryWarningPrinted
&& Config.Common.Logging.Warning.showLowMemoryWarningOnStartup.get())
{
this.lowMemoryWarningPrinted = true;
this.lastStaticWarningMessageSentMsTime = System.currentTimeMillis();
@@ -683,9 +850,9 @@ public class ClientApi
{
String message =
// orange text
"\u00A76" + "Distant Horizons: Low memory detected." + "\u00A7r \n" +
MinecraftTextFormat.ORANGE + "Distant Horizons: Low memory detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"Stuttering or low FPS may occur. \n" +
"Please increase Minecraft's available memory to 4 gigabytes. \n" +
"Please increase Minecraft's available memory to 4 GB or more. \n" +
"This warning can be disabled in DH's config under Advanced -> Logging. \n";
MC_CLIENT.sendChatMessage(message);
}
@@ -694,24 +861,24 @@ public class ClientApi
// high vanilla render distance
if (this.staticStartupMessageSentRecently()) return;
if (!this.highVanillaRenderDistanceWarningPrinted && Config.Common.Logging.Warning.showHighVanillaRenderDistanceWarning.get())
if (!this.highVanillaRenderDistanceWarningPrinted
&& Config.Common.Logging.Warning.showHighVanillaRenderDistanceWarning.get())
{
this.highVanillaRenderDistanceWarningPrinted = true;
// DH generally doesn't need a vanilla render distance above 12
if (MC_RENDER.getRenderDistance() > 12)
{
this.highVanillaRenderDistanceWarningPrinted = true;
this.lastStaticWarningMessageSentMsTime = System.currentTimeMillis();
String message =
// yellow text
"\u00A7e" + "Distant Horizons: High vanilla render distance detected." + "\u00A7r \n" +
"Using a high vanilla render distance uses a lot of CPU power \n" +
"and doesn't improve graphics much after about 12.\n" +
"Lowing your vanilla render distance will give you better FPS\n" +
"and reduce stuttering at a similar visual quality.\n" +
// gray text
"\u00A77" + "A vanilla render distance of 8 is recommended." + "\u00A7r \n" +
"This message can be disabled in DH's config under Advanced -> Logging.\n";
MinecraftTextFormat.YELLOW + "Distant Horizons: High vanilla render distance detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"Using a high vanilla render distance uses a lot of CPU power \n" +
"and doesn't improve graphics much after about 12.\n" +
"Lowering your vanilla render distance will give you better FPS\n" +
"and reduce stuttering at a similar visual quality.\n" +
MinecraftTextFormat.GRAY + "A vanilla render distance of 8 is recommended." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
"This message can be disabled in DH's config under Advanced -> Logging.\n";
MC_CLIENT.sendChatMessage(message);
}
}
@@ -721,7 +888,8 @@ public class ClientApi
{
if (this.lastStaticWarningMessageSentMsTime == 0)
{
return true;
// no static message has ever been sent
return false;
}
long timeSinceLastMessage = System.currentTimeMillis() - this.lastStaticWarningMessageSentMsTime;
@@ -740,4 +908,8 @@ public class ClientApi
*/
public void showOverlayMessageNextFrame(String message) { this.overlayMessageQueueForNextFrame.add(message); }
///endregion
}
@@ -4,14 +4,15 @@ import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent;
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
import com.seibel.distanthorizons.core.network.session.NetworkSession;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import org.apache.logging.log4j.LogManager;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@@ -22,8 +23,10 @@ import java.util.function.Consumer;
*/
public class ClientPluginChannelApi
{
private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
() -> Config.Common.Logging.logNetworkEvent.get());
private static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logNetworkEventToFile)
.build();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
@@ -65,7 +68,7 @@ public class ClientPluginChannelApi
//================//
/** fired when this client connects to a server with DH support */
public void onJoinServer(@NonNull NetworkSession networkSession)
public void onJoinServer(@NotNull NetworkSession networkSession)
{
Objects.requireNonNull(networkSession);
this.networkSession = networkSession;
@@ -75,14 +78,19 @@ public class ClientPluginChannelApi
private void onLevelInitMessage(LevelInitMessage msg)
{
if (!msg.levelKey.matches(LevelInitMessage.VALIDATION_REGEX))
if (!msg.serverKey.isEmpty() && !msg.serverKey.matches(LevelInitMessage.SERVER_KEY_REGEX))
{
throw new IllegalArgumentException("Server sent invalid server key.");
}
if (!msg.levelKey.matches(LevelInitMessage.LEVEL_KEY_REGEX))
{
throw new IllegalArgumentException("Server sent invalid level key.");
}
LOGGER.info("Server level key received: [" + msg.levelKey + "].");
MC.executeOnRenderThread(() ->
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("ClientPluginChannelApi onLevelInitMessage", () ->
{
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
@@ -105,10 +113,12 @@ public class ClientPluginChannelApi
this.levelUnloadHandler.accept(clientLevel);
}
if (existingKeyedClientLevel == null || !existingKeyedClientLevel.getServerLevelKey().equals(msg.levelKey))
if (existingKeyedClientLevel == null
|| !existingKeyedClientLevel.getServerKey().equals(msg.serverKey)
|| !existingKeyedClientLevel.getServerLevelKey().equals(msg.levelKey))
{
LOGGER.info("Loading level with key: [" + msg.levelKey + "].");
IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.levelKey);
IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.serverKey, msg.levelKey);
this.levelLoadHandler.accept(keyedLevel);
}
});
@@ -30,7 +30,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
/**
@@ -41,7 +41,7 @@ public class ServerApi
{
public static final ServerApi INSTANCE = new ServerApi();
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@@ -53,30 +53,6 @@ public class ServerApi
//=============//
// tick events //
//=============//
public void serverTickEvent()
{
try
{
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
if (serverWorld != null)
{
serverWorld.serverTick();
SharedApi.worldGenTick(serverWorld::worldGenTick);
}
}
catch (Exception e)
{
// try catch is necessary to prevent crashing the internal server when an exception is thrown
LOGGER.error("ServerTickEvent error: " + e.getMessage(), e);
}
}
//===============//
// server events //
//===============//
@@ -106,15 +82,15 @@ public class ServerApi
// level events //
//==============//
public void serverLevelLoadEvent(IServerLevelWrapper level)
public void serverLevelLoadEvent(IServerLevelWrapper levelWrapper)
{
LOGGER.debug("Server Level " + level + " loading");
LOGGER.debug("Server Level " + levelWrapper + " loading");
AbstractDhWorld serverWorld = SharedApi.getAbstractDhWorld();
if (serverWorld != null)
{
serverWorld.getOrLoadLevel(level);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(level));
serverWorld.getOrLoadLevel(levelWrapper);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(levelWrapper));
}
}
public void serverLevelUnloadEvent(IServerLevelWrapper level)
@@ -125,7 +101,6 @@ public class ServerApi
if (serverWorld != null)
{
serverWorld.unloadLevel(level);
SharedApi.INSTANCE.clearQueuedChunkUpdates();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(level));
}
}
@@ -136,8 +111,8 @@ public class ServerApi
// chunk modified events //
//=======================//
public void serverChunkLoadEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, false); }
public void serverChunkSaveEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, true); }
public void serverChunkLoadEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level); }
public void serverChunkSaveEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level); }
@@ -152,7 +127,7 @@ public class ServerApi
return;
}
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
IDhServerWorld serverWorld = SharedApi.tryGetDhServerWorld();
LOGGER.info("Player ["+player.getName()+"] joined.");
if (serverWorld != null)
{
@@ -166,7 +141,7 @@ public class ServerApi
return;
}
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
IDhServerWorld serverWorld = SharedApi.tryGetDhServerWorld();
LOGGER.info("Player ["+player.getName()+"] disconnected.");
if (serverWorld != null)
{
@@ -180,7 +155,7 @@ public class ServerApi
return;
}
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
IDhServerWorld serverWorld = SharedApi.tryGetDhServerWorld();
LOGGER.info("Player ["+player.getName()+"] changed level: ["+originLevel.getKeyedLevelDimensionName()+"] -> ["+destinationLevel.getKeyedLevelDimensionName()+"].");
if (serverWorld != null)
{
@@ -200,7 +175,7 @@ public class ServerApi
return;
}
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
IDhServerWorld serverWorld = SharedApi.tryGetDhServerWorld();
if (serverWorld != null)
{
serverWorld.getServerPlayerStateManager().handlePluginMessage(player, message);
@@ -22,173 +22,174 @@ package com.seibel.distanthorizons.core.api.internal;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiWorldLoadEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiWorldUnloadEvent;
import com.seibel.distanthorizons.core.Initializer;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.api.internal.chunkUpdating.ChunkUpdateData;
import com.seibel.distanthorizons.core.api.internal.chunkUpdating.ChunkUpdateQueueManager;
import com.seibel.distanthorizons.core.api.internal.chunkUpdating.WorldChunkUpdateManager;
import com.seibel.distanthorizons.core.config.eventHandlers.IgnoredDimensionCsvHandler;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.level.DhClientLevel;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.world.*;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
/** Contains code and variables used by both {@link ClientApi} and {@link ServerApi} */
public class SharedApi
{
public static final SharedApi INSTANCE = new SharedApi();
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** will be null on the server-side */
@Nullable
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
/** will be null on the server-side */
public static final WorldChunkUpdateManager WORLD_CHUNK_UPDATE_MANAGER = WorldChunkUpdateManager.INSTANCE; // local fariable for quick access
@Nullable
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
private static final UpdateChunkPosManager UPDATE_POS_MANAGER = new UpdateChunkPosManager();
/**
* how many chunks can be queued for updating per thread + player (in multiplayer),
* used to prevent updates from infinitely pilling up if the user flies around extremely fast
*/
private static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD_AND_PLAYER = 1_000;
/** how many milliseconds must pass before an overloaded message can be sent in chat or the log */
private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 30_000;
private static AbstractDhWorld currentWorld;
private static int lastWorldGenTickDelta = 0;
private static long lastOverloadedLogMessageMsTime = 0;
private static final Object worldLockObject = new Object();
//=============//
// constructor //
//=============//
//region
private SharedApi() { }
public static void init() { Initializer.init(); }
//endregion
//===============//
// world methods //
//===============//
//region
public static EWorldEnvironment getEnvironment() { return (currentWorld == null) ? null : currentWorld.environment; }
public static void setDhWorld(AbstractDhWorld newWorld)
{
currentWorld = newWorld;
// starting and stopping the DataRenderTransformer is necessary to prevent attempting to
// access the MC level at inappropriate times, which can cause exceptions
if (currentWorld != null)
synchronized (worldLockObject)
{
ThreadPoolUtil.setupThreadPools();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldLoadEvent.class, new DhApiWorldLoadEvent.EventParam());
}
else
{
ThreadPoolUtil.shutdownThreadPools();
DebugRenderer.clearRenderables();
if (MC_RENDER != null)
AbstractDhWorld oldWorld = currentWorld;
if (oldWorld != null)
{
MC_RENDER.clearTargetFrameBuffer();
oldWorld.close();
}
currentWorld = newWorld;
// shouldn't be necessary, but if we missed closing one of the connections this should make sure they're all closed
AbstractDhRepo.closeAllConnections();
// needs to be closed on world shutdown to clear out un-processed chunks
UPDATE_POS_MANAGER.clear();
// recommend that the garbage collector cleans up any objects from the old world and thread pools
System.gc();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldUnloadEvent.class, new DhApiWorldUnloadEvent.EventParam());
// fired after the unload event so API users can't change the read-only for any new worlds
DhApiWorldProxy.INSTANCE.setReadOnly(false, false);
}
}
public static void worldGenTick(Runnable worldGenRunnable)
{
lastWorldGenTickDelta--;
if (lastWorldGenTickDelta <= 0)
{
worldGenRunnable.run();
lastWorldGenTickDelta = 20;
// starting and stopping the DataRenderTransformer is necessary to prevent attempting to
// access the MC level at inappropriate times, which can cause exceptions
if (currentWorld != null)
{
ThreadPoolUtil.setupThreadPools();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldLoadEvent.class, new DhApiWorldLoadEvent.EventParam());
}
else
{
ThreadPoolUtil.shutdownThreadPools();
// delayed get because SharedApi will be created before the singleton has been bound
AbstractDebugWireframeRenderer debugWireframeRenderer = SingletonInjector.INSTANCE.get(AbstractDebugWireframeRenderer.class);
debugWireframeRenderer.clearRenderables();
if (MC_RENDER != null)
{
MC_RENDER.clearTargetFrameBuffer();
}
// shouldn't be necessary, but if we missed closing one of the connections this should make sure they're all closed
AbstractDhRepo.closeAllConnections();
// needs to be closed on world shutdown to clear out un-processed chunks
WORLD_CHUNK_UPDATE_MANAGER.clear();
RenderThreadTaskHandler.INSTANCE.clearDebugStats();
// recommend that the garbage collector cleans up any objects from the old world and thread pools
System.gc();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldUnloadEvent.class, new DhApiWorldUnloadEvent.EventParam());
// fired after the unload event so API users can't change the read-only for any new worlds
DhApiWorldProxy.INSTANCE.setReadOnly(false, false);
}
}
}
@Nullable
public static AbstractDhWorld getAbstractDhWorld() { return currentWorld; }
/** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientServerWorld} */
public static DhClientServerWorld getDhClientServerWorld() { return (currentWorld instanceof DhClientServerWorld) ? (DhClientServerWorld) currentWorld : null; }
/** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientWorld} or {@link DhClientServerWorld} */
public static IDhClientWorld getIDhClientWorld() { return (currentWorld instanceof IDhClientWorld) ? (IDhClientWorld) currentWorld : null; }
@Nullable
public static IDhClientWorld tryGetDhClientWorld() { return (currentWorld instanceof IDhClientWorld) ? (IDhClientWorld) currentWorld : null; }
/** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhServerWorld} or {@link DhClientServerWorld} */
public static IDhServerWorld getIDhServerWorld() { return (currentWorld instanceof IDhServerWorld) ? (IDhServerWorld) currentWorld : null; }
@Nullable
public static IDhServerWorld tryGetDhServerWorld() { return (currentWorld instanceof IDhServerWorld) ? (IDhServerWorld) currentWorld : null; }
//endregion
//==============//
// chunk update //
//==============//
//region
/**
* Used to prevent getting a full chunk from MC if it isn't necessary. <br>
* This is important since asking MC for a chunk is slow and may block the render thread.
*/
public static boolean isChunkAtBlockPosAlreadyUpdating(int blockPosX, int blockPosZ)
{ return UPDATE_POS_MANAGER.contains(new DhChunkPos(new DhBlockPos2D(blockPosX, blockPosZ))); }
public static boolean isChunkAtChunkPosAlreadyUpdating(int chunkPosX, int chunkPosZ)
{ return UPDATE_POS_MANAGER.contains(new DhChunkPos(chunkPosX, chunkPosZ)); }
/**
* This is often fired when unloading a level.
* This is done to prevent overloading the system when
* rapidly changing dimensions.
* (IE prevent DH from infinitely allocating memory
*/
public void clearQueuedChunkUpdates() { UPDATE_POS_MANAGER.clear(); }
public int getQueuedChunkUpdateCount() { return UPDATE_POS_MANAGER.closestQueue.size(); }
/** handles both block place and break events */
public void chunkBlockChangedEvent(IChunkWrapper chunk, ILevelWrapper level) { this.applyChunkUpdate(chunk, level, true); }
public void chunkLoadEvent(IChunkWrapper chunk, ILevelWrapper level) { this.applyChunkUpdate(chunk, level, false); }
public void applyChunkUpdate(IChunkWrapper chunkWrapper, ILevelWrapper level, boolean updateNeighborChunks)
public static boolean isChunkAtBlockPosAlreadyUpdating(ILevelWrapper levelWrapper, int blockPosX, int blockPosZ)
{
//========================//
// world and level checks //
//========================//
ChunkUpdateQueueManager manager = WORLD_CHUNK_UPDATE_MANAGER.getByLevelWrapper(levelWrapper);
if (manager == null)
{
return true;
}
return manager.contains(new DhChunkPos(new DhBlockPos2D(blockPosX, blockPosZ)));
}
public static boolean isChunkAtChunkPosAlreadyUpdating(ILevelWrapper levelWrapper, int chunkPosX, int chunkPosZ)
{
ChunkUpdateQueueManager manager = WORLD_CHUNK_UPDATE_MANAGER.getByLevelWrapper(levelWrapper);
if (manager == null)
{
return true;
}
return manager.contains(new DhChunkPos(chunkPosX, chunkPosZ));
}
public void applyChunkUpdate(IChunkWrapper chunkWrapper, ILevelWrapper levelWrapper)
{
//===================//
// validation checks //
//===================//
if (chunkWrapper == null)
{
@@ -199,11 +200,11 @@ public class SharedApi
AbstractDhWorld dhWorld = SharedApi.getAbstractDhWorld();
if (dhWorld == null)
{
if (level instanceof IClientLevelWrapper)
if (levelWrapper instanceof IClientLevelWrapper)
{
// If the client world isn't loaded yet, keep track of which chunks were loaded so we can use them later.
// This may happen if the client world and client level load events happen out of order
IClientLevelWrapper clientLevel = (IClientLevelWrapper) level;
IClientLevelWrapper clientLevel = (IClientLevelWrapper) levelWrapper;
ClientApi.INSTANCE.waitingChunkByClientLevelAndPos.replace(new Pair<>(clientLevel, chunkWrapper.getChunkPos()), chunkWrapper);
}
@@ -216,143 +217,70 @@ public class SharedApi
return;
}
// only continue if the level is loaded
IDhLevel dhLevel = dhWorld.getLevel(level);
IDhLevel dhLevel = dhWorld.getLevel(levelWrapper);
if (dhLevel == null)
{
if (level instanceof IClientLevelWrapper)
if (levelWrapper instanceof IClientLevelWrapper)
{
// the client level isn't loaded yet
IClientLevelWrapper clientLevel = (IClientLevelWrapper) level;
IClientLevelWrapper clientLevel = (IClientLevelWrapper) levelWrapper;
ClientApi.INSTANCE.waitingChunkByClientLevelAndPos.replace(new Pair<>(clientLevel, chunkWrapper.getChunkPos()), chunkWrapper);
}
return;
}
// shoudln't normally happen, but just in case
if (UPDATE_POS_MANAGER.contains(chunkWrapper.getChunkPos()))
// ignore chunk updates if the network should handle them
if (dhLevel instanceof DhClientLevel)
{
if (!((DhClientLevel) dhLevel).shouldProcessChunkUpdate(chunkWrapper.getChunkPos()))
{
return;
}
}
// ignore chunk updates for non-rendered levels
String dimName = dhLevel.getLevelWrapper().getDimensionName();
if (IgnoredDimensionCsvHandler.INSTANCE.dimensionNameShouldBeIgnored(dimName))
{
return;
}
ChunkUpdateQueueManager chunkManager = WORLD_CHUNK_UPDATE_MANAGER.getByLevelWrapper(levelWrapper);
// ignore the wrong level wrapper type or
// if the chunk is already queued for handling
if (chunkManager == null
|| chunkManager.contains(chunkWrapper.getChunkPos()))
{
// TODO this will prevent some LODs from updating across dimensions if multiple levels are loaded
return;
}
//===============================//
// update the necessary chunk(s) //
//===============================//
if (!updateNeighborChunks)
{
// only update the center chunk
queueChunkUpdate(chunkWrapper, null, dhLevel);
}
else
{
// update the center with any existing neighbour chunks.
// this is done so lighting changes are propagated correctly
queueChunkUpdate(chunkWrapper, getNeighbourChunkListForChunk(chunkWrapper,dhLevel), dhLevel);
}
}
private static ArrayList<IChunkWrapper> getNeighbourChunkListForChunk(IChunkWrapper chunkWrapper, IDhLevel dhLevel)
{
// get the neighboring chunk list
ArrayList<IChunkWrapper> neighbourChunkList = new ArrayList<>(9);
for (int xOffset = -1; xOffset <= 1; xOffset++)
{
for (int zOffset = -1; zOffset <= 1; zOffset++)
{
if (xOffset == 0 && zOffset == 0)
{
// center chunk
neighbourChunkList.add(chunkWrapper);
}
else
{
// neighboring chunk
DhChunkPos neighbourPos = new DhChunkPos(chunkWrapper.getChunkPos().getX() + xOffset, chunkWrapper.getChunkPos().getZ() + zOffset);
IChunkWrapper neighbourChunk = dhLevel.getLevelWrapper().tryGetChunk(neighbourPos);
if (neighbourChunk != null)
{
neighbourChunkList.add(neighbourChunk);
}
}
}
}
return neighbourChunkList;
queueChunkUpdate(chunkManager, chunkWrapper, dhLevel);
}
private static void queueChunkUpdate(IChunkWrapper chunkWrapper, @Nullable ArrayList<IChunkWrapper> neighbourChunkList, IDhLevel dhLevel)
{ queueChunkUpdate(chunkWrapper, neighbourChunkList, dhLevel,false); }
private static void queueChunkUpdate(IChunkWrapper chunkWrapper, @Nullable ArrayList<IChunkWrapper> neighbourChunkList, IDhLevel dhLevel, boolean lightUpdateOnly)
private static void queueChunkUpdate(ChunkUpdateQueueManager chunkManager, IChunkWrapper chunkWrapper, IDhLevel dhLevel)
{
int maxUpdateSizeMultiplier;
if (MC_CLIENT != null && MC_CLIENT.playerExists())
// return if the chunk is already queued
if (chunkManager.contains(chunkWrapper.getChunkPos()))
{
// Local worlds & multiplayer
UPDATE_POS_MANAGER.setCenter(MC_CLIENT.getPlayerChunkPos());
maxUpdateSizeMultiplier = MC_CLIENT.clientConnectedToDedicatedServer() ? 1 : MC_SHARED.getPlayerCount();
}
else
{
// Dedicated servers
// Also includes spawn chunks since they're likely to be intentionally utilized with updates
maxUpdateSizeMultiplier = 1 + MC_SHARED.getPlayerCount();
}
UPDATE_POS_MANAGER.maxSize = MAX_UPDATING_CHUNK_COUNT_PER_THREAD_AND_PLAYER
* Config.Common.MultiThreading.numberOfThreads.get()
* maxUpdateSizeMultiplier;
UpdateChunkData updateData = new UpdateChunkData(chunkWrapper, neighbourChunkList, dhLevel, lightUpdateOnly);
if(lightUpdateOnly)
{
UPDATE_POS_MANAGER.removeItem(chunkWrapper.getChunkPos());
}
int remainingCapacity = UPDATE_POS_MANAGER.addItem(chunkWrapper.getChunkPos(), updateData);
if (remainingCapacity <= 0)
{
// limit how often an overloaded message can be sent
long msBetweenLastLog = System.currentTimeMillis() - lastOverloadedLogMessageMsTime;
if (msBetweenLastLog >= MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE)
{
lastOverloadedLogMessageMsTime = System.currentTimeMillis();
String message = "\u00A76" + "Distant Horizons overloaded, too many chunks queued for LOD processing. " + "\u00A7r" +
"\nThis may result in holes in your LODs. " +
"\nFix: move through the world slower, decrease your vanilla render distance, slow down your world pre-generator (IE Chunky), or increase the Distant Horizons' CPU thread counts. " +
"\nMax queue count ["+UPDATE_POS_MANAGER.maxSize+"] (["+ MAX_UPDATING_CHUNK_COUNT_PER_THREAD_AND_PLAYER +"] per thread+players).";
boolean showWarningInChat = Config.Common.Logging.Warning.showUpdateQueueOverloadedChatWarning.get();
if (showWarningInChat)
{
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
// Don't log warnings in singleplayer or in hosted LAN since it usually isn't a problem (and if it is it's easy to notice).
// Servers should always log since being overloaded is harder to notice.
EWorldEnvironment environment = SharedApi.getEnvironment();
if (showWarningInChat || environment == EWorldEnvironment.SERVER_ONLY)
{
LOGGER.warn(message);
}
}
return;
}
// add chunk update data to preUpdate queue
ChunkUpdateData updateData = new ChunkUpdateData(chunkWrapper, dhLevel);
chunkManager.addItemToPreUpdateQueue(chunkWrapper.getChunkPos(), updateData);
// queue updates up to the number of CPU cores allocated for the job
// (this prevents doing extra work queuing tasks that may not be necessary)
// and makes sure the chunks closest to the player are updated first
PriorityTaskPicker.Executor executor = ThreadPoolUtil.getChunkToLodBuilderExecutor();
if (executor != null && executor.getQueueSize() < executor.getPoolSize())
// queue the next position if there are still positions to process
AbstractExecutorService executor = ThreadPoolUtil.getChunkToLodBuilderExecutor();
if (executor != null)
{
try
{
executor.execute(SharedApi::processQueuedChunkUpdate);
executor.execute(WORLD_CHUNK_UPDATE_MANAGER::processEachQueue);
}
catch (RejectedExecutionException ignore)
{
@@ -360,311 +288,19 @@ public class SharedApi
}
}
}
private static void processQueuedChunkUpdate()
{
//LOGGER.trace(chunkWrapper.getChunkPos() + " " + executor.getActiveCount() + " / " + executor.getQueue().size() + " - " + executor.getCompletedTaskCount());
UpdateChunkData updateData = UPDATE_POS_MANAGER.popClosest();
if (updateData == null)
{
return;
}
IChunkWrapper chunkWrapper = updateData.chunkWrapper;
@Nullable ArrayList<IChunkWrapper> neighbourChunkList = updateData.neighbourChunkList;
IDhLevel dhLevel = updateData.dhLevel;
try
{
boolean checkChunkHash = !Config.Common.LodBuilding.disableUnchangedChunkCheck.get();
// check if this chunk has been converted into an LOD already
int oldChunkHash = dhLevel.getChunkHash(chunkWrapper.getChunkPos()); // shouldn't happen on the render thread since it may take a few moments to run
int newChunkHash = chunkWrapper.getBlockBiomeHashCode();
if (checkChunkHash)
{
if (oldChunkHash == newChunkHash && !updateData.lightUpdateOnly)
{
// if the chunk hashes are the same then we don't need to bother with lighting the chunk
// or creating/updating the LODs
return;
}
}
// having a list of the nearby chunks is needed for lighting and beacon generation
ArrayList<IChunkWrapper> nearbyChunkList;
if (neighbourChunkList != null)
{
nearbyChunkList = neighbourChunkList;
}
else
{
nearbyChunkList = new ArrayList<>(1);
nearbyChunkList.add(chunkWrapper);
}
// if this chunk will update its lighting
// then queue adjacent chunks to update theirs as well
// adjacent chunk will have 'lightUpdateOnly' true
// so they won't schedule further chunk updates
if (!updateData.lightUpdateOnly)
{
for (IChunkWrapper adjacentChunk : nearbyChunkList)
{
// pulling a new chunkWrapper is necessary to prevent concurrent modification on the existing chunkWrappers
IChunkWrapper newCenterChunk = dhLevel.getLevelWrapper().tryGetChunk(adjacentChunk.getChunkPos());
if (newCenterChunk != null)
{
queueChunkUpdate(newCenterChunk, getNeighbourChunkListForChunk(newCenterChunk, dhLevel), dhLevel, true);
}
}
}
// sky lighting is populated later at the data source level
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT);
dhLevel.updateBeaconBeamsForChunk(chunkWrapper, nearbyChunkList);
dhLevel.updateChunkAsync(chunkWrapper, newChunkHash);
}
catch (Exception e)
{
LOGGER.error("Unexpected error when updating chunk at pos: [" + chunkWrapper.getChunkPos() + "]", e);
}
finally
{
// queue the next position if there are still positions to process
AbstractExecutorService executor = ThreadPoolUtil.getChunkToLodBuilderExecutor();
if (executor != null && !UPDATE_POS_MANAGER.updateDataByChunkPos.isEmpty())
{
try
{
executor.execute(SharedApi::processQueuedChunkUpdate);
}
catch (RejectedExecutionException ignore)
{
// the executor was shut down, it should be back up shortly and able to accept new jobs
}
}
}
}
//endregion
//=========//
// F3 Menu //
//=========//
//region
public String getDebugMenuString()
{
String updatingCountStr = F3Screen.NUMBER_FORMAT.format(UPDATE_POS_MANAGER.closestQueue.size());
String maxUpdateCountStr = F3Screen.NUMBER_FORMAT.format(UPDATE_POS_MANAGER.maxSize);
return "Queued chunk updates: "+updatingCountStr+" / "+maxUpdateCountStr;
}
public ArrayList<String> getDebugMenuString() { return WORLD_CHUNK_UPDATE_MANAGER.getDebugMenuString(); }
//================//
// helper classes //
//================//
/** contains the objects needed to update a chunk */
private static class UpdateChunkData
{
public IChunkWrapper chunkWrapper;
@Nullable
public ArrayList<IChunkWrapper> neighbourChunkList;
public IDhLevel dhLevel;
/** adjacent chunks will only update their light */
public boolean lightUpdateOnly;
public UpdateChunkData(IChunkWrapper chunkWrapper, @Nullable ArrayList<IChunkWrapper> neighbourChunkList, IDhLevel dhLevel, boolean lightUpdateOnly)
{
this.chunkWrapper = chunkWrapper;
this.neighbourChunkList = neighbourChunkList;
this.dhLevel = dhLevel;
this.lightUpdateOnly = lightUpdateOnly;
}
}
/** keeps track of which chunks need to be updated */
private static class UpdateChunkPosManager
{
private final PriorityQueue<DhChunkPos> closestQueue;
private final PriorityQueue<DhChunkPos> furthestQueue;
private final HashMap<DhChunkPos, UpdateChunkData> updateDataByChunkPos;
private final ReentrantLock lock = new ReentrantLock();
private DhChunkPos center;
private int maxSize = 500;
//=============//
// constructor //
//=============//
public UpdateChunkPosManager()
{
this.closestQueue = new PriorityQueue<>(Comparator.comparingDouble(pos -> pos.squaredDistance(this.center)));
this.furthestQueue = new PriorityQueue<>(Comparator.comparingDouble(pos -> ((DhChunkPos)pos).squaredDistance(this.center)).reversed());
this.updateDataByChunkPos = new HashMap<>();
// defaulting to 0,0 is fine since it'll be updated once we start adding items
this.center = new DhChunkPos(0, 0);
}
//==================//
// list/set methods //
//==================//
public boolean contains(DhChunkPos pos)
{
try
{
this.lock.lock();
return this.updateDataByChunkPos.containsKey(pos);
}
finally
{
this.lock.unlock();
}
}
public void clear()
{
try
{
this.lock.lock();
this.updateDataByChunkPos.clear();
this.closestQueue.clear();
this.furthestQueue.clear();
}
finally
{
this.lock.unlock();
}
}
public void removeItem(DhChunkPos pos)
{
try
{
this.lock.lock();
this.updateDataByChunkPos.remove(pos);
this.closestQueue.remove(pos);
this.furthestQueue.remove(pos);
}
finally
{
this.lock.unlock();
}
}
/**
* Adds an item to the queue of chunks that need to be updated.
* If there are no more slots, replaces the item furthest from the center.
*
* @return The number of remaining slots available in the queue.
*/
public int addItem(DhChunkPos pos, UpdateChunkData updateData)
{
try
{
this.lock.lock();
int remainingSlots = this.maxSize - this.updateDataByChunkPos.size();
if (this.updateDataByChunkPos.containsKey(pos))
{
// Chunk is already present in queue, no need to insert
return remainingSlots;
}
// If no slots are left, get one by removing the item furthest from the center
if (remainingSlots <= 0)
{
DhChunkPos furthest = this.furthestQueue.poll();
this.closestQueue.remove(furthest);
this.updateDataByChunkPos.remove(furthest);
}
this.updateDataByChunkPos.put(pos, updateData);
this.closestQueue.add(pos);
this.furthestQueue.add(pos);
return remainingSlots;
}
finally
{
this.lock.unlock();
}
}
//==================//
// position methods //
//==================//
public void setCenter(DhChunkPos newCenter)
{
// if the rebuild time takes too long
// (in James' testing a queue of 500 items only took around 0.1 milliseconds)
// this equation could be changed to only update after moving 2 or 4 chunks from the center
if (newCenter.equals(this.center))
{
return;
}
try
{
this.lock.lock();
this.center = newCenter;
// rebuild the priority queues to match the new center
this.closestQueue.clear();
this.furthestQueue.clear();
for (DhChunkPos pos : this.updateDataByChunkPos.keySet())
{
this.closestQueue.add(pos);
this.furthestQueue.add(pos);
}
}
finally
{
this.lock.unlock();
}
}
public UpdateChunkData popClosest()
{
try
{
this.lock.lock();
if (this.closestQueue.isEmpty())
{
return null;
}
DhChunkPos closest = this.closestQueue.poll();
this.furthestQueue.remove(closest);
return this.updateDataByChunkPos.remove(closest);
}
finally
{
this.lock.unlock();
}
}
}
//endregion
@@ -0,0 +1,127 @@
package com.seibel.distanthorizons.core.api.internal.chunkUpdating;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import org.jetbrains.annotations.Nullable;
import java.util.Comparator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.PriorityBlockingQueue;
public class ChunkPosQueue
{
private final PriorityBlockingQueue<DhChunkPos> closestQueue;
private final PriorityBlockingQueue<DhChunkPos> furthestQueue;
private final ConcurrentHashMap<DhChunkPos, ChunkUpdateData> updateDataByChunkPos;
private DhChunkPos center;
//=============//
// constructor //
//=============//
public ChunkPosQueue()
{
this.closestQueue = new PriorityBlockingQueue<>(500, Comparator.comparingDouble(pos -> pos.squaredDistance(this.center)));
this.furthestQueue = new PriorityBlockingQueue<>(500, Comparator.comparingDouble(pos -> ((DhChunkPos)pos).squaredDistance(this.center)).reversed());
this.updateDataByChunkPos = new ConcurrentHashMap<>();
// defaulting to 0,0 is fine since it'll be updated once we start adding items
this.center = new DhChunkPos(0, 0);
}
//==============//
// list methods //
//==============//
public boolean contains(DhChunkPos pos) { return this.updateDataByChunkPos.containsKey(pos); }
public void clear()
{
this.updateDataByChunkPos.clear();
this.closestQueue.clear();
this.furthestQueue.clear();
}
public void addItem(DhChunkPos pos, ChunkUpdateData updateData)
{
if (this.updateDataByChunkPos.containsKey(pos))
{
// Chunk is already present in queue, no need to insert
return;
}
this.updateDataByChunkPos.put(pos, updateData);
this.closestQueue.add(pos);
this.furthestQueue.add(pos);
}
public int getQueuedCount() { return this.updateDataByChunkPos.size(); }
public boolean isEmpty() { return this.updateDataByChunkPos.isEmpty(); }
//==================//
// position methods //
//==================//
public void setCenter(DhChunkPos newCenter)
{
// if the rebuild time takes too long
// (in James' testing a queue of 500 items only took around 0.1 milliseconds)
// this equation could be changed to only update after moving 2 or 4 chunks from the center
if (newCenter.equals(this.center))
{
return;
}
this.center = newCenter;
// rebuild the priority queues to match the new center
this.closestQueue.clear();
this.furthestQueue.clear();
for (DhChunkPos pos : this.updateDataByChunkPos.keySet())
{
this.closestQueue.add(pos);
this.furthestQueue.add(pos);
}
}
public ChunkUpdateData popClosest()
{
if (this.closestQueue.isEmpty())
{
return null;
}
DhChunkPos closest = this.closestQueue.poll();
if (closest == null)
{
return null;
}
this.furthestQueue.remove(closest);
return this.updateDataByChunkPos.remove(closest);
}
@Nullable
public ChunkUpdateData popFurthest()
{
if (this.furthestQueue.isEmpty())
{
return null;
}
DhChunkPos furthest = this.furthestQueue.poll();
if (furthest == null)
{
return null;
}
this.closestQueue.remove(furthest);
return this.updateDataByChunkPos.remove(furthest);
}
}
@@ -0,0 +1,21 @@
package com.seibel.distanthorizons.core.api.internal.chunkUpdating;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
public class ChunkUpdateData
{
public IChunkWrapper chunkWrapper;
public IDhLevel dhLevel;
public ChunkUpdateData(IChunkWrapper chunkWrapper, IDhLevel dhLevel)
{
this.chunkWrapper = chunkWrapper;
this.dhLevel = dhLevel;
}
}
@@ -0,0 +1,408 @@
package com.seibel.distanthorizons.core.api.internal.chunkUpdating;
import com.google.common.cache.CacheBuilder;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.*;
/**
* @see WorldChunkUpdateManager
*/
public class ChunkUpdateQueueManager
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
/**
* how many chunks can be queued for updating per thread + player (in multiplayer),
* used to prevent updates from infinitely pilling up if the user flies around extremely fast
*/
public static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD_AND_PLAYER = 1_000;
/** how many milliseconds must pass before an overloaded message can be sent in chat or the log */
public static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 30_000;
private final Set<DhChunkPos> ignoredChunkPosSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
private static long lastOverloadedLogMessageMsTime = 0;
public final ChunkPosQueue updateQueue;
public final ChunkPosQueue preUpdateQueue;
public final ConcurrentMap<DhChunkPos, IChunkWrapper> queuedChunkWrapperByChunkPos = CacheBuilder.newBuilder()
.expireAfterWrite(20, TimeUnit.SECONDS)
.<DhChunkPos, IChunkWrapper>build()
.asMap();
/** dynamically changes based on the number of threads currently available */
public int maxSize = 500;
/** used to prevent flickering */
public long lastMsTimeShownActiveInF3Screen = System.currentTimeMillis();
//=============//
// constructor //
//=============//
//region
public ChunkUpdateQueueManager()
{
this.updateQueue = new ChunkPosQueue();
this.preUpdateQueue = new ChunkPosQueue();
}
//endregion
//==================//
// list/set methods //
//==================//
//region
public boolean contains(DhChunkPos pos)
{
return this.updateQueue.contains(pos)
|| this.ignoredChunkPosSet.contains(pos)
|| this.preUpdateQueue.contains(pos);
}
public void clear()
{
this.updateQueue.clear();
this.preUpdateQueue.clear();
this.ignoredChunkPosSet.clear();
}
public int getQueuedCount() { return this.updateQueue.getQueuedCount() + this.preUpdateQueue.getQueuedCount(); }
public boolean updateQueuesEmpty()
{
return this.updateQueue.isEmpty()
&& this.preUpdateQueue.isEmpty();
}
/**
* Adds an item to the pre-update queue of chunks that might need to be updated.
* If there are no more slots, replaces the item furthest from the center in the update queue.
*/
public void addItemToPreUpdateQueue(DhChunkPos pos, ChunkUpdateData updateData)
{ this.addItemToQueue(pos, updateData, this.preUpdateQueue); }
public void addItemToUpdateQueue(DhChunkPos pos, ChunkUpdateData updateData)
{ this.addItemToQueue(pos, updateData, this.updateQueue); }
private void addItemToQueue(DhChunkPos pos, ChunkUpdateData updateData, ChunkPosQueue queue)
{
int remainingSlots = this.maxSize - this.getQueuedCount();
// If no slots are left, get one by removing the item furthest from the center
if (remainingSlots <= 0)
{
ChunkUpdateData removedData = queue.popFurthest();
if (removedData != null)
{
this.queuedChunkWrapperByChunkPos.remove(removedData.chunkWrapper.getChunkPos());
}
}
queue.addItem(pos,updateData);
this.queuedChunkWrapperByChunkPos.putIfAbsent(pos, updateData.chunkWrapper);
remainingSlots = this.maxSize - this.getQueuedCount();
if (remainingSlots <= 0)
{
this.sendOverloadMessage();
}
}
private void sendOverloadMessage()
{
// limit how often an overloaded message can be sent
long msBetweenLastLog = System.currentTimeMillis() - lastOverloadedLogMessageMsTime;
if (msBetweenLastLog >= MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE)
{
lastOverloadedLogMessageMsTime = System.currentTimeMillis();
String message = MinecraftTextFormat.ORANGE + "Distant Horizons overloaded, too many chunks queued for LOD processing. " + MinecraftTextFormat.CLEAR_FORMATTING +
"\nThis may result in holes in your LODs. " +
"\nFix: move through the world slower, decrease your vanilla render distance, slow down your world pre-generator (IE Chunky), or increase the Distant Horizons' CPU thread counts. " +
"\nMax queue count [" + this.maxSize + "] ([" + MAX_UPDATING_CHUNK_COUNT_PER_THREAD_AND_PLAYER + "] per thread+players).";
boolean showWarningInChat = Config.Common.Logging.Warning.showUpdateQueueOverloadedChatWarning.get();
if (showWarningInChat)
{
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
// Don't log warnings in singleplayer or in hosted LAN since it usually isn't a problem (and if it is it's easy to notice).
// Servers should always log since being overloaded is harder to notice.
EWorldEnvironment environment = SharedApi.getEnvironment();
if (showWarningInChat || environment == EWorldEnvironment.SERVER_ONLY)
{
LOGGER.warn(message);
}
}
}
/**
* Tries to return a cloned chunk wrapper from memory.
* Returns null if no chunk is available.
* <br><br>
* This is done instead of accessing the MC level since
* accessing the level often requires running on the render or server
* thread, which causes stuttering.
*/
@Nullable
public IChunkWrapper tryGetChunk(DhChunkPos pos)
{
IChunkWrapper existingWrapper = this.queuedChunkWrapperByChunkPos.get(pos);
if (existingWrapper == null)
{
return null;
}
return existingWrapper.copy();
}
//endregion
//=========//
// ignores //
//=========//
//region
public void addPosToIgnore(DhChunkPos chunkPos) { this.ignoredChunkPosSet.add(chunkPos); }
public void removePosToIgnore(DhChunkPos chunkPos) { this.ignoredChunkPosSet.remove(chunkPos); }
//endregion
//===================//
// update processing //
//===================//
//region
public void processQueue()
{
// update the center & max size of the queue manager
int maxUpdateSizeMultiplier;
if (MC_CLIENT != null && MC_CLIENT.playerExists())
{
// Local worlds & multiplayer
this.setCenter(MC_CLIENT.getPlayerChunkPos());
maxUpdateSizeMultiplier = MC_CLIENT.clientConnectedToDedicatedServer() ? 1 : MC_SHARED.getPlayerCount();
}
else
{
// Dedicated servers
// Also includes spawn chunks since they're likely to be intentionally utilized with updates
maxUpdateSizeMultiplier = 1 + MC_SHARED.getPlayerCount();
}
this.maxSize = MAX_UPDATING_CHUNK_COUNT_PER_THREAD_AND_PLAYER
* Config.Common.MultiThreading.numberOfThreads.get()
* maxUpdateSizeMultiplier;
//===============================//
// update the necessary chunk(s) //
//===============================//
this.processQueuedChunkPreUpdate();
this.processQueuedChunkUpdate();
// queue the next position if there are still positions to process
AbstractExecutorService executor = ThreadPoolUtil.getChunkToLodBuilderExecutor();
if (executor != null && !this.updateQueuesEmpty())
{
try
{
executor.execute(this::processQueue);
}
catch (RejectedExecutionException ignore)
{
// the executor was shut down, it should be back up shortly and able to accept new jobs
}
}
}
private void processQueuedChunkPreUpdate()
{
ChunkUpdateData preUpdateData = this.preUpdateQueue.popClosest();
if (preUpdateData == null)
{
return;
}
IDhLevel dhLevel = preUpdateData.dhLevel;
IChunkWrapper chunkWrapper = preUpdateData.chunkWrapper;
chunkWrapper.createDhHeightMaps();
try
{
// check if this chunk has been converted into an LOD already
boolean checkChunkHash = !Config.Common.LodBuilding.disableUnchangedChunkCheck.get();
if (checkChunkHash)
{
int oldChunkHash = dhLevel.getChunkHash(chunkWrapper.getChunkPos()); // shouldn't happen on the render thread since it may take a few moments to run
int newChunkHash = chunkWrapper.getBlockBiomeHashCode();
boolean hasNewChunkHash = (oldChunkHash != newChunkHash);
if (!hasNewChunkHash)
{
// do not update the chunk if the hash is the same
return;
}
}
this.addItemToUpdateQueue(chunkWrapper.getChunkPos(), preUpdateData);
}
catch (Exception e)
{
LOGGER.error("Unexpected error when pre-updating chunk at pos: [" + chunkWrapper.getChunkPos() + "]", e);
}
}
private void processQueuedChunkUpdate()
{
ChunkUpdateData updateData = this.updateQueue.popClosest();
if (updateData == null)
{
return;
}
IChunkWrapper chunkWrapper = updateData.chunkWrapper;
IDhLevel dhLevel = updateData.dhLevel;
ILevelWrapper levelWrapper = dhLevel.getLevelWrapper();
// having a list of the nearby chunks is needed for lighting and beacon generation
ArrayList<IChunkWrapper> nearbyChunkList = this.tryGetNeighborChunkListForChunk(chunkWrapper);
try
{
// sky lighting is populated later at the data source level
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(chunkWrapper, nearbyChunkList, levelWrapper.hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT);
dhLevel.updateBeaconBeamsForChunk(chunkWrapper, nearbyChunkList);
int newChunkHash = chunkWrapper.getBlockBiomeHashCode();
dhLevel.updateChunkAsync(chunkWrapper, newChunkHash);
}
catch (Exception e)
{
LOGGER.error("Unexpected error when updating chunk at pos: [" + chunkWrapper.getChunkPos() + "]", e);
}
this.queuedChunkWrapperByChunkPos.remove(updateData.chunkWrapper.getChunkPos());
}
private ArrayList<IChunkWrapper> tryGetNeighborChunkListForChunk(IChunkWrapper chunkWrapper)
{
// get the neighboring chunk list
ArrayList<IChunkWrapper> neighborChunkList = new ArrayList<>(9);
for (int xOffset = -1; xOffset <= 1; xOffset++)
{
for (int zOffset = -1; zOffset <= 1; zOffset++)
{
if (xOffset == 0 && zOffset == 0)
{
// center chunk
neighborChunkList.add(chunkWrapper);
}
else
{
// neighboring chunk
DhChunkPos neighborPos = new DhChunkPos(chunkWrapper.getChunkPos().getX() + xOffset, chunkWrapper.getChunkPos().getZ() + zOffset);
IChunkWrapper neighborChunk = this.tryGetChunk(neighborPos);
if (neighborChunk != null)
{
neighborChunkList.add(neighborChunk);
}
}
}
}
return neighborChunkList;
}
//endregion
//==================//
// position methods //
//==================//
//region
public void setCenter(DhChunkPos newCenter)
{
this.updateQueue.setCenter(newCenter);
this.preUpdateQueue.setCenter(newCenter);
}
//endregion
//=========//
// F3 Menu //
//=========//
//region
public String getDebugMenuString()
{
String y = MinecraftTextFormat.YELLOW;
String o = MinecraftTextFormat.ORANGE;
String cf = MinecraftTextFormat.CLEAR_FORMATTING;
String preUpdatingCountStr = F3Screen.NUMBER_FORMAT.format(this.preUpdateQueue.getQueuedCount());
String updatingCountStr = F3Screen.NUMBER_FORMAT.format(this.updateQueue.getQueuedCount());
String queuedCountStr = F3Screen.NUMBER_FORMAT.format(this.getQueuedCount());
String maxUpdateCountStr = F3Screen.NUMBER_FORMAT.format(this.maxSize);
return "Queued chunk updates: "+"("+y+preUpdatingCountStr+cf+" + "+o+updatingCountStr+cf+") ["+queuedCountStr+"/"+maxUpdateCountStr+"]";
}
//endregion
}
@@ -0,0 +1,190 @@
package com.seibel.distanthorizons.core.api.internal.chunkUpdating;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Holds all the {@link ChunkUpdateQueueManager} for a loaded world.
* Different queues are needed for each level to prevent
* chunks from bleeding between levels (IE a nether chunk applied to the overworld).
*
* @see ChunkUpdateQueueManager
*/
public class WorldChunkUpdateManager
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** singleton since we only expect to have one world loaded at a time */
public static final WorldChunkUpdateManager INSTANCE = new WorldChunkUpdateManager();
public static final Set<String> LOGGED_GET_ERROR_MESSAGES = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
/**
* Queues are only removed during world shutdown.
* The assumption is that there will be a limited number of {@link ILevelWrapper}'s
* for a given world.
*/
private final ConcurrentHashMap<ILevelWrapper, ChunkUpdateQueueManager> updateQueueByLevelWrapper = new ConcurrentHashMap<>();
//=============//
// constructor //
//=============//
//region
private WorldChunkUpdateManager() { }
//endregion
//=================//
// manager methods //
//=================//
//region
/**
* @return null if the world is unloaded or the given level wrapper is the wrong type
*/
@Nullable
public ChunkUpdateQueueManager getByLevelWrapper(ILevelWrapper levelWrapper)
{
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
if (world == null)
{
// world isn't loaded, no warnings need to be logged
return null;
}
// we only want to load chunks for certain level wrappers
// this is done specifically on a local-server to prevent
// loading both the server and client level wrappers
if (world.environment == EWorldEnvironment.CLIENT_ONLY
// when connected to a server we should only ever load client wrappers anyway
// but this check confirms it
&& !(levelWrapper instanceof IClientLevelWrapper))
{
// how did we get a server level wrapper on the client?
// this shouldn't happen, but just in case
return null;
}
else if (
(world.environment == EWorldEnvironment.SERVER_ONLY
|| world.environment == EWorldEnvironment.CLIENT_SERVER)
// when hosting a server we only care about the server wrappers
&& !(levelWrapper instanceof IServerLevelWrapper))
{
// ignore client updates on the server
return null;
}
ChunkUpdateQueueManager queueManager = this.updateQueueByLevelWrapper.get(levelWrapper);
if (queueManager != null)
{
return queueManager;
}
return this.updateQueueByLevelWrapper.compute(levelWrapper,
(ILevelWrapper newLevelWrapper, ChunkUpdateQueueManager oldQueueManager) ->
{
if (oldQueueManager != null)
{
return oldQueueManager;
}
oldQueueManager = new ChunkUpdateQueueManager();
return oldQueueManager;
});
}
public void processEachQueue()
{
this.updateQueueByLevelWrapper.forEach(
(ILevelWrapper levelWrapper, ChunkUpdateQueueManager updateManager) ->
{
updateManager.processQueue();
});
}
public int getTotalQueuedCount()
{
AtomicInteger queueCountRef = new AtomicInteger(0);
this.updateQueueByLevelWrapper.forEach(
(ILevelWrapper levelWrapper, ChunkUpdateQueueManager updateManager) ->
{
queueCountRef.addAndGet(updateManager.getQueuedCount());
});
return queueCountRef.get();
}
public void clear() { this.updateQueueByLevelWrapper.clear(); }
//endregion
//=========//
// F3 Menu //
//=========//
//region
public ArrayList<String> getDebugMenuString()
{
ArrayList<String> stringList = new ArrayList<>();
stringList.add("");// placeholder for the total count
// add each queue to the list
AtomicInteger totalQueueCountRef = new AtomicInteger(0);
AtomicInteger activeQueueCountRef = new AtomicInteger(0);
this.updateQueueByLevelWrapper.forEach(
(ILevelWrapper levelWrapper, ChunkUpdateQueueManager updateManager) ->
{
// is this queue active?
if (!updateManager.updateQueuesEmpty())
{
updateManager.lastMsTimeShownActiveInF3Screen = System.currentTimeMillis();
activeQueueCountRef.incrementAndGet();
}
// show this queue if it hasn't been empty long enough
// (done to prevent flickering on the F3 screen when the queue rapidly fills/empties)
long timeSinceQueueLastShownActiveMs = System.currentTimeMillis() - updateManager.lastMsTimeShownActiveInF3Screen;
if (timeSinceQueueLastShownActiveMs < 4_000)
{
stringList.add(levelWrapper.getDimensionName() + ": " + updateManager.getDebugMenuString());
}
totalQueueCountRef.incrementAndGet();
});
// replace the first line with the number of total/active queues
// (helpful if we need to diagnose a leak due to a massive number of queue level wrappers)
stringList.set(0, "Chunk Update Queues: "+activeQueueCountRef.get()+"/"+totalQueueCountRef.get());
return stringList;
}
//endregion
}
@@ -0,0 +1,89 @@
package com.seibel.distanthorizons.core.api.internal.rendering;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
/**
* Used to track the rendering state for the current frame.
*
* @see ClientApi
*/
public class DhRenderState
{
public Mat4f mcModelViewMatrix = null;
public Mat4f mcProjectionMatrix = null;
/**
* percentage of time into the current client tick. <br><br>
*
* Can be converted to a millisecond frametime
* (IE time between frames in milliseconds) using the formula: <br>
* <code>
* (partialTickTime/20*1000)
* </code> <br>
* IE 60 FPS = 16.6 MS <br>
*
* @link https://fpstoms.com/
* @see IMinecraftRenderWrapper#getPartialTickTime()
*/
public float partialTickTime = -1;
public IClientLevelWrapper clientLevelWrapper = null;
/**
* This will generally be true if the player is: <br>
* - blinded <br>
* - under lava/water <br>
* <br>
* In those cases some rendering logic may need to be changed
* to look correct.
*/
public boolean vanillaFogEnabled = false;
//========//
// checks //
//========//
public String unableToRenderBecause()
{
String errorReasons = "";
// the matrix may be the identity matrix or and old/incorrect matrix
// but we did set it at least once before this
if (this.mcModelViewMatrix == null)
{
errorReasons += "no MVM Matrix, ";
}
if (this.mcProjectionMatrix == null)
{
errorReasons += "no Projection Matrix, ";
}
if (this.partialTickTime == -1)
{
errorReasons += "no Frame Time, ";
}
if (this.clientLevelWrapper == null)
{
errorReasons += "no Level Wrapper, ";
}
return errorReasons;
}
public void canRenderOrThrow() throws IllegalStateException
{
String errorReasons = this.unableToRenderBecause();
if (!errorReasons.isEmpty())
{
throw new IllegalStateException(errorReasons);
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,275 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.config;
import com.seibel.distanthorizons.core.config.file.ConfigFileHandling;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.config.types.AbstractConfigType;
import com.seibel.distanthorizons.core.config.types.ConfigCategory;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.core.config.types.ConfigUiLinkedEntry;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.*;
/**
* Indexes and sets everything up for the file handling and gui
*
* @author coolGi
* @author Ran
* @version 2023-8-26
*/
// Init the config after singletons have been blinded
public class ConfigBase
{
/** Our own config instance, don't modify unless you are the DH mod */
public static ConfigBase INSTANCE;
public ConfigFileHandling configFileINSTANCE;
private final Logger logger;
public final String modID;
public final String modName;
public final int configVersion;
public boolean isLoaded = false;
/**
* What the config works with
* <br>
* <br> {@link Enum}
* <br> {@link Boolean}
* <br> {@link Byte}
* <br> {@link Integer}
* <br> {@link Double}
* <br> {@link Short}
* <br> {@link Long}
* <br> {@link Float}
* <br> {@link String}
* <br>
* <br> // Below, "T" should be a value from above
* <br> // Note: This is not checked, so we trust that you are doing the right thing (TODO: Check it)
* <br> List<T>
* <br> ArrayList<T>
* <br> Map<String, T>
* <br> HashMap<String, T>
*/
public static final List<Class<?>> acceptableInputs = new ArrayList<Class<?>>()
{{
add(Boolean.class);
add(Byte.class);
add(Integer.class);
add(Double.class);
add(Short.class);
add(Long.class);
add(Float.class);
add(String.class);
// TODO[CONFIG]: Check the type of these is valid
add(List.class);
add(ArrayList.class);
add(Map.class);
add(HashMap.class);
}};
/** Disables the minimum and maximum of any variable */
public boolean disableMinMax = false; // Very fun to use, but should always be disabled by default
public final List<AbstractConfigType<?, ?>> entries = new ArrayList<>();
public ConfigBase(String modID, String modName, Class<?> configClass)
{
this(modID, modName, configClass, getConfigPath(modName), -1);
}
public ConfigBase(String modID, String modName, Class<?> configClass, Path configPath)
{
this(modID, modName, configClass, configPath, -1);
}
public ConfigBase(String modID, String modName, Class<?> configClass, int configVersion)
{
this(modID, modName, configClass, getConfigPath(modName), configVersion);
}
public ConfigBase(String modID, String modName, Class<?> configClass, Path configPath, int configVersion)
{
this.logger = LogManager.getLogger(this.getClass().getSimpleName() + ", " + modID);
this.logger.info("Initialising config for " + modName);
this.modID = modID;
this.modName = modName;
this.configVersion = configVersion;
this.initNestedClass(configClass, ""); // Init root category
// File handling (load from file)
this.configFileINSTANCE = new ConfigFileHandling(this, configPath);
this.configFileINSTANCE.loadFromFile();
this.isLoaded = true;
this.logger.info("Config for " + modName + " initialised");
}
private void initNestedClass(Class<?> configClass, String category)
{
// Put all the entries in entries
for (Field field : configClass.getFields())
{
if (AbstractConfigType.class.isAssignableFrom(field.getType()))
{
try
{
this.entries.add((AbstractConfigType<?, ?>) field.get(field.getType()));
}
catch (IllegalAccessException exception)
{
this.logger.warn(exception);
}
AbstractConfigType<?, ?> entry = this.entries.get(this.entries.size() - 1);
entry.category = category;
entry.name = field.getName();
entry.configBase = this;
if (ConfigEntry.class.isAssignableFrom(field.getType()))
{ // If item is type ConfigEntry
if (!isAcceptableType(entry.getType()))
{
this.logger.error("Invalid variable type at [" + (category.isEmpty() ? "" : category + ".") + field.getName() + "].");
this.logger.error("Type [" + entry.getType() + "] is not one of these types [" + acceptableInputs.toString() + "]");
this.entries.remove(this.entries.size() - 1); // Delete the entry if it is invalid so the game can still run
}
}
if (ConfigCategory.class.isAssignableFrom(field.getType()))
{ // If it's a category then init the stuff inside it and put it in the category list
assert entry instanceof ConfigCategory;
if (((ConfigCategory) entry).getDestination() == null)
((ConfigCategory) entry).destination = entry.getNameWCategory();
if (entry.get() != null)
{
this.initNestedClass(((ConfigCategory) entry).get(), ((ConfigCategory) entry).getDestination());
}
}
}
}
}
private static boolean isAcceptableType(Class<?> Clazz)
{
if (Clazz.isEnum())
return true;
return acceptableInputs.contains(Clazz);
}
/** Gets the default config path given a mod name */
public static Path getConfigPath(String modName)
{
return SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class)
.getInstallationDirectory().toPath().resolve("config").resolve(modName + ".toml");
}
/**
* Used for checking that all the lang files for the config exist
*
* @param onlyShowNew If disabled then it would basically remake the config lang
* @param checkEnums Checks if all the lang for the enum's exist
*/
// This is just to re-format the lang or check if there is something in the lang that is missing
@SuppressWarnings("unchecked")
public String generateLang(boolean onlyShowNew, boolean checkEnums)
{
ILangWrapper langWrapper = SingletonInjector.INSTANCE.get(ILangWrapper.class);
List<Class<? extends Enum<?>>> enumList = new ArrayList<>();
String generatedLang = "";
String starter = " \"";
String separator = "\":\n \"";
String ending = "\",\n";
for (AbstractConfigType<?, ?> entry : this.entries)
{
String entryPrefix = "lod.config." + entry.getNameWCategory();
if (checkEnums && entry.getType().isEnum() && !enumList.contains(entry.getType()))
{ // Put it in an enum list to work with at the end
enumList.add((Class<? extends Enum<?>>) entry.getType());
}
if (!onlyShowNew || langWrapper.langExists(entryPrefix))
{
if (!ConfigUiLinkedEntry.class.isAssignableFrom(entry.getClass()))
{ // If it is a linked item, dont generate the base lang
generatedLang += starter
+ entryPrefix
+ separator
+ langWrapper.getLang(entryPrefix)
+ ending
;
}
// Adds tooltips
if (langWrapper.langExists(entryPrefix + ".@tooltip"))
{
generatedLang += starter
+ entryPrefix + ".@tooltip"
+ separator
+ langWrapper.getLang(entryPrefix + ".@tooltip")
.replaceAll("\n", "\\\\n")
.replaceAll("\"", "\\\\\"")
+ ending
;
}
}
}
if (!enumList.isEmpty())
{
generatedLang += "\n"; // Separate the main lang with the enum's
for (Class<? extends Enum> anEnum : enumList)
{
for (Object enumStr : new ArrayList<>(EnumSet.allOf(anEnum)))
{
String enumPrefix = "lod.config.enum." + anEnum.getSimpleName() + "." + enumStr.toString();
if (!onlyShowNew || langWrapper.langExists(enumPrefix))
{
generatedLang += starter
+ enumPrefix
+ separator
+ langWrapper.getLang(enumPrefix)
+ ending
;
}
}
}
}
return generatedLang;
}
}
@@ -0,0 +1,330 @@
/*
* 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.core.config;
import com.seibel.distanthorizons.core.config.file.ConfigFileHandler;
import com.seibel.distanthorizons.core.config.types.*;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.util.*;
/**
* Sets up everything in {@link Config} for the file/GUI and keeps track of all
* entries therein. <br>
* This should be run after the singletons have been bound.
*
* @author coolGi
* @author Ran
*
* @see Config
*/
public class ConfigHandler
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
/**
* What the config works with
* <br>
* <br> {@link Enum}
* <br> {@link Boolean}
* <br> {@link Byte}
* <br> {@link Integer}
* <br> {@link Double}
* <br> {@link Short}
* <br> {@link Long}
* <br> {@link Float}
* <br> {@link String}
* <br>
* <br> // Below, "T" should be a value from above
* <br> // Note: This is not checked, so we trust that you are doing the right thing
* <br> List<T>
* <br> ArrayList<T>
* <br> Map<String, T>
* <br> HashMap<String, T>
*/
private static final List<Class<?>> ACCEPTABLE_INPUTS = new ArrayList<Class<?>>()
{{
this.add(Boolean.class);
this.add(Byte.class);
this.add(Integer.class);
this.add(Double.class);
this.add(Short.class);
this.add(Long.class);
this.add(Float.class);
this.add(String.class);
// partially implemented but not entirely
this.add(List.class);
this.add(ArrayList.class);
this.add(Map.class);
this.add(HashMap.class);
}};
public static final ConfigHandler INSTANCE = new ConfigHandler();
public final ConfigFileHandler configFileHandler = new ConfigFileHandler(getConfigPath());
public final List<AbstractConfigBase<?>> configBaseList = new ArrayList<>();
public boolean isLoaded = false;
/**
* Disables the minimum and maximum validation. <Br>
* Fun to use, but should be disabled by default.
*/
public boolean runMinMaxValidation = true;
//=============//
// constructor //
//=============//
public static void tryRunFirstTimeSetup()
{
if (INSTANCE.isLoaded)
{
LOGGER.debug("ConfigHandler setup already run, ignoring.");
return;
}
INSTANCE.runFirstTimeSetup();
}
private void runFirstTimeSetup()
{
LOGGER.info("Initialising config for [" + ModInfo.NAME + "]");
this.initNestedClass(Config.class, ""); // Init root category
this.configFileHandler.loadFromFile();
this.runMinMaxValidation = !Config.Client.Advanced.Debugging.allowUnsafeValues.get();
this.isLoaded = true;
LOGGER.info("[" + ModInfo.NAME + "] Config initialised");
}
/** Gets the default config path given a mod name */
private static Path getConfigPath()
{
return MC_SHARED
.getInstallationDirectory().toPath()
.resolve("config")
.resolve(ModInfo.NAME + ".toml");
}
/** Put all the config entries into configEntryList */
private void initNestedClass(Class<?> configClass, String category)
{
Field[] fields = configClass.getFields();
for (Field field : fields)
{
// ignore any non-config variables
if (!AbstractConfigBase.class.isAssignableFrom(field.getType()))
{
continue;
}
// add this config to the master list
try
{
this.configBaseList.add((AbstractConfigBase<?>) field.get(field.getType()));
}
catch (IllegalAccessException e)
{
LOGGER.warn("Unable to add config ["+field.getType().getName()+"], error: ["+e.getMessage()+"].", e);
continue;
}
// set any necessary variables in this config
AbstractConfigBase<?> configBase = this.configBaseList.get(this.configBaseList.size() - 1);
configBase.category = category;
configBase.name = field.getName();
// validate the config's input type
if (ConfigEntry.class.isAssignableFrom(field.getType()))
{
if (!isAcceptableType(configBase.getType()))
{
LOGGER.error("Invalid variable type at [" + (category.isEmpty() ? "" : category + ".") + field.getName() + "].");
LOGGER.error("Type [" + configBase.getType() + "] is not one of these types [" + ACCEPTABLE_INPUTS.toString() + "]");
this.configBaseList.remove(this.configBaseList.size() - 1); // Delete the entry if it is invalid so the game can still run
}
}
// recursively add deeper categories if present
if (ConfigCategory.class.isAssignableFrom(field.getType()))
{
ConfigCategory configCategory = (ConfigCategory) configBase;
if (configCategory.getDestination() == null)
{
configCategory.destination = configBase.getNameAndCategory();
}
// shouldn't happen, but just in case
if (configBase.get() != null)
{
this.initNestedClass(configCategory.get(), configCategory.getDestination());
}
}
}
}
private static boolean isAcceptableType(Class<?> inputClass)
{
if (inputClass.isEnum())
{
return true;
}
return ACCEPTABLE_INPUTS.contains(inputClass);
}
//===============//
// lang handling //
//===============//
/**
* Used for checking that all the lang files for the config exist.
* This is just to re-format the lang or check if there is something in the lang that is missing
*
* @param onlyShowMissing If false then this will remake the entire config lang
* @param checkEnums Checks if all the lang for the enum's exist
*/
@SuppressWarnings("unchecked")
public String generateLang(boolean onlyShowMissing, boolean checkEnums)
{
ILangWrapper langWrapper = SingletonInjector.INSTANCE.get(ILangWrapper.class);
List<Class<? extends Enum<?>>> enumList = new ArrayList<>();
String generatedLang = "";
String starter = " \"";
String separator = "\":\n \"";
String ending = "\",\n";
// config entries
for (AbstractConfigBase<?> entry : this.configBaseList)
{
String entryPrefix = "distanthorizons.config." + entry.getNameAndCategory();
if (checkEnums
&& entry.getType().isEnum()
&& !enumList.contains(entry.getType()))
{
// Put it in an enum list to work with at the end
enumList.add((Class<? extends Enum<?>>) entry.getType());
}
// config file items don't need lang entries
if (!entry.getAppearance().showInGui)
{
continue;
}
// some entries don't need localization
if (ConfigUiLinkedEntry.class.isAssignableFrom(entry.getClass())
|| ConfigUISpacer.class.isAssignableFrom(entry.getClass()))
{
continue;
}
if (ConfigUIComment.class.isAssignableFrom(entry.getClass())
&& ((ConfigUIComment)entry).parentConfigPath != null)
{
entryPrefix = "distanthorizons.config." + ((ConfigUIComment)entry).parentConfigPath;
}
if (langWrapper.langExists(entryPrefix)
&& onlyShowMissing)
{
continue;
}
generatedLang += starter
+ entryPrefix
+ separator
+ langWrapper.getLang(entryPrefix)
+ ending
;
// only add tooltips for entries that are also missing
// their primary lang
// this is done since not all menu items need a tooltip
if (!langWrapper.langExists(entryPrefix + ".@tooltip")
|| !onlyShowMissing)
{
generatedLang += starter
+ entryPrefix + ".@tooltip"
+ separator
+ langWrapper.getLang(entryPrefix + ".@tooltip")
.replaceAll("\n", "\\\\n")
.replaceAll("\"", "\\\\\"")
+ ending
;
}
}
// enums
if (!enumList.isEmpty())
{
generatedLang += "\n"; // Separate the main lang with the enum's
for (Class<? extends Enum> anEnum : enumList)
{
for (Object enumStr : new ArrayList<Object>(EnumSet.allOf(anEnum)))
{
String enumPrefix = "distanthorizons.config.enum." + anEnum.getSimpleName() + "." + enumStr.toString();
if (!langWrapper.langExists(enumPrefix)
|| !onlyShowMissing)
{
generatedLang += starter
+ enumPrefix
+ separator
+ langWrapper.getLang(enumPrefix)
+ ending
;
}
}
}
}
return generatedLang;
}
}
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import java.util.HashMap;
import java.util.HashSet;
public class ConfigEntryWithPresetOptions<TQuickEnum, TConfig>
public class ConfigPresetOptions<TQuickEnum, TConfig>
{
public final ConfigEntry<TConfig> configEntry;
@@ -32,7 +32,11 @@ public class ConfigEntryWithPresetOptions<TQuickEnum, TConfig>
public ConfigEntryWithPresetOptions(ConfigEntry<TConfig> configEntry, HashMap<TQuickEnum, TConfig> configOptionByQualityOption)
//=============//
// constructor //
//=============//
public ConfigPresetOptions(ConfigEntry<TConfig> configEntry, HashMap<TQuickEnum, TConfig> configOptionByQualityOption)
{
this.configEntry = configEntry;
this.configOptionByQualityOption = configOptionByQualityOption;
@@ -40,6 +44,10 @@ public class ConfigEntryWithPresetOptions<TQuickEnum, TConfig>
//=========//
// methods //
//=========//
public void updateConfigEntry(TQuickEnum quickQuality)
{
TConfig newValue = this.configOptionByQualityOption.get(quickQuality);
@@ -48,7 +56,9 @@ public class ConfigEntryWithPresetOptions<TQuickEnum, TConfig>
public HashSet<TQuickEnum> getPossibleQualitiesFromCurrentOptionValue()
{
TConfig inputOptionValue = this.configEntry.get();
// get true value so we can ignore API overrides,
// users find this confusing if their preset is set to "CUSTOM"
TConfig inputOptionValue = this.configEntry.getTrueValue();
HashSet<TQuickEnum> possibleQualities = new HashSet<>();
for (TQuickEnum key : this.configOptionByQualityOption.keySet())
@@ -17,12 +17,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.api.objects.config;
package com.seibel.distanthorizons.core.config.api;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.coreapi.interfaces.config.IConverter;
import com.seibel.distanthorizons.coreapi.util.converters.DefaultConverter;
import com.seibel.distanthorizons.core.config.api.converters.DefaultConverter;
import java.util.function.Consumer;
@@ -41,11 +41,17 @@ import java.util.function.Consumer;
*/
public class DhApiConfigValue<coreType, apiType> implements IDhApiConfigValue<apiType>
{
private final IConfigEntry<coreType> configEntry;
private final ConfigEntry<coreType> configBase;
private final IConverter<coreType, apiType> configConverter;
//==============//
// constructors //
//==============//
//region
/**
* This constructor should only be called internally. <br>
* There is no reason for API users to create this object. <br><br>
@@ -53,9 +59,9 @@ public class DhApiConfigValue<coreType, apiType> implements IDhApiConfigValue<ap
* Uses the default object converter, this requires coreType and apiType to be the same.
*/
@SuppressWarnings("unchecked") // DefaultConverter's cast is safe
public DhApiConfigValue(IConfigEntry<coreType> newConfigEntry)
public DhApiConfigValue(ConfigEntry<coreType> configBase)
{
this.configEntry = newConfigEntry;
this.configBase = configBase;
this.configConverter = (IConverter<coreType, apiType>) new DefaultConverter<coreType>();
}
@@ -63,22 +69,40 @@ public class DhApiConfigValue<coreType, apiType> implements IDhApiConfigValue<ap
* This constructor should only be called internally. <br>
* There is no reason for API users to create this object. <br><br>
*/
public DhApiConfigValue(IConfigEntry<coreType> newConfigEntry, IConverter<coreType, apiType> newConverter)
public DhApiConfigValue(ConfigEntry<coreType> configBase, IConverter<coreType, apiType> newConverter)
{
this.configEntry = newConfigEntry;
this.configBase = configBase;
this.configConverter = newConverter;
}
//endregion
public apiType getValue() { return this.configConverter.convertToApiType(this.configEntry.get()); }
public apiType getTrueValue() { return this.configConverter.convertToApiType(this.configEntry.getTrueValue()); }
public apiType getApiValue() { return this.configConverter.convertToApiType(this.configEntry.getApiValue()); }
//===========//
// overrides //
//===========//
//region
@Override public apiType getValue() { return this.configConverter.convertToApiType(this.configBase.get()); }
@Override public apiType getTrueValue() { return this.configConverter.convertToApiType(this.configBase.getTrueValue()); }
@Override public apiType getApiValue()
{
// if no API value is set, this should return null
if (this.configBase.getApiValue() == null)
{
return null;
}
return this.configConverter.convertToApiType(this.configBase.getApiValue());
}
@Override
public boolean setValue(apiType newValue)
{
if (this.configEntry.getAllowApiOverride())
if (this.configBase.getAllowApiOverride())
{
this.configEntry.setApiValue(this.configConverter.convertToCoreType(newValue));
this.configBase.setApiValue(this.configConverter.convertToCoreType(newValue));
return true;
}
else
@@ -87,22 +111,39 @@ public class DhApiConfigValue<coreType, apiType> implements IDhApiConfigValue<ap
}
}
public boolean clearValue() { return this.setValue(null); }
@Override
public boolean clearValue()
{
if (this.configBase.getAllowApiOverride())
{
// no converter should be used here since null objects may need to be handled differently
this.configBase.setApiValue(null);
return true;
}
else
{
return false;
}
}
public boolean getCanBeOverrodeByApi() { return this.configEntry.getAllowApiOverride(); }
@Override
public boolean getCanBeOverrodeByApi() { return this.configBase.getAllowApiOverride(); }
public apiType getDefaultValue() { return this.configConverter.convertToApiType(this.configEntry.getDefaultValue()); }
public apiType getMaxValue() { return this.configConverter.convertToApiType(this.configEntry.getMax()); }
public apiType getMinValue() { return this.configConverter.convertToApiType(this.configEntry.getMin()); }
@Override public apiType getDefaultValue() { return this.configConverter.convertToApiType(this.configBase.getDefaultValue()); }
@Override public apiType getMaxValue() { return this.configConverter.convertToApiType(this.configBase.getMax()); }
@Override public apiType getMinValue() { return this.configConverter.convertToApiType(this.configBase.getMin()); }
@Override
public void addChangeListener(Consumer<apiType> onValueChangeFunc)
{
this.configEntry.addValueChangeListener((coreValue) ->
this.configBase.addValueChangeListener((coreValue) ->
{
apiType apiValue = this.configConverter.convertToApiType(coreValue);
onValueChangeFunc.accept(apiValue);
});
}
//endregion
}
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.coreapi.util.converters;
package com.seibel.distanthorizons.core.config.api.converters;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogDrawMode;
import com.seibel.distanthorizons.coreapi.interfaces.config.IConverter;
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.coreapi.util.converters;
package com.seibel.distanthorizons.core.config.api.converters;
import com.seibel.distanthorizons.coreapi.interfaces.config.IConverter;
@@ -17,34 +17,27 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.enums.worldGeneration;
package com.seibel.distanthorizons.core.config.api.converters;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
import com.seibel.distanthorizons.coreapi.interfaces.config.IConverter;
/**
* MULTI_THREADED, <br>
* SINGLE_THREADED, <br>
* SERVER_THREAD, <br>
* Used to support deprecated config options that may be identical
* in implementation but with the On/Off values flipped.
*
* @author James Seibel
* @version 7-25-2022
* @version 2025-12-22
*/
public enum EWorldGenThreadMode
public class InvertedBoolConverter implements IConverter<Boolean, Boolean>
{
/**
* This world generator can be run on an unlimited number
* of concurrent threads.
*/
MULTI_THREADED,
/**
* This world generator can only be run on one thread at
* a time, however that thread can run concurrently
* to Minecraft's server thread.
*/
SINGLE_THREADED,
@Override
public Boolean convertToCoreType(Boolean core)
{ return !core; }
@Override
public Boolean convertToApiType(Boolean api)
{ return !api; }
/**
* This world generator can only be run on Minecraft's
* server thread.
*/
SERVER_THREAD,
}
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.coreapi.util.converters;
package com.seibel.distanthorizons.core.config.api.converters;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
import com.seibel.distanthorizons.coreapi.interfaces.config.IConverter;
@@ -31,14 +31,12 @@ import com.seibel.distanthorizons.coreapi.interfaces.config.IConverter;
public class RenderModeEnabledConverter implements IConverter<EDhApiRendererMode, Boolean>
{
@Override public EDhApiRendererMode convertToCoreType(Boolean renderingEnabled)
{
return renderingEnabled ? EDhApiRendererMode.DEFAULT : EDhApiRendererMode.DISABLED;
}
@Override
public EDhApiRendererMode convertToCoreType(Boolean renderingEnabled)
{ return renderingEnabled ? EDhApiRendererMode.DEFAULT : EDhApiRendererMode.DISABLED; }
@Override public Boolean convertToApiType(EDhApiRendererMode renderingMode)
{
return renderingMode == EDhApiRendererMode.DEFAULT;
}
@Override
public Boolean convertToApiType(EDhApiRendererMode renderingMode)
{ return renderingMode == EDhApiRendererMode.DEFAULT; }
}
@@ -0,0 +1,125 @@
/*
* 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.core.config.eventHandlers;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
public class IgnoredDimensionCsvHandler extends DhApiBeforeRenderEvent implements IConfigListener
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static IgnoredDimensionCsvHandler INSTANCE = new IgnoredDimensionCsvHandler();
private String[] dimensionNames = null;
//=============//
// constructor //
//=============//
/** private since we only ever need one handler at a time */
private IgnoredDimensionCsvHandler() { }
//=================//
// config handling //
//=================//
@Override
public void onConfigValueSet()
{
String ignoredDimensionCsvString = Config.Client.Advanced.Graphics.Experimental.ignoredDimensionCsv.get();
if (ignoredDimensionCsvString == null
|| ignoredDimensionCsvString.isEmpty())
{
LOGGER.info("Dimension ignoring disabled, DH will render all dimensions.");
this.dimensionNames = null;
}
else
{
try
{
this.dimensionNames = ignoredDimensionCsvString.split(",");
LOGGER.info("DH set to ignore dimensions: ["+ StringUtil.join(", ", this.dimensionNames)+"].");
}
catch (Exception e)
{
LOGGER.error("Failed to separate ignored dimensions from CSV string, error: ["+e.getMessage()+"].", e);
this.dimensionNames = null;
}
}
}
//===================//
// external handling //
//===================//
@Override
public void beforeRender(DhApiCancelableEventParam<DhApiRenderParam> event)
{
String dimName = event.value.clientLevelWrapper.getDimensionName();
if (IgnoredDimensionCsvHandler.INSTANCE.dimensionNameShouldBeIgnored(dimName))
{
event.cancelEvent();
Config.Client.Advanced.Graphics.Fog.enableVanillaFog.setApiValue(true);
Config.Client.Advanced.Graphics.Quality.vanillaFadeMode.setApiValue(EDhApiMcRenderingFadeMode.NONE);
}
else
{
Config.Client.Advanced.Graphics.Fog.enableVanillaFog.setApiValue(null);
Config.Client.Advanced.Graphics.Quality.vanillaFadeMode.setApiValue(null);
}
}
public boolean dimensionNameShouldBeIgnored(String dimName)
{
if (this.dimensionNames == null
|| this.dimensionNames.length == 0)
{
return false;
}
for (int i = 0; i < this.dimensionNames.length; i++)
{
String dimNameToIgnore = this.dimensionNames[i];
if (dimName.equalsIgnoreCase(dimNameToIgnore))
{
return true;
}
}
return false;
}
}
@@ -35,8 +35,17 @@ public class QuickRenderToggleConfigEventHandler
/** private since we only ever need one handler at a time */
private QuickRenderToggleConfigEventHandler()
{
this.quickRenderChangeListener = new ConfigChangeListener<>(Config.Client.quickEnableRendering, (val) -> { Config.Client.Advanced.Debugging.rendererMode.set(Config.Client.quickEnableRendering.get() ? EDhApiRendererMode.DEFAULT : EDhApiRendererMode.DISABLED); });
this.rendererModeChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Debugging.rendererMode, (val) -> { Config.Client.quickEnableRendering.set(Config.Client.Advanced.Debugging.rendererMode.get() != EDhApiRendererMode.DISABLED); });
this.quickRenderChangeListener = new ConfigChangeListener<>(Config.Client.quickEnableRendering,
(val) -> {
Config.Client.Advanced.Debugging.rendererMode.set(Config.Client.quickEnableRendering.get()
? EDhApiRendererMode.DEFAULT
: EDhApiRendererMode.DISABLED);
});
this.rendererModeChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Debugging.rendererMode,
(val) -> {
Config.Client.quickEnableRendering.set(
Config.Client.Advanced.Debugging.rendererMode.get() != EDhApiRendererMode.DISABLED);
});
}
/**
@@ -45,7 +54,8 @@ public class QuickRenderToggleConfigEventHandler
*/
public void setUiOnlyConfigValues()
{
Config.Client.quickEnableRendering.set(Config.Client.Advanced.Debugging.rendererMode.get() != EDhApiRendererMode.DISABLED);
boolean enableRendering = Config.Client.Advanced.Debugging.rendererMode.get() != EDhApiRendererMode.DISABLED;
Config.Client.quickEnableRendering.set(enableRendering);
}
}
@@ -22,13 +22,77 @@ package com.seibel.distanthorizons.core.config.eventHandlers;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
import com.seibel.distanthorizons.core.util.TimerUtil;
import java.util.Timer;
import java.util.TimerTask;
public class ReloadLodsConfigEventHandler implements IConfigListener
{
public static ReloadLodsConfigEventHandler INSTANCE = new ReloadLodsConfigEventHandler();
/**
* should be used for user facing UI options
* this allows the user a second to click through options before they're applied
*/
public static ReloadLodsConfigEventHandler DELAYED_INSTANCE = new ReloadLodsConfigEventHandler(2_000L);
/** should be used for debug options so their change can be seen instantly */
public static ReloadLodsConfigEventHandler INSTANT_INSTANCE = new ReloadLodsConfigEventHandler(0);
/** how long to wait in milliseconds before applying the config changes */
private final long timeoutInMs;
private Timer cacheClearingTimer;
//=============//
// constructor //
//=============//
public ReloadLodsConfigEventHandler(long timeoutInMs)
{
this.timeoutInMs = timeoutInMs;
}
//========//
// events //
//========//
@Override
public void onConfigValueSet()
{
if (this.timeoutInMs > 0)
{
this.refreshRenderDataAfterTimeout();
}
else
{
clearRenderDataCache();
}
}
/** Calling this method multiple times will reset the timer */
private synchronized void refreshRenderDataAfterTimeout() // synchronized to prevent potential threading issues when adding/removing the timer
{
// stop the previous timer if one exists
if (this.cacheClearingTimer != null)
{
this.cacheClearingTimer.cancel();
}
// create a new timer task
TimerTask timerTask = new TimerTask()
{
public void run()
{
clearRenderDataCache();
}
};
this.cacheClearingTimer = TimerUtil.CreateTimer("RenderCacheClearConfigTimer");
this.cacheClearingTimer.schedule(timerTask, this.timeoutInMs);
}
private static void clearRenderDataCache()
{
IDhApiRenderProxy renderProxy = DhApi.Delayed.renderProxy;
if (renderProxy != null)
@@ -37,4 +101,5 @@ public class ReloadLodsConfigEventHandler implements IConfigListener
}
}
}
@@ -0,0 +1,69 @@
/*
* 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.core.config.eventHandlers;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
public class RenderBlockCacheCsvHandler implements IConfigListener
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static RenderBlockCacheCsvHandler INSTANCE = new RenderBlockCacheCsvHandler();
//=============//
// constructor //
//=============//
/** private since we only ever need one handler at a time */
private RenderBlockCacheCsvHandler() { }
//=================//
// config handling //
//=================//
@Override
public void onConfigValueSet()
{
IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
if (wrapperFactory != null)
{
wrapperFactory.resetCachedIgnoredBlocksSets();
DhApi.Delayed.renderProxy.clearRenderDataCache();
}
}
}
@@ -1,114 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.config.eventHandlers;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.config.*;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.util.TimerUtil;
import java.util.Timer;
import java.util.TimerTask;
/**
* Listens to the config and will automatically
* clear the current render cache if certain settings are changed. <br> <br>
*
* Note: if additional settings should clear the render cache, add those to this listener, don't create a new listener
*/
public class RenderCacheConfigEventHandler
{
private static RenderCacheConfigEventHandler INSTANCE;
// previous values used to check if a watched setting was actually modified
private final ConfigChangeListener<EDhApiMaxHorizontalResolution> horizontalResolutionChangeListener;
private final ConfigChangeListener<EDhApiVerticalQuality> verticalQualityChangeListener;
private final ConfigChangeListener<EDhApiTransparency> transparencyChangeListener;
private final ConfigChangeListener<EDhApiBlocksToAvoid> blocksToIgnoreChangeListener;
private final ConfigChangeListener<Boolean> tintWithAvoidedBlocksChangeListener;
private final ConfigChangeListener<Double> brightnessMultiplierChangeListener;
private final ConfigChangeListener<Double> saturationMultiplierChangeListener;
private final ConfigChangeListener<EDhApiLodShading> lodShadingChangeListener;
private final ConfigChangeListener<EDhApiGrassSideRendering> grassSideChangeListener;
private final ConfigChangeListener<EDhApiDebugRendering> debugRenderingChangeListener;
/** how long to wait in milliseconds before applying the config changes */
private static final long TIMEOUT_IN_MS = 4_000L;
private Timer cacheClearingTimer;
public static RenderCacheConfigEventHandler getInstance()
{
if (INSTANCE == null)
{
INSTANCE = new RenderCacheConfigEventHandler();
}
return INSTANCE;
}
/** private since we only ever need one handler at a time */
private RenderCacheConfigEventHandler()
{
this.horizontalResolutionChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution, (newValue) -> this.refreshRenderDataAfterTimeout());
this.verticalQualityChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.verticalQuality, (newValue) -> this.refreshRenderDataAfterTimeout());
this.transparencyChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.transparency, (newValue) -> this.refreshRenderDataAfterTimeout());
this.blocksToIgnoreChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.blocksToIgnore, (newValue) -> this.refreshRenderDataAfterTimeout());
this.tintWithAvoidedBlocksChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks, (newValue) -> this.refreshRenderDataAfterTimeout());
this.brightnessMultiplierChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.brightnessMultiplier, (newValue) -> this.refreshRenderDataAfterTimeout());
this.saturationMultiplierChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.saturationMultiplier, (newValue) -> this.refreshRenderDataAfterTimeout());
this.lodShadingChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.lodShading, (newValue) -> this.refreshRenderDataAfterTimeout());
this.grassSideChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.grassSideRendering, (newValue) -> this.refreshRenderDataAfterTimeout());
this.debugRenderingChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Debugging.debugRendering, (newValue) -> this.refreshRenderDataAfterTimeout());
}
/** Calling this method multiple times will reset the timer */
private void refreshRenderDataAfterTimeout()
{
// stop the previous timer if one exists
if (this.cacheClearingTimer != null)
{
this.cacheClearingTimer.cancel();
}
// create a new timer task
TimerTask timerTask = new TimerTask()
{
public void run()
{
DhApi.Delayed.renderProxy.clearRenderDataCache();
}
};
this.cacheClearingTimer = TimerUtil.CreateTimer("RenderCacheClearConfigTimer");
this.cacheClearingTimer.schedule(timerTask, TIMEOUT_IN_MS);
}
}
@@ -20,23 +20,22 @@
package com.seibel.distanthorizons.core.config.eventHandlers;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
/**
* handles enabling/disabling config validation when the
* {@link Config.Client.Advanced.Debugging#allowUnsafeValues} option
* is changed.
*/
public class UnsafeValuesConfigListener implements IConfigListener
{
public static UnsafeValuesConfigListener INSTANCE = new UnsafeValuesConfigListener();
@Override
public void onConfigValueSet()
{
Config.Client.Advanced.Debugging.allowUnsafeValues.configBase.disableMinMax =
Config.Client.Advanced.Debugging.allowUnsafeValues.get();
}
{ ConfigHandler.INSTANCE.runMinMaxValidation = !Config.Client.Advanced.Debugging.allowUnsafeValues.get(); }
@Override
public void onUiModify()
{
}
}
@@ -35,7 +35,7 @@ public class WorldCurvatureConfigEventHandler implements IConfigListener
{
public static WorldCurvatureConfigEventHandler INSTANCE = new WorldCurvatureConfigEventHandler();
private static final int MIN_VALID_CURVE_VALUE = 50;
public static final int MIN_VALID_CURVE_VALUE = 50;
/** private since we only ever need one handler at a time */
@@ -52,6 +52,11 @@ public class WorldCurvatureConfigEventHandler implements IConfigListener
// shouldn't update the UI, otherwise we may end up fighting the user
Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.set(MIN_VALID_CURVE_VALUE);
}
else if (curveRatio < 0 && curveRatio > -MIN_VALID_CURVE_VALUE)
{
// same as above, but in the negative direction
Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.set(-MIN_VALID_CURVE_VALUE);
}
}
@@ -19,30 +19,30 @@
package com.seibel.distanthorizons.core.config.eventHandlers.presets;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.ConfigPresetOptions;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<?>> implements IConfigListener
{
private static final Logger LOGGER = LogManager.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final long MS_DELAY_BEFORE_APPLYING_PRESET = 3_000;
@Nullable
private static IConfigGui configGui = SingletonInjector.INSTANCE.get(IConfigGui.class);
private static boolean guiListenersAdded = false;
private static final IConfigGui CONFIG_GUI = SingletonInjector.INSTANCE.get(IConfigGui.class);
protected final ArrayList<ConfigEntryWithPresetOptions<TPresetEnum, ?>> configList = new ArrayList<>();
protected final ArrayList<ConfigPresetOptions<TPresetEnum, ?>> configList = new ArrayList<>();
/** this timer is used so each preset isn't applied while a user is clicking through the config options */
protected Timer applyPresetTimer = null;
/** the enum to apply after the timer expires or the UI screen changes. */
@@ -59,9 +59,9 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
public AbstractPresetConfigEventHandler()
{
// don't update the UI when running on a server
if (configGui != null)
if (CONFIG_GUI != null)
{
configGui.addOnScreenChangeListener(this::onConfigUiClosed);
CONFIG_GUI.addOnScreenChangeListener(this::onConfigUiClosed);
}
}
@@ -90,7 +90,7 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
public void onConfigValueSet()
{
// don't try modifying the config before it's been loaded from file
if (!ConfigBase.INSTANCE.isLoaded)
if (!ConfigHandler.INSTANCE.isLoaded)
{
return;
}
@@ -140,11 +140,11 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
LOGGER.info("changing preset to: " + newPresetEnum);
LOGGER.debug("changing preset to: [" + newPresetEnum + "].");
this.changingPreset = true;
// update the controlled config values
for (ConfigEntryWithPresetOptions<TPresetEnum, ?> configEntry : this.configList)
for (ConfigPresetOptions<TPresetEnum, ?> configEntry : this.configList)
{
configEntry.updateConfigEntry(newPresetEnum);
}
@@ -152,7 +152,7 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
this.setUiOnlyConfigValues();
this.changingPreset = false;
LOGGER.info("preset active: " + newPresetEnum);
LOGGER.debug("preset active: [" + newPresetEnum + "].");
}
/**
@@ -200,7 +200,7 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
// remove any quick options that aren't possible with the currently selected options
for (ConfigEntryWithPresetOptions<TPresetEnum, ?> configEntry : this.configList)
for (ConfigPresetOptions<TPresetEnum, ?> configEntry : this.configList)
{
HashSet<TPresetEnum> optionPresetSet = configEntry.getPossibleQualitiesFromCurrentOptionValue();
possiblePresetSet.retainAll(optionPresetSet);
@@ -230,7 +230,7 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
// abstract methods //
//==================//
protected abstract IConfigEntry<TPresetEnum> getPresetConfigEntry();
protected abstract AbstractConfigBase<TPresetEnum> getPresetConfigEntry();
protected abstract List<TPresetEnum> getPresetEnumList();
protected abstract TPresetEnum getCustomPresetEnum();
@@ -1,62 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.config.eventHandlers.presets;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorProgressDisplayLocation;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
public class QuickShowWorldGenProgressConfigEventHandler
{
public static QuickShowWorldGenProgressConfigEventHandler INSTANCE = new QuickShowWorldGenProgressConfigEventHandler();
private final ConfigChangeListener<Boolean> quickChangeListener;
private final ConfigChangeListener<EDhApiDistantGeneratorProgressDisplayLocation> fullChangeListener;
/** private since we only ever need one handler at a time */
private QuickShowWorldGenProgressConfigEventHandler()
{
this.quickChangeListener = new ConfigChangeListener<>(Config.Client.quickShowWorldGenProgress,
(val) ->
{
Config.Common.WorldGenerator.showGenerationProgress.set(Config.Client.quickShowWorldGenProgress.get()
? Config.Common.WorldGenerator.showGenerationProgress.getDefaultValue()
: EDhApiDistantGeneratorProgressDisplayLocation.DISABLED);
});
this.fullChangeListener = new ConfigChangeListener<>(Config.Common.WorldGenerator.showGenerationProgress,
(val) ->
{
Config.Client.quickShowWorldGenProgress.set(Config.Common.WorldGenerator.showGenerationProgress.get() != EDhApiDistantGeneratorProgressDisplayLocation.DISABLED);
});
}
/**
* Set the UI only config based on what is set in the file. <br>
* This should only be called once.
*/
public void setUiOnlyConfigValues()
{
Config.Client.quickEnableRendering.set(Config.Common.WorldGenerator.showGenerationProgress.get() != EDhApiDistantGeneratorProgressDisplayLocation.DISABLED);
}
}
@@ -26,11 +26,11 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiVerticalQuality;
import com.seibel.distanthorizons.api.enums.config.quickOptions.EDhApiQualityPreset;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions;
import com.seibel.distanthorizons.core.config.ConfigPresetOptions;
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.*;
@@ -39,10 +39,10 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
{
public static final RenderQualityPresetConfigEventHandler INSTANCE = new RenderQualityPresetConfigEventHandler();
private static final Logger LOGGER = LogManager.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiMaxHorizontalResolution> drawResolution = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution,
private final ConfigPresetOptions<EDhApiQualityPreset, EDhApiMaxHorizontalResolution> drawResolution = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution,
new HashMap<EDhApiQualityPreset, EDhApiMaxHorizontalResolution>()
{{
this.put(EDhApiQualityPreset.MINIMUM, EDhApiMaxHorizontalResolution.TWO_BLOCKS);
@@ -51,7 +51,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.HIGH, EDhApiMaxHorizontalResolution.BLOCK);
this.put(EDhApiQualityPreset.EXTREME, EDhApiMaxHorizontalResolution.BLOCK);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiVerticalQuality> verticalQuality = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.verticalQuality,
private final ConfigPresetOptions<EDhApiQualityPreset, EDhApiVerticalQuality> verticalQuality = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Quality.verticalQuality,
new HashMap<EDhApiQualityPreset, EDhApiVerticalQuality>()
{{
this.put(EDhApiQualityPreset.MINIMUM, EDhApiVerticalQuality.HEIGHT_MAP);
@@ -60,7 +60,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.HIGH, EDhApiVerticalQuality.HIGH);
this.put(EDhApiQualityPreset.EXTREME, EDhApiVerticalQuality.EXTREME);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiHorizontalQuality> horizontalQuality = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.horizontalQuality,
private final ConfigPresetOptions<EDhApiQualityPreset, EDhApiHorizontalQuality> horizontalQuality = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Quality.horizontalQuality,
new HashMap<EDhApiQualityPreset, EDhApiHorizontalQuality>()
{{
this.put(EDhApiQualityPreset.MINIMUM, EDhApiHorizontalQuality.LOWEST);
@@ -69,7 +69,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.HIGH, EDhApiHorizontalQuality.HIGH);
this.put(EDhApiQualityPreset.EXTREME, EDhApiHorizontalQuality.EXTREME);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiTransparency> transparency = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.transparency,
private final ConfigPresetOptions<EDhApiQualityPreset, EDhApiTransparency> transparency = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Quality.transparency,
new HashMap<EDhApiQualityPreset, EDhApiTransparency>()
{{
this.put(EDhApiQualityPreset.MINIMUM, EDhApiTransparency.DISABLED);
@@ -78,7 +78,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.HIGH, EDhApiTransparency.COMPLETE);
this.put(EDhApiQualityPreset.EXTREME, EDhApiTransparency.COMPLETE);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, Boolean> ssaoEnabled = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Ssao.enableSsao,
private final ConfigPresetOptions<EDhApiQualityPreset, Boolean> ssaoEnabled = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.enableSsao,
new HashMap<EDhApiQualityPreset, Boolean>()
{{
this.put(EDhApiQualityPreset.MINIMUM, false);
@@ -87,7 +87,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.HIGH, true);
this.put(EDhApiQualityPreset.EXTREME, true);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiMcRenderingFadeMode> vanillaFade = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.vanillaFadeMode,
private final ConfigPresetOptions<EDhApiQualityPreset, EDhApiMcRenderingFadeMode> vanillaFade = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Quality.vanillaFadeMode,
new HashMap<EDhApiQualityPreset, EDhApiMcRenderingFadeMode>()
{{
this.put(EDhApiQualityPreset.MINIMUM, EDhApiMcRenderingFadeMode.NONE);
@@ -96,7 +96,16 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.HIGH, EDhApiMcRenderingFadeMode.DOUBLE_PASS);
this.put(EDhApiQualityPreset.EXTREME, EDhApiMcRenderingFadeMode.DOUBLE_PASS);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, Boolean> dhDither = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.ditherDhFade,
private final ConfigPresetOptions<EDhApiQualityPreset, Boolean> dhFadeFarClipPlane = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Quality.dhFadeFarClipPlane,
new HashMap<EDhApiQualityPreset, Boolean>()
{{
this.put(EDhApiQualityPreset.MINIMUM, false);
this.put(EDhApiQualityPreset.LOW, false);
this.put(EDhApiQualityPreset.MEDIUM, true);
this.put(EDhApiQualityPreset.HIGH, true);
this.put(EDhApiQualityPreset.EXTREME, true);
}});
private final ConfigPresetOptions<EDhApiQualityPreset, Boolean> dhDither = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Quality.ditherDhFade,
new HashMap<EDhApiQualityPreset, Boolean>()
{{
this.put(EDhApiQualityPreset.MINIMUM, false);
@@ -105,6 +114,24 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.HIGH, true);
this.put(EDhApiQualityPreset.EXTREME, true);
}});
private final ConfigPresetOptions<EDhApiQualityPreset, Boolean> caveCulling = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Culling.enableCaveCulling,
new HashMap<EDhApiQualityPreset, Boolean>()
{{
this.put(EDhApiQualityPreset.MINIMUM, true);
this.put(EDhApiQualityPreset.LOW, true);
this.put(EDhApiQualityPreset.MEDIUM, true);
this.put(EDhApiQualityPreset.HIGH, false);
this.put(EDhApiQualityPreset.EXTREME, false);
}});
private final ConfigPresetOptions<EDhApiQualityPreset, Integer> biomeBlending = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Quality.lodBiomeBlending,
new HashMap<EDhApiQualityPreset, Integer>()
{{
this.put(EDhApiQualityPreset.MINIMUM, 0);
this.put(EDhApiQualityPreset.LOW, 1);
this.put(EDhApiQualityPreset.MEDIUM, 3);
this.put(EDhApiQualityPreset.HIGH, 3);
this.put(EDhApiQualityPreset.EXTREME, 3);
}});
@@ -121,11 +148,14 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.configList.add(this.horizontalQuality);
this.configList.add(this.transparency);
this.configList.add(this.ssaoEnabled);
this.configList.add(this.dhFadeFarClipPlane);
this.configList.add(this.vanillaFade);
this.configList.add(this.dhDither);
this.configList.add(this.caveCulling);
this.configList.add(this.biomeBlending);
for (ConfigEntryWithPresetOptions<EDhApiQualityPreset, ?> config : this.configList)
for (ConfigPresetOptions<EDhApiQualityPreset, ?> config : this.configList)
{
// ignore try-using, the listener should only ever be added once and should never be removed
new ConfigChangeListener<>(config.configEntry, (val) -> { this.onConfigValueChanged(); });
@@ -139,7 +169,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
//==============//
@Override
protected IConfigEntry<EDhApiQualityPreset> getPresetConfigEntry() { return Config.Client.qualityPresetSetting; }
protected AbstractConfigBase<EDhApiQualityPreset> getPresetConfigEntry() { return Config.Client.qualityPresetSetting; }
@Override
protected List<EDhApiQualityPreset> getPresetEnumList() { return Arrays.asList(EDhApiQualityPreset.values()); }
@@ -22,11 +22,12 @@ package com.seibel.distanthorizons.core.config.eventHandlers.presets;
import com.seibel.distanthorizons.api.enums.config.quickOptions.EDhApiThreadPreset;
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions;
import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry;
import com.seibel.distanthorizons.core.config.ConfigPresetOptions;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.coreapi.util.MathUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.Arrays;
import java.util.HashMap;
@@ -37,11 +38,11 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
{
public static final ThreadPresetConfigEventHandler INSTANCE = new ThreadPresetConfigEventHandler();
private static final Logger LOGGER = LogManager.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static int getDefaultThreadCount() { return getThreadCountByPercent(0.5); }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Integer> threadCount = new ConfigEntryWithPresetOptions<>(Config.Common.MultiThreading.numberOfThreads,
private final ConfigPresetOptions<EDhApiThreadPreset, Integer> threadCount = new ConfigPresetOptions<>(Config.Common.MultiThreading.numberOfThreads,
new HashMap<EDhApiThreadPreset, Integer>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, getThreadCountByPercent(0.1));
@@ -51,7 +52,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
}});
public static double getDefaultRunTimeRatio() { return 1.0; }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Double> threadRunTime = new ConfigEntryWithPresetOptions<>(Config.Common.MultiThreading.threadRunTimeRatio,
private final ConfigPresetOptions<EDhApiThreadPreset, Double> threadRunTime = new ConfigPresetOptions<>(Config.Common.MultiThreading.threadRunTimeRatio,
new HashMap<EDhApiThreadPreset, Double>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.5);
@@ -74,7 +75,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
this.configList.add(this.threadCount);
this.configList.add(this.threadRunTime);
for (ConfigEntryWithPresetOptions<EDhApiThreadPreset, ?> config : this.configList)
for (ConfigPresetOptions<EDhApiThreadPreset, ?> config : this.configList)
{
// ignore try-using, the listeners should only ever be added once and should never be removed
new ConfigChangeListener<>(config.configEntry, (val) -> { this.onConfigValueChanged(); });
@@ -119,7 +120,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
//==============//
@Override
protected IConfigEntry<EDhApiThreadPreset> getPresetConfigEntry() { return Config.Client.threadPresetSetting; }
protected AbstractConfigBase<EDhApiThreadPreset> getPresetConfigEntry() { return Config.Client.threadPresetSetting; }
@Override
protected List<EDhApiThreadPreset> getPresetEnumList() { return Arrays.asList(EDhApiThreadPreset.values()); }
@@ -20,16 +20,18 @@
package com.seibel.distanthorizons.core.config.file;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.types.AbstractConfigType;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.core.jar.EPlatform;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -41,17 +43,13 @@ import java.util.concurrent.locks.ReentrantLock;
* @author coolGi
* @version 2023-8-26
*/
public class ConfigFileHandling
public class ConfigFileHandler
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public final ConfigBase configBase;
public final Path configPath;
private final Logger logger;
/** This is the object for night-config */
private final CommentedFileConfig nightConfig;
@@ -64,17 +62,23 @@ public class ConfigFileHandling
// constructor //
//=============//
public ConfigFileHandling(ConfigBase configBase, Path configPath)
public ConfigFileHandler(Path configPath)
{
this.logger = LogManager.getLogger(this.getClass().getSimpleName() + ", " + configBase.modID);
this.configBase = configBase;
this.configPath = configPath;
this.nightConfig = CommentedFileConfig.builder(this.configPath.toFile()).build();
this.nightConfig = CommentedFileConfig
.builder(this.configPath.toFile())
// sync is needed so file reading/writing only happens during locked sections,
// otherwise some GUI changes may be lost when changing screens
.sync()
.build();
}
//====================//
// entire config file //
//====================//
/** Saves the entire config to the file */
public void saveToFile() { this.saveToFile(this.nightConfig); }
@@ -96,7 +100,7 @@ public class ConfigFileHandling
this.loadNightConfig(nightConfig);
for (AbstractConfigType<?, ?> entry : this.configBase.entries)
for (AbstractConfigBase<?> entry : ConfigHandler.INSTANCE.configBaseList)
{
if (ConfigEntry.class.isAssignableFrom(entry.getClass()))
{
@@ -134,7 +138,7 @@ public class ConfigFileHandling
{
this.readWriteLock.lock();
int currentCfgVersion = this.configBase.configVersion;
int currentCfgVersion = ModInfo.CONFIG_FILE_VERSION;
try
{
// Dont load the real `this.nightConfig`, instead create a tempoary one
@@ -146,29 +150,29 @@ public class ConfigFileHandling
}
catch (Exception ignored) { }
if (currentCfgVersion == this.configBase.configVersion)
if (currentCfgVersion == ModInfo.CONFIG_FILE_VERSION)
{
// handle normally
}
else if (currentCfgVersion > this.configBase.configVersion)
else if (currentCfgVersion > ModInfo.CONFIG_FILE_VERSION)
{
this.logger.warn("Found config version [" + currentCfgVersion + "] which is newer than current mods config version of [" + this.configBase.configVersion + "]. You may have downgraded the mod and items may have been moved, you have been warned");
LOGGER.warn("Found config version [" + currentCfgVersion + "] which is newer than current mods config version of [" + ModInfo.CONFIG_FILE_VERSION + "]. You may have downgraded the mod and items may have been moved, you have been warned");
}
else // if (currentCfgVersion < configBase.configVersion)
{
this.logger.warn(this.configBase.modName + " config is of an older version, currently there is no config updater... so resetting config");
LOGGER.warn(ModInfo.NAME + " config is of an older version, currently there is no config updater... so resetting config");
try
{
Files.delete(this.configPath);
}
catch (Exception e)
{
this.logger.error(e);
LOGGER.error("Unable to delete outdated config file at: ["+this.configPath+"], error: ["+e.getMessage()+"].", e);
}
}
this.loadFromFile(this.nightConfig);
this.nightConfig.set("_version", this.configBase.configVersion);
this.nightConfig.set("_version", ModInfo.CONFIG_FILE_VERSION);
}
finally
{
@@ -194,7 +198,7 @@ public class ConfigFileHandling
// Load all the entries
for (AbstractConfigType<?, ?> entry : this.configBase.entries)
for (AbstractConfigBase<?> entry : ConfigHandler.INSTANCE.configBaseList)
{
if (ConfigEntry.class.isAssignableFrom(entry.getClass())
&& entry.getAppearance().showInFile)
@@ -218,6 +222,9 @@ public class ConfigFileHandling
//=======================//
// single config entries //
//=======================//
// Save an entry when only given the entry
public void saveEntry(ConfigEntry<?> entry)
@@ -234,11 +241,11 @@ public class ConfigFileHandling
}
else if (entry.getTrueValue() == null)
{
// TODO when can this happen?
throw new IllegalArgumentException("Entry [" + entry.getNameWCategory() + "] is null, this may be a problem with [" + this.configBase.modName + "]. Please contact the authors.");
// shouldn't happen, but just in case
throw new IllegalArgumentException("ConfigEntry [" + entry.getNameAndCategory() + "] is null, how did this happen?");
}
workConfig.set(entry.getNameWCategory(), ConfigTypeConverters.attemptToConvertToString(entry.getType(), entry.getTrueValue()));
workConfig.set(entry.getNameAndCategory(), ConfigTypeConverters.attemptToConvertToString(entry.getType(), entry.getTrueValue()));
}
/** Loads an entry when only given the entry */
@@ -248,9 +255,11 @@ public class ConfigFileHandling
public <T> void loadEntry(ConfigEntry<T> entry, CommentedFileConfig nightConfig)
{
if (!entry.getAppearance().showInFile)
{
return;
}
if (!nightConfig.contains(entry.getNameWCategory()))
if (!nightConfig.contains(entry.getNameAndCategory()))
{
this.saveEntry(entry, nightConfig);
return;
@@ -261,34 +270,33 @@ public class ConfigFileHandling
{
if (entry.getType().isEnum())
{
entry.pureSet((T) (nightConfig.getEnum(entry.getNameWCategory(), (Class<? extends Enum>) entry.getType())));
entry.setWithoutFiringEvents((T) (nightConfig.getEnum(entry.getNameAndCategory(), (Class<? extends Enum>) entry.getType())));
return;
}
// try converting the value if necessary
Class<?> expectedValueClass = entry.getType();
Object value = nightConfig.get(entry.getNameWCategory());
Object value = nightConfig.get(entry.getNameAndCategory());
Object convertedValue = ConfigTypeConverters.attemptToConvertFromString(expectedValueClass, value);
if (!convertedValue.getClass().equals(expectedValueClass))
{
this.logger.error("Unable to convert config value ["+value+"] from ["+(value != null ? value.getClass() : "NULL")+"] to ["+expectedValueClass+"] for config ["+entry.name+"], " +
LOGGER.error("Unable to convert config value ["+value+"] from ["+(value != null ? value.getClass() : "NULL")+"] to ["+expectedValueClass+"] for config ["+entry.name+"], " +
"the default config value will be used instead ["+entry.getDefaultValue()+"]. " +
"Make sure a converter is defined in ["+ConfigTypeConverters.class.getSimpleName()+"].");
convertedValue = entry.getDefaultValue();
}
entry.pureSet((T) convertedValue);
entry.setWithoutFiringEvents((T) convertedValue);
if (entry.getTrueValue() == null)
{
this.logger.warn("Entry [" + entry.getNameWCategory() + "] returned as null from the config. Using default value.");
entry.pureSet(entry.getDefaultValue());
LOGGER.warn("BlockBiomeWrapperPair [" + entry.getNameAndCategory() + "] returned as null from the config. Using default value.");
entry.setWithoutFiringEvents(entry.getDefaultValue());
}
}
catch (Exception e)
{
// e.printStackTrace();
this.logger.warn("Entry [" + entry.getNameWCategory() + "] had an invalid value when loading the config. Using default value.");
entry.pureSet(entry.getDefaultValue());
LOGGER.warn("BlockBiomeWrapperPair [" + entry.getNameAndCategory() + "] had an invalid value when loading the config. Using default value.");
entry.setWithoutFiringEvents(entry.getDefaultValue());
}
}
@@ -309,22 +317,21 @@ public class ConfigFileHandling
// the new line makes it easier to read and separate configs
// the space makes sure the first word of a comment isn't directly in line with the "#"
comment = "\n " + comment;
nightConfig.setComment(entry.getNameWCategory(), comment);
nightConfig.setComment(entry.getNameAndCategory(), comment);
}
//=============//
// nightconfig //
//=============//
/**
* Uses {@link ConfigFileHandling#nightConfig} to do {@link CommentedFileConfig#load()} but with error checking
* Uses {@link ConfigFileHandler#nightConfig} to do {@link CommentedFileConfig#load()} but with error checking
*
* @apiNote This overwrites any value currently stored in the config
*/
public void loadNightConfig()
{
loadNightConfig(this.nightConfig);
}
public void loadNightConfig() { this.loadNightConfig(this.nightConfig); }
/**
* Does {@link CommentedFileConfig#load()} but with error checking
*
@@ -342,23 +349,30 @@ public class ConfigFileHandling
}
catch (Exception e)
{
this.logger.warn("Loading file failed because of this expectation:\n" + e);
LOGGER.warn("Loading file failed because of this expectation:\n" + e);
reCreateFile(this.configPath);
nightConfig.load();
}
}
catch (Exception ex)
catch (Exception e)
{
System.out.println("Creating file failed");
this.logger.error(ex);
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Loading file and resetting config file failed at path [" + configPath + "]. Please check the file is ok and you have the permissions", ex);
LOGGER.error("File creation failed at ["+this.configPath+"], error: ["+e.getMessage()+"].", e);
// delayed MC getter since this object may be created before
// the singleton has been bound
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
mc.crashMinecraft("Loading file and resetting config file failed at path [" + this.configPath + "]. Please check the file is ok and you have the permissions", e);
}
}
//===============//
// file handling //
//===============//
public static void reCreateFile(Path path)
{
try

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