Compare commits

..

415 Commits

Author SHA1 Message Date
s809 b2f98bdf63 Add tests 2024-09-26 17:39:05 +05:00
James Seibel fd0a4c55ac Change FullDataSourceDTO checksum/hash logic to hopefully be more consistent 2024-09-25 21:57:49 -05:00
James Seibel b0aa211464 Fix default generic rendering instanced mode 2024-09-25 18:44:39 -05:00
James Seibel 14e7918ea0 fix readme logo filepaths 2024-09-22 17:25:14 -05:00
James Seibel 524ea77ce5 Update readme logos 2024-09-22 17:23:35 -05:00
James Seibel 9f763bcd6a Add new logos 2024-09-22 17:18:33 -05:00
James Seibel 166875283c Fix cloud debug colors 2024-09-22 16:53:27 -05:00
James Seibel abb264bdf0 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2024-09-22 16:32:21 -05:00
James Seibel 70aa5724a9 Closes !68 (Allow toggling generic obj instanced rendering via config) 2024-09-22 16:32:10 -05:00
James Seibel 520d37f410 Add generic obj cube count to F3 menu 2024-09-22 16:31:06 -05:00
s809 29c2756e75 Merge branch 'feature/lan-support' 2024-09-23 00:46:30 +05:00
s809 6c278ea3b1 LAN multiplayer kinda works 2024-09-23 00:02:31 +05:00
s809 b18460b825 Abstract away serverside parts of world & level 2024-09-22 03:28:53 +05:00
James Seibel d0b50d9633 Add Sqlite library validation to Initializer 2024-09-21 11:45:44 -05:00
James Seibel c051b3584f Replace "jdbc:sqlite" with a constant 2024-09-21 11:43:58 -05:00
James Seibel d4cad8f718 Fix config file handler corruption due to reading/writing concurrently 2024-09-20 07:29:32 -05:00
James Seibel f7bf05b62f Update network semaphore comments 2024-09-17 07:40:28 -05:00
s809 840b0a7fe2 Bump protocol version because of removed InvalidSectionPosException 2024-09-17 12:41:46 +05:00
s809 5369bf628a Fix naming of some things and comments 2024-09-17 09:42:22 +05:00
James Seibel 79d2269218 Fix very high file handler jobs and pool some render data sources 2024-09-16 20:35:57 -05:00
James Seibel f21c791269 fix comment 2024-09-15 21:15:57 -05:00
James Seibel 11e58eecda Up API version 3.0.1 -> 4.0.0 2024-09-15 20:36:10 -05:00
James Seibel fbf13833a0 Up version 2.2.2 -> 2.3.0 2024-09-15 20:35:54 -05:00
James Seibel 2528f4a725 Merge server side branch and refactor 2024-09-15 20:35:38 -05:00
James Seibel 6d6cbd8a44 add more getDimensinoName() calls and minor cleanup 2024-09-11 17:11:55 -05:00
James Seibel 2fe3c261b0 Add DhClientWorld F3 debug string prep 2024-09-11 17:05:57 -05:00
James Seibel 5086f40d03 Add commit info to F3 screen 2024-09-11 17:04:18 -05:00
James Seibel 7766c49cbd Add DhChunkPos Vec3d constructor 2024-09-11 07:41:56 -05:00
James Seibel c6d86cfa3b remove unneeded IDimensionTypeWrapper.getTeleportationScale() 2024-09-11 07:40:44 -05:00
James Seibel 935cfec3d4 Add network compression thread config and pools 2024-09-11 07:37:39 -05:00
James Seibel ce2e64dc7e ClientOnlySaveStructure rename getDhDataFoldersForDimension -> getDhDataFoldersForLevel 2024-09-11 07:31:24 -05:00
James Seibel 15774ffe2a Only attempt to set Java Swing headless on clients 2024-09-11 07:25:01 -05:00
James Seibel 218cb04696 Don't load the client level in shouldLodsRender()
This might cause issues with the Replay mod, but we'll see
2024-09-11 07:20:29 -05:00
James Seibel 45fc36543b Add ILevelWrapper.getDimensionName() 2024-09-11 07:14:21 -05:00
James Seibel 554bb89690 remove unused message classes 2024-09-10 21:46:47 -05:00
James Seibel 2aa048b0cb Don't send chat messages on dedicated servers for configBasedLogger 2024-09-10 21:46:16 -05:00
James Seibel 570619b114 Ignore config preset UI updating when on the sever 2024-09-10 21:17:50 -05:00
James Seibel fb3e47ec3f Add FullDataSourceRepo.getTimestampForPos() 2024-09-10 20:46:18 -05:00
James Seibel 0f27dd79d7 minor refactoring and style cleanup 2024-09-10 07:24:56 -05:00
James Seibel abe0e284aa Remove unneeded networking classes 2024-09-10 07:15:37 -05:00
James Seibel 7a97b9dcbf Fix low quality LODs not loading when flying in a new straight line 2024-09-09 07:41:12 -05:00
James Seibel 23c98e2253 Clean up LodRenderSection async loading logic 2024-09-09 07:37:08 -05:00
James Seibel 06cce40ac6 hide attempting to... warnings for closed databases 2024-09-09 07:36:19 -05:00
s809 7cd1a37914 Restore ordering of session config entries 2024-09-08 21:21:38 +05:00
s809 8ca2052748 Refactor session config 2024-09-08 19:05:42 +05:00
James Seibel ce4259d98f add brown mushrooms to the list of ignored blocks to fix swamp issues 2024-09-07 14:22:41 -05:00
James Seibel d96ba5ae54 Add faster sky light engine from Builderb0y
Closes !67
2024-09-07 12:07:48 -05:00
s809 576d0f5666 Fix crash on F3 when commit hash was failed to retrieve 2024-09-07 21:47:43 +05:00
s809 877588ebed Fix rare NPE when receiving LODs 2024-09-07 21:47:02 +05:00
James Seibel 3bee25053f Add missing LightingTestChunkWrapper methods 2024-09-06 21:55:48 -05:00
James Seibel a75d3ec5b0 Fix unit test compiling 2024-09-06 19:36:32 -05:00
James Seibel d5222ed20f Closes #805 (Hide "Distant Horizons overloaded")
Also update chunks closest to the player first
2024-09-06 18:22:57 -05:00
s809 875b5fffcc Fix client crashing 2024-09-05 23:45:04 +05:00
James Seibel d03a887620 Add BuilderB0y's getBlockState optimization 2024-09-05 07:50:18 -05:00
s809 6293cc8c27 Prefill levelKeyPrefix in new worlds 2024-09-05 14:22:00 +05:00
James Seibel 847cfa3ca9 Add current size to rolling average 2024-09-04 16:40:17 -05:00
s809 0361e5c69b Fix compilation 2024-09-04 22:53:02 +05:00
s809 654355c991 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside 2024-09-04 18:22:47 +05:00
James Seibel 4712a77d00 Up version number 2.2.1 -> 2.2.2-dev 2024-09-04 06:58:59 -05:00
James Seibel 57c5b2d5fc Up version 2.2.1-dev -> 2.2.1 and API 3.0.0 -> 3.0.1 2024-09-04 06:58:20 -05:00
s809 a2949b8124 Change some string & remove unused config item 2024-09-03 16:52:10 +05:00
James Seibel 50c5701836 Fix referencing unavailable GLFW methods for MC 1.18.2 and lower 2024-09-02 08:00:43 -05:00
s809 6fe0477ca7 Limit number of retries on request errors
Add a delay if rate limit is hit
2024-09-02 15:31:39 +05:00
James Seibel 18e075538d Fix LODs flashing when moving and improve LodQuadTree code 2024-09-01 21:10:22 -05:00
James Seibel b00e8a08e9 add annotations to QuadTree 2024-09-01 21:09:50 -05:00
James Seibel ac4ab11a74 Fix LodRenderSection and QuadNode toString methods 2024-09-01 21:09:17 -05:00
James Seibel c26631db57 Cull beacons based on X/Z distance instead of 3D distance 2024-09-01 17:28:08 -05:00
James Seibel 1daa06fff4 Disable instanced rendering on Mac when Sodium is present
Closes !793 (Generic Rendering crashes with Sodium on M1 Mac)
2024-09-01 17:02:43 -05:00
James Seibel f3ef6f25f4 Fix some beacon rendering/updating issues 2024-09-01 16:36:37 -05:00
James Seibel ec012d9fd6 Fix glass panes not affecting beacon colors 2024-09-01 15:04:24 -05:00
James Seibel fc90cf3377 add disableUnchangedChunkCheck config 2024-08-31 22:11:25 -05:00
James Seibel e1e42d1caf Fix cloud color not matching MC 2024-08-31 21:55:54 -05:00
James Seibel 95ce29e355 Update all pos objects to use getters to match with DhBlockPos
I'd prefer not to need getters/setters since it's cleaner to just call pos.x, but that doesn't allow for immutable/mutable distinction.
2024-08-31 20:50:22 -05:00
James Seibel 0fd818b077 Allow users to re-activate DH rendering if a rendering error is thrown 2024-08-31 20:20:51 -05:00
James Seibel ba59daf747 Add beacon nearby culling 2024-08-31 20:20:24 -05:00
James Seibel b7d94c2ed1 Fix RenderableBoxGroup not supporting clear() 2024-08-31 20:06:04 -05:00
James Seibel 7a057a8d53 fix Lod builder not showing the correct stacktrace in Intellij 2024-08-31 15:04:30 -05:00
James Seibel 49c6ab97a9 Remove unused threadpool 2024-08-31 15:04:12 -05:00
James Seibel ed0d80b37e Reduce deplayed LOD modified save from 2 sec -> 500 ms
This should make some LOD update operations appear faster
2024-08-31 15:01:37 -05:00
James Seibel 9768728c92 Add RollingAverage object 2024-08-31 14:58:08 -05:00
James Seibel 0c68544f2f Fix DhLightingEngine putting lights at relative (0,0) and add debug logic 2024-08-31 12:45:39 -05:00
s809 048b36f80d Add comment 2024-08-31 20:50:50 +05:00
s809 40f902e2f5 Delay loading first level when on server 2024-08-31 20:46:58 +05:00
James Seibel b1f154a0ea Add DhBlockPosMutable and make the original immutable
This is to prevent issues with some methods accidentally mutating shared positions
2024-08-30 07:35:59 -05:00
James Seibel 628c9b071f DhBlockPos cleanup 2024-08-29 20:05:26 -05:00
James Seibel ed39b6181f Fix DH beacon detection logic mutating input block pos
alternate title: Fix DH beacon detection logic breaking the lighting engine
2024-08-29 19:54:36 -05:00
James Seibel 1d6d712483 Fix LODs not updating in the nether or when blocks are changed underground 2024-08-29 07:32:55 -05:00
s809 62ddb46674 Restore LOD fetching by distance 2024-08-29 17:09:08 +05:00
James Seibel 73c4f0ffcd Add a quick DH cloud UI config 2024-08-28 07:16:20 -05:00
s809 375cd44cbd Do not start generator until server responds with config 2024-08-27 16:35:22 +05:00
s809 ce057525d3 Show incompatible protocol version in F3 2024-08-27 16:02:15 +05:00
s809 01c879951c Fix sync on login 2024-08-26 16:53:59 +05:00
s809 4f19f05f3b Change naming of rate limit related items 2024-08-26 00:39:28 +05:00
s809 47f68c7ed3 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside 2024-08-24 02:58:17 +05:00
s809 39db421aa1 Remove mentions of generation task priorities 2024-08-23 17:28:15 +05:00
s809 f4b0c08822 Fix network compression pool not shutting down 2024-08-23 17:11:32 +05:00
s809 f17c3fa267 "Fix" buffer release errors in FullDataPayload 2024-08-23 14:16:20 +05:00
James Seibel 48d1005be6 Up version number 2.2.0 -> 2.2.1-dev 2024-08-20 19:16:18 -05:00
James Seibel e71e8d1966 Up version number 2.1.3-dev -> 2.2.0 2024-08-20 17:44:41 -05:00
James Seibel 489b3d3ae1 Fix Legacy GL causing fog to smear 2024-08-20 17:44:41 -05:00
s809 81e6f55dbf Use same packet resource for all versions 2024-08-19 17:55:59 +05:00
James Seibel 1b162f10e6 Add sharedApi.isChunkAtChunkPosAlreadyUpdating() 2024-08-18 14:46:12 -05:00
James Seibel 301cce3d11 Remove unused chunkUnloadEvent 2024-08-18 14:25:51 -05:00
James Seibel e68d0d5c45 Fix fog and SSAO being broken by some mods 2024-08-17 22:29:28 -05:00
s809 b5e2019d28 Add logging of request group lifecycle 2024-08-15 15:46:24 +05:00
James Seibel 2cf952fb76 Deprecate IDhApiWorldGenerator.isBusy(), task queuing is now handled internally 2024-08-12 22:20:06 -05:00
James Seibel c7c5ab17bc Add optional DhApiChunk validation for world gen 2024-08-12 21:47:48 -05:00
James Seibel a98955530f Allow adding empty lists to DhApiChunk 2024-08-11 22:01:24 -05:00
James Seibel c63a509f9e Fix DhApiChunk setDataPoints failing for empty lists 2024-08-11 21:55:11 -05:00
s809 84dca85447 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside 2024-08-11 20:59:23 +05:00
James Seibel dac51a9eea Add mod compat warning message config 2024-08-11 09:54:54 -05:00
s809 8c3e8136be Do not clear keyed level on close event 2024-08-10 23:53:22 +05:00
s809 606c157958 Make encode/decode error handling work correctly 2024-08-10 23:26:04 +05:00
s809 ba3677b641 Make error handling somewhat work 2024-08-10 19:49:33 +05:00
James Seibel 8d78a1ad74 Rename BeaconBeamDTO.pos -> blockPos 2024-08-09 07:25:49 -05:00
James Seibel 7c11bb4258 Fix beacons not enabling/disabling correctly 2024-08-09 07:25:24 -05:00
James Seibel 0d6ec3d836 Fix frustum culling when the screen is warped 2024-08-07 18:55:02 -05:00
s809 dee13a4ec4 Merge branch 'serverside-experimental/prevent-disconnects' into serverside 2024-08-07 22:20:29 +05:00
James Seibel 9449433fe8 Fix deleting beacons 2024-08-07 07:47:11 -05:00
James Seibel 2c976c9fb1 Improve fast chunk hash to respect surface block heights 2024-08-07 07:47:02 -05:00
James Seibel 90fdfbbe61 Fix direct memory leak and remove config for GpuUpload 2024-08-07 07:29:52 -05:00
James Seibel a8df13fdd2 Fix LodRenderSection holding onto unnecessary memory 2024-08-06 21:47:03 -05:00
James Seibel f05eac2637 test 2024-08-06 20:47:23 -05:00
s809 71e4cd6272 Fix real-time updates 2024-08-06 23:12:13 +05:00
s809 82c5de7dfe Fix Neoforge not being able to connect to vanilla servers 2024-08-05 14:51:17 +05:00
James Seibel 4ae30b3d47 Improve LOD detail level detection and hole filling 2024-08-04 08:30:58 -05:00
James Seibel 8abefdcfd5 Attempt to improve LOD building speed and reduce broken lighting on servers 2024-08-03 17:11:18 -05:00
James Seibel 801a126de0 Fix chunk has being saved before LOD update 2024-08-03 16:50:12 -05:00
James Seibel 377d0fbe12 Allow DhApiChunk to accept top down or bottom up data point orders 2024-08-03 09:32:57 -05:00
James Seibel 1ecf968668 Remove unused ChunkToLodBuilder 2024-08-03 09:32:29 -05:00
s809 9d11733444 Fix dimension switching (untested) 2024-08-03 15:42:43 +05:00
James Seibel c43b985f4e Fix off by 1 error in Render data transformer 2024-08-02 18:30:55 -05:00
James Seibel b7fea64925 Fix out of bounds exception in Full Data Transformer 2024-08-02 17:56:25 -05:00
s809 e62c6a5c55 [skip ci] Prevent disconnects on encode/decode/handle errors 2024-08-01 22:19:10 +05:00
James Seibel 116d7f4a3f Fix incorrect comment in DhSectionPos 2024-08-01 06:52:05 -05:00
James Seibel 0c36dc03a5 Fix cave culling affecting floating islands and add LOD reload to some configs 2024-07-31 19:06:44 -05:00
James Seibel c658269eb7 Remove deprecated methods and move method to StringUtil 2024-07-30 17:06:55 -05:00
James Seibel defaea86f0 Remove unused sodium and McRenderWrapper methods
Removed methods were originally used to cull LODs if MC had loaded chunks, however this turned out to be more trouble than it was worth and caused more problems than it solved.
2024-07-30 17:00:59 -05:00
James Seibel f1564cc90b Fix presets only using "custom" after any value was changed 2024-07-30 15:47:48 -05:00
James Seibel 66feb0b9c2 Fix SSAO shader crashing when entering empty config values 2024-07-30 15:35:01 -05:00
s809 93b57ae2e1 Increase defaults for network compression threads 2024-07-30 10:52:27 +05:00
James Seibel 79bfa21115 Fix default logging debug to file 2024-07-29 20:40:23 -05:00
James Seibel 5588b0d2fa Add TODO to cleanup LodRenderSection futures 2024-07-29 07:29:48 -05:00
James Seibel e9788acb46 Fix rapidly changing dimensions causing the game to crash 2024-07-29 07:13:06 -05:00
James Seibel 8056a5b8bf Add Buffer Builder/Uploader thread pools to the F3 menu 2024-07-29 07:08:42 -05:00
s809 e5033a0c0f Use FullDataPayload instead of reusing messages 2024-07-29 13:10:04 +05:00
James Seibel e0ad956e34 Fix incorrect DhApiChunk create constructor parameter order (again) 2024-07-28 20:18:15 -05:00
s809 32abe15b09 Add build number on F3 screen 2024-07-28 20:19:38 +05:00
s809 2b65e33aa7 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside 2024-07-28 19:44:25 +05:00
James Seibel 9546f9cbc8 Close !66 (fix incorrect positions being fed into biome color code) 2024-07-28 09:33:24 -05:00
s809 bc6ab6c840 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside 2024-07-28 19:01:28 +05:00
James Seibel 9834b20a9f Revert and Deprecate DhApiChunk and DhApiTerrainDataPoint constructors 2024-07-28 08:56:18 -05:00
James Seibel 752008e8ac Re-add deprecated IDhApiLevelWrapper.getHeight() 2024-07-28 08:55:54 -05:00
James Seibel fbe81021c0 Update API javadocs 2024-07-28 08:55:35 -05:00
s809 ccbb071704 Reapply "Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside"
This reverts commit d26cb41048.
2024-07-28 17:05:58 +05:00
James Seibel 882c5399bd Fix holes in LODs boarding different detail levels 2024-07-27 21:06:50 -05:00
James Seibel dbe0461d5f Fix LOD upload warning 2024-07-27 20:25:55 -05:00
James Seibel d3d166dd02 Undo experimental change to LOD reloading 2024-07-27 20:11:28 -05:00
James Seibel d0dd1f38ff Fix LODs flashing twice when changing configs 2024-07-27 20:11:00 -05:00
James Seibel 53300a3028 Update IDhApiRenderProxy.clearRenderDataCache() to also clear cached block colors 2024-07-27 17:36:40 -05:00
James Seibel 56303dd82a level wrapper renamings 2024-07-27 16:51:08 -05:00
s809 d26cb41048 Revert "Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside"
This reverts commit a15aaa573d, reversing
changes made to 83c1a2fd63.
2024-07-27 23:06:34 +05:00
s809 a15aaa573d Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside 2024-07-27 21:43:21 +05:00
James Seibel 19d8c89bd8 Fix ice/water vertical LOD lighting 2024-07-27 09:30:41 -05:00
James Seibel adba3e4c15 clear testing default debug config values 2024-07-27 08:40:59 -05:00
James Seibel 479ce8093e minor cleanup/refactor 2024-07-27 08:28:31 -05:00
James Seibel 1ed6c619d9 Add localization for new column builder debug options 2024-07-27 08:26:46 -05:00
Builderb0y 825f439ffb fix incorrect positions being fed into biome color code. 2024-07-27 02:32:31 +00:00
James Seibel b1b487e63f Improve RenderDataPointUtil toString() 2024-07-24 06:46:28 -05:00
James Seibel 2763a7ca75 add missing brackets 2024-07-23 20:41:22 -05:00
James Seibel b4b1a2a549 remove deprecated RenderDataPointUtil logic 2024-07-23 20:05:41 -05:00
James Seibel 5865317394 Remove deprecated LodQuadBuilder code 2024-07-23 19:53:13 -05:00
James Seibel b70c090e94 Refactor and cleanup buffer building 2024-07-23 19:39:44 -05:00
James Seibel a9f6b924c2 Move CubicLodTemplate to ColumnRenderBufferBuilder 2024-07-23 07:05:28 -05:00
James Seibel 9c25a6450a comment ColumnArrayView 2024-07-22 20:47:34 -05:00
James Seibel 448982fbaf refactor FullDataToRenderDataTransformer 2024-07-22 19:50:23 -05:00
James Seibel 2ee06c59f1 Add configs for testing the ColumnRenderBufferBuilder 2024-07-22 17:34:04 -05:00
s809 83c1a2fd63 Fix handler registration 2024-07-20 23:56:24 +05:00
s809 0e904a388c Raise full data chunk size limit 2024-07-16 23:56:48 +05:00
s809 3723137fea Remove unused method 2024-07-16 23:52:28 +05:00
s809 71e54cc9ab Up protocol version 2024-07-16 23:51:30 +05:00
s809 b4cf962a85 Fix event handler registration 2024-07-16 23:40:20 +05:00
s809 1a30f240ef Client-side chunk handling 2024-07-16 17:49:09 +05:00
s809 b3b5ac63c8 Server-side logic is pretty much finished 2024-07-16 00:27:22 +05:00
s809 16abc0faa1 [skip ci] Initial chunked full data message payloads 2024-07-15 07:57:20 +05:00
s809 778c2f894e Fix level handling for real time updates 2024-07-12 23:11:36 +05:00
s809 2617cd294d Add cache for ignoring responses for cancelled requests 2024-07-11 23:06:23 +05:00
s809 43b421042d Add a comment to semaphores 2024-07-11 22:08:10 +05:00
s809 af69678545 Fix player tracking on server 2024-07-10 23:51:39 +05:00
s809 85341f7a6d Add missing semaphore release 2024-07-09 18:06:16 +05:00
s809 9e0edd0cf3 Prevent request cancellation deadlock 2024-07-09 14:47:45 +05:00
s809 84e90a7a9b Use dedicated thread pool for data compression 2024-07-08 23:07:58 +05:00
s809 48a8cdc365 Disable parent update propagation 2024-07-07 19:46:47 +05:00
s809 171e6b9bcd Reset state of level detection on world exit 2024-07-07 01:49:44 +05:00
s809 7e48c49e33 Make data source encoding lazy and move it off server thread 2024-07-01 00:15:54 +05:00
s809 1b48d61d3f Rename CloseEvent 2024-06-30 22:35:41 +05:00
s809 47541fa99c Move fixing the dimension name into even more correct place 2024-06-28 23:18:24 +05:00
s809 13638cedee Move fixing the dimension name into correct place 2024-06-28 19:28:36 +05:00
s809 4cdf31cfe8 Fix paths unable to be created on Windows 2024-06-28 17:25:09 +05:00
s809 3ff9a93066 Fix test compilation failing 2024-06-28 15:52:39 +05:00
s809 083a036666 Use level's ResourceLocation instead of dimension type's 2024-06-28 15:28:33 +05:00
s809 a0efe44e8d Clean up package structure 2024-06-26 23:57:37 +05:00
s809 8d110e22dd Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into refactor/remove-tcp-connection 2024-06-26 15:13:03 +05:00
s809 4d6e11fdeb Fix compilation 2024-06-26 14:55:21 +05:00
s809 454b15ff60 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into refactor/remove-tcp-connection 2024-06-26 14:54:00 +05:00
s809 d2f4972693 Put messages received before player joining into queue 2024-06-16 00:45:16 +05:00
s809 acb299530d Avoid reloading levels when unnecessary 2024-06-14 16:25:20 +05:00
s809 7c705015e6 Use level keys 2024-06-13 16:44:37 +05:00
s809 bcb21be848 Show dimension names 2024-06-10 23:39:57 +05:00
s809 562594de2f Better error on invalid dimension 2024-06-09 22:01:30 +05:00
s809 03a00bb7ca Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into refactor/remove-tcp-connection 2024-06-09 21:18:08 +05:00
s809 dc3aa939db Fix logging errors 2024-06-08 00:07:24 +05:00
s809 39391b944f Add toString to messages & fix incorrect call 2024-06-06 21:25:19 +05:00
s809 a6fc8f1702 Fix compilation 2024-06-02 20:00:09 +05:00
s809 b0777789fd Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into refactor/remove-tcp-connection 2024-06-02 19:58:53 +05:00
s809 24a2a48deb Increase section reload delay 2024-06-01 19:49:23 +05:00
s809 e08faa7943 Fix requests breaking on rejoining 2024-06-01 12:17:04 +05:00
s809 a000afbc60 Kinda works, rejoining is broken 2024-05-29 23:36:34 +05:00
s809 a3e34c9738 [skip ci] Still a mess 2024-05-27 22:01:57 +05:00
s809 1f63bdf124 [skip ci] Somewhere 2024-05-22 23:18:18 +05:00
s809 44205664b5 [skip ci] Incomplete 2024-05-21 22:54:57 +05:00
s809 40d019d7e8 Merge remote-tracking branch 'origin/main' 2024-05-18 22:14:19 +05:00
s809 96755e6174 Move overrides in LAN to config 2024-05-18 22:09:19 +05:00
s809 bd5866787f Update Config.java 2024-05-17 12:41:09 +00:00
s809 d9651bbd27 Merge branch 'main' into 'main'
rewrite config comments

See merge request s809/distant-horizons-core!1
2024-05-17 12:39:54 +00:00
Yeshi0 a1f4442a53 rewrite config comments 2024-05-14 20:16:44 +02:00
s809 e6b140245b Fix awt dependency error 2024-05-12 17:34:58 +05:00
s809 08336027b2 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into feature/2.0.4 2024-05-11 19:16:01 +05:00
s809 543b5ed49a Fix dimension switching (at cost of breaking immersive portals) 2024-05-10 23:56:07 +05:00
s809 3d86c5c5ee Fix generated sections not appearing 2024-05-10 21:13:40 +05:00
s809 8e1009ab3f Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into feature/2.0.4 2024-05-05 19:51:21 +05:00
s809 551a5f1a72 Fix updates 2024-05-05 16:55:15 +05:00
s809 2664f96e0f Generation works, updates don't 2024-05-03 22:56:01 +05:00
s809 dde2bcc947 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2024-05-02 21:47:35 +05:00
s809 3e3e97385b Fix 1.16.5 and 1.17.1 builds 2024-04-17 21:02:14 +05:00
s809 05fba12038 Prevent generation when config is not acknowledged by server 2024-04-16 21:55:02 +05:00
s809 c1f2803c56 Add missing newlines for override configs 2024-04-16 21:39:56 +05:00
s809 7006c669f0 More verbose invalid level error 2024-04-16 21:38:37 +05:00
s809 7cd0c956f6 Fix IPv6 addresses not being properly handled 2024-04-16 21:27:26 +05:00
s809 7c33dda11b Add FlushConsolidationHandler 2024-04-16 20:32:50 +05:00
s809 5f9f0f23b8 Fix integer overflows 2024-04-12 23:14:58 +05:00
s809 284191a904 Fix Neoforge 2024-04-12 22:36:04 +05:00
s809 0ad60cd5d9 Merge branch 'feature/plugin-channel' 2024-04-02 22:34:45 +05:00
s809 8485d1585a Remove localhost from check 2024-04-02 22:27:36 +05:00
s809 1cd2f75dd2 Fix compilation 2024-04-01 21:52:20 +05:00
s809 bdc4fa4477 Fix errors when on vanilla server 2024-04-01 00:55:15 +05:00
s809 ff7720a8d6 Adjust server port change behavior 2024-04-01 00:07:32 +05:00
s809 6829709123 Fix reconnection logic 2024-03-30 23:40:05 +05:00
s809 0d355f675a Incomplete 2024-03-26 00:30:57 +05:00
s809 af2f0e8582 Fix Forge 2024-03-18 21:37:58 +05:00
s809 3362570123 Add thread pool task buildup limiter 2024-03-16 12:28:38 +05:00
s809 e008eb8a13 Server side plugin networking
Untested port for Forge
2024-03-14 21:46:23 +05:00
s809 1f438d8f87 Fix world setting 2024-03-10 21:09:52 +05:00
s809 278ae04532 Initial buggy plugin channel support 2024-03-05 22:09:30 +05:00
s809 2d4f033891 Do not wait for world gen tasks to stop while shutting down queue 2024-02-25 18:05:45 +05:00
s809 1fd85f2249 Show server side messages in F3 on disconnect 2024-02-24 22:48:24 +05:00
s809 d966697ecb Change some wording 2024-02-24 21:37:54 +05:00
s809 2e6c0ba356 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2024-02-22 23:06:21 +05:00
s809 ebb47990f4 Fix immersive portals 2024-02-15 23:15:01 +05:00
s809 15fe39c1bb Fix connection exception handling 2024-02-13 22:03:55 +05:00
s809 7bb0ec3148 Fix compilation 2024-02-12 21:39:30 +05:00
s809 6fcfacd346 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2024-02-12 21:38:58 +05:00
s809 48212ba746 Add another config option
Fix rate limiting issues (kinda)
2024-02-11 19:39:35 +05:00
s809 7bf9ce141a Avoid doing requests on join 2024-02-10 17:06:49 +05:00
s809 2353bd7545 Fix compilation 2024-02-07 21:03:37 +05:00
s809 02ddf5773c Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2024-02-07 20:49:49 +05:00
s809 3e37e9ee08 Remove unused packets 2024-02-04 21:57:57 +05:00
s809 b1b907bfe5 Silence a warning 2024-02-04 21:33:21 +05:00
s809 d347dbd222 Fix typo 2024-02-04 21:29:21 +05:00
s809 44527630da Fix queue not filled when generation is toggled 2024-02-04 21:00:02 +05:00
s809 df6ac6d740 Add connection state on F3 screen
Hide refresh queue from debug when disabled
2024-02-04 20:20:57 +05:00
s809 2ce08f8957 Merge remote-tracking branch 'origin/main' 2024-02-04 02:36:04 +05:00
s809 1baa666d23 Add priority distance limiting by ratio 2024-02-04 02:34:53 +05:00
s809 e5e4551038 Some logging changes 2024-02-03 23:02:31 +05:00
s809 0cfbe09558 Use ConfigBasedLogger
Add sub-sectioning to server networking section
2024-02-03 22:24:34 +05:00
s809 7d72e82325 Change a comment in Config 2024-02-03 20:17:21 +05:00
s809 3f823c58cc Fix exception while saving new data 2024-02-02 05:48:36 +00:00
s809 3932ea21c2 Limit rate+concurrency instead of only concurrency
Rename post-relog update to Login sync
2024-01-29 22:36:13 +05:00
s809 c9d426e581 Refactor ILevelRelatedMessage and RemotePlayerConnectionHandler 2024-01-28 22:37:50 +05:00
s809 61c516df1d Post-relog updates 2024-01-27 19:45:30 +05:00
s809 7e2019abd4 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2024-01-21 17:20:49 +05:00
s809 f0f3614b9d Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2024-01-16 20:17:43 +05:00
s809 573a284580 Fix generation 2024-01-13 19:44:48 +05:00
s809 310c890474 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2024-01-13 16:51:12 +05:00
s809 8a309b56d9 Fix crash when changing config while disconnected 2024-01-13 16:08:49 +05:00
s809 adc3f15c97 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2024-01-08 17:50:00 +05:00
s809 962f523af9 Fix 1.16.5 and 1.17.1 builds 2024-01-05 22:37:27 +05:00
s809 dbc1ad4cb7 Add names to network threads 2024-01-05 22:08:37 +05:00
s809 8b0f6a4414 Daemonize timers 2024-01-05 22:08:16 +05:00
s809 502aaf6a8a Add a config to delay generation requests, for cases when gen tasks are not filled properly yet 2023-12-28 18:58:50 +05:00
s809 5c644fbf5b Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-12-24 19:16:03 +05:00
s809 da60ca7560 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-12-21 19:17:51 +05:00
s809 a5a56c7eb7 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-12-21 11:47:51 +05:00
s809 ff8b1f24d0 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-12-20 23:18:57 +05:00
s809 904c1a7d71 Add protection against hang on shutdown 2023-12-19 23:20:23 +05:00
s809 02aca6f044 Allow section state requests to be controlled separately 2023-12-19 22:52:19 +05:00
s809 b4ea632b93 Add kick trigger on repeated rate limit hitting
Remove some unused code
2023-12-18 23:10:39 +05:00
s809 a2cc1c2913 Revert "Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core"
This reverts commit 522a799516, reversing
changes made to 04e43ebec8.
2023-12-18 22:15:34 +05:00
s809 522a799516 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-12-18 19:17:14 +05:00
s809 04e43ebec8 Remove current post-relog update feature 2023-12-18 19:13:57 +05:00
s809 077d72c23b Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-12-15 20:29:15 +05:00
s809 cb95c45d76 Fix compilation 2023-12-06 22:06:37 +05:00
s809 a9de079132 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-12-06 21:09:19 +05:00
s809 166cc55e3c Abstract away ChannelHandlerContext from user code 2023-11-27 23:46:05 +05:00
s809 f36bffa4b7 Add missing config entries in chat commands 2023-11-26 21:32:50 +05:00
s809 6da1e75dad Fix server side generation setting being ignored 2023-11-25 18:04:25 +05:00
s809 82d66ca392 Basic chat commands 2023-11-25 17:44:51 +05:00
s809 e98cf1f2b5 Hide all unrelated config entries 2023-11-23 20:58:09 +05:00
s809 77bd333fff Fix RejectedExecutionException when leaving server 2023-11-20 17:18:05 +05:00
s809 b791a185a2 Deduplicate code in MultiplayerConfigChangeListener 2023-11-20 16:54:17 +05:00
s809 c64d7fedd2 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-11-19 22:56:56 +05:00
s809 d2f5c02238 Bandaid fix to prevent duplicate section requests 2023-11-12 17:17:15 +05:00
s809 9cfcf37fb3 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-11-11 19:20:33 +05:00
s809 84d36df388 Do not clean up rendering on a dedicated server 2023-11-04 16:56:15 +05:00
s809 880abd0124 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-11-03 21:05:27 +05:00
s809 72274bfd7d Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-10-30 15:26:29 +05:00
s809 9640169be9 Make post-relog update independent from generation toggle 2023-10-23 23:18:52 +05:00
Steveplays28 9daf0c7317 chore: Add missing language entries for the server networking config 2023-10-20 14:07:42 +02:00
Steveplays28 a4df5a8ed8 style: Rename renderDistance to renderDistanceRadius to stay in sync with the main config 2023-10-20 14:03:19 +02:00
Steveplays28 df3d20f94e fix: Fix getting LOD render distance radius from the config
This config option was renamed recently, to fix LODs not rendering out to the LOD render distance border. Also removed an unused import.
2023-10-20 13:49:55 +02:00
Steveplays28 f3b40f51a2 Merge branch 'main-upstream'
# Conflicts:
#	core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java
#	core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java
#	core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java
2023-10-20 13:41:33 +02:00
s809 e3d7598501 Fix compilation 2023-10-14 22:13:01 +05:00
s809 596b822a5d Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-10-14 22:09:15 +05:00
s809 e9e7ac48b1 Fix compilation 2023-09-28 23:28:18 +05:00
s809 2e2254fbbd Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-09-28 22:54:35 +05:00
s809 8bda3351b8 Make worldGenLoopingQueue lock free 2023-09-27 21:40:59 +05:00
Steveplays28 532f3adba8 Merge branch 'main-upstream' 2023-09-27 14:09:04 +02:00
s809 cc8b97f2fe Improve management of frequent real time updates 2023-09-26 22:03:04 +05:00
s809 db31c54309 Fix Enable Distant Generation config being ignored 2023-09-26 13:21:37 +05:00
s809 375e81b17d Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-09-26 11:40:38 +05:00
s809 4fb9126d49 Fix unloaded file check 2023-09-24 18:48:14 +05:00
Steveplays28 ed2fca240e fix: Fix compiling after merge 2023-09-24 11:33:49 +02:00
Steveplays28 f5549f2ebe Merge remote-tracking branch 'origin-core/main' 2023-09-24 11:28:38 +02:00
Steveplays28 18e9cfe2a5 Merge branch 'main-upstream' 2023-09-24 11:24:55 +02:00
s809 e4e6753a02 Add gen task rate display 2023-09-24 14:24:31 +05:00
Steveplays28 8e7855405d Merge remote-tracking branch 'origin-core/main' 2023-09-23 16:34:07 +02:00
Steveplays28 381cae7307 feat: Add config option for server port 2023-09-23 16:33:43 +02:00
s809 e94d67916d Fix compiling
Remove unused config category
Use pooling when receiving sections
Fix use of real time update config
Fix debug renderer not unregistered on queue close
2023-09-23 15:32:24 +05:00
Steveplays28 c98f9476cb Merge remote-tracking branch 'upstream-core/main'
# Conflicts:
#	core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiDebuggingConfig.java
#	core/src/main/java/com/seibel/distanthorizons/core/config/Config.java
#	core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java
#	core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java
#	core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java
#	core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderDataMetaFile.java
#	core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java
#	core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java
#	core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java
#	core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java
2023-09-22 15:40:31 +02:00
s809 6c36f3c7e9 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-09-21 21:04:58 +05:00
s809 877a89d17a Add config for a few slow features 2023-09-21 21:00:23 +05:00
s809 d325a69e3f Move context levels to requests 2023-09-21 10:16:34 +05:00
s809 08704aad2c Allow selecting specific IDebugRenderable's for rendering 2023-09-19 21:32:59 +05:00
s809 a80bb082b3 Validate response types 2023-09-19 20:19:25 +05:00
s809 fccd197cd5 Fix chunk updates sent to incomplete connections
Hide useless warns, for cases when listener is not configured yet
2023-09-19 18:53:10 +05:00
s809 2cfc2c81c8 Fix future id collisions between c<->s
(cause of occasional hangs on disconnection)
Add packet trace logging
2023-09-19 17:28:26 +05:00
s809 6f4e105542 Fix compiling 2023-09-18 13:44:19 +05:00
s809 633b3f2033 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-09-18 13:24:43 +05:00
Steveplays28 f0a62c813a Merge remote-tracking branch 'upstream-core/main' 2023-09-12 17:35:29 +02:00
s809 21f03526f8 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-09-10 12:03:29 +05:00
Steveplays28 0e7a96d308 fix: Fix missing IServerKeyedClientLevel import 2023-09-09 21:03:17 +02:00
Steveplays28 87572246a6 Merge remote-tracking branch 'upstream-core/main' 2023-09-09 20:53:44 +02:00
Steveplays28 025484d5b4 Merge remote-tracking branch 'upstream-core/main' 2023-09-09 14:47:07 +02:00
Steveplays28 15e4b1316e Merge remote-tracking branch 'upstream-core/main' 2023-09-08 19:54:43 +02:00
s809 e02eddc60e Fix compiling 2023-09-07 21:07:16 +05:00
s809 1e1ddd505b Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-09-07 20:07:30 +05:00
s809 415f16507b Add post-rejoin updates 2023-09-02 01:23:56 +05:00
s809 cb95978502 Fix compilation 2023-08-27 19:56:12 +05:00
s809 1b729f3fe7 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-08-27 19:10:25 +05:00
s809 d84e097fa2 Merge branch 'client-level-error' of https://gitlab.com/s809/distant-horizons-core into client-level-error 2023-08-27 18:43:06 +05:00
s809 51ad901206 Fix loading ClientLevel on server 2023-08-27 18:41:47 +05:00
s809 517925a207 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-08-26 21:33:44 +05:00
s809 c5abc22c58 Merge branch 'feat/server-updates' 2023-08-24 00:01:07 +05:00
s809 2330377212 Send updates at chunk level instead of sections 2023-08-23 23:55:16 +05:00
s809 9db56bbf87 test 2023-08-22 20:41:42 +05:00
s809 52a90fec6c Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into feat/server-updates 2023-08-22 19:12:43 +05:00
Steveplays28 3630dc724d fix: Add null check to config GUI when adding a screen change listener 2023-08-22 10:52:19 +02:00
s809 627bfbc007 Incomplete 2023-08-21 22:08:35 +05:00
Steveplays28 d9283e938b feat: Update rendering block ignores
Barrier blocks, structure void blocks, light blocks, and air blocks now share 2 `HashMap`s that define blocks that should be ignored by the LOD builder.
2023-08-21 03:41:27 +02:00
s809 ce4d50654d Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-08-19 22:28:13 +05:00
s809 7568ab56f6 Fix hang when near completion of loading from server
Replace total value with sum of pending+finished requests
2023-08-19 22:21:01 +05:00
s809 bf58654a10 Fix prioritization not working properly 2023-08-19 21:22:51 +05:00
Steveplays28 e4576c7d51 Merge remote-tracking branch 'upstream-core/main' 2023-08-19 11:41:38 +02:00
Steveplays28 a1f07c075e Merge remote-tracking branch 'upstream-core/main' 2023-08-18 15:12:11 +02:00
s809 84c4ad6cdd Add missing level wrapper argument (and a bunch of null checks) 2023-08-18 15:04:59 +02:00
Steveplays28 47aef1f349 Merge remote-tracking branch 'upstream-core/main'
# Conflicts:
#	core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java
#	core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java
#	core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java
#	core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java
#	core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java
#	core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java
#	core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java
#	core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java
#	core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java
#	core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java
#	core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java
#	core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java
#	core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java
#	core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/messages/AckMessage.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/messages/HelloMessage.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerUUIDMessage.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/objects/RemotePlayer.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/protocol/EMessageHandlerSide.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkMessage.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java
#	core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java
#	core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java
#	core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java
#	core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java
#	core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java
#	core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java
2023-08-16 11:36:00 +02:00
Steveplays28 1e7a25046a fix: Fix BlockStateWrapper and Biome serializing to empty strings 2023-08-15 00:03:13 +02:00
Steveplays28 87a9e93278 fix: Fix server trying to access client instance 2023-08-14 19:23:39 +02:00
Steveplays28 2d3062008e Merge remote-tracking branch 'upstream-core/main' 2023-08-14 15:40:41 +02:00
Steveplays28 8dcec7a1bd refactor: Re-add old serialization method
This is used by some override methods that don't have access to the level.
These old serialization methods have `FIXME`s so they're easy to find in the future.
2023-08-13 23:18:55 +02:00
Steveplays28 d839b6e4bd Merge remote-tracking branch 'upstream-core/main'
# Conflicts:
#	core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java
#	core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java
#	core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java
#	core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java
#	core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java
2023-08-13 22:59:43 +02:00
s809 0c155ac8cd Generation task prioritization (loaded > unloaded > ungenerated) 2023-08-13 18:59:08 +05:00
s809 271b193543 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-08-13 01:53:06 +05:00
s809 e4ac25f4ce Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-08-08 20:57:30 +05:00
s809 52f9e3e9e4 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-08-08 12:35:21 +05:00
s809 6bf32ff85c Add debug renderer for remote generation queue 2023-08-05 22:30:33 +05:00
s809 9bd432ad7f Fix memory leak related to requests 2023-08-04 22:12:09 +05:00
s809 04fc4aa676 Remove unnecessary logging 2023-08-04 21:51:53 +05:00
s809 8ee4b8b4c9 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-08-04 21:10:41 +05:00
s809 5767668efa Somewhat proper request cancellation 2023-08-04 21:04:34 +05:00
s809 76b226b865 Fix switching dimensions (request cancellation is broken) 2023-08-04 13:06:56 +05:00
s809 1788c18d59 Everything I've done so far (not working/finished) 2023-08-01 22:31:16 +05:00
s809 2251cd4c25 Attempt to fix dimension switching 2023-08-01 17:04:40 +05:00
s809 781b588980 Implement server-side rate limiting 2023-08-01 12:55:36 +05:00
s809 74ac4fe64f Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-07-31 17:43:16 +05:00
s809 6d1f9803ce Avoid losing requests due to concurrency 2023-07-31 17:42:14 +05:00
s809 95d721e1a3 World generation 2023-07-31 15:23:56 +05:00
s809 d2f2a3b8aa Attempt to use GeneratedFullDataFileHandler 2023-07-24 19:59:16 +05:00
s809 768ce5b8fd something 2023-07-24 19:16:05 +05:00
s809 3e9f741d62 Implement transferring of missing full data source types 2023-07-24 14:15:03 +05:00
s809 133f007bde Fix mistake 2023-07-24 11:01:10 +05:00
s809 a623a3bfab Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-07-24 10:41:17 +05:00
s809 4157a39589 Somewhat working generation 2023-07-24 01:46:09 +05:00
s809 10e2873497 Downloads already visited chunks 2023-07-23 23:48:42 +05:00
s809 d7ef6c8a72 Got chunks to generate on server 2023-07-23 20:34:40 +05:00
s809 d29ba9d423 Requests now work properly
Merge request tracker into event source
2023-07-23 14:39:29 +05:00
s809 0c60395426 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-07-23 11:08:31 +05:00
s809 4ac1c0d4b3 Sending some responses
Rejoin is horribly broken
2023-07-19 22:29:33 +05:00
Steveplays28 9a0c9e9b7d feat: Implement syncing of DhSectionPos for chunk requests/responses 2023-07-19 16:48:43 +02:00
s809 79408c081a Close network request tacker properly 2023-07-19 17:40:38 +05:00
s809 218411902e Incomplete something 2023-07-19 15:54:17 +05:00
s809 a42f8367a4 Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-07-19 12:20:11 +05:00
s809 02acfaa3ed Finer control over message handling (untested) 2023-07-19 12:19:39 +05:00
s809 9a2799e83b Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core 2023-07-16 12:47:44 +05:00
s809 73e6ce75b0 Replace "tell version to client" part with simple ack because YAGNI 2023-07-16 12:38:32 +05:00
298 changed files with 10531 additions and 6883 deletions
+1
View File
@@ -0,0 +1 @@
Distant Horizons logos © 2024 by Pankakes are licensed under CC BY-SA 4.0
+2 -2
View File
@@ -1,7 +1,7 @@
# Distant Horizons
# <img src="https://gitlab.com/jeseibel/distant-horizons-core/-/raw/main/_Misc%20Files%2Flogo%20files%2Fnew%2FSVG%2FDistant-Horizons-Core.svg" height="128px">
This repo is for the Distant Horizons mod.
The purpose of this submodule is to isolate code that isn't tied to a specific version of minecraft. This prevents us from having duplicate code; reducing errors and potentially helping us port to different versions faster and easier.
The purpose of this submodule is to isolate code that isn't tied to a specific version of minecraft. This prevents us from having duplicate code; reducing errors and helping us port to different versions faster and easier.
Check out the mod's main GitLab page here:
https://gitlab.com/jeseibel/distant-horizons
+44
View File
@@ -0,0 +1,44 @@
# Distant Horizons brand guidelines
To keep our look consistent and recognizable, weve created some simple guidelines for using our logos. We have a Primary logo, Core and API logos. Keep them cool 😎
## Logos
Please do not edit, change, distort, recolour, or reconfigure our logos.
| ![Distant Horizons primary logo](./PNG/Distant-Horizons.png) | ![Distant Horizons Core logo](./PNG/Distant-Horizons-Core.png) | ![Distant Horizons API logo](./PNG/Distant-Horizons-API.png) |
|--|--|--|
|Primary &nbsp; &nbsp; &nbsp; &nbsp; [.png](/PNG/Distant-Horizons.png) &nbsp; &nbsp; &nbsp; &nbsp; [.svg](./SVG/Distant-Horizons-Logo.svg)| Core &nbsp; &nbsp; &nbsp; &nbsp; [.png](/PNG/Distant-Horizons-Core.png) &nbsp; &nbsp; &nbsp; &nbsp; [.svg](./SVG/Distant-Horizons-Core.svg) | API &nbsp; &nbsp; &nbsp; &nbsp; [.png](./PNG/Distant-Horizons-API.png) &nbsp; &nbsp; &nbsp; &nbsp; [.svg](./SVG/Distant-Horizons-API.svg) |
## Marks
Use these only when the Distant Horizons brand is clearly visible or has been well established elsewhere on the page or in the design. (When in doubt, use the other ones.)
> *Keep in mind the Distant Horizons API mark needs to be optically centered to align.*
<!-- | ![Distant Horizons primary mark](./PNG/Distant-Horizons-Mark.png) | ![Distant Horizons Core mark](./PNG/Distant-Horizons-Core-Mark.png) | ![Distant Horizons API mark](./PNG/Distant-Horizons-API-Mark.png) |
|--|--|--|
|Primary &nbsp; &nbsp; &nbsp; &nbsp; [.png](/PNG/Distant-Horizons-Mark.png.png) &nbsp; &nbsp; &nbsp; &nbsp; [.svg](./SVG/Distant-Horizons-Mark.svg.svg)| Core &nbsp; &nbsp; &nbsp; &nbsp; [.png](/PNG/Distant-Horizons-Core-Mark.png.png) &nbsp; &nbsp; &nbsp; &nbsp; [.svg](./SVG/Distant-Horizons-Core-Mark.svg.svg) | API &nbsp; &nbsp; &nbsp; &nbsp; [.png](./PNG/Distant-Horizons-API-Mark.png.png) &nbsp; &nbsp; &nbsp; &nbsp; [.svg](./SVG/Distant-Horizons-API-Mark.svg.svg) | -->
| <img src="./PNG/Distant-Horizons-Mark.png" width="100"/> | <img src="./PNG/Distant-Horizons-Core-Mark.png" width="100"/> | <img src="./PNG/Distant-Horizons-API-Mark.png" width="100"/> |
|--|--|--|
|Primary &nbsp; &nbsp; &nbsp; &nbsp; [.png](/PNG/Distant-Horizons-Mark.png.png) &nbsp; &nbsp; &nbsp; &nbsp; [.svg](./SVG/Distant-Horizons-Mark.svg.svg)| Core &nbsp; &nbsp; &nbsp; &nbsp; [.png](/PNG/Distant-Horizons-Core-Mark.png.png) &nbsp; &nbsp; &nbsp; &nbsp; [.svg](./SVG/Distant-Horizons-Core-Mark.svg.svg) | API &nbsp; &nbsp; &nbsp; &nbsp; [.png](./PNG/Distant-Horizons-API-Mark.png.png) &nbsp; &nbsp; &nbsp; &nbsp; [.svg](./SVG/Distant-Horizons-API-Mark.svg.svg) |
## Spacing
Every logo needs room to breathe.
Ours needs the free space of the height and width of the letter **"H"** in Distant **H**orizons.
> *Some leeway is allowed for the Distant Horizons API mark due to it's shape.*
| ![Distant Horizons logo spacing](./Guidelines_images//Distant-Horizons-Logo-Spacing2.png) | ![Distant Horizons mark spacing](./Guidelines_images//Distant-Horizons-Mark-Spacing2.png) |
|--|--|
___
The logotype we are using in our logos is a modified [Karmatic Arcade font by Vic Fieger](https://www.dafont.com/karmatic-arcade.font?fpp=100&psize=s)
This branding guideline was influenced by [Discord's own](https://discord.com/branding)
Distant Horizons logos © 2024 by Pankakes are licensed under CC BY-SA 4.0
Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 306.5 359.13"><defs><style>.cls-1{fill:#7ec138;}.cls-2{fill:#12af68;}.cls-3{fill:#fff;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Logo_RGB_White" data-name="Logo RGB White"><path id="Outline" d="M175.21,356.1l123.72-71.92A15.22,15.22,0,0,0,306.5,271V126.5a22.32,22.32,0,0,0-11.1-19.29L173.18,36.13a18.26,18.26,0,0,0-18.36,0L32.59,107.21A22.31,22.31,0,0,0,21.5,126.5V271a15.22,15.22,0,0,0,7.56,13.15L152.78,356.1A22.29,22.29,0,0,0,175.21,356.1Z"/><g id="Right_Path_" data-name="Right &lt;Path&gt;"><path class="cls-1" d="M224.5,254.1v51.61a8,8,0,0,1-4,6.92l-44,25.4a4,4,0,0,1-6-3.46V283a8,8,0,0,1,4-6.92l44-25.41A4,4,0,0,1,224.5,254.1Z"/></g><g id="_Path_" data-name="&lt;Path&gt;"><path class="cls-2" d="M113.82,173.07l-11-6.36a4,4,0,0,0-6,3.46v51.72a8,8,0,0,0,4,6.92l11,6.35a4,4,0,0,0,6-3.46V180A8,8,0,0,0,113.82,173.07Z"/></g><g id="_Path_2" data-name="&lt;Path&gt;"><path class="cls-2" d="M129.26,150.11l11.19-6.46a4,4,0,0,0,0-6.93l-11.19-6.46a8,8,0,0,0-8,0l-11.2,6.46a4,4,0,0,0,0,6.93l11.2,6.46A8,8,0,0,0,129.26,150.11Z"/></g><g id="Right_Path_2" data-name="Right &lt;Path&gt;"><path class="cls-2" d="M170.5,244v12.74a4,4,0,0,0,6,3.46l44-25.4a8,8,0,0,0,4-6.92V215.15a4,4,0,0,0-6-3.46l-44,25.4A8,8,0,0,0,170.5,244Z"/></g><g id="Right_Path_3" data-name="Right &lt;Path&gt;"><path class="cls-1" d="M243.5,299.35l45-26a8,8,0,0,0,4-6.92V136.92a4,4,0,0,0-6-3.46l-45,26a8,8,0,0,0-4,6.92V295.89A4,4,0,0,0,243.5,299.35Z"/></g><g id="Left_Path_" data-name="Left &lt;Path&gt;"><path class="cls-2" d="M157.5,244v12.74a4,4,0,0,1-6,3.46l-11-6.35a8,8,0,0,1-4-6.92V234.2a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,157.5,244Z"/></g><g id="Left_Path_2" data-name="Left &lt;Path&gt;"><path class="cls-1" d="M153.5,276.08l-112-64.66a4,4,0,0,0-6,3.46v51.57a8,8,0,0,0,4,6.92L151.5,338a4,4,0,0,0,6-3.46V283A8,8,0,0,0,153.5,276.08Z"/></g><g id="_Path_3" data-name="&lt;Path&gt;"><path class="cls-3" d="M157.5,205v12.74a4,4,0,0,1-6,3.46l-11-6.36a8,8,0,0,1-4-6.92V195.23a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,157.5,205Z"/></g><path d="M91.15,171.19V235a16,16,0,0,1-24,13.85L11.59,216.81A23.27,23.27,0,0,1,0,196.73V132.89A16,16,0,0,1,24,119l55.56,32.07A23.24,23.24,0,0,1,91.15,171.19Z"/><g id="_Path_4" data-name="&lt;Path&gt;"><path class="cls-1" d="M14,138.09v58.64a9.17,9.17,0,0,0,4.6,8l51.05,29.48a5,5,0,0,0,7.5-4.33V171.19a9.18,9.18,0,0,0-4.59-8L21.5,133.76A5,5,0,0,0,14,138.09Z"/></g><g id="_Path_5" data-name="&lt;Path&gt;"><path class="cls-3" d="M170.5,217.78V205.05a8,8,0,0,1,4-6.93l11-6.35a4,4,0,0,1,6,3.46V208a8,8,0,0,1-4,6.93l-11,6.35A4,4,0,0,1,170.5,217.78Z"/></g><g id="_Path_6" data-name="&lt;Path&gt;"><path class="cls-2" d="M209.5,202.19l11-6.36a8,8,0,0,0,4-6.92V176.18a4,4,0,0,0-6-3.47l-11,6.36a8,8,0,0,0-4,6.93v12.72A4,4,0,0,0,209.5,202.19Z"/></g><path d="M300.65,84.93a15.94,15.94,0,0,1-7.92,13.73L245.5,125.91a20,20,0,0,1-20.06,0L164,90.44l-61.45,35.48a20.11,20.11,0,0,1-20.06,0L35.27,98.66a15.84,15.84,0,0,1,0-27.44L154,2.69a20,20,0,0,1,20.06,0l118.7,68.53A15.89,15.89,0,0,1,300.65,84.93Z"/><g id="_Path_7" data-name="&lt;Path&gt;"><path class="cls-2" d="M169,117.4l-11,6.34a4,4,0,0,0,0,6.93l44.93,26a8,8,0,0,0,8,0l11-6.35a4,4,0,0,0,0-6.93L177,117.4A8,8,0,0,0,169,117.4Z"/></g><g id="_Path_8" data-name="&lt;Path&gt;"><path class="cls-3" d="M168,187.21l11.19-6.46a4,4,0,0,0,0-6.93L168,167.36a8,8,0,0,0-8,0l-11.2,6.46a4,4,0,0,0,0,6.93l11.2,6.46A8,8,0,0,0,168,187.21Z"/></g><g id="_Path_9" data-name="&lt;Path&gt;"><path class="cls-1" d="M280.42,87.31l-44.73,25.82a8,8,0,0,1-8,0L168,78.66a8,8,0,0,0-8,0l-59.7,34.47a8,8,0,0,1-8,0L47.58,87.31a4,4,0,0,1,0-6.93L160,15.47a8,8,0,0,1,8,0L280.42,80.38A4,4,0,0,1,280.42,87.31Z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 285 325.47"><defs><style>.cls-1{fill:#e13e1e;}.cls-2{fill:#f7a612;}.cls-3{fill:#fff;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Logo_RGB_White" data-name="Logo RGB White"><path id="Outline" d="M153.72,322.45l123.71-71.93A15.22,15.22,0,0,0,285,237.36V92.84a22.32,22.32,0,0,0-11.1-19.29L151.68,2.47a18.31,18.31,0,0,0-18.36,0L11.1,73.55A22.32,22.32,0,0,0,0,92.84V237.35A15.2,15.2,0,0,0,7.57,250.5l123.71,71.94A22.31,22.31,0,0,0,153.72,322.45Z"/><g id="Right_Path_" data-name="Right &lt;Path&gt;"><path class="cls-1" d="M203,220.44v51.61a8,8,0,0,1-4,6.93l-44,25.39a4,4,0,0,1-6-3.46v-51.6a8,8,0,0,1,4-6.92L197,217A4,4,0,0,1,203,220.44Z"/></g><g id="Right_Path_2" data-name="Right &lt;Path&gt;"><path class="cls-2" d="M149,210.36v12.73a4,4,0,0,0,6,3.46l44-25.39a8,8,0,0,0,4-6.93V181.49a4,4,0,0,0-6-3.46l-44,25.4A8,8,0,0,0,149,210.36Z"/></g><g id="Right_Path_3" data-name="Right &lt;Path&gt;"><path class="cls-1" d="M222,265.69l45-26a8,8,0,0,0,4-6.93V103.26a4,4,0,0,0-6-3.46l-45,26a8,8,0,0,0-4,6.93V262.23A4,4,0,0,0,222,265.69Z"/></g><g id="Left_Path_" data-name="Left &lt;Path&gt;"><path class="cls-2" d="M136,210.36v12.73a4,4,0,0,1-6,3.46l-11-6.34a8,8,0,0,1-4-6.93V200.54a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,136,210.36Z"/></g><g id="Left_Path_2" data-name="Left &lt;Path&gt;"><path class="cls-1" d="M132,242.42,20,177.76a4,4,0,0,0-6,3.46v51.57a8,8,0,0,0,4,6.93l112,64.65a4,4,0,0,0,6-3.46V249.35A8,8,0,0,0,132,242.42Z"/></g><g id="_Path_" data-name="&lt;Path&gt;"><path class="cls-3" d="M136,171.39v12.73a4,4,0,0,1-6,3.46l-11-6.35a8,8,0,0,1-4-6.93V161.57a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,136,171.39Z"/></g><g id="_Path_2" data-name="&lt;Path&gt;"><path class="cls-2" d="M99,145.41l-11-6.35a4,4,0,0,0-6,3.46v51.71a8,8,0,0,0,4,6.93l11,6.34a4,4,0,0,0,6-3.46v-51.7A8,8,0,0,0,99,145.41Z"/></g><g id="_Path_3" data-name="&lt;Path&gt;"><path class="cls-1" d="M14.33,103.26V155a8,8,0,0,0,4,6.93l45,26a4,4,0,0,0,6-3.46v-51.7a8,8,0,0,0-4-6.93l-45-26A4,4,0,0,0,14.33,103.26Z"/></g><g id="_Path_4" data-name="&lt;Path&gt;"><path class="cls-3" d="M149,184.12V171.39a8,8,0,0,1,4-6.93l11-6.35a4,4,0,0,1,6,3.46V174.3a8,8,0,0,1-4,6.93l-11,6.35A4,4,0,0,1,149,184.12Z"/></g><g id="_Path_5" data-name="&lt;Path&gt;"><path class="cls-2" d="M188,168.53l11-6.35a8,8,0,0,0,4-6.93V142.52a4,4,0,0,0-6-3.47l-11,6.36a8,8,0,0,0-4,6.93v12.72A4,4,0,0,0,188,168.53Z"/></g><g id="_Path_6" data-name="&lt;Path&gt;"><path class="cls-1" d="M258.92,88.65l-44.73,25.82a8,8,0,0,1-8,0L146.5,80a8,8,0,0,0-8,0L78.8,114.47a8,8,0,0,1-8,0L26.08,88.65a4,4,0,0,1,0-6.93L138.5,16.82a8,8,0,0,1,8,0l112.42,64.9A4,4,0,0,1,258.92,88.65Z"/></g><g id="_Path_7" data-name="&lt;Path&gt;"><path class="cls-2" d="M138.5,94.74l-11,6.35a4,4,0,0,0,0,6.92l44.94,26a8,8,0,0,0,8,0l11-6.35a4,4,0,0,0,0-6.93L146.5,94.74A8,8,0,0,0,138.5,94.74Z"/></g><g id="_Path_8" data-name="&lt;Path&gt;"><path class="cls-2" d="M112.76,134.08,124,127.61a4,4,0,0,0,0-6.93l-11.19-6.46a8,8,0,0,0-8,0l-11.2,6.46a4,4,0,0,0,0,6.93l11.2,6.47A8,8,0,0,0,112.76,134.08Z"/></g><g id="_Path_9" data-name="&lt;Path&gt;"><path class="cls-3" d="M146.5,153.56l11.19-6.47a4,4,0,0,0,0-6.93L146.5,133.7a8,8,0,0,0-8,0l-11.19,6.46a4,4,0,0,0,0,6.93l11.19,6.47A8,8,0,0,0,146.5,153.56Z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.2 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 285 325.47"><defs><style>.cls-1{fill:#38c138;}.cls-2{fill:#f6d182;}.cls-3{fill:#fff;}.cls-4{fill:#00a9f4;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Logo_RGB_White" data-name="Logo RGB White"><path id="Outline" d="M153.72,322.45l123.71-71.93A15.22,15.22,0,0,0,285,237.36V92.84a22.32,22.32,0,0,0-11.1-19.29L151.68,2.47a18.31,18.31,0,0,0-18.36,0L11.1,73.55A22.32,22.32,0,0,0,0,92.84V237.35A15.2,15.2,0,0,0,7.57,250.5l123.71,71.94A22.31,22.31,0,0,0,153.72,322.45Z"/><path id="Green" class="cls-1" d="M203,220.44v51.61a8,8,0,0,1-4,6.93l-44,25.39a4,4,0,0,1-6-3.46v-51.6a8,8,0,0,1,4-6.92L197,217A4,4,0,0,1,203,220.44Zm-54-10.08v12.73a4,4,0,0,0,6,3.46l44-25.39a8,8,0,0,0,4-6.93V181.49a4,4,0,0,0-6-3.46l-44,25.4A8,8,0,0,0,149,210.36Zm73,55.33,45-26a8,8,0,0,0,4-6.93V103.26a4,4,0,0,0-6-3.46l-45,26a8,8,0,0,0-4,6.93V262.23A4,4,0,0,0,222,265.69Z"/><path id="Yellow" class="cls-2" d="M136,210.36v12.73a4,4,0,0,1-6,3.46l-11-6.34a8,8,0,0,1-4-6.93V200.54a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,136,210.36Zm-4,32.06L20,177.76a4,4,0,0,0-6,3.46v51.57a8,8,0,0,0,4,6.93l112,64.65a4,4,0,0,0,6-3.46V249.35A8,8,0,0,0,132,242.42Z"/><g id="_Path_" data-name="&lt;Path&gt;"><path class="cls-3" d="M136,171.39v12.73a4,4,0,0,1-6,3.46l-11-6.35a8,8,0,0,1-4-6.93V161.57a4,4,0,0,1,6-3.46l11,6.35A8,8,0,0,1,136,171.39Z"/></g><g id="_Path_2" data-name="&lt;Path&gt;"><path class="cls-4" d="M99,145.41l-11-6.35a4,4,0,0,0-6,3.46v51.71a8,8,0,0,0,4,6.93l11,6.34a4,4,0,0,0,6-3.46v-51.7A8,8,0,0,0,99,145.41Z"/></g><g id="_Path_3" data-name="&lt;Path&gt;"><path class="cls-4" d="M14,103.26V155a8,8,0,0,0,4,6.93l45,26a4,4,0,0,0,6-3.46v-51.7a8,8,0,0,0-4-6.93l-45-26A4,4,0,0,0,14,103.26Z"/></g><g id="_Path_4" data-name="&lt;Path&gt;"><path class="cls-3" d="M149,184.12V171.39a8,8,0,0,1,4-6.93l11-6.35a4,4,0,0,1,6,3.46V174.3a8,8,0,0,1-4,6.93l-11,6.35A4,4,0,0,1,149,184.12Z"/></g><g id="_Path_5" data-name="&lt;Path&gt;"><path class="cls-4" d="M188,168.53l11-6.35a8,8,0,0,0,4-6.93V142.52a4,4,0,0,0-6-3.47l-11,6.36a8,8,0,0,0-4,6.93v12.72A4,4,0,0,0,188,168.53Z"/></g><g id="_Path_6" data-name="&lt;Path&gt;"><path class="cls-4" d="M258.92,88.65l-44.73,25.82a8,8,0,0,1-8,0L146.5,80a8,8,0,0,0-8,0L78.8,114.47a8,8,0,0,1-8,0L26.08,88.65a4,4,0,0,1,0-6.93L138.5,16.82a8,8,0,0,1,8,0l112.42,64.9A4,4,0,0,1,258.92,88.65Z"/></g><g id="_Path_7" data-name="&lt;Path&gt;"><path class="cls-4" d="M138.5,94.74l-11,6.35a4,4,0,0,0,0,6.92l44.94,26a8,8,0,0,0,8,0l11-6.35a4,4,0,0,0,0-6.93L146.5,94.74A8,8,0,0,0,138.5,94.74Z"/></g><g id="_Path_8" data-name="&lt;Path&gt;"><path class="cls-4" d="M112.76,134.08,124,127.61a4,4,0,0,0,0-6.93l-11.19-6.46a8,8,0,0,0-8,0l-11.2,6.46a4,4,0,0,0,0,6.93l11.2,6.47A8,8,0,0,0,112.76,134.08Z"/></g><g id="_Path_9" data-name="&lt;Path&gt;"><path class="cls-3" d="M146.5,153.56l11.19-6.47a4,4,0,0,0,0-6.93L146.5,133.7a8,8,0,0,0-8,0l-11.19,6.46a4,4,0,0,0,0,6.93l11.19,6.47A8,8,0,0,0,146.5,153.56Z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before

Width:  |  Height:  |  Size: 191 KiB

After

Width:  |  Height:  |  Size: 191 KiB

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 194 KiB

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 174 KiB

@@ -60,15 +60,16 @@ public class DhApi
*
* Note: Don't use this string in your code. It may change and is only for reference.
*/
public static final String READ_ME =
"""
If you don't see Javadocs something is wrong.
If you are only using the full DH Mod in your build script, you won't have access to our javadocs and could potentially call into unsafe code.
Please use the API jar in your build script as a compile time dependency and the full DH jar as a runtime dependency.
Please refer to the example API project or the DH Developer Wiki for additional information and suggested setup.
"""; // DH Dev note: no links were included to prevent link rot.
public static final String READ_ME =
"If you don't see Javadocs something is wrong. \n" +
"If you are only using the full DH Mod in your build script, you won't have access to our javadocs and could potentially call into unsafe code. \n" +
"\n" +
"Please use the API jar in your build script as a compile time dependency " +
"and the full DH jar as a runtime dependency. \n" +
"\n" +
"Please refer to the example API project or the DH Developer Wiki for additional information " +
"and suggested setup. \n" + // DH Dev note: no links were included to prevent link rot.
"";
public static String readMe() { return READ_ME; }
/**
@@ -179,7 +180,7 @@ public class DhApi
* This version should be updated whenever non-breaking fixes are added to the Distant Horizons API.
* @since API 1.0.0
*/
public static int getApiPatchVersion() { return ModInfo.API_PATH_VERSION; }
public static int getApiPatchVersion() { return ModInfo.API_PATCH_VERSION; }
/**
* Returns the mod's semantic version number in the format: Major.Minor.Patch
@@ -24,7 +24,7 @@ package com.seibel.distanthorizons.api.enums;
* CHUNK - Detail Level: 4, width 16 block, <br>
* REGION - Detail Level: 9, width 512 block <br> <br>
*
* Detail levels in Distant Horizons represent how large a section (of either LODs or MC chunks)
* Detail levels in Distant Horizons represent how large a LOD
* is, with the smallest being 0 (1 block wide). <br>
* The width of a detail level can be calculated by putting the detail level to the power of 2. <br>
* Example for the chunk detail level (4): 2^4 = 16 blocks wide <Br><br>
@@ -23,13 +23,12 @@ package com.seibel.distanthorizons.api.enums.config;
* AUTO, <br>
* BUFFER_STORAGE, <br>
* SUB_DATA, <br>
* BUFFER_MAPPING, <br>
* DATA <br>
*
* @author Leetom
* @author James Seibel
* @version 2024-4-6
* @since API 2.0.0
* @since API 3.0.0
*/
public enum EDhApiGpuUploadMethod
{
@@ -49,7 +48,10 @@ public enum EDhApiGpuUploadMethod
* 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. */
@@ -21,6 +21,7 @@ package com.seibel.distanthorizons.api.enums.rendering;
* AIR, <br>
* ILLUMINATED, <br>
*
* @author IMS
* @author James Seibel
* @since API 3.0.0
* @version 2024-7-11
@@ -56,23 +56,34 @@ public enum EDhApiDebugRendering
public static EDhApiDebugRendering next(EDhApiDebugRendering type)
{
return switch (type) {
case OFF -> SHOW_DETAIL;
case SHOW_DETAIL -> SHOW_BLOCK_MATERIAL;
case SHOW_BLOCK_MATERIAL -> SHOW_OVERLAPPING_QUADS;
case SHOW_OVERLAPPING_QUADS -> SHOW_RENDER_SOURCE_FLAG;
default -> OFF;
};
switch (type)
{
case OFF:
return SHOW_DETAIL;
case SHOW_DETAIL:
return SHOW_BLOCK_MATERIAL;
case SHOW_BLOCK_MATERIAL:
return SHOW_OVERLAPPING_QUADS;
case SHOW_OVERLAPPING_QUADS:
return SHOW_RENDER_SOURCE_FLAG;
default:
return OFF;
}
}
public static EDhApiDebugRendering previous(EDhApiDebugRendering type)
{
return switch (type) {
case OFF -> SHOW_RENDER_SOURCE_FLAG;
case SHOW_RENDER_SOURCE_FLAG -> SHOW_OVERLAPPING_QUADS;
case SHOW_OVERLAPPING_QUADS -> SHOW_DETAIL;
default -> OFF;
};
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;
default:
return OFF;
}
}
}
@@ -42,21 +42,29 @@ public enum EDhApiRendererMode
/** Used by the config GUI to cycle through the available rendering options */
public static EDhApiRendererMode next(EDhApiRendererMode type)
{
return switch (type) {
case DEFAULT -> DEBUG;
case DEBUG -> DISABLED;
default -> DEFAULT;
};
switch (type)
{
case DEFAULT:
return DEBUG;
case DEBUG:
return DISABLED;
default:
return DEFAULT;
}
}
/** Used by the config GUI to cycle through the available rendering options */
public static EDhApiRendererMode previous(EDhApiRendererMode type)
{
return switch (type) {
case DEFAULT -> DISABLED;
case DEBUG -> DEFAULT;
default -> DEBUG;
};
switch (type)
{
case DEFAULT:
return DISABLED;
case DEBUG:
return DEFAULT;
default:
return DEBUG;
}
}
}
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.api.interfaces.block;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
/**
@@ -44,7 +45,11 @@ public interface IDhApiBlockStateWrapper extends IDhApiUnsafeWrapper
* @since API 3.0.0
*/
String getSerialString();
/** @since API 3.0.0 */
/**
* Returns the byte value representing the {@link EDhApiBlockMaterial} enum.
* @see EDhApiBlockMaterial
* @since API 3.0.0
*/
byte getMaterialId();
}
@@ -36,7 +36,6 @@ public interface IDhApiConfig
IDhApiWorldGenerationConfig worldGenerator();
IDhApiMultiplayerConfig multiplayer();
IDhApiMultiThreadingConfig multiThreading();
IDhApiGpuBuffersConfig gpuBuffers();
// note: DON'T add the Auto Updater to this API. We only want the user's to have the ability to control when things are downloaded to their machines.
//IDhApiLoggingConfig logging(); // TODO implement
IDhApiDebuggingConfig debugging();
@@ -1,48 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.api.interfaces.config.client;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
/**
* Distant Horizons' OpenGL buffer configuration.
*
* @author James Seibel
* @version 2023-6-14
* @since API 1.0.0
*/
public interface IDhApiGpuBuffersConfig extends IDhApiConfigGroup
{
/** Defines how geometry data is uploaded to the GPU. */
IDhApiConfigValue<EDhApiGpuUploadMethod> gpuUploadMethod();
/**
* Defines how long we should wait after uploading one
* Megabyte of geometry data to the GPU before uploading
* the next Megabyte of data. <br>
* This can be set to a non-zero number to reduce stuttering caused by
* uploading buffers to the GPU.
*/
IDhApiConfigValue<Integer> gpuUploadPerMegabyteInMilliseconds();
}
@@ -90,11 +90,29 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
*/
default byte getMaxGenerationGranularity() { return (byte) (EDhApiDetailLevel.CHUNK.detailLevel + 2); }
/**
* @return true if the generator is unable to accept new generation requests.
/**
* Starting in API 3.0.0 DH now handles future queuing/management internally. <br><br>
*
* Previous description: <br>
* true if the generator is unable to accept new generation requests. <br>
*
* @since API 1.0.0
* @deprecated API 3.0.0
*/
boolean isBusy();
@Deprecated
default boolean isBusy() { return false; }
/**
* Only used if {@link #getReturnType()} returns {@link EDhApiWorldGeneratorReturnType#API_CHUNKS}. <Br>
* If true DH will run additional validation on the {@link DhApiChunk}'s returned. <Br>
* This should be disabled during release but should be enabled during development to help spot issues with your data format.
*
* @see #getReturnType()
* @see DhApiChunk
* @see EDhApiWorldGeneratorReturnType#API_CHUNKS
* @since API 3.0.0
*/
default boolean runApiChunkValidation() { return true; }
@@ -26,7 +26,7 @@ import com.seibel.distanthorizons.api.objects.DhApiResult;
* Used to interact with Distant Horizons' rendering system.
*
* @author James Seibel
* @version 2023-10-13
* @version 2024-7-27
* @since API 1.0.0
*/
public interface IDhApiRenderProxy
@@ -39,10 +39,8 @@ public interface IDhApiRenderProxy
* If this is called on a dedicated server it won't do anything and will return {@link DhApiResult#success} = false <Br><Br>
*
* Background: <Br>
* Distant Horizons has two different file formats: Full data and Render data. <Br>
* - Full data files store the block, biome, etc. information and is the result of loading or generating new chunks. <Br>
* - Render data files store LOD colors and are created using the Full data and currently loaded resource packs. <Br>
* This is the data cleared by this method.
* When rendering Distant Horizons bakes each block's color into the geometry that's rendered. <Br>
* This improves rendering speed and VRAM size, but prevents dynamically changing LOD colors. <Br>
*/
DhApiResult<Boolean> clearRenderDataCache();
@@ -28,8 +28,6 @@ import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
*/
public interface IDhApiDimensionTypeWrapper extends IDhApiUnsafeWrapper
{
String getDimensionName();
boolean hasCeiling();
boolean hasSkyLight();
@@ -28,20 +28,34 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist
* A level is equivalent to a dimension in vanilla Minecraft.
*
* @author James Seibel
* @version 2022-7-14
* @version 2024-7-28
* @since API 1.0.0
*/
public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper
{
IDhApiDimensionTypeWrapper getDimensionType();
/** @since API 4.0.0 */
String getDimensionName();
EDhApiLevelType getLevelType();
boolean hasCeiling();
boolean hasSkyLight();
/** Returns the max block height of the level(?) */
/**
* Deprecated, use {@link IDhApiLevelWrapper#getMaxHeight} instead. <br>
* Returns the max block height of the level.
*
* @see IDhApiLevelWrapper#getMaxHeight
*/
@Deprecated
default int getHeight() { return this.getMaxHeight(); }
/**
* Returns the max block height of the level
* @since API 3.0.0
*/
int getMaxHeight();
/**
@@ -53,6 +67,8 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper
/**
* Will return null if called on the server,
* or if called before the renderer has been set up.
*
* @since API 3.0.0
*/
IDhApiCustomRenderRegister getRenderRegister();
@@ -59,12 +59,12 @@ public class DhApiResult<T>
public static <Pt> DhApiResult<Pt> createSuccess() { return new DhApiResult<>(true, ""); }
public static <Pt> DhApiResult<Pt> createSuccess(Pt payload) { return new DhApiResult<>(true, "", payload); }
public static <Pt> DhApiResult<Pt> createSuccess(Pt payload) { return new DhApiResult<Pt>(true, "", payload); }
// There is no createSuccess(String message) method because it would be too easy to confuse with createSuccess(Pt payload) when returning null
public static <Pt> DhApiResult<Pt> createSuccess(String message, Pt payload) { return new DhApiResult<>(true, message, payload); }
public static <Pt> DhApiResult<Pt> createSuccess(String message, Pt payload) { return new DhApiResult<Pt>(true, message, payload); }
// there is no createFail() since all fail results should give a reason for their failure
public static <Pt> DhApiResult<Pt> createFail(String message) { return new DhApiResult<>(false, message); }
public static <Pt> DhApiResult<Pt> createFail(String message, Pt payload) { return new DhApiResult<>(false, message, payload); }
public static <Pt> DhApiResult<Pt> createFail(String message, Pt payload) { return new DhApiResult<Pt>(false, message, payload); }
}
@@ -21,8 +21,10 @@ package com.seibel.distanthorizons.api.objects.data;
import com.seibel.distanthorizons.api.interfaces.factories.IDhApiWrapperFactory;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
import org.apache.logging.log4j.LogManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
@@ -52,7 +54,27 @@ public class DhApiChunk
// constructors //
//==============//
public DhApiChunk(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos)
/**
* 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
*/
public static DhApiChunk create(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos)
{ return new DhApiChunk(chunkPosX, chunkPosZ, bottomYBlockPos, topYBlockPos, false); }
/**
* 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)
{
this.chunkPosX = chunkPosX;
this.chunkPosZ = chunkPosZ;
@@ -94,27 +116,34 @@ public class DhApiChunk
*/
public void setDataPoints(int relX, int relZ, List<DhApiTerrainDataPoint> dataPoints) throws IndexOutOfBoundsException, IllegalArgumentException
{
//==================//
// basic validation //
//==================//
// heavier validation is done in the world generator if requested
int internalArrayIndex = (relZ << 4) | relX;
throwIfRelativePosOutOfBounds(relX, relZ);
// validate the incoming datapoints
if (dataPoints != null)
if (dataPoints == null)
{
for (int i = 0; i < dataPoints.size(); i++) // standard for-loop used instead of an enhanced for-loop to slightly reduce GC overhead due to iterator allocation
{
DhApiTerrainDataPoint dataPoint = dataPoints.get(i);
if (dataPoint == null)
{
throw new IllegalArgumentException("Null DhApiTerrainDataPoints are not allowed. If you want to represent empty terrain, please use AIR.");
}
if (dataPoint.detailLevel != 0)
{
throw new IllegalArgumentException("DhApiTerrainDataPoints has the wrong detail level ["+dataPoint.detailLevel+"], all data points must be block sized; IE their detail level must be [0].");
}
}
// we don't allow null columns
throw new IllegalArgumentException("Null columns aren't allowed. If you want to remove all data from a column please clear the list or pass in an empty list.");
}
this.dataPoints.set((relZ << 4) | relX, dataPoints);
//================//
// set datapoints //
//================//
List<DhApiTerrainDataPoint> column = this.dataPoints.get(internalArrayIndex);
if (column == null)
{
column = new ArrayList<>();
this.dataPoints.set(internalArrayIndex, column);
}
column.addAll(dataPoints);
}
@@ -19,9 +19,12 @@
package com.seibel.distanthorizons.api.objects.data;
import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import java.util.ArrayList;
/**
* Holds a single datapoint of terrain data.
*
@@ -37,6 +40,8 @@ public class DhApiTerrainDataPoint
* 2 = 4x4 blocks <br>
* 4 = chunk (16x16 blocks) <br>
* 9 = region (512x512 blocks) <br>
*
* @see EDhApiDetailLevel
*/
public final byte detailLevel;
@@ -50,7 +55,57 @@ public class DhApiTerrainDataPoint
public DhApiTerrainDataPoint(byte detailLevel, int blockLightLevel, int skyLightLevel, int bottomYBlockPos, int topYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper)
//==============//
// 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
*/
public static DhApiTerrainDataPoint create(
byte detailLevel,
int blockLightLevel, int skyLightLevel,
int bottomYBlockPos, int topYBlockPos,
IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper
)
{
return new DhApiTerrainDataPoint(
detailLevel, blockLightLevel, skyLightLevel,
bottomYBlockPos, topYBlockPos,
blockStateWrapper, biomeWrapper,
false);
}
/**
* Only visible to internal DH methods
* @param ignoredParameter is only present to differentiate the two constructors and isn't actually used
*/
private DhApiTerrainDataPoint(
byte detailLevel,
int blockLightLevel, int skyLightLevel,
int bottomYBlockPos, int topYBlockPos,
IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper,
boolean ignoredParameter
)
{
this.detailLevel = detailLevel;
@@ -163,8 +163,9 @@ public class ApiEventInjector extends DependencyInjector<IDhApiEvent> implements
DhApiEventParam<T> eventParam = createEventParamWrapper(event, input);
event.fireEvent(eventParam);
if (eventParam instanceof DhApiCancelableEventParam<T> cancelableEventParam)
if (eventParam instanceof DhApiCancelableEventParam)
{
DhApiCancelableEventParam<T> cancelableEventParam = (DhApiCancelableEventParam<T>) eventParam;
cancelEvent |= cancelableEventParam.isEventCanceled();
}
@@ -87,7 +87,7 @@ public class DependencyInjector<BindableType extends IBindable> implements IDepe
// make sure the hashSet has an array to hold the dependency
if (!this.dependencies.containsKey(dependencyInterface))
{
this.dependencies.put(dependencyInterface, new ArrayList<>());
this.dependencies.put(dependencyInterface, new ArrayList<BindableType>());
}
// add the dependency
@@ -134,7 +134,7 @@ public class DependencyInjector<BindableType extends IBindable> implements IDepe
@Override
public <T extends BindableType> T get(Class<T> interfaceClass) throws ClassCastException
{
return (T) this.getInternalLogic(interfaceClass, false).getFirst();
return (T) this.getInternalLogic(interfaceClass, false).get(0);
}
@Override
@@ -146,7 +146,7 @@ public class DependencyInjector<BindableType extends IBindable> implements IDepe
@Override
public <T extends BindableType> T get(Class<T> interfaceClass, boolean allowIncompleteDependencies) throws ClassCastException
{
return (T) this.getInternalLogic(interfaceClass, allowIncompleteDependencies).getFirst();
return (T) this.getInternalLogic(interfaceClass, allowIncompleteDependencies).get(0);
}
/**
@@ -175,7 +175,7 @@ public class DependencyInjector<BindableType extends IBindable> implements IDepe
// return an empty list to prevent null pointers
ArrayList<T> emptyList = new ArrayList<>();
ArrayList<T> emptyList = new ArrayList<T>();
emptyList.add(null);
return emptyList;
}
@@ -64,14 +64,14 @@ public class OverridePriorityListContainer implements IBindable
else
{
// last item should have the highest priority
return this.overridePairList.getLast().override;
return this.overridePairList.get(this.overridePairList.size() - 1).override;
}
}
public IDhApiOverrideable getOverrideWithHighestPriority()
{
if (this.overridePairList.size() != 0)
{
return this.overridePairList.getFirst().override;
return this.overridePairList.get(0).override;
}
else
{
@@ -26,27 +26,28 @@ package com.seibel.distanthorizons.coreapi;
public final class ModInfo
{
public static final String ID = "distanthorizons";
/** The internal protocol version used for networking */
public static final int PROTOCOL_VERSION = 1;
/** The protocol version used for multiverse networking */
public static final int MULTIVERSE_PLUGIN_PROTOCOL_VERSION = 1;
public static final String RESOURCE_NAMESPACE = "distant_horizons";
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 = 4;
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.1.3-a-dev";
public static final String VERSION = "2.3.0-a-dev";
/** Returns true if the current build is an unstable developer build, false otherwise. */
public static 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 = 3;
public static final int API_MAJOR_VERSION = 4;
/** 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_PATH_VERSION = 0;
public static final String NETWORKING_RESOURCE_NAMESPACE = "distant_horizons";
public static final String MULTIVERSE_PLUGIN_NAMESPACE = "world_control";
public static final int API_PATCH_VERSION = 0;
/** All DH owned threads should start with this string to allow for easier debugging and profiling. */
public static final String THREAD_NAME_PREFIX = "DH-";
@@ -24,12 +24,12 @@ import java.util.Arrays;
/**
* Miscellaneous string helper functions.
*
* @author James Seibel
* @version 2022-7-19
*/
public class StringUtil
{
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
/**
* Returns the n-th index of the given string. <br> <br>
*
@@ -67,8 +67,6 @@ public class StringUtil
return stringBuilder.toString();
}
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
/**
* Converts the given byte array into a hex string representation. <br>
* source: https://stackoverflow.com/a/9855338
@@ -85,4 +83,20 @@ public class StringUtil
return new String(hexChars);
}
/**
* Returns a shortened version of the given string that is no longer than maxLength. <br>
* If null returns the empty string.
*/
public static String shortenString(String str, int maxLength)
{
if (str == null)
{
return "";
}
else
{
return str.substring(0, Math.min(str.length(), maxLength));
}
}
}
+12 -3
View File
@@ -10,7 +10,7 @@ application {
}
configurations {
downgradedArtifact // Used by DH to specify that we want to implement the shadowed core JAR file instead of the regular JAR file
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
}
@@ -39,7 +39,9 @@ dependencies { // All of these dependencies are in Vanilla Minecraft, but we nee
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
implementation "org.joml:joml:${rootProject.joml_version}"
// 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
// Some other dependencies
@@ -47,8 +49,15 @@ dependencies { // All of these dependencies are in Vanilla Minecraft, but we nee
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")
}
artifacts {
downgradedArtifact shadeDowngradedApi // Setup the configuration downgradedArtifact to be the `shadeDowngradedApi` which downgrades the core to a specified Java version
shadowedArtifact shadowJar // Setup the configuration shadowedArtifact to be the shadowJar
}
shadowJar {
def librariesLocation = "DistantHorizons.libraries"
// relocate "it.unimi.dsi.fastutil", "${librariesLocation}.unimi.dsi.fastutil"
mergeServiceFiles()
}
@@ -23,6 +23,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory;
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;
@@ -32,14 +33,19 @@ import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import net.jpountz.lz4.LZ4FrameOutputStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.sqlite.SQLiteJDBCLoader;
import org.sqlite.util.OSInfo;
import org.tukaani.xz.XZOutputStream;
import java.awt.*;
import java.io.File;
/** Handles first time Core setup. */
public class Initializer
{
private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME + "-" + Initializer.class.getSimpleName());
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
public static void init()
{
@@ -54,12 +60,44 @@ public class Initializer
Class<?> config = com.electronwill.nightconfig.core.Config.class;
Class<?> oldFastUtil = it.unimi.dsi.fastutil.longs.LongArrayList.class; // available in 8.2.1
//Class<?> newFastUtil = it.unimi.dsi.fastutil.ints.IntUnaryOperator.class; // available in 8.5.13
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)
{
throw new RuntimeException("Failed to load SQLite native library. Hopefully SQLite logged a reason for this failure.");
}
}
catch (Throwable e)
{
LOGGER.fatal("Critical programmer error: One or more libraries aren't present. Error: [" + e.getMessage() + "].");
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 e;
throw new RuntimeException(e);
}
// confirm the resource directory is present
@@ -77,16 +115,18 @@ public class Initializer
throw new RuntimeException(e);
}
// attempt to setup Swing so we can display dialogs (popup windows)
System.setProperty("java.awt.headless", "false");
if (GraphicsEnvironment.isHeadless())
if (MC_CLIENT != null)
{
LOGGER.warn("Java.awt.headless is false. This means Distant Horizons can't display error and info dialog windows.");
}
else
{
LOGGER.info("Java.awt.headless set to true. Distant Horizons can correctly display error and info dialog windows.");
// attempt to set up Swing so we can display dialogs (popup windows)
System.setProperty("java.awt.headless", "false");
if (GraphicsEnvironment.isHeadless())
{
LOGGER.warn("Java.awt.headless is false. This means Distant Horizons can't display error and info dialog windows.");
}
else
{
LOGGER.info("Java.awt.headless set to true. Distant Horizons can correctly display error and info dialog windows.");
}
}
// link Core's config to the API
@@ -42,8 +42,6 @@ public class DhApiConfig implements IDhApiConfig
@Override
public IDhApiMultiThreadingConfig multiThreading() { return DhApiMultiThreadingConfig.INSTANCE; }
@Override
public IDhApiGpuBuffersConfig gpuBuffers() { return DhApiGpuBuffersConfig.INSTANCE; }
@Override
public IDhApiDebuggingConfig debugging() { return DhApiDebuggingConfig.INSTANCE; }
}
@@ -35,30 +35,30 @@ public class DhApiAmbientOcclusionConfig implements IDhApiAmbientOcclusionConfig
@Override
public IDhApiConfigValue<Boolean> enabled()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Ssao.enabled); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.Ssao.enabled); }
@Override
public IDhApiConfigValue<Integer> sampleCount()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Ssao.sampleCount); }
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.Ssao.sampleCount); }
@Override
public IDhApiConfigValue<Double> radius()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Ssao.radius); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Ssao.radius); }
@Override
public IDhApiConfigValue<Double> strength()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Ssao.strength); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Ssao.strength); }
@Override
public IDhApiConfigValue<Double> bias()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Ssao.bias); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Ssao.bias); }
@Override
public IDhApiConfigValue<Double> minLight()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Ssao.minLight); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Ssao.minLight); }
@Override
public IDhApiConfigValue<Integer> blurRadius()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Ssao.blurRadius); }
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.Ssao.blurRadius); }
}
@@ -34,18 +34,18 @@ public class DhApiDebuggingConfig implements IDhApiDebuggingConfig
public IDhApiConfigValue<EDhApiDebugRendering> debugRendering()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Debugging.debugRendering); }
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRendering); }
public IDhApiConfigValue<Boolean> debugKeybindings()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Debugging.enableDebugKeybindings); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.enableDebugKeybindings); }
public IDhApiConfigValue<Boolean> renderWireframe()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Debugging.renderWireframe); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.renderWireframe); }
public IDhApiConfigValue<Boolean> lodOnlyMode()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Debugging.lodOnlyMode); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.lodOnlyMode); }
public IDhApiConfigValue<Boolean> debugWireframeRendering()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Debugging.DebugWireframe.enableRendering); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.DebugWireframe.enableRendering); }
}
@@ -35,26 +35,26 @@ public class DhApiFarFogConfig implements IDhApiFarFogConfig
@Override
public IDhApiConfigValue<Double> farFogStartDistance()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogStart); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogStart); }
@Override
public IDhApiConfigValue<Double> farFogEndDistance()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogEnd); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogEnd); }
@Override
public IDhApiConfigValue<Double> farFogMinThickness()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogMin); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogMin); }
@Override
public IDhApiConfigValue<Double> farFogMaxThickness()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogMax); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogMax); }
@Override
public IDhApiConfigValue<EDhApiFogFalloff> farFogFalloff()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogFalloff); }
{ return new DhApiConfigValue<EDhApiFogFalloff, EDhApiFogFalloff>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogFalloff); }
@Override
public IDhApiConfigValue<Double> farFogDensity()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogDensity); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogDensity); }
}
@@ -35,12 +35,12 @@ public class DhApiGenericRenderingConfig implements IDhApiGenericRenderingConfig
@Override
public IDhApiConfigValue<Boolean> renderingEnabled()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.GenericRendering.enableRendering); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.GenericRendering.enableRendering); }
@Override
public IDhApiConfigValue<Boolean> beaconRenderingEnabled()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering); }
@Override
public IDhApiConfigValue<Boolean> cloudRenderingEnabled()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering); }
}
@@ -1,42 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.api.external.methods.config.client;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiGpuBuffersConfig;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
public class DhApiGpuBuffersConfig implements IDhApiGpuBuffersConfig
{
public static DhApiGpuBuffersConfig INSTANCE = new DhApiGpuBuffersConfig();
private DhApiGpuBuffersConfig() { }
public IDhApiConfigValue<EDhApiGpuUploadMethod> gpuUploadMethod()
{ return new DhApiConfigValue<>(Config.Client.Advanced.GpuBuffers.gpuUploadMethod); }
public IDhApiConfigValue<Integer> gpuUploadPerMegabyteInMilliseconds()
{ return new DhApiConfigValue<>(Config.Client.Advanced.GpuBuffers.gpuUploadPerMegabyteInMilliseconds); }
}
@@ -56,15 +56,15 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
@Override
public IDhApiConfigValue<Integer> chunkRenderDistance()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius); }
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius); }
@Override
public IDhApiConfigValue<Boolean> renderingEnabled()
{ return new DhApiConfigValue<>(Config.Client.quickEnableRendering); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.quickEnableRendering); }
@Override
public IDhApiConfigValue<EDhApiRendererMode> renderingMode()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Debugging.rendererMode); }
{ return new DhApiConfigValue<EDhApiRendererMode, EDhApiRendererMode>(Config.Client.Advanced.Debugging.rendererMode); }
@@ -74,27 +74,27 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
@Override
public IDhApiConfigValue<EDhApiMaxHorizontalResolution> maxHorizontalResolution()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution); }
{ return new DhApiConfigValue<EDhApiMaxHorizontalResolution, EDhApiMaxHorizontalResolution>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution); }
@Override
public IDhApiConfigValue<EDhApiVerticalQuality> verticalQuality()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Quality.verticalQuality); }
{ return new DhApiConfigValue<EDhApiVerticalQuality, EDhApiVerticalQuality>(Config.Client.Advanced.Graphics.Quality.verticalQuality); }
@Override
public IDhApiConfigValue<EDhApiHorizontalQuality> horizontalQuality()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Quality.horizontalQuality); }
{ return new DhApiConfigValue<EDhApiHorizontalQuality, EDhApiHorizontalQuality>(Config.Client.Advanced.Graphics.Quality.horizontalQuality); }
@Override
public IDhApiConfigValue<EDhApiTransparency> transparency()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Quality.transparency); }
{ return new DhApiConfigValue<EDhApiTransparency, EDhApiTransparency>(Config.Client.Advanced.Graphics.Quality.transparency); }
@Override
public IDhApiConfigValue<EDhApiBlocksToAvoid> blocksToAvoid()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Quality.blocksToIgnore); }
{ return new DhApiConfigValue<EDhApiBlocksToAvoid, EDhApiBlocksToAvoid>(Config.Client.Advanced.Graphics.Quality.blocksToIgnore); }
@Override
public IDhApiConfigValue<Boolean> tintWithAvoidedBlocks()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks); }
// TODO re-implement
// @Override
@@ -109,47 +109,47 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
@Override
public IDhApiConfigValue<Double> overdrawPreventionRadius()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.AdvancedGraphics.overdrawPrevention); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.AdvancedGraphics.overdrawPrevention); }
@Override
public IDhApiConfigValue<Double> brightnessMultiplier()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.AdvancedGraphics.brightnessMultiplier); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.AdvancedGraphics.brightnessMultiplier); }
@Override
public IDhApiConfigValue<Double> saturationMultiplier()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.AdvancedGraphics.saturationMultiplier); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.AdvancedGraphics.saturationMultiplier); }
@Override
public IDhApiConfigValue<Boolean> caveCullingEnabled()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling); }
@Override
public IDhApiConfigValue<Integer> caveCullingHeight()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight); }
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight); }
@Override
public IDhApiConfigValue<Integer> earthCurvatureRatio()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio); }
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio); }
@Override
public IDhApiConfigValue<Boolean> lodOnlyMode()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Debugging.lodOnlyMode); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.lodOnlyMode); }
@Override
public IDhApiConfigValue<Double> lodBias()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.AdvancedGraphics.lodBias); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.AdvancedGraphics.lodBias); }
@Override
public IDhApiConfigValue<EDhApiLodShading> lodShading()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading); }
{ return new DhApiConfigValue<EDhApiLodShading, EDhApiLodShading>(Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading); }
@Override
public IDhApiConfigValue<Boolean> disableFrustumCulling()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.AdvancedGraphics.disableFrustumCulling); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.AdvancedGraphics.disableFrustumCulling); }
@Override
public IDhApiConfigValue<Boolean> disableShadowFrustumCulling()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.AdvancedGraphics.disableShadowPassFrustumCulling); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.AdvancedGraphics.disableShadowPassFrustumCulling); }
@@ -37,38 +37,38 @@ public class DhApiHeightFogConfig implements IDhApiHeightFogConfig
@Override
public IDhApiConfigValue<EDhApiHeightFogMixMode> heightFogMixMode()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMixMode); }
{ return new DhApiConfigValue<EDhApiHeightFogMixMode, EDhApiHeightFogMixMode>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMixMode); }
@Override
public IDhApiConfigValue<EDhApiHeightFogMode> heightFogMode()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMode); }
{ return new DhApiConfigValue<EDhApiHeightFogMode, EDhApiHeightFogMode>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMode); }
@Override
public IDhApiConfigValue<Double> heightFogBaseHeight()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogBaseHeight); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogBaseHeight); }
@Override
public IDhApiConfigValue<Double> heightFogStartingHeightPercent()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogStart); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogStart); }
@Override
public IDhApiConfigValue<Double> heightFogEndingHeightPercent()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogEnd); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogEnd); }
@Override
public IDhApiConfigValue<Double> heightFogMinThickness()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMin); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMin); }
@Override
public IDhApiConfigValue<Double> heightFogMaxThickness()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMax); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMax); }
@Override
public IDhApiConfigValue<EDhApiFogFalloff> heightFogFalloff()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogFalloff); }
{ return new DhApiConfigValue<EDhApiFogFalloff, EDhApiFogFalloff>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogFalloff); }
@Override
public IDhApiConfigValue<Double> heightFogDensity()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogDensity); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogDensity); }
}
@@ -34,14 +34,14 @@ public class DhApiMultiThreadingConfig implements IDhApiMultiThreadingConfig
@Override
public IDhApiConfigValue<Integer> worldGeneratorThreads()
{ return new DhApiConfigValue<>(Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads); }
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads); }
@Override
public IDhApiConfigValue<Integer> fileHandlerThreads()
{ return new DhApiConfigValue<>(Config.Client.Advanced.MultiThreading.numberOfFileHandlerThreads); }
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.MultiThreading.numberOfFileHandlerThreads); }
@Override
public IDhApiConfigValue<Integer> lodBuilderThreads()
{ return new DhApiConfigValue<>(Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads); }
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads); }
}
@@ -34,9 +34,9 @@ public class DhApiMultiplayerConfig implements IDhApiMultiplayerConfig
public IDhApiConfigValue<EDhApiServerFolderNameMode> folderSavingMode()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Multiplayer.serverFolderNameMode); }
{ return new DhApiConfigValue<EDhApiServerFolderNameMode, EDhApiServerFolderNameMode>(Config.Client.Advanced.Multiplayer.serverFolderNameMode); }
public IDhApiConfigValue<Double> multiverseSimilarityRequirement()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent); }
}
@@ -34,18 +34,18 @@ public class DhApiNoiseTextureConfig implements IDhApiNoiseTextureConfig
@Override
public IDhApiConfigValue<Boolean> noiseEnabled()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseEnabled); }
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseEnabled); }
@Override
public IDhApiConfigValue<Integer> noiseSteps()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseSteps); }
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseSteps); }
@Override
public IDhApiConfigValue<Double> noiseIntensity()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseIntensity); }
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseIntensity); }
@Override
public IDhApiConfigValue<Integer> noiseDropoff()
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseDropoff); }
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseDropoff); }
}
@@ -187,20 +187,22 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
return DhApiResult.createFail("Unable to get terrain data before the world has loaded.");
}
if (!(levelWrapper instanceof ILevelWrapper coreLevelWrapper))
if (!(levelWrapper instanceof ILevelWrapper))
{
// custom level wrappers aren't supported,
// the API user must get a level wrapper from our code somewhere
return DhApiResult.createFail("Unsupported [" + IDhApiLevelWrapper.class.getSimpleName() + "] implementation, only the core class [" + IDhLevel.class.getSimpleName() + "] is a valid parameter.");
}
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
if (!(apiDataCache instanceof DhApiTerrainDataCache dataCache))
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;
IDhLevel level = currentWorld.getLevel(coreLevelWrapper);
@@ -324,9 +326,10 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
int height = FullDataPointUtil.getHeight(dataPoint);
int topY = bottomY + height;
return new DhApiTerrainDataPoint(detailLevel,
return DhApiTerrainDataPoint.create(
detailLevel,
FullDataPointUtil.getBlockLight(dataPoint), FullDataPointUtil.getSkyLight(dataPoint),
topY, bottomY,
bottomY, topY,
blockState, biomeWrapper);
}
@@ -24,18 +24,20 @@ 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.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.world.*;
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;
import com.seibel.distanthorizons.coreapi.ModInfo;
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;
@@ -43,21 +45,21 @@ 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.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
//import io.netty.buffer.ByteBuf;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Queue;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -75,9 +77,7 @@ public class ClientApi
public static final ClientApi INSTANCE = new ClientApi();
public static final TestRenderer TEST_RENDERER = new TestRenderer();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
public static final long SPAM_LOGGER_FLUSH_NS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
@@ -90,10 +90,11 @@ public class ClientApi
private long lastFlushNanoTime = 0;
private boolean isServerCommunicationEnabled = false;
private final ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent);
/** set to true if any unexpected responses are received from the server */
private boolean serverNetworkingIsMalformed = false;
/** Delay loading the first level to give the server some time to respond with level to actually load */
private Timer firstLevelLoadTimer;
private static final long FIRST_LEVEL_LOAD_DELAY_IN_MS = 1_000;
/** Holds any levels that were loaded before the {@link ClientApi#onClientOnlyConnected} was fired. */
public final HashSet<IClientLevelWrapper> waitingClientLevels = new HashSet<>();
@@ -127,8 +128,8 @@ public class ClientApi
public synchronized void onClientOnlyConnected()
{
// only continue if the client is connected to a different server
boolean connectedToServer = MC.clientConnectedToDedicatedServer();
boolean connectedToReplay = MC.connectedToReplay();
boolean connectedToServer = MC_CLIENT.clientConnectedToDedicatedServer();
boolean connectedToReplay = MC_CLIENT.connectedToReplay();
if (connectedToServer || connectedToReplay)
{
if (connectedToServer)
@@ -141,19 +142,22 @@ public class ClientApi
if (Config.Client.Advanced.Logging.showReplayWarningOnStartup.get())
{
MC.sendChatMessage("\u00A76" + "Distant Horizons: Replay detected." + "\u00A7r"); // gold color
MC.sendChatMessage("DH may behave strangely or have missing functionality.");
MC.sendChatMessage("In order to use pre-generated LODs, put your DH database(s) in:");
MC.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.sendChatMessage("This can be disabled in DH's config under Advanced -> Logging.");
MC.sendChatMessage("");
MC_CLIENT.sendChatMessage("\u00A76" + "Distant Horizons: Replay detected." + "\u00A7r"); // gold color
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("");
}
}
// firing after clientLevelLoadEvent
// TODO if level has prepped to load it should fire level load event
SharedApi.setDhWorld(new DhClientWorld());
DhClientWorld world = new DhClientWorld();
SharedApi.setDhWorld(world);
this.pluginChannelApi.onJoinServer(world.networkState.getSession());
world.networkState.sendConfigMessage();
LOGGER.info("Loading [" + this.waitingClientLevels.size() + "] waiting client level wrappers.");
for (IClientLevelWrapper level : this.waitingClientLevels)
@@ -168,6 +172,13 @@ public class ClientApi
/** Synchronized to prevent a rare issue where multiple disconnect events are triggered on top of each other. */
public synchronized void onClientOnlyDisconnected()
{
// clear the first time timer
if (this.firstLevelLoadTimer != null)
{
this.firstLevelLoadTimer.cancel();
this.firstLevelLoadTimer = null;
}
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
if (world != null)
{
@@ -177,11 +188,7 @@ public class ClientApi
SharedApi.setDhWorld(null);
}
// clear the previous server's information
this.isServerCommunicationEnabled = false;
this.serverNetworkingIsMalformed = false;
KEYED_CLIENT_LEVEL_MANAGER.setUseOverrideWrapper(false);
KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(null);
this.pluginChannelApi.reset();
// remove any waiting items
this.waitingChunkByClientLevelAndPos.clear();
@@ -194,16 +201,16 @@ public class ClientApi
// level events //
//==============//
public void clientLevelUnloadEvent(@Nullable IClientLevelWrapper level)
public void clientLevelUnloadEvent(IClientLevelWrapper level)
{
try
{
if (level == null)
LOGGER.info("Unloading client level [" + level + "]-["+level.getDimensionName()+"].");
if (level instanceof IServerKeyedClientLevel)
{
// can happen on certain multiverse servers
return;
this.pluginChannelApi.onClientLevelUnload();
}
LOGGER.info("Unloading client level [" + level + "]-["+level.getDimensionType().getDimensionName()+"].");
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
if (world != null)
@@ -223,30 +230,42 @@ public class ClientApi
}
}
public void clientLevelLoadEvent(@Nullable IClientLevelWrapper level) { this.clientLevelLoadEvent(level, false); }
public void multiverseClientLevelLoadEvent(@Nullable IClientLevelWrapper level) { this.clientLevelLoadEvent(level, true); }
private void clientLevelLoadEvent(@Nullable IClientLevelWrapper level, boolean isServerCommunication)
public void clientLevelLoadEvent(IClientLevelWrapper level)
{
// wait a moment before loading the level to give the server a chance to handle the client's login request
if (MC_CLIENT.clientConnectedToDedicatedServer())
{
if (this.firstLevelLoadTimer == null)
{
this.firstLevelLoadTimer = TimerUtil.CreateTimer("FirstLevelLoadTimer");
this.firstLevelLoadTimer.schedule(new TimerTask()
{
@Override
public void run() { ClientApi.this.clientLevelLoadEvent(level); }
}, FIRST_LEVEL_LOAD_DELAY_IN_MS);
return;
}
this.firstLevelLoadTimer.cancel();
}
try
{
if (this.isServerCommunicationEnabled && !isServerCommunication)
{
LOGGER.info("Server supports communication, deferring loading.");
return;
}
if (level == null)
{
// can happen on certain multiverse servers
return;
}
LOGGER.info("Loading " + (isServerCommunication ? "Multiverse" : "") + " client level [" + level + "]-["+level.getDimensionType().getDimensionName()+"].");
LOGGER.info("Loading client level [" + level + "]-["+level.getDimensionName()+"].");
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
if (world != null)
{
if (!this.pluginChannelApi.allowLevelLoading(level))
{
LOGGER.info("Levels in this connection are managed by the server, skipping auto-load.");
// Instead of attempting to load themselves, send the config and wait for a server provided level key.
((DhClientWorld) world).networkState.sendConfigMessage();
return;
}
world.getOrLoadLevel(level);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(level));
@@ -295,7 +314,7 @@ public class ClientApi
{
LOGGER.info("Renderer shutting down.");
IProfilerWrapper profiler = MC.getProfiler();
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
profiler.push("DH-RendererShutdown");
profiler.pop();
@@ -305,7 +324,7 @@ public class ClientApi
{
LOGGER.info("Renderer starting up.");
IProfilerWrapper profiler = MC.getProfiler();
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
profiler.push("DH-RendererStartup");
// make sure the GLProxy is created before the LodBufferBuilder needs it
@@ -315,7 +334,7 @@ public class ClientApi
public void clientTickEvent()
{
IProfilerWrapper profiler = MC.getProfiler();
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
profiler.push("DH-ClientTick");
try
@@ -337,7 +356,7 @@ public class ClientApi
// Ignore local world gen, as it's managed by server ticking
if (!(clientWorld instanceof DhClientServerWorld))
{
SharedApi.worldGenTick(clientWorld::doWorldGen);
SharedApi.worldGenTick(clientWorld::worldGenTick);
}
}
}
@@ -355,124 +374,15 @@ public class ClientApi
//============//
// networking //
//============//
// /** @param byteBuf is Netty's {@link ByteBuffer} wrapper. */
// public void serverMessageReceived(ByteBuf byteBuf)
// {
// if (!Config.Client.Advanced.Multiplayer.enableMultiverseNetworking.get())
// {
// // multiverse networking disabled, ignore anything sent from the server
// return;
// }
//
//
//
// // either value can be set to true to debug the received byte stream
// boolean stopAndDisplayInputAsByteArray = false;
// boolean stopAndDisplayInputAsString = false;
// if (stopAndDisplayInputAsByteArray || stopAndDisplayInputAsString)
// {
// String messageString = "";
// if (stopAndDisplayInputAsByteArray)
// {
// int byteCount = byteBuf.readableBytes();
// byte[] arr = new byte[byteCount];
// StringBuilder stringBuilder = new StringBuilder("Server message received: [");
// for (int i = 0; i < byteCount; i++)
// {
// arr[i] = byteBuf.readByte();
// stringBuilder.append(arr[i]);
// }
// stringBuilder.append("]");
//
// messageString = stringBuilder.toString();
// }
// else if (stopAndDisplayInputAsString)
// {
// messageString = byteBuf.toString(StandardCharsets.UTF_8);
// }
//
// // this is logged as an error so it is easier to see in an Intellij log
// LOGGER.error(messageString);
// return;
// }
//
//
//
//
// // It is important to ensure malicious server input is ignored.
// if (this.serverNetworkingIsMalformed)
// {
// return;
// }
//
// // check that the incoming message is within the expected size
// short commandLength = byteBuf.readShort();
// if (commandLength < 1 || commandLength > 32)
// {
// LOGGER.error("Server command length ["+commandLength+"] outside the expected range of 1 to 32 (inclusive).");
// ClientApi.INSTANCE.serverNetworkingIsMalformed = true;
// return;
// }
//
// // parse the command
// String eventType;
// try
// {
// eventType = byteBuf.readCharSequence(commandLength, StandardCharsets.UTF_8).toString();
// }
// catch (Exception e)
// {
// LOGGER.error("Server sent un-parsable command. Error: "+e.getMessage());
// return;
// }
//
// switch (eventType)
// {
// case "ServerCommsEnabled":
// LOGGER.info("Server supports DH multiverse protocol.");
// ClientApi.INSTANCE.isServerCommunicationEnabled = true;
// KEYED_CLIENT_LEVEL_MANAGER.setUseOverrideWrapper(true);
// MC.executeOnRenderThread(() ->
// {
// // Unload the current world, since it may be wrong.
// // A followup WorldChanged event should be received from the server soon after this.
// LOGGER.info("Unloading current client level so the server can define the correct multiverse level.");
// this.clientLevelUnloadEvent((IClientLevelWrapper) MC.getWrappedClientWorld());
// });
// break;
//
// case "LevelChanged":
// short levelKeyLength = byteBuf.readShort();
// if (levelKeyLength < 1 || levelKeyLength > 128) // TODO 128 should be put into a constant somewhere
// {
// LOGGER.error("Server [LevelChanged] command length ["+commandLength+"] outside the expected range of 1 to 128 (inclusive).");
// this.serverNetworkingIsMalformed = true;
// return;
// }
//
// String levelKey = byteBuf.readCharSequence(levelKeyLength, StandardCharsets.UTF_8).toString();
// if (!levelKey.matches("[a-zA-Z0-9_]+"))
// {
// LOGGER.error("Server sent invalid world key name, and is being ignored.");
// this.isServerCommunicationEnabled = false;
// this.serverNetworkingIsMalformed = true;
// return;
// }
//
// LOGGER.info("Server level change event received, changing the level to ["+levelKey+"].");
// MC.executeOnRenderThread(() -> {
// if (MC.getWrappedClientWorld() != null)
// {
// this.clientLevelUnloadEvent((IClientLevelWrapper) MC.getWrappedClientWorld());
// }
// IServerKeyedClientLevel clientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel(MC.getWrappedClientWorld(), levelKey);
// KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel);
// this.multiverseClientLevelLoadEvent(clientLevel);
// });
// break;
// }
// }
public void pluginMessageReceived(@NotNull AbstractNetworkMessage message)
{
NetworkSession networkSession = this.pluginChannelApi.networkSession;
if (networkSession != null)
{
networkSession.tryHandleMessage(message);
}
}
@@ -497,9 +407,9 @@ public class ClientApi
{
// logging //
this.sendChatMessagesNow();
this.sendQueuedChatMessages();
IProfilerWrapper profiler = MC.getProfiler();
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
profiler.pop(); // get out of "terrain"
profiler.push("DH-RenderLevel");
@@ -550,10 +460,24 @@ public class ClientApi
{
return;
}
IDhClientLevel level = dhClientWorld.getOrLoadClientLevel(levelWrapper);
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;
}
@@ -603,10 +527,10 @@ public class ClientApi
this.rendererDisabledBecauseOfExceptions = true;
LOGGER.error("Unexpected Renderer error in render pass [" + renderPass + "]. Error: " + e.getMessage(), e);
MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons renderer has encountered an exception!");
MC.sendChatMessage("\u00A74Renderer is now disabled to prevent further issues.");
MC.sendChatMessage("\u00A74Please restart your game to re-enable Distant Horizons' LOD rendering.");
MC.sendChatMessage("\u00A74Exception detail: " + 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
{
@@ -646,32 +570,34 @@ public class ClientApi
if (glfwKey == GLFW.GLFW_KEY_F8)
{
Config.Client.Advanced.Debugging.debugRendering.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get()));
MC.sendChatMessage("F8: Set debug mode to " + 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)
{
Config.Client.Advanced.Debugging.rendererMode.set(EDhApiRendererMode.next(Config.Client.Advanced.Debugging.rendererMode.get()));
MC.sendChatMessage("F6: Set rendering to " + 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)
{
prefLoggerEnabled = !prefLoggerEnabled;
MC.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
MC_CLIENT.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
}
}
private void sendChatMessagesNow()
private void sendQueuedChatMessages()
{
// dev build
if (ModInfo.IS_DEV_BUILD && !this.configOverrideReminderPrinted && MC.playerExists())
if (ModInfo.IS_DEV_BUILD && !this.configOverrideReminderPrinted && MC_CLIENT.playerExists())
{
this.configOverrideReminderPrinted = true;
// remind the user that this is a development build
MC.sendChatMessage("\u00A72" + "Distant Horizons: nightly/unstable build, version: [" + ModInfo.VERSION+"]." + "\u00A7r");
MC.sendChatMessage("Issues may occur with this version.");
MC.sendChatMessage("Here be dragons!");
MC.sendChatMessage("");
String message =
// green text
"\u00A72" + "Distant Horizons: nightly/unstable build, version: [" + ModInfo.VERSION+"]." + "\u00A7r\n" +
"Issues may occur with this version.\n" +
"Here be dragons!\n";
MC_CLIENT.sendChatMessage(message);
}
// memory
@@ -686,11 +612,13 @@ public class ClientApi
long maxMemoryInBytes = Runtime.getRuntime().maxMemory();
if (maxMemoryInBytes < minimumRecommendedMemoryInBytes)
{
MC.sendChatMessage("\u00A76" + "Distant Horizons: Low memory detected." + "\u00A7r");
MC.sendChatMessage("Stuttering or low FPS may occur.");
MC.sendChatMessage("Please increase Minecraft's available memory to 4 gigabytes.");
MC.sendChatMessage("This warning can be disabled in DH's config under Advanced -> Logging.");
MC.sendChatMessage("");
String message =
// orange text
"\u00A76" + "Distant Horizons: Low memory detected." + "\u00A7r \n" +
"Stuttering or low FPS may occur. \n" +
"Please increase Minecraft's available memory to 4 gigabytes. \n" +
"This warning can be disabled in DH's config under Advanced -> Logging. \n";
MC_CLIENT.sendChatMessage(message);
}
}
@@ -703,7 +631,7 @@ public class ClientApi
// done to prevent potential null pointers
message = "";
}
MC.sendChatMessage(message);
MC_CLIENT.sendChatMessage(message);
}
}
@@ -0,0 +1,134 @@
package com.seibel.distanthorizons.core.api.internal;
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.network.event.internal.CloseInternalEvent;
import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage;
import com.seibel.distanthorizons.core.network.session.NetworkSession;
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.Nullable;
import java.util.Objects;
import java.util.function.Consumer;
/**
* This class is used to manage the level keys.
*/
public class ClientPluginChannelApi
{
private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
() -> Config.Client.Advanced.Logging.logNetworkEvent.get());
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
private final Consumer<IServerKeyedClientLevel> levelLoadHandler;
private final Consumer<IClientLevelWrapper> levelUnloadHandler;
@Nullable
public NetworkSession networkSession;
//=============//
// constructor //
//=============//
public ClientPluginChannelApi(Consumer<IServerKeyedClientLevel> levelLoadHandler, Consumer<IClientLevelWrapper> levelUnloadHandler)
{
this.levelLoadHandler = levelLoadHandler;
this.levelUnloadHandler = levelUnloadHandler;
}
//============//
// properties //
//============//
/** @return true if the level loading is handled by the server */
public boolean allowLevelLoading(IClientLevelWrapper level)
{
return (KEYED_CLIENT_LEVEL_MANAGER.hasLevelSet() && level instanceof IServerKeyedClientLevel)
|| !KEYED_CLIENT_LEVEL_MANAGER.hasLevelSet();
}
//================//
// network events //
//================//
/** fired when this client connects to a server with DH support */
public void onJoinServer(@NonNull NetworkSession networkSession)
{
Objects.requireNonNull(networkSession);
this.networkSession = networkSession;
this.networkSession.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage);
this.networkSession.registerHandler(CloseInternalEvent.class, this::onClose);
}
private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg)
{
// prefix@namespace:path
// 1-50 characters in total, all parts except namespace can be omitted
if (!msg.levelKey.matches("^(?=.{1,50}$)([a-zA-Z0-9-_]+@)?[a-zA-Z0-9-_]+(:[a-zA-Z0-9-_]+)?$"))
{
throw new IllegalArgumentException("Server sent invalid level key.");
}
LOGGER.info("Server level key received: [" + msg.levelKey + "].");
MC.executeOnRenderThread(() ->
{
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
if (existingKeyedClientLevel != null)
{
if (!existingKeyedClientLevel.getServerLevelKey().equals(msg.levelKey))
{
LOGGER.info("Unloading previous level with key: [" + existingKeyedClientLevel.getServerLevelKey() + "].");
this.levelUnloadHandler.accept(existingKeyedClientLevel);
}
else
{
LOGGER.info("Level key matches the previous level key, ignoring the message.");
}
}
else
{
LOGGER.info("Unloading non-keyed level: [" + clientLevel.getDimensionName() + "].");
this.levelUnloadHandler.accept(clientLevel);
}
if (existingKeyedClientLevel == null || !existingKeyedClientLevel.getServerLevelKey().equals(msg.levelKey))
{
LOGGER.info("Loading level with key: [" + msg.levelKey + "].");
IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.levelKey);
this.levelLoadHandler.accept(keyedLevel);
}
});
}
public void onClientLevelUnload() { KEYED_CLIENT_LEVEL_MANAGER.clearKeyedLevel(); }
//==========//
// shutdown //
//==========//
private void onClose(CloseInternalEvent event) { this.reset(); }
public void reset()
{
this.networkSession = null;
KEYED_CLIENT_LEVEL_MANAGER.clearKeyedLevel();
}
}
@@ -21,13 +21,9 @@ package com.seibel.distanthorizons.core.api.internal;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
import com.seibel.distanthorizons.core.world.DhClientServerWorld;
import com.seibel.distanthorizons.core.world.DhServerWorld;
@@ -37,6 +33,7 @@ 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 org.jetbrains.annotations.NotNull;
/**
* This holds the methods that should be called by the host mod loader (Fabric,
@@ -70,7 +67,7 @@ public class ServerApi
if (serverWorld != null)
{
serverWorld.serverTick();
SharedApi.worldGenTick(serverWorld::doWorldGen);
SharedApi.worldGenTick(serverWorld::worldGenTick);
}
}
catch (Exception e)
@@ -152,19 +149,37 @@ public class ServerApi
public void serverPlayerJoinEvent(IServerPlayerWrapper player)
{
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well
LOGGER.info("Player [${player.getName()}] joined.");
if (serverWorld != null)
{
LOGGER.debug("Waiting for player to connect: " + player.getUUID());
((DhServerWorld) serverWorld).addPlayer(player);
serverWorld.addPlayer(player);
}
}
public void serverPlayerDisconnectEvent(IServerPlayerWrapper player)
{
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well
LOGGER.info("Player [${player.getName()}] disconnected.");
if (serverWorld != null)
{
LOGGER.debug("Removing player from connect wait list: " + player.getUUID());
((DhServerWorld) serverWorld).removePlayer(player);
serverWorld.removePlayer(player);
}
}
public void serverPlayerLevelChangeEvent(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel)
{
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
LOGGER.info("Player [${player.getName()}] changed level: [${originLevel.getKeyedLevelDimensionName()}] -> [${destinationLevel.getKeyedLevelDimensionName()}].");
if (serverWorld != null)
{
serverWorld.changePlayerLevel(player, originLevel, destinationLevel);
}
}
public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message)
{
IDhServerWorld serverWorld = SharedApi.getIDhServerWorld();
if (serverWorld != null)
{
serverWorld.getServerPlayerStateManager().handlePluginMessage(player, message);
}
}
@@ -26,16 +26,16 @@ 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.DhBlockPos2D;
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.sql.dto.BeaconBeamDTO;
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.objects.Pair;
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.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
@@ -43,9 +43,8 @@ import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
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
@@ -54,17 +53,15 @@ public class SharedApi
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final Set<DhChunkPos> UPDATING_CHUNK_POS_SET = ConcurrentHashMap.newKeySet();
/** how many chunks can be queued for updating per thread, used to prevent updates from infinitely pilling up if the user flys around extremely fast */
private static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD = 500;
private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 30_000;
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final Timer CHUNK_UPDATE_TIMER = TimerUtil.CreateTimer("ChunkUpdateTimer");
private static final UpdateChunkPosManager UPDATE_POS_MANAGER = new UpdateChunkPosManager();
/** how many chunks can be queued for updating per thread, 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 = 500;
private static AbstractDhWorld currentWorld;
private static int lastWorldGenTickDelta = 0;
private static long lastOverloadedLogMessageMsTime = 0;
@@ -101,7 +98,7 @@ public class SharedApi
// 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
UPDATING_CHUNK_POS_SET.clear();
UPDATE_POS_MANAGER.clear();
// recommend that the garbage collector cleans up any objects from the old world and thread pools
System.gc();
@@ -120,11 +117,11 @@ public class SharedApi
public static AbstractDhWorld getAbstractDhWorld() { return currentWorld; }
/** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientServerWorld} */
public static DhClientServerWorld getDhClientServerWorld() { return (currentWorld != null && DhClientServerWorld.class.isInstance(currentWorld)) ? (DhClientServerWorld) currentWorld : null; }
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 != null && IDhClientWorld.class.isInstance(currentWorld)) ? (IDhClientWorld) currentWorld : null; }
public static IDhClientWorld getIDhClientWorld() { 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 != null && IDhServerWorld.class.isInstance(currentWorld)) ? (IDhServerWorld) currentWorld : null; }
public static IDhServerWorld getIDhServerWorld() { return (currentWorld instanceof IDhServerWorld) ? (IDhServerWorld) currentWorld : null; }
@@ -137,25 +134,15 @@ public class SharedApi
* 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 UPDATING_CHUNK_POS_SET.contains(new DhChunkPos(new DhBlockPos2D(blockPosX, 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)); }
/** 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 chunkUnloadEvent(IChunkWrapper chunk, ILevelWrapper level)
{
// temporarily disabled since this was originally incorrectly designated as "chunkSaveEvent"
// but didn't actually fire on chunk save
// and generally this is unnecessary and drastically reduces LOD building performance
// when traveling around the world
if (false)
{
this.applyChunkUpdate(chunk, level, false);
}
}
public void applyChunkUpdate(IChunkWrapper chunkWrapper, ILevelWrapper level, boolean updateNeighborChunks)
{
@@ -172,10 +159,11 @@ public class SharedApi
AbstractDhWorld dhWorld = SharedApi.getAbstractDhWorld();
if (dhWorld == null)
{
if (level instanceof IClientLevelWrapper clientLevel)
if (level 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;
ClientApi.INSTANCE.waitingChunkByClientLevelAndPos.replace(new Pair<>(clientLevel, chunkWrapper.getChunkPos()), chunkWrapper);
}
@@ -186,9 +174,10 @@ public class SharedApi
IDhLevel dhLevel = dhWorld.getLevel(level);
if (dhLevel == null)
{
if (level instanceof IClientLevelWrapper clientLevel)
if (level instanceof IClientLevelWrapper)
{
// the client level isn't loaded yet
IClientLevelWrapper clientLevel = (IClientLevelWrapper) level;
ClientApi.INSTANCE.waitingChunkByClientLevelAndPos.replace(new Pair<>(clientLevel, chunkWrapper.getChunkPos()), chunkWrapper);
}
@@ -197,43 +186,6 @@ public class SharedApi
//=====================//
// task limiting check //
//=====================//
int currentQueueCount = UPDATING_CHUNK_POS_SET.size();
int maxQueueCount = MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get();
if (currentQueueCount >= maxQueueCount)
{
// The maximum number of chunks are already queued, don't add more.
// This is done to prevent overloading the system if the user fly's extremely fast and queues too many chunks
long msBetweenLastLog = System.currentTimeMillis() - lastOverloadedLogMessageMsTime;
if (msBetweenLastLog >= MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE)
{
lastOverloadedLogMessageMsTime = System.currentTimeMillis();
String message = "Distant Horizons overloaded, too many chunks queued for updating. " +
"\nThis may result in holes in your LODs. " +
"\nPlease move through the world slower, decrease your vanilla render distance, slow down your world pre-generator, or increase the Distant Horizons' CPU load config. " +
"\nMax queue count ["+maxQueueCount+"] (["+MAX_UPDATING_CHUNK_COUNT_PER_THREAD+"] per thread).";
ClientApi.INSTANCE.showChatMessageNextFrame(message);
LOGGER.warn(message);
}
return;
}
// prevent duplicate update requests
if (UPDATING_CHUNK_POS_SET.contains(chunkWrapper.getChunkPos()))
{
// this chunk is already being updated
return;
}
UPDATING_CHUNK_POS_SET.add(chunkWrapper.getChunkPos());
//===============================//
// update the necessary chunk(s) //
//===============================//
@@ -242,7 +194,7 @@ public class SharedApi
{
// only update the center chunk
bakeChunkLightingAndSendToLevelAsync(chunkWrapper, null, dhLevel);
queueChunkUpdate(chunkWrapper, null, dhLevel);
}
else
{
@@ -263,7 +215,7 @@ public class SharedApi
else
{
// neighboring chunk
DhChunkPos neighbourPos = new DhChunkPos(chunkWrapper.getChunkPos().x + xOffset, chunkWrapper.getChunkPos().z + zOffset);
DhChunkPos neighbourPos = new DhChunkPos(chunkWrapper.getChunkPos().getX() + xOffset, chunkWrapper.getChunkPos().getZ() + zOffset);
IChunkWrapper neighbourChunk = dhLevel.getLevelWrapper().tryGetChunk(neighbourPos);
if (neighbourChunk != null)
{
@@ -276,113 +228,120 @@ public class SharedApi
// light and send the chunks
for (IChunkWrapper litChunk : neighbourChunkList)
{
bakeChunkLightingAndSendToLevelAsync(litChunk, neighbourChunkList, dhLevel);
queueChunkUpdate(litChunk, neighbourChunkList, dhLevel);
}
}
}
private static void bakeChunkLightingAndSendToLevelAsync(IChunkWrapper chunkWrapper, @Nullable ArrayList<IChunkWrapper> neighbourChunkList, IDhLevel dhLevel)
private static void queueChunkUpdate(IChunkWrapper chunkWrapper, @Nullable ArrayList<IChunkWrapper> neighbourChunkList, IDhLevel dhLevel)
{
// lighting the chunk needs to be done on a separate thread to prevent lagging any of the event threads
ThreadPoolExecutor executor = ThreadPoolUtil.getLightPopulatorExecutor();
if (executor == null)
if (MC_CLIENT != null && MC_CLIENT.playerExists())
{
UPDATE_POS_MANAGER.setCenter(MC_CLIENT.getPlayerChunkPos());
UPDATE_POS_MANAGER.maxSize = MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get();
}
UpdateChunkData updateData = new UpdateChunkData(chunkWrapper, neighbourChunkList, dhLevel);
UPDATE_POS_MANAGER.addItem(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
ThreadPoolExecutor executor = ThreadPoolUtil.getChunkToLodBuilderExecutor();
if (executor != null && executor.getQueue().size() < executor.getCorePoolSize())
{
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
}
}
}
/** returning a {@link CompletableFuture} isn't necessary, but allows Intellij to properly show the full stack trace when debugging. */
@SuppressWarnings("UnusedReturnValue")
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
{
executor.execute(() ->
boolean checkChunkHash = !Config.Client.Advanced.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)
{
// if the chunk hashes are the same then we don't need to bother with lighting the chunk
// or creating/updating the LODs
//LOGGER.info("skipping: "+chunkWrapper.getChunkPos()+" "+newChunkHash);
return;
}
else
{
//LOGGER.info("g: "+chunkWrapper.getChunkPos()+" "+newChunkHash);
}
}
// 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);
}
// 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
ThreadPoolExecutor executor = ThreadPoolUtil.getChunkToLodBuilderExecutor();
if (executor != null && !UPDATE_POS_MANAGER.positionMap.isEmpty())
{
//LOGGER.trace(chunkWrapper.getChunkPos() + " " + executor.getActiveCount() + " / " + executor.getQueue().size() + " - " + executor.getCompletedTaskCount());
try
{
// 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 (oldChunkHash == newChunkHash)
{
// if the chunk hashes are the same then we don't need to bother with lighting the chunk
// or creating/updating the LODs
//LOGGER.info("skipping: "+chunkWrapper.getChunkPos()+" "+newChunkHash);
return;
}
else
{
//LOGGER.info("g: "+chunkWrapper.getChunkPos()+" "+newChunkHash);
}
// 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);
}
// Save or populate the chunk wrapper's lighting
// this is done so we don't have to worry about MC unloading the lighting data for this chunk
boolean onlyUseDhLighting = Config.Client.Advanced.LodBuilding.onlyUseDhLightingEngine.get();
if (!onlyUseDhLighting && chunkWrapper.isLightCorrect())
{
try
{
// If MC's lighting engine isn't thread safe this may cause the server thread to lag
chunkWrapper.bakeDhLightingUsingMcLightingEngine();
}
catch (IllegalStateException e)
{
LOGGER.warn("Chunk light baking error: " + e.getMessage(), e);
}
}
else
{
// generate the chunk's lighting, using neighboring chunks if present
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? 15 : 0);
}
// get this chunk's active beacons
List<BeaconBeamDTO> beaconBeamList = chunkWrapper.getAllActiveBeacons(nearbyChunkList);
dhLevel.setBeaconBeamsForChunk(chunkWrapper.getChunkPos(), beaconBeamList);
dhLevel.updateChunkAsync(chunkWrapper);
dhLevel.setChunkHash(chunkWrapper.getChunkPos(), newChunkHash);
executor.execute(SharedApi::processQueuedChunkUpdate);
}
catch (Exception e)
catch (RejectedExecutionException ignore)
{
LOGGER.error("Unexpected error when updating chunk at pos: [" + chunkWrapper.getChunkPos() + "]", e);
// the executor was shut down, it should be back up shortly and able to accept new jobs
}
finally
{
// the LOD chunk has finished being updated
int updateTimeoutInSec = Config.Client.Advanced.LodBuilding.minTimeBetweenChunkUpdatesInSeconds.get();
if (updateTimeoutInSec != 0)
{
// prevent updating this chunk again until the timeout finishes
CHUNK_UPDATE_TIMER.schedule(new TimerTask()
{
@Override
public void run() { UPDATING_CHUNK_POS_SET.remove(chunkWrapper.getChunkPos()); }
}, updateTimeoutInSec * 1000L);
}
else
{
// instantly allow this chunk to be updated again
UPDATING_CHUNK_POS_SET.remove(chunkWrapper.getChunkPos());
}
}
});
}
}
catch (RejectedExecutionException ignore) { /* the executor was shut down, it should be back up shortly and able to accept new jobs */ }
}
@@ -393,11 +352,185 @@ public class SharedApi
public String getDebugMenuString()
{
int maxUpdateCount = MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get();
String updatingCountStr = F3Screen.NUMBER_FORMAT.format(UPDATING_CHUNK_POS_SET.size());
String maxUpdateCountStr = F3Screen.NUMBER_FORMAT.format(maxUpdateCount);
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;
}
//================//
// 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;
public UpdateChunkData(IChunkWrapper chunkWrapper, @Nullable ArrayList<IChunkWrapper> neighbourChunkList, IDhLevel dhLevel)
{
this.chunkWrapper = chunkWrapper;
this.neighbourChunkList = neighbourChunkList;
this.dhLevel = dhLevel;
}
}
/** 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> positionMap;
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.positionMap = 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.positionMap.containsKey(pos);
}
finally
{
this.lock.unlock();
}
}
public void clear()
{
try
{
this.lock.lock();
this.positionMap.clear();
this.closestQueue.clear();
this.furthestQueue.clear();
}
finally
{
this.lock.unlock();
}
}
public void addItem(DhChunkPos pos, UpdateChunkData updateData)
{
try
{
this.lock.lock();
if (this.positionMap.containsKey(pos))
{
return;
}
if (this.positionMap.size() >= this.maxSize)
{
// Remove item furthest from the center
DhChunkPos furthest = this.furthestQueue.poll();
this.closestQueue.remove(furthest);
this.positionMap.remove(furthest);
}
this.positionMap.put(pos, updateData);
this.closestQueue.add(pos);
this.furthestQueue.add(pos);
}
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.positionMap.keySet())
{
this.closestQueue.add(pos);
this.furthestQueue.add(pos);
}
}
finally
{
this.lock.unlock();
}
}
public UpdateChunkData popClosest()
{
if (this.closestQueue.isEmpty())
{
return null;
}
try
{
this.lock.lock();
DhChunkPos closest = this.closestQueue.poll();
this.furthestQueue.remove(closest);
return this.positionMap.remove(closest);
}
finally
{
this.lock.unlock();
}
}
}
}
@@ -43,6 +43,7 @@ import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
@@ -54,7 +55,6 @@ import java.util.List;
* Otherwise, you will have issues where only some of the config entries will exist when your listener is created.
*
* @author coolGi
* @version 2023-7-16
*/
public class Config
@@ -107,6 +107,8 @@ public class Config
public static ConfigLinkedEntry quickEnableWorldGenerator = new ConfigLinkedEntry(Advanced.WorldGenerator.enableDistantGeneration);
public static ConfigLinkedEntry quickLodCloudRendering = new ConfigLinkedEntry(Advanced.Graphics.GenericRendering.enableCloudRendering);
public static ConfigEntry<Boolean> optionsButton = new ConfigEntry.Builder<Boolean>()
.set(true)
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
@@ -126,7 +128,6 @@ public class Config
public static ConfigCategory multiplayer = new ConfigCategory.Builder().set(Multiplayer.class).build();
public static ConfigCategory lodBuilding = new ConfigCategory.Builder().set(LodBuilding.class).build();
public static ConfigCategory multiThreading = new ConfigCategory.Builder().set(MultiThreading.class).build();
public static ConfigCategory buffers = new ConfigCategory.Builder().set(GpuBuffers.class).build();
public static ConfigCategory autoUpdater = new ConfigCategory.Builder().set(AutoUpdater.class).build();
public static ConfigCategory logging = new ConfigCategory.Builder().set(Logging.class).build();
@@ -163,9 +164,19 @@ public class Config
.build();
public static ConfigEntry<Integer> lodChunkRenderDistanceRadius = new ConfigEntry.Builder<Integer>()
.setServersideShortName("renderDistanceRadius")
.setMinDefaultMax(32, 128, 4096)
.comment("The radius of the mod's render distance. (measured in chunks)")
.comment("" +
"The radius of the mod's render distance. (measured in chunks)\n" +
"On server changes the distance players will receive real-time updates for, if enabled." +
"\n" +
"Note for servers:\n" +
"This setting does not prevent players from generating farther out.\n" +
"If you want to limit performance impact, change rate limits\n" +
"and thread count/runtime ratio settings instead.\n" +
"It also does not affect the visuals on clients.")
.setPerformance(EConfigEntryPerformance.HIGH)
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<EDhApiVerticalQuality> verticalQuality = new ConfigEntry.Builder<EDhApiVerticalQuality>()
@@ -179,6 +190,7 @@ public class Config
+ "Lowest Quality: " + EDhApiVerticalQuality.HEIGHT_MAP + "\n"
+ "Highest Quality: " + EDhApiVerticalQuality.EXTREME)
.setPerformance(EConfigEntryPerformance.VERY_HIGH)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<EDhApiHorizontalQuality> horizontalQuality = new ConfigEntry.Builder<EDhApiHorizontalQuality>()
@@ -200,6 +212,7 @@ public class Config
+ EDhApiTransparency.DISABLED + ": LODs will be opaque. \n"
+ "")
.setPerformance(EConfigEntryPerformance.MEDIUM)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<EDhApiBlocksToAvoid> blocksToIgnore = new ConfigEntry.Builder<EDhApiBlocksToAvoid>()
@@ -211,6 +224,7 @@ public class Config
+ EDhApiBlocksToAvoid.NON_COLLIDING + ": Only represent solid blocks in the LODs (tall grass, torches, etc. won't count for a LOD's height) \n"
+ "")
.setPerformance(EConfigEntryPerformance.NONE)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Boolean> tintWithAvoidedBlocks = new ConfigEntry.Builder<Boolean>()
@@ -222,6 +236,7 @@ public class Config
+ "False: skipped blocks will not change color of surface below them. "
+ "")
.setPerformance(EConfigEntryPerformance.NONE)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
// TODO fixme
@@ -559,6 +574,14 @@ public class Config
+ "")
.build();
public static ConfigEntry<Boolean> enableInstancedRendering = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "Can be disabled to use much slower but more compatible direct rendering. \n"
+ "Disabling this can be used to fix some crashes on Mac. \n"
+ "")
.build();
}
public static class AdvancedGraphics
@@ -587,6 +610,7 @@ public class Config
+ "0 = black \n"
+ "1 = normal \n"
+ "2 = near white")
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Double> saturationMultiplier = new ConfigEntry.Builder<Double>() // TODO: Make this a float (the ClassicConfigGUI doesnt support floats)
@@ -597,6 +621,7 @@ public class Config
+ "0 = black and white \n"
+ "1 = normal \n"
+ "2 = very saturated")
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Boolean> enableCaveCulling = new ConfigEntry.Builder<Boolean>()
@@ -611,14 +636,15 @@ public class Config
+ "Additional Info: Currently this cull all faces \n"
+ " with skylight value of 0 in dimensions that \n"
+ " does not have a ceiling.")
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
@Deprecated
public static ConfigEntry<Integer> caveCullingHeight = new ConfigEntry.Builder<Integer>()
.setMinDefaultMax(-4096, 40, 4096)
.setAppearance(EConfigEntryAppearance.ONLY_IN_API)
.setMinDefaultMax(-4096, 60, 4096)
.comment(""
+ "At what Y value should cave culling start?")
+ "At what Y value should cave culling start? \n"
+ "Lower this value if you get walls for areas with 0 light.")
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Integer> earthCurveRatio = new ConfigEntry.Builder<Integer>()
@@ -656,6 +682,7 @@ public class Config
+ EDhApiLodShading.DISABLED + ": All LOD sides will be rendered with the same brightness. \n"
+ "")
.setPerformance(EConfigEntryPerformance.NONE)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Boolean> disableFrustumCulling = new ConfigEntry.Builder<Boolean>()
@@ -690,6 +717,18 @@ public class Config
+ EDhApiGrassSideRendering.AS_DIRT + ": sides render entirely as dirt. \n"
+ "")
.setPerformance(EConfigEntryPerformance.NONE)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Boolean> disableBeaconDistanceCulling = new ConfigEntry.Builder<Boolean>()
.set(false)
.comment(""
+ "If true all beacons near the camera won't be drawn to prevent vanilla overdraw. \n"
+ "If false all beacons will be rendered. \n"
+ "\n"
+ "Generally this should be left as false. It's main purpose is for debugging\n"
+ "beacon updating/rendering.\n"
+ "")
.build();
}
@@ -699,16 +738,16 @@ public class Config
public static class WorldGenerator
{
public static ConfigEntry<Boolean> enableDistantGeneration = new ConfigEntry.Builder<Boolean>()
.setServersideShortName("enableDistantGeneration")
.set(true)
.comment(""
+ " Should Distant Horizons slowly generate LODs \n"
+ " outside the vanilla render distance?\n"
+ "\n"
+ " Note: when on a server, distant generation isn't supported \n"
+ " and will always be disabled.")
+ " outside the vanilla render distance?")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<EDhApiDistantGeneratorMode> distantGeneratorMode = new ConfigEntry.Builder<EDhApiDistantGeneratorMode>()
.setServersideShortName("distantGeneratorMode")
.set(EDhApiDistantGeneratorMode.FEATURES)
.comment(""
+ "How detailed should LODs be generated outside the vanilla render distance? \n"
@@ -738,32 +777,36 @@ public class Config
+ EDhApiDistantGeneratorMode.FEATURES + " \n"
+ "Generate everything except structures. \n"
+ "WARNING: This may cause world generator bugs or instability when paired with certain world generator mods. \n"
//not currently implemented
//+ "\n"
//+ EDhApiDistantGeneratorMode.FULL + " \n"
//+ "Ask the local server to generate/load each chunk. \n"
//+ "This is the most compatible, but will cause server/simulation lag. \n"
//+ "- Slow (15-50 ms, with spikes up to 200 ms) \n"
+ "")
/*
// FULL isn't currently implemented
+ "\n"
+ EDhApiDistantGeneratorMode.FULL + " \n"
+ "Ask the local server to generate/load each chunk. \n"
+ "This is the most compatible, but will cause server/simulation lag. \n"
+ "- Slow (15-50 ms, with spikes up to 200 ms) \n"
*/
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<Integer> worldGenerationTimeoutLengthInSeconds = new ConfigEntry.Builder<Integer>()
.setServersideShortName("worldGenerationTimeout")
.setMinDefaultMax(5, 60 * 3, 60 * 10/*10 minutes*/ )
.comment(""
+ "How long should a world generator thread run for before timing out? \n"
+ "Note: If you are experiencing timeout errors it is better to lower your CPU usage first \n"
+ "via the thread config before changing this value. \n"
+ "")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
}
public static class LodBuilding
{
@Deprecated
public static ConfigEntry<Integer> minTimeBetweenChunkUpdatesInSeconds = new ConfigEntry.Builder<Integer>()
.setServersideShortName("minTimeBetweenChunkUpdates")
.setMinDefaultMax(0, 1, 60)
.setAppearance(EConfigEntryAppearance.ONLY_IN_API)
.comment(""
+ "Determines how long must pass between LOD chunk updates before another. \n"
+ "update can occur\n"
@@ -773,8 +816,23 @@ public class Config
+ "")
.build();
public static ConfigEntry<Boolean> disableUnchangedChunkCheck = new ConfigEntry.Builder<Boolean>()
.set(false)
.comment(""
+ "Normally DH will attempt to skip creating LODs for chunks it's already seen\n"
+ "and that haven't changed.\n"
+ "\n"
+ "However sometimes that logic incorrecly prevents LODs from being updated.\n"
+ "Disabling this check may fix issues where LODs aren't updated after\n"
+ "blocks have been changed.\n"
+ "")
.build();
/** Currently we always use the DH lighting engine because there's a high likelyhood of MC returning incorrect lighting otherwise */
@Deprecated
public static ConfigEntry<Boolean> onlyUseDhLightingEngine = new ConfigEntry.Builder<Boolean>()
.set(false)
.setAppearance(EConfigEntryAppearance.ONLY_IN_API)
.comment(""
+ "If false LODs will be lit by Minecraft's lighting engine when possible \n"
+ "and fall back to the DH lighting engine only when necessary. \n"
@@ -849,7 +907,7 @@ public class Config
.build();
public static ConfigEntry<String> ignoredRenderBlockCsv = new ConfigEntry.Builder<String>()
.set("minecraft:barrier,minecraft:structure_void,minecraft:light,minecraft:tripwire")
.set("minecraft:barrier,minecraft:structure_void,minecraft:light,minecraft:tripwire,minecraft:brown_mushroom")
.comment(""
+ "A comma separated list of block resource locations that won't be rendered by DH. \n"
+ "Note: air is always included in this list. \n"
@@ -867,8 +925,8 @@ public class Config
static
{
ignoredRenderBlockCsv.addListener(new ConfigChangeListener<>(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv,
(blockCsv) ->
ignoredRenderBlockCsv.addListener(new ConfigChangeListener<String>(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv,
(blockCsv) ->
{
IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
if (wrapperFactory != null)
@@ -878,8 +936,8 @@ public class Config
}
}));
ignoredRenderCaveBlockCsv.addListener(new ConfigChangeListener<>(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv,
(blockCsv) ->
ignoredRenderCaveBlockCsv.addListener(new ConfigChangeListener<String>(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv,
(blockCsv) ->
{
IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
if (wrapperFactory != null)
@@ -930,27 +988,98 @@ public class Config
+ "")
.build();
// not currently implemented
private static ConfigEntry<Boolean> enableMultiverseNetworking = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "If true Distant Horizons will attempt to communicate with the connected \n"
+ "server in order to improve multiverse support. \n"
+ "")
.build();
// not currently implemented
private static ConfigEntry<Boolean> enableServerNetworking = new ConfigEntry.Builder<Boolean>()
.set(false)
.comment(""
+ "Attention: this is only for developers and hasn't been implemented.\n"
+ "\n"
+ "If true Distant Horizons will attempt to communicate with the connected \n"
+ "server in order to load LODs outside your vanilla render distance. \n"
+ "\n"
+ "Note: This requires DH to be installed on the server in order to function. \n"
+ "")
.build();
public static ConfigCategory serverNetworking = new ConfigCategory.Builder().set(ServerNetworking.class).build();
public static class ServerNetworking
{
public static ConfigUIComment generalSectionNote = new ConfigUIComment();
public static ConfigEntry<Boolean> enableServerNetworking = new ConfigEntry.Builder<Boolean>()
.setServersideShortName("enableServerNetworking")
.set(true)
.comment(""
+ "WARNING!\n"
+ "Server-client networking is not yet fully implemented!\n"
+ "Both the server and client must be running the server-side fork with this option enabled\n"
+ "for Distant Horizons data to be transceived.\n"
+ "\n"
+ "If true, the server and client will attempt to communicate to transceive Distant Horizons data.\n"
+ "This allows for further distant generation and LOD updates on all clients.\n"
+ "\n"
+ "This should only be used on trusted servers with trusted players!\n"
+ "")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<Boolean> sendLevelKeys = new ConfigEntry.Builder<Boolean>()
.setServersideShortName("sendLevelKeys")
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
.set(true)
.comment(""
+ "Makes the server send level keys for each world.\n"
+ "Disable this if you use alternative ways to send level keys.\n"
+ "")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<String> levelKeyPrefix = new ConfigEntry.Builder<String>()
.setServersideShortName("levelKeyPrefix")
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
.set(getDefaultLevelKeyPrefix())
.comment(""
+ "Prefix of the level keys sent to the clients.\n"
+ "Should be set to a unique value for each backend server behind a proxy,\n"
+ "or empty if you don't use a proxy.\n"
+ "")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigUIComment generationSectionNote = new ConfigUIComment();
public static ConfigEntry<Integer> generationRequestRateLimit = new ConfigEntry.Builder<Integer>()
.setServersideShortName("generationRequestRateLimit")
.setMinDefaultMax(1, 20, 100)
.comment(""
+ "How many LOD generation requests per second should a client send? \n"
+ "Also limits the amount of player's requests allowed to stay in the server's queue."
+ "")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigUIComment realTimeUpdatesSectionNote = new ConfigUIComment();
public static ConfigEntry<Boolean> enableRealTimeUpdates = new ConfigEntry.Builder<Boolean>()
.setServersideShortName("enableRealTimeUpdates")
.set(false)
.comment(""
+ "If true, the client will receive real-time LOD updates for chunks outside the client's render distance."
+ "")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
// TODO rename
public static ConfigUIComment syncOnLoginSectionNote = new ConfigUIComment();
public static ConfigEntry<Boolean> synchronizeOnLogin = new ConfigEntry.Builder<Boolean>()
.setServersideShortName("synchronizeOnLogin")
.set(false)
.comment(""
+ "If true, clients will receive updated LODs on join if any changes occurred since last join."
+ "")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<Integer> syncOnLoginRateLimit = new ConfigEntry.Builder<Integer>()
.setServersideShortName("syncOnLoginRateLimit")
.setMinDefaultMax(1, 50, 100)
.comment(""
+ "How many LOD sync requests per second should a client send? \n"
+ "Also limits the amount of player's requests allowed to stay in the server's queue."
+ "")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
}
}
@@ -972,6 +1101,7 @@ public class Config
public static final ConfigEntry<Integer> numberOfWorldGenerationThreads = new ConfigEntry.Builder<Integer>()
.setServersideShortName("numberOfWorldGenerationThreads")
.setMinDefaultMax(1,
ThreadPresetConfigEventHandler.getWorldGenDefaultThreadCount(),
Runtime.getRuntime().availableProcessors())
@@ -985,13 +1115,17 @@ public class Config
+ "generation speed, increase this number. \n"
+ "\n"
+ THREAD_NOTE)
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static final ConfigEntry<Double> runTimeRatioForWorldGenerationThreads = new ConfigEntry.Builder<Double>()
.setServersideShortName("runTimeRatioForWorldGenerationThreads")
.setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getWorldGenDefaultRunTimeRatio(), 1.0)
.comment(THREAD_RUN_TIME_RATIO_NOTE)
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static final ConfigEntry<Integer> numberOfFileHandlerThreads = new ConfigEntry.Builder<Integer>()
.setServersideShortName("numberOfFileHandlerThreads")
.setMinDefaultMax(1,
ThreadPresetConfigEventHandler.getFileHandlerDefaultThreadCount(),
Runtime.getRuntime().availableProcessors())
@@ -1003,10 +1137,13 @@ public class Config
+ "quickly flying through existing LODs. \n"
+ "\n"
+ THREAD_NOTE)
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static final ConfigEntry<Double> runTimeRatioForFileHandlerThreads = new ConfigEntry.Builder<Double>()
.setServersideShortName("runTimeRatioForFileHandlerThreads")
.setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getFileHandlerDefaultRunTimeRatio(), 1.0)
.comment(THREAD_RUN_TIME_RATIO_NOTE)
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static final ConfigEntry<Integer> numberOfUpdatePropagatorThreads = new ConfigEntry.Builder<Integer>()
@@ -1035,6 +1172,7 @@ public class Config
.build();
public static final ConfigEntry<Integer> numberOfLodBuilderThreads = new ConfigEntry.Builder<Integer>()
.setServersideShortName("numberOfLodBuilderThreads")
.setMinDefaultMax(1,
ThreadPresetConfigEventHandler.getLodBuilderDefaultThreadCount(),
Runtime.getRuntime().availableProcessors())
@@ -1045,12 +1183,16 @@ public class Config
+ "certain graphics settings are changed, and when moving around the world. \n"
+ "\n"
+ THREAD_NOTE)
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static final ConfigEntry<Double> runTimeRatioForLodBuilderThreads = new ConfigEntry.Builder<Double>()
.setServersideShortName("runTimeRatioForLodBuilderThreads")
.setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getLodBuilderDefaultRunTimeRatio(), 1.0)
.comment(THREAD_RUN_TIME_RATIO_NOTE)
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static final ConfigEntry<Boolean> enableLodBuilderThreadLimiting = new ConfigEntry.Builder<Boolean>()
.setServersideShortName("enableLodBuilderThreadLimiting")
.set(true)
.comment(""
+ "Should only be disabled if deadlock occurs and LODs refuse to update. \n"
@@ -1058,55 +1200,29 @@ public class Config
+ "\n"
+ "Note that if deadlock did occur restarting MC may be necessary to stop the locked threads. \n"
+ "")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
}
public static class GpuBuffers
{
public static ConfigEntry<EDhApiGpuUploadMethod> gpuUploadMethod = new ConfigEntry.Builder<EDhApiGpuUploadMethod>()
.set(EDhApiGpuUploadMethod.AUTO)
public static final ConfigEntry<Integer> numberOfNetworkCompressionThreads = new ConfigEntry.Builder<Integer>()
.setServersideShortName("numberOfNetworkCompressionThreads")
.setMinDefaultMax(1,
ThreadPresetConfigEventHandler.getNetworkCompressionDefaultThreadCount(),
Runtime.getRuntime().availableProcessors())
.comment(""
+ "What method should be used to upload geometry to the GPU? \n"
+ "How many threads should be used when (de)compressing LODs \n"
+ "that are received/sent over the network?\n"
+ "\n"
+ EDhApiGpuUploadMethod.AUTO + ": Picks the best option based on the GPU you have. \n"
+ "This pool doesn't do anything in singleplayer or when connected \n"
+ "to a server that doesn't support DH networking. \n"
+ "\n"
+ EDhApiGpuUploadMethod.BUFFER_STORAGE + ": Default if OpenGL 4.5 is supported. \n"
+ " Fast rendering, no stuttering. \n"
+ "\n"
+ EDhApiGpuUploadMethod.SUB_DATA + ": Backup option for NVIDIA. \n"
+ " Fast rendering but may stutter when uploading. \n"
+ "\n"
+ EDhApiGpuUploadMethod.BUFFER_MAPPING + ": Slow rendering but won't stutter when uploading. \n"
+ " Generally the best option for integrated GPUs. \n"
+ " Default option for AMD/Intel if OpenGL 4.5 isn't supported. \n"
+ " May end up storing buffers in System memory. \n"
+ " Fast rendering if in GPU memory, slow if in system memory, \n"
+ " but won't stutter when uploading. \n"
+ "\n"
+ EDhApiGpuUploadMethod.DATA + ": Fast rendering but will stutter when uploading. \n"
+ " Backup option for AMD/Intel. \n"
+ " Fast rendering but may stutter when uploading. \n"
+ "\n"
+ "If you don't see any difference when changing these settings, \n"
+ "or the world looks corrupted: restart your game."
+ "")
+ THREAD_NOTE)
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<Integer> gpuUploadPerMegabyteInMilliseconds = new ConfigEntry.Builder<Integer>()
.setMinDefaultMax(0, 0, 50)
.comment(""
+ "How long should a buffer wait per Megabyte of data uploaded? \n"
+ "Helpful resource for frame times: https://fpstoms.com \n"
+ "\n"
+ "Longer times may reduce stuttering but will make LODs \n"
+ "transition and load slower. Change this to [0] for no timeout. \n"
+ "\n"
+ "NOTE:\n"
+ "Before changing this config, try changing the \"GPU Upload method\" first. \n"
+ "")
public static final ConfigEntry<Double> runTimeRatioForNetworkCompressionThreads = new ConfigEntry.Builder<Double>()
.setServersideShortName("runTimeRatioForNetworkCompressionThreads")
.setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getNetworkCompressionDefaultRunTimeRatio(), 1.0)
.comment(THREAD_RUN_TIME_RATIO_NOTE)
.build();
}
public static class AutoUpdater
@@ -1140,66 +1256,80 @@ public class Config
// TODO add change all option
// TODO default to error chat and info file
public static ConfigEntry<EDhApiLoggerMode> logWorldGenEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.setServersideShortName("logWorldGenEvent")
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE)
.comment(""
+ "If enabled, the mod will log information about the world generation process. \n"
+ "This can be useful for debugging.")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<EDhApiLoggerMode> logWorldGenPerformance = new ConfigEntry.Builder<EDhApiLoggerMode>()
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.setServersideShortName("logWorldGenPerformance")
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE)
.comment(""
+ "If enabled, the mod will log performance about the world generation process. \n"
+ "This can be useful for debugging.")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<EDhApiLoggerMode> logWorldGenLoadEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.setServersideShortName("logWorldGenPerformance")
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE)
.comment(""
+ "If enabled, the mod will log information about the world generation process. \n"
+ "This can be useful for debugging.")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<EDhApiLoggerMode> logLodBuilderEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.setServersideShortName("logLodBuilderEvent")
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE)
.comment(""
+ "If enabled, the mod will log information about the LOD generation process. \n"
+ "This can be useful for debugging.")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<EDhApiLoggerMode> logRendererBufferEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE)
.comment(""
+ "If enabled, the mod will log information about the renderer buffer process. \n"
+ "This can be useful for debugging.")
.build();
public static ConfigEntry<EDhApiLoggerMode> logRendererGLEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE)
.comment(""
+ "If enabled, the mod will log information about the renderer OpenGL process. \n"
+ "This can be useful for debugging.")
.build();
public static ConfigEntry<EDhApiLoggerMode> logFileReadWriteEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.setServersideShortName("logFileReadWriteEvent")
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE)
.comment(""
+ "If enabled, the mod will log information about file read/write operations. \n"
+ "This can be useful for debugging.")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<EDhApiLoggerMode> logFileSubDimEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.setServersideShortName("logFileSubDimEvent")
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE)
.comment(""
+ "If enabled, the mod will log information about file sub-dimension operations. \n"
+ "This can be useful for debugging.")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
public static ConfigEntry<EDhApiLoggerMode> logNetworkEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.setServersideShortName("logNetworkEvent")
.set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE)
.comment(""
+ "If enabled, the mod will log information about network operations. \n"
+ "This can be useful for debugging.")
.setSide(EConfigEntryRelevantSide.BOTH)
.build();
@@ -1217,6 +1347,13 @@ public class Config
+ "giving some basic information about how DH will function.")
.build();
public static ConfigEntry<Boolean> showModCompatibilityWarningsOnStartup = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "If enabled, a chat message will be displayed when a potentially problematic \n"
+ "mod is installed alongside DH.")
.build();
}
public static class Debugging
@@ -1303,22 +1440,38 @@ public class Config
public static ConfigEntry<Boolean> columnBuilderDebugEnable = new ConfigEntry.Builder<Boolean>()
.set(false)
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
.addListener(DebugColumnConfigEventHandler.INSTANCE)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Integer> columnBuilderDebugDetailLevel = new ConfigEntry.Builder<Integer>()
.set((int) DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL)
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
.addListener(DebugColumnConfigEventHandler.INSTANCE)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Integer> columnBuilderDebugXPos = new ConfigEntry.Builder<Integer>()
.set(0)
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
.addListener(DebugColumnConfigEventHandler.INSTANCE)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Integer> columnBuilderDebugZPos = new ConfigEntry.Builder<Integer>()
.set(0)
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
.addListener(DebugColumnConfigEventHandler.INSTANCE)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Integer> columnBuilderDebugXRow = new ConfigEntry.Builder<Integer>()
.set(-1)
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Integer> columnBuilderDebugZRow = new ConfigEntry.Builder<Integer>()
.set(-1)
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Integer> columnBuilderDebugColumnIndex = new ConfigEntry.Builder<Integer>()
.set(-1)
.setAppearance(EConfigEntryAppearance.ONLY_IN_GUI)
.addListener(ReloadLodsConfigEventHandler.INSTANCE)
.build();
@@ -1497,25 +1650,27 @@ public class Config
.build();
public static ConfigEntry<List<String>> listTest = new ConfigEntry.Builder<List<String>>()
.set(new ArrayList<>(Arrays.asList("option 1", "option 2", "option 3")))
.set(new ArrayList<String>(Arrays.asList("option 1", "option 2", "option 3")))
.build();
public static ConfigEntry<Map<String, String>> mapTest = new ConfigEntry.Builder<Map<String, String>>()
.set(new HashMap<>())
.set(new HashMap<String, String>())
.build();
public static ConfigUIButton uiButtonTest = new ConfigUIButton(() ->
new Thread(() ->
public static ConfigUIButton uiButtonTest = new ConfigUIButton(() ->
{
new Thread(() ->
{
if (!GraphicsEnvironment.isHeadless())
{
if (!GraphicsEnvironment.isHeadless())
{
JOptionPane.showMessageDialog(null, "Button pressed!", "UITester dialog", JOptionPane.INFORMATION_MESSAGE);
}
else
{
LOGGER.info("button pressed!");
}
}));
JOptionPane.showMessageDialog(null, "Button pressed!", "UITester dialog", JOptionPane.INFORMATION_MESSAGE);
}
else
{
LOGGER.info("button pressed!");
}
});
});
public static ConfigCategory categoryTest = new ConfigCategory.Builder().set(CategoryTest.class).build();
@@ -1586,4 +1741,17 @@ public class Config
}
}
private static String getDefaultLevelKeyPrefix()
{
IMinecraftSharedWrapper mcWrapper = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
if (!mcWrapper.isDedicatedServer() || mcWrapper.isWorldNew())
{
return "";
}
else
{
return "server" + ThreadLocalRandom.current().nextInt(1, 1000);
}
}
}
@@ -77,7 +77,7 @@ public class ConfigBase
* <br> Map<String, T>
* <br> HashMap<String, T>
*/
public static final List<Class<?>> acceptableInputs = new ArrayList<>()
public static final List<Class<?>> acceptableInputs = new ArrayList<Class<?>>()
{{
add(Boolean.class);
add(Byte.class);
@@ -149,7 +149,7 @@ public class ConfigBase
LOGGER.warn(exception);
}
AbstractConfigType<?, ?> entry = entries.getLast();
AbstractConfigType<?, ?> entry = entries.get(entries.size() - 1);
entry.category = category;
entry.name = field.getName();
entry.configBase = this;
@@ -160,7 +160,7 @@ public class ConfigBase
{
LOGGER.error("Invalid variable type at [" + (category.isEmpty() ? "" : category + ".") + field.getName() + "].");
LOGGER.error("Type [" + entry.getType() + "] is not one of these types [" + acceptableInputs.toString() + "]");
entries.removeLast(); // Delete the entry if it is invalid so the game can still run
entries.remove(entries.size() - 1); // Delete the entry if it is invalid so the game can still run
}
}
@@ -32,7 +32,7 @@ import java.util.Map;
public class NumberUtil
{
// Is there no better way of doing this?
public static Map<Class<?>, Number> minValues = new HashMap<>()
public static Map<Class<?>, Number> minValues = new HashMap<Class<?>, Number>()
{{
put(Byte.class, Byte.MIN_VALUE);
put(Short.class, Short.MIN_VALUE);
@@ -41,7 +41,7 @@ public class NumberUtil
put(Double.class, Double.MIN_VALUE);
put(Float.class, Float.MIN_VALUE);
}};
public static Map<Class<?>, Number> maxValues = new HashMap<>()
public static Map<Class<?>, Number> maxValues = new HashMap<Class<?>, Number>()
{{
put(Byte.class, Byte.MAX_VALUE);
put(Short.class, Short.MAX_VALUE);
@@ -35,8 +35,8 @@ 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); });
}
/**
@@ -23,9 +23,9 @@ import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
public class DebugColumnConfigEventHandler implements IConfigListener
public class ReloadLodsConfigEventHandler implements IConfigListener
{
public static DebugColumnConfigEventHandler INSTANCE = new DebugColumnConfigEventHandler();
public static ReloadLodsConfigEventHandler INSTANCE = new ReloadLodsConfigEventHandler();
@Override
public void onConfigValueSet()
@@ -32,7 +32,7 @@ public class ResetConfigEventHandler
/** private since we only ever need one handler at a time */
private ResetConfigEventHandler()
{
this.configChangeListener = new ConfigChangeListener<>(Config.Client.ResetConfirmation.resetAllSettings, this::doStuff);
this.configChangeListener = new ConfigChangeListener<>(Config.Client.ResetConfirmation.resetAllSettings, (resetSettings) -> { doStuff(resetSettings); });
}
@@ -19,7 +19,6 @@
package com.seibel.distanthorizons.core.config.eventHandlers.presets;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
@@ -30,6 +29,7 @@ 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 org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -38,6 +38,7 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
private static final Logger LOGGER = LogManager.getLogger();
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;
@@ -57,7 +58,11 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
public AbstractPresetConfigEventHandler()
{
configGui.addOnScreenChangeListener(this::onConfigUiClosed);
// don't update the UI when running on a server
if (configGui != null)
{
configGui.addOnScreenChangeListener(this::onConfigUiClosed);
}
}
@@ -168,7 +173,7 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
if (newPreset != currentPreset)
{
this.getPresetConfigEntry().set(this.getCustomPresetEnum());
this.getPresetConfigEntry().set(newPreset);
}
}
@@ -216,7 +221,7 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
possiblePrestList.add(this.getCustomPresetEnum());
}
return possiblePrestList.getFirst();
return possiblePrestList.get(0);
}
@@ -33,6 +33,7 @@ import org.apache.logging.log4j.Logger;
import java.util.*;
@SuppressWarnings("FieldCanBeLocal")
public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigEventHandler<EDhApiQualityPreset>
{
public static final RenderQualityPresetConfigEventHandler INSTANCE = new RenderQualityPresetConfigEventHandler();
@@ -41,7 +42,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiMaxHorizontalResolution> drawResolution = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution,
new HashMap<>()
new HashMap<EDhApiQualityPreset, EDhApiMaxHorizontalResolution>()
{{
this.put(EDhApiQualityPreset.MINIMUM, EDhApiMaxHorizontalResolution.TWO_BLOCKS);
this.put(EDhApiQualityPreset.LOW, EDhApiMaxHorizontalResolution.BLOCK);
@@ -50,7 +51,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.EXTREME, EDhApiMaxHorizontalResolution.BLOCK);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiVerticalQuality> verticalQuality = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.verticalQuality,
new HashMap<>()
new HashMap<EDhApiQualityPreset, EDhApiVerticalQuality>()
{{
this.put(EDhApiQualityPreset.MINIMUM, EDhApiVerticalQuality.HEIGHT_MAP);
this.put(EDhApiQualityPreset.LOW, EDhApiVerticalQuality.LOW);
@@ -59,7 +60,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.EXTREME, EDhApiVerticalQuality.EXTREME);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiHorizontalQuality> horizontalQuality = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.horizontalQuality,
new HashMap<>()
new HashMap<EDhApiQualityPreset, EDhApiHorizontalQuality>()
{{
this.put(EDhApiQualityPreset.MINIMUM, EDhApiHorizontalQuality.LOWEST);
this.put(EDhApiQualityPreset.LOW, EDhApiHorizontalQuality.LOW);
@@ -68,7 +69,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.EXTREME, EDhApiHorizontalQuality.EXTREME);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiTransparency> transparency = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.transparency,
new HashMap<>()
new HashMap<EDhApiQualityPreset, EDhApiTransparency>()
{{
this.put(EDhApiQualityPreset.MINIMUM, EDhApiTransparency.DISABLED);
this.put(EDhApiQualityPreset.LOW, EDhApiTransparency.DISABLED); // should be fake if/when fake is fixed
@@ -77,7 +78,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
this.put(EDhApiQualityPreset.EXTREME, EDhApiTransparency.COMPLETE);
}});
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, Boolean> ssaoEnabled = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Ssao.enabled,
new HashMap<>()
new HashMap<EDhApiQualityPreset, Boolean>()
{{
this.put(EDhApiQualityPreset.MINIMUM, false);
this.put(EDhApiQualityPreset.LOW, false);
@@ -106,7 +107,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
for (ConfigEntryWithPresetOptions<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());
new ConfigChangeListener<>(config.configEntry, (val) -> { this.onConfigValueChanged(); });
}
}
@@ -32,6 +32,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@SuppressWarnings("FieldCanBeLocal")
public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHandler<EDhApiThreadPreset>
{
public static final ThreadPresetConfigEventHandler INSTANCE = new ThreadPresetConfigEventHandler();
@@ -42,7 +43,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
public static int getWorldGenDefaultThreadCount() { return getThreadCountByPercent(0.1); }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Integer> worldGenThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads,
new HashMap<>()
new HashMap<EDhApiThreadPreset, Integer>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1);
this.put(EDhApiThreadPreset.LOW_IMPACT, getWorldGenDefaultThreadCount());
@@ -52,7 +53,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
}});
public static double getWorldGenDefaultRunTimeRatio() { return 0.5; }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Double> worldGenRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForWorldGenerationThreads,
new HashMap<>()
new HashMap<EDhApiThreadPreset, Double>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.1);
this.put(EDhApiThreadPreset.LOW_IMPACT, getWorldGenDefaultRunTimeRatio());
@@ -64,7 +65,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
public static int getFileHandlerDefaultThreadCount() { return getThreadCountByPercent(0.1); }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Integer> fileHandlerThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfFileHandlerThreads,
new HashMap<>()
new HashMap<EDhApiThreadPreset, Integer>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1);
this.put(EDhApiThreadPreset.LOW_IMPACT, getFileHandlerDefaultThreadCount());
@@ -74,7 +75,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
}});
public static double getFileHandlerDefaultRunTimeRatio() { return 0.5; }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Double> fileHandlerRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForFileHandlerThreads,
new HashMap<>()
new HashMap<EDhApiThreadPreset, Double>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.25);
this.put(EDhApiThreadPreset.LOW_IMPACT, getFileHandlerDefaultRunTimeRatio());
@@ -86,7 +87,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
public static int getUpdatePropagatorDefaultThreadCount() { return getThreadCountByPercent(0.10); }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Integer> UpdatePropagatorThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfUpdatePropagatorThreads,
new HashMap<>()
new HashMap<EDhApiThreadPreset, Integer>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1);
this.put(EDhApiThreadPreset.LOW_IMPACT, getUpdatePropagatorDefaultThreadCount());
@@ -96,7 +97,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
}});
public static double getUpdatePropagatorDefaultRunTimeRatio() { return 0.25; }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Double> UpdatePropagatorRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForUpdatePropagatorThreads,
new HashMap<>()
new HashMap<EDhApiThreadPreset, Double>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.1);
this.put(EDhApiThreadPreset.LOW_IMPACT, getUpdatePropagatorDefaultRunTimeRatio());
@@ -108,7 +109,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
public static int getLodBuilderDefaultThreadCount() { return getThreadCountByPercent(0.1); }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Integer> lodBuilderThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads,
new HashMap<>()
new HashMap<EDhApiThreadPreset, Integer>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1);
this.put(EDhApiThreadPreset.LOW_IMPACT, getLodBuilderDefaultThreadCount());
@@ -118,7 +119,7 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
}});
public static double getLodBuilderDefaultRunTimeRatio() { return 0.25; }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Double> lodBuilderRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForLodBuilderThreads,
new HashMap<>()
new HashMap<EDhApiThreadPreset, Double>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.1);
this.put(EDhApiThreadPreset.LOW_IMPACT, getLodBuilderDefaultRunTimeRatio());
@@ -128,6 +129,28 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
}});
public static int getNetworkCompressionDefaultThreadCount() { return getThreadCountByPercent(0.3); }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Integer> networkCompressionThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfNetworkCompressionThreads,
new HashMap<EDhApiThreadPreset, Integer>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1);
this.put(EDhApiThreadPreset.LOW_IMPACT, getNetworkCompressionDefaultThreadCount());
this.put(EDhApiThreadPreset.BALANCED, getThreadCountByPercent(0.4));
this.put(EDhApiThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.6));
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(0.8));
}});
public static double getNetworkCompressionDefaultRunTimeRatio() { return 0.5; }
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Double> networkCompressionRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForNetworkCompressionThreads,
new HashMap<EDhApiThreadPreset, Double>()
{{
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.25);
this.put(EDhApiThreadPreset.LOW_IMPACT, getNetworkCompressionDefaultRunTimeRatio());
this.put(EDhApiThreadPreset.BALANCED, 0.75);
this.put(EDhApiThreadPreset.AGGRESSIVE, 1.0);
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
}});
//==============//
// constructors //
@@ -149,11 +172,14 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
this.configList.add(this.lodBuilderThreadCount);
this.configList.add(this.lodBuilderRunTime);
this.configList.add(this.networkCompressionThreadCount);
this.configList.add(this.networkCompressionRunTime);
for (ConfigEntryWithPresetOptions<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());
new ConfigChangeListener<>(config.configEntry, (val) -> { this.onConfigValueChanged(); });
}
}
@@ -20,17 +20,20 @@
package com.seibel.distanthorizons.core.config.file;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryRelevantSide;
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.ConfigEntry;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.locks.ReentrantLock;
/**
* Handles reading and writing config files.
@@ -40,58 +43,82 @@ import java.nio.file.Path;
*/
public class ConfigFileHandling
{
private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
public final ConfigBase configBase;
public final Path configPath;
private final Logger LOGGER;
private final Logger logger;
/** This is the object for night-config */
private final CommentedFileConfig nightConfig;
/** prevents readers/writers from overlapping and causing the config file from being duplicated or corrupted */
private final ReentrantLock readWriteLock = new ReentrantLock();
//=============//
// constructor //
//=============//
public ConfigFileHandling(ConfigBase configBase, Path configPath)
{
this.LOGGER = LogManager.getLogger(this.getClass().getSimpleName() + ", " + configBase.modID);
this.logger = LogManager.getLogger(this.getClass().getSimpleName() + ", " + configBase.modID);
this.configBase = configBase;
this.configPath = configPath;
this.nightConfig = CommentedFileConfig.builder(this.configPath.toFile()).build();
}
/** Saves the entire config to the file */
public void saveToFile()
{
saveToFile(this.nightConfig);
}
public void saveToFile() { this.saveToFile(this.nightConfig); }
/** Saves the entire config to the file */
public void saveToFile(CommentedFileConfig nightConfig)
{
if (!Files.exists(configPath)) // Try to check if the config exists
{
reCreateFile(configPath);
}
loadNightConfig(nightConfig);
for (AbstractConfigType<?, ?> entry : this.configBase.entries)
{
if (ConfigEntry.class.isAssignableFrom(entry.getClass()))
{
createComment((ConfigEntry<?>) entry, nightConfig);
saveEntry((ConfigEntry<?>) entry, nightConfig);
}
}
try
{
nightConfig.save();
this.readWriteLock.lock();
if (!Files.exists(this.configPath)) // Try to check if the config exists
{
reCreateFile(this.configPath);
}
this.loadNightConfig(nightConfig);
for (AbstractConfigType<?, ?> entry : this.configBase.entries)
{
if (ConfigEntry.class.isAssignableFrom(entry.getClass()))
{
this.createComment((ConfigEntry<?>) entry, nightConfig);
this.saveEntry((ConfigEntry<?>) entry, nightConfig);
}
}
try
{
nightConfig.save();
}
catch (Exception e)
{
// If it fails to save, crash game
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + this.configPath + "]", e);
}
}
catch (Exception e)
finally
{
// If it fails to save, crash game
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + configPath.toString() + "]", e);
this.readWriteLock.unlock();
}
}
@@ -102,67 +129,77 @@ public class ConfigFileHandling
*/
public void loadFromFile()
{
int currentCfgVersion = configBase.configVersion;
try
{
// Dont load the real `this.nightConfig`, instead create a tempoary one
CommentedFileConfig tmpNightConfig = CommentedFileConfig.builder(this.configPath.toFile()).build();
tmpNightConfig.load();
// Attempt to get the version number
currentCfgVersion = (Integer) tmpNightConfig.get("_version");
tmpNightConfig.close();
} catch (Exception ignored) { }
if (currentCfgVersion == configBase.configVersion)
{}
else if (currentCfgVersion > configBase.configVersion)
{
LOGGER.warn("Found config version [" + String.valueOf(currentCfgVersion) + "] which is newer than current mods config version of [" + String.valueOf(configBase.configVersion) + "]. You may have downgraded the mod and items may have been moved, you have been warned");
}
else // if (currentCfgVersion < configBase.configVersion)
{
LOGGER.warn(configBase.modName +" config is of an older version, currently there is no config updater... so resetting config");
this.readWriteLock.lock();
int currentCfgVersion = this.configBase.configVersion;
try
{
Files.delete(configPath);
// Dont load the real `this.nightConfig`, instead create a tempoary one
CommentedFileConfig tmpNightConfig = CommentedFileConfig.builder(this.configPath.toFile()).build();
tmpNightConfig.load();
// Attempt to get the version number
currentCfgVersion = (Integer) tmpNightConfig.get("_version");
tmpNightConfig.close();
}
catch (Exception e)
catch (Exception ignored) { }
if (currentCfgVersion == this.configBase.configVersion)
{
LOGGER.error(e);
// handle normally
}
else if (currentCfgVersion > this.configBase.configVersion)
{
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");
}
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");
try
{
Files.delete(this.configPath);
}
catch (Exception e)
{
this.logger.error(e);
}
}
this.loadFromFile(this.nightConfig);
this.nightConfig.set("_version", this.configBase.configVersion);
}
finally
{
this.readWriteLock.unlock();
}
loadFromFile(nightConfig);
nightConfig.set("_version", configBase.configVersion);
}
/**
* Loads the entire config from the file
*
* @apiNote This overwrites any value currently stored in the config
*/
public void loadFromFile(CommentedFileConfig nightConfig)
private void loadFromFile(CommentedFileConfig nightConfig)
{
// Attempt to load the file and if it fails then save config to file
if (Files.exists(configPath))
if (Files.exists(this.configPath))
{
loadNightConfig(nightConfig);
this.loadNightConfig(nightConfig);
}
else
{
reCreateFile(configPath);
reCreateFile(this.configPath);
}
// Load all the entries
for (AbstractConfigType<?, ?> entry : this.configBase.entries)
{
if (
ConfigEntry.class.isAssignableFrom(entry.getClass()) &&
entry.getAppearance().showInFile
)
if (ConfigEntry.class.isAssignableFrom(entry.getClass())
&& entry.getAppearance().showInFile)
{
createComment((ConfigEntry<?>) entry, nightConfig);
loadEntry((ConfigEntry<?>) entry, nightConfig);
this.createComment((ConfigEntry<?>) entry, nightConfig);
this.loadEntry((ConfigEntry<?>) entry, nightConfig);
}
}
@@ -174,7 +211,7 @@ public class ConfigFileHandling
catch (Exception e)
{
// If it fails to save, crash game
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + configPath.toString() + "]", e);
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + this.configPath + "]", e);
}
}
@@ -184,24 +221,34 @@ public class ConfigFileHandling
// Save an entry when only given the entry
public void saveEntry(ConfigEntry<?> entry)
{
saveEntry(entry, nightConfig);
nightConfig.save();
this.saveEntry(entry, this.nightConfig);
this.nightConfig.save();
}
/** Save an entry */
public void saveEntry(ConfigEntry<?> entry, CommentedFileConfig workConfig)
{
if (!entry.getAppearance().showInFile) return;
if (entry.getTrueValue() == null)
throw new IllegalArgumentException("Entry [" + entry.getNameWCategory() + "] is null, this may be a problem with [" + configBase.modName + "]. Please contact the authors");
if (!entry.getAppearance().showInFile)
{
return;
}
else if ((entry.getRelevantSide() == EConfigEntryRelevantSide.CLIENT && MC_SHARED.isDedicatedServer())
|| (entry.getRelevantSide() == EConfigEntryRelevantSide.SERVER && !MC_SHARED.isDedicatedServer()))
{
// don't save server/client specific configs on the opposite
// (this keeps the config file clean of unnecessary items)
return;
}
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.");
}
workConfig.set(entry.getNameWCategory(), ConfigTypeConverters.attemptToConvertToString(entry.getType(), entry.getTrueValue()));
}
/** Loads an entry when only given the entry */
public void loadEntry(ConfigEntry<?> entry)
{
loadEntry(entry, nightConfig);
}
public void loadEntry(ConfigEntry<?> entry) { this.loadEntry(entry, this.nightConfig); }
/** Loads an entry */
@SuppressWarnings("unchecked")
public <T> void loadEntry(ConfigEntry<T> entry, CommentedFileConfig nightConfig)
@@ -211,7 +258,7 @@ public class ConfigFileHandling
if (!nightConfig.contains(entry.getNameWCategory()))
{
saveEntry(entry, nightConfig);
this.saveEntry(entry, nightConfig);
return;
}
@@ -230,7 +277,7 @@ public class ConfigFileHandling
Object convertedValue = ConfigTypeConverters.attemptToConvertFromString(expectedValueClass, value);
if (!convertedValue.getClass().equals(expectedValueClass))
{
LOGGER.error("Unable to convert config value ["+value+"] from ["+(value != null ? value.getClass() : "NULL")+"] to ["+expectedValueClass+"] for config ["+entry.name+"], " +
this.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();
@@ -239,31 +286,39 @@ public class ConfigFileHandling
if (entry.getTrueValue() == null)
{
LOGGER.warn("Entry [" + entry.getNameWCategory() + "] returned as null from the config. Using default value.");
this.logger.warn("Entry [" + entry.getNameWCategory() + "] returned as null from the config. Using default value.");
entry.pureSet(entry.getDefaultValue());
}
}
catch (Exception e)
{
// e.printStackTrace();
LOGGER.warn("Entry [" + entry.getNameWCategory() + "] had an invalid value when loading the config. Using default value.");
this.logger.warn("Entry [" + entry.getNameWCategory() + "] had an invalid value when loading the config. Using default value.");
entry.pureSet(entry.getDefaultValue());
}
}
// Creates the comment for an entry when only given the entry
public void createComment(ConfigEntry<?> entry)
{
createComment(entry, nightConfig);
}
public void createComment(ConfigEntry<?> entry) { this.createComment(entry, this.nightConfig); }
// Creates a comment for an entry
public void createComment(ConfigEntry<?> entry, CommentedFileConfig nightConfig)
{
if (
!entry.getAppearance().showInFile ||
entry.getComment() == null
)
if (!entry.getAppearance().showInFile
|| entry.getComment() == null)
{
return;
}
if ((entry.getRelevantSide() == EConfigEntryRelevantSide.CLIENT && MC_SHARED.isDedicatedServer())
|| (entry.getRelevantSide() == EConfigEntryRelevantSide.SERVER && !MC_SHARED.isDedicatedServer()))
{
// don't save server/client specific configs on the opposite
// (this keeps the config file clean of unnecessary items)
return;
}
String comment = entry.getComment().replaceAll("\n", "\n ").trim();
// the new line makes it easier to read and separate configs
@@ -302,7 +357,7 @@ public class ConfigFileHandling
}
catch (Exception e)
{
LOGGER.warn("Loading file failed because of this expectation:\n" + e);
this.logger.warn("Loading file failed because of this expectation:\n" + e);
reCreateFile(this.configPath);
@@ -312,7 +367,7 @@ public class ConfigFileHandling
catch (Exception ex)
{
System.out.println("Creating file failed");
LOGGER.error(ex);
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);
}
}
@@ -35,7 +35,7 @@ import java.util.Map;
public class ConfigTypeConverters
{
// Once you've made a converter add it to here where the first value is the type you want to convert and the 2nd value is the converter
public static final Map<Class<?>, ConverterBase> convertObjects = new HashMap<>()
public static final Map<Class<?>, ConverterBase> convertObjects = new HashMap<Class<?>, ConverterBase>()
{{
this.put(Short.class, new ShortConverter());
this.put(Long.class, new LongConverter());
@@ -73,28 +73,33 @@ public final class EmbeddedFrameUtil
private static String getEmbeddedFrameImpl()
{
return switch (EPlatform.get())
switch (EPlatform.get())
{
case LINUX -> "sun.awt.X11.XEmbeddedFrame";
case WINDOWS -> "sun.awt.windows.WEmbeddedFrame";
case MACOS -> "sun.lwawt.macosx.CViewEmbeddedFrame";
default -> throw new IllegalStateException();
};
case LINUX:
return "sun.awt.X11.XEmbeddedFrame";
case WINDOWS:
return "sun.awt.windows.WEmbeddedFrame";
case MACOS:
return "sun.lwawt.macosx.CViewEmbeddedFrame";
default:
throw new IllegalStateException();
}
}
private static long getEmbeddedFrameHandle(long window)
{
return switch (EPlatform.get())
switch (EPlatform.get())
{
case LINUX -> glfwGetX11Window(window);
case WINDOWS -> glfwGetWin32Window(window);
case MACOS ->
{
case LINUX:
return glfwGetX11Window(window);
case WINDOWS:
return glfwGetWin32Window(window);
case MACOS:
long objc_msgSend = ObjCRuntime.getLibrary().getFunctionAddress("objc_msgSend");
yield invokePPP(glfwGetCocoaWindow(window), sel_getUid("contentView"), objc_msgSend);
}
default -> throw new IllegalStateException();
};
return invokePPP(glfwGetCocoaWindow(window), sel_getUid("contentView"), objc_msgSend);
default:
throw new IllegalStateException();
}
}
public static Frame embeddedFrameCreate(long window)
@@ -25,6 +25,7 @@ import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance;
import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryPerformance;
import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryRelevantSide;
import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry;
import java.util.ArrayList;
@@ -44,6 +45,10 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
private T min;
private T max;
private final ArrayList<IConfigListener> listenerList;
private final String serversideShortName;
private final EConfigEntryPerformance performance;
private final EConfigEntryRelevantSide relevantSide;
// API control //
/**
@@ -53,19 +58,25 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
public final boolean allowApiOverride;
private T apiValue;
private final EConfigEntryPerformance performance;
/** Creates the entry */
private ConfigEntry(EConfigEntryAppearance appearance, T value, String comment, T min, T max, boolean allowApiOverride, EConfigEntryPerformance performance, ArrayList<IConfigListener> listenerList)
private ConfigEntry(
EConfigEntryAppearance appearance,
T value, String comment, T min, T max,
String serversideShortName, boolean allowApiOverride,
EConfigEntryPerformance performance, EConfigEntryRelevantSide relevantSide,
ArrayList<IConfigListener> listenerList)
{
super(appearance, value);
this.comment = comment;
this.min = min;
this.max = max;
this.serversideShortName = serversideShortName;
this.allowApiOverride = allowApiOverride;
this.performance = performance;
this.relevantSide = relevantSide;
this.listenerList = listenerList;
}
@@ -179,6 +190,9 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
if (validness == 1) this.value = (T) NumberUtil.getMaximum(this.value.getClass());
}
// TODO is this for command line use?
public String getServersideShortName() { return this.serversideShortName; }
@Override
public String getComment() { return this.comment; }
@Override
@@ -186,6 +200,8 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
/** Gets the performance impact of an option */
public EConfigEntryPerformance getPerformance() { return this.performance; }
/** Gets whether this config should apply to the client, server, or both */
public EConfigEntryRelevantSide getRelevantSide() { return this.relevantSide; }
/** Fired whenever the config value changes to a new value. */
public void addValueChangeListener(Consumer<T> onValueChangeFunc)
@@ -315,8 +331,10 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
private String tmpComment = null;
private T tmpMin = null;
private T tmpMax = null;
protected String tmpServersideShortName = null;
private boolean tmpUseApiOverwrite = true;
private EConfigEntryPerformance tmpPerformance = EConfigEntryPerformance.DONT_SHOW;
private EConfigEntryRelevantSide tmpRelevantSide = EConfigEntryRelevantSide.CLIENT;
protected ArrayList<IConfigListener> tmpIConfigListener = new ArrayList<>();
public Builder<T> comment(String newComment)
@@ -352,6 +370,12 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
return this;
}
public Builder<T> setServersideShortName(String name)
{
this.tmpServersideShortName = name;
return this;
}
public Builder<T> setUseApiOverwrite(boolean newUseApiOverwrite)
{
this.tmpUseApiOverwrite = newUseApiOverwrite;
@@ -364,6 +388,12 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
return this;
}
public Builder<T> setSide(EConfigEntryRelevantSide relevantSide)
{
this.tmpRelevantSide = relevantSide;
return this;
}
public Builder<T> replaceListeners(ArrayList<IConfigListener> newConfigListener)
@@ -394,7 +424,11 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
public ConfigEntry<T> build()
{
return new ConfigEntry<>(this.tmpAppearance, this.tmpValue, this.tmpComment, this.tmpMin, this.tmpMax, this.tmpUseApiOverwrite, this.tmpPerformance, this.tmpIConfigListener);
return new ConfigEntry<>(
this.tmpAppearance,
this.tmpValue, this.tmpComment, this.tmpMin, this.tmpMax,
this.tmpServersideShortName, this.tmpUseApiOverwrite,
this.tmpPerformance, this.tmpRelevantSide, this.tmpIConfigListener);
}
}
@@ -17,14 +17,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.core.network.protocol;
package com.seibel.distanthorizons.core.config.types.enums;
/**
* CLIENT, <br>
* SERVER, <br>
* BOTH, <br>
* SERVER, <br>
* CLIENT, <br>
*
* Defines whether this config entry is for the client, server, or both.
* TODO this needs a better name.
*/
public enum EMessageHandlerSide
public enum EConfigEntryRelevantSide
{
BOTH,
SERVER,
CLIENT,
SERVER
}
@@ -516,9 +516,10 @@ public class FullDataPointIdMap
if (otherObj == this)
return true;
if (!(otherObj instanceof Entry other))
if (!(otherObj instanceof Entry))
return false;
Entry other = (Entry) otherObj;
return other.biome.getSerialString().equals(this.biome.getSerialString())
&& other.blockState.getSerialString().equals(this.blockState.getSerialString());
}
@@ -90,12 +90,14 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
/**
* stores how far each column has been generated should start with {@link EDhApiWorldGenerationStep#EMPTY}
* @see EDhApiWorldGenerationStep
*
* @see EDhApiWorldGenerationStep
*/
public byte[] columnGenerationSteps;
/**
* stores what world compression was used for each column.
* @see EDhApiWorldCompressionMode
*
* @see EDhApiWorldCompressionMode
*/
public byte[] columnWorldCompressionMode;
@@ -144,7 +146,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
this.columnWorldCompressionMode = columnWorldCompressionMode;
}
public static FullDataSourceV2 createFromChunk(IChunkWrapper chunkWrapper) { return LodDataBuilder.createGeneratedDataSource(chunkWrapper); }
public static FullDataSourceV2 createFromChunk(IChunkWrapper chunkWrapper) { return LodDataBuilder.createFromChunk(chunkWrapper); }
public static FullDataSourceV2 createFromLegacyDataSourceV1(FullDataSourceV1 legacyData)
{
@@ -180,7 +182,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
boolean isAir = legacyData.mapping.getBlockStateWrapper(FullDataPointUtil.getId(dataPoint)).isAir();
byte blockLight = (byte) FullDataPointUtil.getBlockLight(dataPoint);
if (isAir)
{
// air shouldn't have any light, otherwise down sampling will look weird
@@ -379,7 +381,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
for (int x = 0; x < WIDTH; x += 2)
{
for (int z = 0; z < WIDTH; z += 2)
{
{
int recipientX = (x / 2) + recipientOffsetX;
int recipientZ = (z / 2) + recipientOffsetZ;
int recipientIndex = relativePosToIndex(recipientX, recipientZ);
@@ -612,7 +614,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
int id = determineMostValueInColumnSlice(mergeIds, inputDataSource.mapping);
byte blockLight = (byte) determineAverageValueInColumnSlice(mergeBlockLights);
byte skyLight = (byte) determineAverageValueInColumnSlice(mergeSkyLights);
// if this slice is different then the last one, create a new one
if (id != lastId
// block and sky light might not be necessary
@@ -698,7 +700,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
{
LodUtil.assertTrue(sliceArray.length == 4, "Column Slice should only contain 4 values.");
}
int value0 = sliceArray[0];
int count0 = 0;
int value1 = sliceArray[1];
@@ -719,26 +721,42 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
}
if (value == value0)
{
count0++;
}
else if (value == value1)
{
count1++;
}
else if (value == value2)
{
count2++;
}
else
{
count3++;
}
}
// return the most common occurance
int maxCount = Math.max(count0, Math.max(count1, Math.max(count2, count3)));
if (maxCount == count0)
// if the max count is 1 then we'll just go with the first column
// if the max count is 1 then we'll just go with the first column
{
return value0;
}
else if (maxCount == count1)
{
return value1;
}
else if (maxCount == count2)
{
return value2;
else
}
else
{
return value3;
}
}
private static int determineAverageValueInColumnSlice(int[] sliceArray)
{
@@ -876,8 +894,8 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
{
int index = relativePosToIndex(relX, relZ);
this.dataPoints[index] = longArray;
this.columnGenerationSteps[index] = worldGenStep.value;
this.columnWorldCompressionMode[index] = worldCompressionMode.value;
this.columnGenerationSteps[index] = worldGenStep.value;
this.columnWorldCompressionMode[index] = worldCompressionMode.value;
if (RUN_UPDATE_DEV_VALIDATION)
@@ -905,6 +923,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
@Override
public String toString() { return DhSectionPos.toString(this.pos); }
/** Only includes the base data in this object, not the mapping */
@Override
public int hashCode()
{
@@ -927,10 +946,11 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof FullDataSourceV2 other))
if (!(obj instanceof FullDataSourceV2))
{
return false;
}
FullDataSourceV2 other = (FullDataSourceV2) obj;
if (other.pos != this.pos)
{
@@ -941,7 +961,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
// the positions are the same, use the hash as a quick method
// to determine if the data inside is the same.
// Note: this isn't perfect, but should work well enough for our use case.
return other.hashCode() == this.hashCode();
return other.hashCode() == this.hashCode();
}
}
@@ -25,7 +25,7 @@ import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRender
import com.seibel.distanthorizons.core.file.DataSourcePool;
import com.seibel.distanthorizons.core.file.IDataSource;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
@@ -182,7 +182,7 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
EDhApiWorldGenerationStep worldGenStep = inputFullDataSource.getWorldGenStepAtRelativePos(x, z);
if (dataColumn != null && worldGenStep != EDhApiWorldGenerationStep.EMPTY)
{
FullDataToRenderDataTransformer.updateRenderDataViewWithFullDataColumn(
FullDataToRenderDataTransformer.updateOrReplaceRenderDataViewColumnWithFullDataColumn(
level, inputFullDataSource.mapping,
minBlockPos.x + x,
minBlockPos.z + z,
@@ -288,7 +288,7 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
String SUBDATA_DELIMITER = ",";
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(this.pos);
stringBuilder.append(DhSectionPos.toString(this.pos));
stringBuilder.append(LINE_DELIMITER);
int size = 1;
@@ -99,23 +99,33 @@ public final class BufferQuad
if (compareDirection == BufferMergeDirectionEnum.EastWest)
{
return switch (this.direction.getAxis())
switch (this.direction.getAxis())
{
case X -> threeDimensionalCompare(this.x, this.y, this.z, quad.x, quad.y, quad.z);
case Y -> threeDimensionalCompare(this.y, this.z, this.x, quad.y, quad.z, quad.x);
case Z -> threeDimensionalCompare(this.z, this.y, this.x, quad.z, quad.y, quad.x);
default -> throw new IllegalArgumentException("Invalid Axis enum: " + this.direction.getAxis());
};
case X:
return threeDimensionalCompare(this.x, this.y, this.z, quad.x, quad.y, quad.z);
case Y:
return threeDimensionalCompare(this.y, this.z, this.x, quad.y, quad.z, quad.x);
case Z:
return threeDimensionalCompare(this.z, this.y, this.x, quad.z, quad.y, quad.x);
default:
throw new IllegalArgumentException("Invalid Axis enum: " + this.direction.getAxis());
}
}
else
{
return switch (this.direction.getAxis())
switch (this.direction.getAxis())
{
case X -> threeDimensionalCompare(this.x, this.z, this.y, quad.x, quad.z, quad.y);
case Y -> threeDimensionalCompare(this.y, this.x, this.z, quad.y, quad.x, quad.z);
case Z -> threeDimensionalCompare(this.z, this.x, this.y, quad.z, quad.x, quad.y);
default -> throw new IllegalArgumentException("Invalid Axis enum: " + this.direction.getAxis());
};
case X:
return threeDimensionalCompare(this.x, this.z, this.y, quad.x, quad.z, quad.y);
case Y:
return threeDimensionalCompare(this.y, this.x, this.z, quad.y, quad.x, quad.z);
case Z:
return threeDimensionalCompare(this.z, this.x, this.y, quad.z, quad.x, quad.y);
default:
throw new IllegalArgumentException("Invalid Axis enum: " + this.direction.getAxis());
}
}
}
/**
@@ -19,29 +19,60 @@
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.coreapi.util.MathUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
public class ColumnBox
{
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
/**
* if the skylight has this value that means
* no data is expected
*/
private static final byte SKYLIGHT_EMPTY = -1;
/**
* if the skylight has this value that means
* that block position is covered/occuled by an adjacent block/column.
*/
private static final byte SKYLIGHT_COVERED = -2;
private static final ThreadLocal<byte[]> THREAD_LOCAL_SKY_LIGHT_ARRAY = ThreadLocal.withInitial(() ->
{
byte[] array = new byte[RenderDataPointUtil.MAX_WORLD_Y_SIZE];
Arrays.fill(array, SKYLIGHT_EMPTY);
return array;
});
//=========//
// builder //
//=========//
public static void addBoxQuadsToBuilder(
LodQuadBuilder builder,
LodQuadBuilder builder, IDhClientLevel clientLevel,
short xSize, short ySize, short zSize,
short x, short minY, short z,
int color, byte irisBlockMaterialId, byte skyLight, byte blockLight,
long topData, long bottomData, ColumnArrayView[][] adjData)
long topData, long bottomData, ColumnArrayView[] adjData, boolean[] isAdjDataSameDetailLevel)
{
//================//
// variable setup //
//================//
short maxX = (short) (x + xSize);
short maxY = (short) (minY + ySize);
short maxZ = (short) (z + zSize);
@@ -53,33 +84,24 @@ public class ColumnBox
boolean isTopTransparent = RenderDataPointUtil.getAlpha(topData) < 255 && LodRenderer.transparencyEnabled;
boolean isBottomTransparent = RenderDataPointUtil.getAlpha(bottomData) < 255 && LodRenderer.transparencyEnabled;
// defaulting to a value far below what we can normally render means we
// don't need to have an additional "is cave culling enabled" check
int caveCullingMaxY = Integer.MIN_VALUE;
if (Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get())
{
caveCullingMaxY = Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight.get() - clientLevel.getMinY();
}
// if there isn't any data below this LOD, make this LOD's color opaque to prevent seeing void through transparent blocks
// Note: this LOD should still be considered transparent for this method's checks, otherwise rendering bugs may occur
// FIXME this transparency change should be applied before this point since this could affect other areas
// This may also be better than handling the LOD as transparent, but that is TBD
if (!RenderDataPointUtil.doesDataPointExist(bottomData))
{
color = ColorUtil.setAlpha(color, 255);
}
// cave culling prevention
// prevents certain faces from being culled underground that should be allowed
if (builder.skipQuadsWithZeroSkylight
&& 0 == skyLight
&& builder.skyLightCullingBelow > maxY
&& (
(RenderDataPointUtil.getAlpha(topData) < 255 && RenderDataPointUtil.getYMax(topData) >= builder.skyLightCullingBelow)
|| (RenderDataPointUtil.getYMin(topData) >= builder.skyLightCullingBelow)
|| !RenderDataPointUtil.doesDataPointExist(topData)
)
)
{
maxY = builder.skyLightCullingBelow;
}
// fake ocean transparency
if (LodRenderer.transparencyEnabled && LodRenderer.fakeOceanFloor)
{
@@ -99,7 +121,9 @@ public class ColumnBox
// add top and bottom faces if requested //
//==========================//
// add top and bottom faces //
//==========================//
boolean skipTop = RenderDataPointUtil.doesDataPointExist(topData) && (RenderDataPointUtil.getYMin(topData) == maxY) && !isTopTransparent;
if (!skipTop)
@@ -114,409 +138,291 @@ public class ColumnBox
}
// add North, south, east, and west faces if requested //
// TODO merge duplicate code
//NORTH face vertex creation
//========================================//
// add North, south, east, and west faces //
//========================================//
// NORTH face
{
ColumnArrayView[] adjDataNorth = adjData[EDhDirection.NORTH.ordinal() - 2]; // TODO can we use something other than ordinal-2?
int adjOverlapNorth = ColorUtil.INVISIBLE;
if (adjDataNorth == null)
ColumnArrayView adjCol = adjData[EDhDirection.NORTH.ordinal() - 2]; // TODO can we use something other than ordinal-2?
boolean adjSameDetailLevel = isAdjDataSameDetailLevel[EDhDirection.NORTH.ordinal() - 2];
// if the adjacent column is null that generally means the adjacent area hasn't been generated yet
if (adjCol == null)
{
// add an adjacent face if this is opaque face or transparent over the void
// Add an adjacent face if this is opaque face or transparent over the void.
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.NORTH, x, minY, z, xSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
}
else if (adjDataNorth.length == 1)
{
makeAdjVerticalQuad(builder, adjDataNorth[0], EDhDirection.NORTH, x, minY, z, xSize, ySize,
color, adjOverlapNorth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
}
else
{
makeAdjVerticalQuad(builder, adjDataNorth[0], EDhDirection.NORTH, x, minY, z, (short) (xSize / 2), ySize,
color, adjOverlapNorth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjDataNorth[1], EDhDirection.NORTH, (short) (x + xSize / 2), minY, z, (short) (xSize / 2), ySize,
color, adjOverlapNorth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.NORTH, x, minY, z, xSize, ySize,
color, irisBlockMaterialId, blockLight);
}
}
//SOUTH face vertex creation
// SOUTH face
{
ColumnArrayView[] adjDataSouth = adjData[EDhDirection.SOUTH.ordinal() - 2];
int adjOverlapSouth = ColorUtil.INVISIBLE;
if (adjDataSouth == null)
ColumnArrayView adjCol = adjData[EDhDirection.SOUTH.ordinal() - 2];
boolean adjSameDetailLevel = isAdjDataSameDetailLevel[EDhDirection.SOUTH.ordinal() - 2];
if (adjCol == null)
{
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.SOUTH, x, minY, maxZ, xSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
else if (adjDataSouth.length == 1)
{
makeAdjVerticalQuad(builder, adjDataSouth[0], EDhDirection.SOUTH, x, minY, maxZ, xSize, ySize,
color, adjOverlapSouth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
}
}
else
{
makeAdjVerticalQuad(builder, adjDataSouth[0], EDhDirection.SOUTH, x, minY, maxZ, (short) (xSize / 2), ySize,
color, adjOverlapSouth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjDataSouth[1], EDhDirection.SOUTH, (short) (x + xSize / 2), minY, maxZ, (short) (xSize / 2), ySize,
color, adjOverlapSouth, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.SOUTH, x, minY, maxZ, xSize, ySize,
color, irisBlockMaterialId, blockLight);
}
}
//WEST face vertex creation
// WEST face
{
ColumnArrayView[] adjDataWest = adjData[EDhDirection.WEST.ordinal() - 2];
int adjOverlapWest = ColorUtil.INVISIBLE;
if (adjDataWest == null)
ColumnArrayView adjCol = adjData[EDhDirection.WEST.ordinal() - 2];
boolean adjSameDetailLevel = isAdjDataSameDetailLevel[EDhDirection.WEST.ordinal() - 2];
if (adjCol == null)
{
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.WEST, x, minY, z, zSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
else if (adjDataWest.length == 1)
{
makeAdjVerticalQuad(builder, adjDataWest[0], EDhDirection.WEST, x, minY, z, zSize, ySize,
color, adjOverlapWest, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
}
}
else
{
makeAdjVerticalQuad(builder, adjDataWest[0], EDhDirection.WEST, x, minY, z, (short) (zSize / 2), ySize,
color, adjOverlapWest, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjDataWest[1], EDhDirection.WEST, x, minY, (short) (z + zSize / 2), (short) (zSize / 2), ySize,
color, adjOverlapWest, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.WEST, x, minY, z, zSize, ySize,
color, irisBlockMaterialId, blockLight);
}
}
//EAST face vertex creation
// EAST face
{
ColumnArrayView[] adjDataEast = adjData[EDhDirection.EAST.ordinal() - 2];
int adjOverlapEast = ColorUtil.INVISIBLE;
if (adjData[EDhDirection.EAST.ordinal() - 2] == null)
ColumnArrayView adjCol = adjData[EDhDirection.EAST.ordinal() - 2];
boolean adjSameDetailLevel = isAdjDataSameDetailLevel[EDhDirection.EAST.ordinal() - 2];
if (adjCol == null)
{
if (!isTransparent || overVoid)
{
builder.addQuadAdj(EDhDirection.EAST, maxX, minY, z, zSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
}
else if (adjDataEast.length == 1)
{
makeAdjVerticalQuad(builder, adjDataEast[0], EDhDirection.EAST, maxX, minY, z, zSize, ySize,
color, adjOverlapEast, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
}
}
else
{
makeAdjVerticalQuad(builder, adjDataEast[0], EDhDirection.EAST, maxX, minY, z, (short) (zSize / 2), ySize,
color, adjOverlapEast, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjDataEast[1], EDhDirection.EAST, maxX, minY, (short) (z + zSize / 2), (short) (zSize / 2), ySize,
color, adjOverlapEast, irisBlockMaterialId, skyLightTop, blockLight,
topData, bottomData);
makeAdjVerticalQuad(builder, adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.EAST, maxX, minY, z, zSize, ySize,
color, irisBlockMaterialId, blockLight);
}
}
}
// the overlap color can be used to see faces that shouldn't be rendered
private static void makeAdjVerticalQuad(
LodQuadBuilder builder, ColumnArrayView adjColumnView, EDhDirection direction,
LodQuadBuilder builder, @NotNull ColumnArrayView adjColumnView, boolean adjacentIsSameDetailLevel, int caveCullingMaxY, EDhDirection direction,
short x, short yMin, short z, short horizontalWidth, short ySize,
int color, int debugOverlapColor, byte irisBlockMaterialId, byte skyLightTop, byte blockLight,
long topData, long bottomData)
int color, byte irisBlockMaterialId, byte blockLight)
{
//==================//
// create face with //
// no adjacent data //
//==================//
color = ColorUtil.applyShade(color, MC.getShade(direction));
if (adjColumnView == null || adjColumnView.size == 0 || RenderDataPointUtil.isVoid(adjColumnView.get(0)))
// if there isn't any data adjacent to this LOD,
// just add the full vertical quad
if (adjColumnView.size == 0 || RenderDataPointUtil.isVoid(adjColumnView.get(0)))
{
// there isn't any data adjacent to this LOD, add the vertical quad
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
return;
}
int yMax = yMin + ySize;
int adjIndex;
boolean firstFace = true;
boolean inputAboveAdjLods = true;
short previousAdjDepth = -1;
byte nextTopSkyLight = skyLightTop;
boolean inputTransparent = ColorUtil.getAlpha(color) < 255 && LodRenderer.transparencyEnabled;
boolean lastAdjWasTransparent = false;
//===========================//
// Determine face visibility //
// based on it's neighbors //
//===========================//
short yMax = (short) (yMin + ySize); // min is inclusive, max is exclusive
byte[] skyLightAtInputPos = THREAD_LOCAL_SKY_LIGHT_ARRAY.get();
if (!RenderDataPointUtil.doesDataPointExist(bottomData))
try
{
// there isn't anything under this LOD,
// to prevent seeing through the world, make it opaque
color = ColorUtil.setAlpha(color, 255);
}
// Add adjacent faces if this LOD is surrounded by transparent LODs
// (prevents invisible sides underwater)
int adjCount = adjColumnView.size();
for (adjIndex = 0; // iterates top down
adjIndex < adjCount
&& RenderDataPointUtil.doesDataPointExist(adjColumnView.get(adjIndex))
&& !RenderDataPointUtil.isVoid(adjColumnView.get(adjIndex));
adjIndex++)
{
long adjPoint = adjColumnView.get(adjIndex);
// set the initial sky-lights for this face,
// if nothing overlaps or overhangs the face should have max sky light
Arrays.fill(skyLightAtInputPos, yMin, yMax, LodUtil.MAX_MC_LIGHT);
// if the adjacent data point is over the void
// don't consider it as transparent
// FIXME this transparency change should be applied before this point since this could affect other areas
boolean adjOverVoid = false;
if (adjIndex > 0)
// iterate top down
int adjCount = adjColumnView.size();
for (int adjIndex = 0; adjIndex < adjCount; adjIndex++)
{
long adjBellowPoint = adjColumnView.get(adjIndex-1);
adjOverVoid = !RenderDataPointUtil.doesDataPointExist(adjBellowPoint);
}
boolean adjTransparent = !adjOverVoid && RenderDataPointUtil.getAlpha(adjPoint) < 255 && LodRenderer.transparencyEnabled;
// continue if this data point is transparent or the adjacent point is not
if (inputTransparent || !adjTransparent) // TODO inputIsTransparent may be unnecessary
{
short adjYMin = RenderDataPointUtil.getYMin(adjPoint);
short adjYMax = RenderDataPointUtil.getYMax(adjPoint);
long adjPoint = adjColumnView.get(adjIndex);
short adjMinY = RenderDataPointUtil.getYMin(adjPoint);
short adjMaxY = RenderDataPointUtil.getYMax(adjPoint);
// if fake transparency is enabled, allow for 1 block of transparency,
// everything under that should be opaque
if (LodRenderer.transparencyEnabled && LodRenderer.fakeOceanFloor)
// skip empty adjacent datapoints
if (!RenderDataPointUtil.doesDataPointExist(adjPoint)
|| RenderDataPointUtil.isVoid(adjPoint))
{
if (lastAdjWasTransparent && !adjTransparent)
{
adjYMax = (short) (RenderDataPointUtil.getYMax(adjColumnView.get(adjIndex - 1)) - 1);
}
else if (adjTransparent && (adjIndex + 1) < adjCount)
{
if (RenderDataPointUtil.getAlpha(adjColumnView.get(adjIndex + 1)) == 255)
{
adjYMin = (short) (adjYMax - 1);
}
}
}
if (yMax <= adjYMin)
{
// the adjacent LOD is above the input LOD and won't affect its rendering,
// skip to the next adjacent
continue;
}
inputAboveAdjLods = false;
if (adjYMax < yMin)
// skip this adjacent datapoint if it's above the input datapoint (since it can't affect the input data point)
if (yMax <= adjMinY)
{
// the adjacent LOD is below the input LOD
// getting the skylight is more complicated
// since LODs can be adjacent to water, which changes how skylight works
byte skyLight;
if (adjIndex == 0)
{
// this adj LOD is at the highest position,
// its sky lighting won't be affected by anything above it
skyLight = RenderDataPointUtil.getLightSky(adjPoint);
}
else
{
// TODO improve the comments here, this is a bit confusing
long aboveAdjPoint = adjColumnView.get(adjIndex - 1);
if (RenderDataPointUtil.getAlpha(aboveAdjPoint) != 255)
{
// above adjacent LOD is transparent...
boolean inputMaxHigherThanAboveAdj = yMax > RenderDataPointUtil.getYMax(aboveAdjPoint);
if (inputMaxHigherThanAboveAdj)
{
// ...and higher than the input yMax,
// use its sky light
skyLight = RenderDataPointUtil.getLightSky(aboveAdjPoint);
}
else
{
// ...and at or below the input yMax,
skyLight = RenderDataPointUtil.getLightSky(adjPoint);
}
}
else
{
// LOD above adjacent is opaque, use the adj LOD's skylight
skyLight = RenderDataPointUtil.getLightSky(adjPoint);
}
}
if (firstFace)
{
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, color, irisBlockMaterialId, skyLight, blockLight);
}
else
{
// Now: adjMaxHeight < y < previousAdjDepth < yMax
if (previousAdjDepth == -1)
{
// TODO why is this an error?
throw new RuntimeException("Loop error");
}
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, (short) (previousAdjDepth - yMin), color, irisBlockMaterialId, skyLight, blockLight);
previousAdjDepth = -1;
}
// TODO why break here?
break;
continue;
}
if (adjYMin <= yMin)
long adjAbovePoint = (adjIndex != 0) ? adjColumnView.get(adjIndex - 1) : RenderDataPointUtil.EMPTY_DATA;
long adjBelowPoint = (adjIndex + 1 < adjCount) ? adjColumnView.get(adjIndex + 1) : RenderDataPointUtil.EMPTY_DATA;
// if the adjacent data point is over the void
// don't consider it as transparent
boolean adjOverVoid = !RenderDataPointUtil.doesDataPointExist(adjBelowPoint);
boolean adjTransparent = !adjOverVoid && RenderDataPointUtil.getAlpha(adjPoint) < 255 && LodRenderer.transparencyEnabled;
//=================================//
// set sky light based on adjacent //
//=================================//
// set light based on overlapping adjacent
if (!adjTransparent)
{
// the adjacent LOD's base is at or below the input's base
if (yMax <= adjYMax)
// adj opaque
// mark positions adjacent is covering
byte adjSkyLight = RenderDataPointUtil.getLightSky(adjPoint);
for (int i = adjMinY; i < adjMaxY; i++)
{
// The input face is completely inside the adj's face, don't render it
if (debugOverlapColor != 0)
{
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
}
}
else
{
// the adj data intersects the lower part of the input data, don't render below the intersection
byte skyLightAtPos = skyLightAtInputPos[i];
if (adjYMax > yMin && debugOverlapColor != 0)
{
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, (short) (adjYMax - yMin), debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
}
// if the adjacent is a different detail level, we want to render adjacent opaque
// faces to try and reduce the chance of holes on detail level borders
boolean adjacentCoversThis =
// if the adjacent is the same detail level, no special handling is necessary
!adjacentIsSameDetailLevel
// if the adjacent face is underground we probably don't need it
&& RenderDataPointUtil.getYMax(adjPoint) >= caveCullingMaxY
// check if this face is on a border
&&
(
(x == 0 && direction == EDhDirection.WEST)
|| (z == 0 && direction == EDhDirection.NORTH)
// TODO why does 256 represent a border? aren't LODs only 64 datapoints wide?
|| (x == 256 && direction == EDhDirection.EAST)
|| (z == 256 && direction == EDhDirection.SOUTH)
);
// if this is the only face, use the yMax and break,
// if there was another face finish the last one and then break
if (firstFace)
{
builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (yMax - adjYMax), color, irisBlockMaterialId,
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
}
else
{
// Now: depth <= y <= height <= previousAdjDepth < yMax
if (previousAdjDepth == -1)
{
// TODO why is this an error?
throw new RuntimeException("Loop error");
}
if (previousAdjDepth > adjYMax)
{
builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (previousAdjDepth - adjYMax), color, irisBlockMaterialId,
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
}
previousAdjDepth = -1;
}
byte newSkyLightAtPos = adjacentCoversThis ? adjSkyLight : SKYLIGHT_COVERED;
skyLightAtInputPos[i] = (byte) Math.min(newSkyLightAtPos, skyLightAtPos);
}
// we don't need to check any other adjacent LODs
// since this one completely covers the input
break;
}
// In here always true: y < adjYMin < yMax
// _________________&&: y < ________ (height and yMax)
if (adjYMax >= yMax)
{
// Basically: y _______ < yMax <= height
// _______&&: y < depth < yMax
// the adj data intersects the higher part of the current data
if (debugOverlapColor != 0)
{
builder.addQuadAdj(direction, x, adjYMin, z, horizontalWidth, (short) (yMax - adjYMin), debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
}
// we start the creation of a new face
}
else
{
// Otherwise: y < _____ height < yMax
// _______&&: y < depth ______ < yMax
if (debugOverlapColor != 0)
// adjacent is transparent,
// use datapoint below adjacent for lighting
byte belowSkyLight = RenderDataPointUtil.getLightSky(adjBelowPoint);
for (int i = adjMinY; i < adjMaxY; i++)
{
builder.addQuadAdj(direction, x, adjYMin, z, horizontalWidth, (short) (adjYMax - adjYMin), debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
}
if (firstFace)
{
builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (yMax - adjYMax), color, irisBlockMaterialId,
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
}
else
{
// Now: y < depth < height <= previousAdjDepth < yMax
if (previousAdjDepth == -1)
throw new RuntimeException("Loop error");
if (previousAdjDepth > adjYMax)
{
if (irisBlockMaterialId == EDhApiBlockMaterial.GRASS.index)
{
// this LOD is underneath another, grass will never show here
irisBlockMaterialId = EDhApiBlockMaterial.DIRT.index;
}
builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (previousAdjDepth - adjYMax), color, irisBlockMaterialId,
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
}
previousAdjDepth = -1;
byte skyLightAtPos = skyLightAtInputPos[i];
skyLightAtInputPos[i] = (byte) Math.min(belowSkyLight, skyLightAtPos);
}
}
// set next top as current depth
previousAdjDepth = adjYMin;
firstFace = false;
nextTopSkyLight = skyLightTop;
if (adjIndex + 1 < adjColumnView.size() && RenderDataPointUtil.doesDataPointExist(adjColumnView.get(adjIndex + 1)))
// fill in sky light up to the next DP,
// this is done to handle overhangs
byte adjSkyLight = RenderDataPointUtil.getLightSky(adjPoint);
int adjAboveMinY = RenderDataPointUtil.getYMin(adjAbovePoint);
for (int i = adjMaxY; i < adjAboveMinY; i++)
{
nextTopSkyLight = RenderDataPointUtil.getLightSky(adjColumnView.get(adjIndex + 1));
byte skyLightAtPos = skyLightAtInputPos[i];
skyLightAtInputPos[i] = (byte) Math.min(adjSkyLight, skyLightAtPos);
}
}
//=======================//
// create vertical faces //
//=======================//
boolean inputTransparent = ColorUtil.getAlpha(color) < 255 && LodRenderer.transparencyEnabled;
byte lastSkyLight = skyLightAtInputPos[yMin];
int quadBottomY = yMin;
int quadTopY = -1;
// walk up the sky lights and create a new face
// whenever the light changes to different valid value
for (int i = yMin; i < yMax; i++)
{
byte skyLight = skyLightAtInputPos[i];
if (skyLight != lastSkyLight)
{
// the sky light changed, create the in-progress face
tryAddVerticalFaceWithSkyLightToBuilder(
builder, direction,
x, z, horizontalWidth,
color, irisBlockMaterialId, blockLight,
lastSkyLight, inputTransparent, quadTopY, quadBottomY
);
lastSkyLight = skyLight;
quadBottomY = i;
}
lastAdjWasTransparent = adjTransparent;
quadTopY = (i + 1);
}
// add the in-progress face if present
if (quadTopY != -1)
{
tryAddVerticalFaceWithSkyLightToBuilder(
builder, direction,
x, z, horizontalWidth,
color, irisBlockMaterialId, blockLight,
lastSkyLight, inputTransparent, quadTopY, quadBottomY
);
}
}
if (inputAboveAdjLods)
finally
{
// the input LOD is above all adjacent LODs and won't be affected
// by them, add the vertical quad using the input's lighting and height
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, color, irisBlockMaterialId, skyLightTop, blockLight);
// clean up the array before the next thread uses it
// (may be unnecessary since we only work between the yMin-yMax anyway, but is helpful for debugging)
Arrays.fill(skyLightAtInputPos, yMin, yMax, SKYLIGHT_EMPTY);
}
else if (previousAdjDepth != -1)
}
private static void tryAddVerticalFaceWithSkyLightToBuilder(
LodQuadBuilder builder, EDhDirection direction,
short x, short z, short horizontalWidth,
int color, byte irisBlockMaterialId, byte blockLight,
byte lastSkyLight, boolean inputTransparent, int quadTopY, int quadBottomY
)
{
// invalid positions will have a negative skylight
if (lastSkyLight >= 0)
{
// We need to finish the last quad.
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, (short) (previousAdjDepth - yMin), color, irisBlockMaterialId, nextTopSkyLight, blockLight);
// Don't add transparent vertical faces
// unless the adjacent position is empty.
// This is done to prevent walls between water blocks in the ocean.
if (!inputTransparent
|| (lastSkyLight == LodUtil.MAX_MC_LIGHT))
{
// don't add negative/empty height faces
short height = (short) (quadTopY - quadBottomY);
if (height > 0)
{
builder.addQuadAdj(direction, x, (short) quadBottomY, z, horizontalWidth, height, color, irisBlockMaterialId, lastSkyLight, blockLight);
}
}
}
}
}
@@ -19,23 +19,23 @@
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.objects.StatsMap;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
import com.seibel.distanthorizons.core.util.*;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import org.apache.logging.log4j.Logger;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.concurrent.*;
/**
@@ -91,7 +91,7 @@ public class ColumnRenderBuffer implements AutoCloseable
/** Should be run on a DH thread. */
public void uploadBuffer(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) throws InterruptedException
{
LodUtil.assertTrue(Thread.currentThread().getName().startsWith(ThreadUtil.THREAD_NAME_PREFIX), "Buffer uploading needs to be done on a DH thread to prevent locking up any MC threads.");
LodUtil.assertTrue(DhApi.isDhThread(), "Buffer uploading needs to be done on a DH thread to prevent locking up any MC threads.");
// upload on MC's render thread
@@ -100,7 +100,7 @@ public class ColumnRenderBuffer implements AutoCloseable
{
try
{
this.uploadBuffersUsingUploadMethod(builder, gpuUploadMethod);
this.uploadBuffers(builder, gpuUploadMethod);
uploadFuture.complete(null);
}
catch (InterruptedException e)
@@ -126,72 +126,46 @@ public class ColumnRenderBuffer implements AutoCloseable
//LOGGER.warn("Error uploading builder ["+builder+"] synchronously. Error: "+e.getMessage(), e);
}
}
private void uploadBuffersUsingUploadMethod(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) throws InterruptedException
private void uploadBuffers(LodQuadBuilder builder, EDhApiGpuUploadMethod method) throws InterruptedException
{
if (gpuUploadMethod.useEarlyMapping)
{
this.uploadBuffersMapped(builder, gpuUploadMethod);
}
else
{
this.uploadBuffersDirect(builder, gpuUploadMethod);
}
// uploading mapped buffers used to be done here,
// however due to a memory leak and complication with the previous code,
// now we only allow direct uploading.
// (There's also insufficient data to state whether mapped buffers are necessary
// for DH to upload without stuttering the main thread)
this.vbos = makeAndUploadBuffers(builder, method, this.vbos, builder.makeOpaqueVertexBuffers());
this.vbosTransparent = makeAndUploadBuffers(builder, method, this.vbosTransparent, builder.makeTransparentVertexBuffers());
this.buffersUploaded = true;
}
private void uploadBuffersMapped(LodQuadBuilder builder, EDhApiGpuUploadMethod method)
/** This resizes and returns the vbo array if necessary based on the amount of data needed for this area. */
private static GLVertexBuffer[] makeAndUploadBuffers(LodQuadBuilder builder, EDhApiGpuUploadMethod method, GLVertexBuffer[] vbos, ArrayList<ByteBuffer> buffers) throws InterruptedException
{
// opaque vbos //
this.vbos = ColumnRenderBufferBuilder.resizeBuffer(this.vbos, builder.getCurrentNeededOpaqueVertexBufferCount());
for (int i = 0; i < this.vbos.length; i++)
try
{
if (this.vbos[i] == null)
vbos = resizeBuffer(vbos, buffers.size());
uploadBuffersDirect(vbos, buffers, method);
}
finally
{
// all the buffers must be manually freed to prevent memory leaks
if (buffers != null)
{
this.vbos[i] = new GLVertexBuffer(method.useBufferStorage);
for (ByteBuffer buffer : buffers)
{
MemoryUtil.memFree(buffer);
}
}
}
LodQuadBuilder.BufferFiller func = builder.makeOpaqueBufferFiller(method);
for (GLVertexBuffer vbo : this.vbos)
{
func.fill(vbo);
}
// transparent vbos //
this.vbosTransparent = ColumnRenderBufferBuilder.resizeBuffer(this.vbosTransparent, builder.getCurrentNeededTransparentVertexBufferCount());
for (int i = 0; i < this.vbosTransparent.length; i++)
{
if (this.vbosTransparent[i] == null)
{
this.vbosTransparent[i] = new GLVertexBuffer(method.useBufferStorage);
}
}
LodQuadBuilder.BufferFiller transparentFillerFunc = builder.makeTransparentBufferFiller(method);
for (GLVertexBuffer vbo : this.vbosTransparent)
{
transparentFillerFunc.fill(vbo);
}
// return the array in case it was resized
return vbos;
}
private void uploadBuffersDirect(LodQuadBuilder builder, EDhApiGpuUploadMethod method) throws InterruptedException
private static void uploadBuffersDirect(GLVertexBuffer[] vbos, ArrayList<ByteBuffer> byteBuffers, EDhApiGpuUploadMethod method) throws InterruptedException
{
this.vbos = ColumnRenderBufferBuilder.resizeBuffer(this.vbos, builder.getCurrentNeededOpaqueVertexBufferCount());
uploadBuffersDirect(this.vbos, builder.makeOpaqueVertexBuffers(), method);
this.vbosTransparent = ColumnRenderBufferBuilder.resizeBuffer(this.vbosTransparent, builder.getCurrentNeededTransparentVertexBufferCount());
uploadBuffersDirect(this.vbosTransparent, builder.makeTransparentVertexBuffers(), method);
}
private static void uploadBuffersDirect(GLVertexBuffer[] vbos, Iterator<ByteBuffer> iter, EDhApiGpuUploadMethod method) throws InterruptedException
{
long remainingMS = 0;
long MBPerMS = Config.Client.Advanced.GpuBuffers.gpuUploadPerMegabyteInMilliseconds.get();
int vboIndex = 0;
while (iter.hasNext())
for (int i = 0; i < byteBuffers.size(); i++)
{
if (vboIndex >= vbos.length)
{
@@ -207,13 +181,13 @@ public class ColumnRenderBuffer implements AutoCloseable
GLVertexBuffer vbo = vbos[vboIndex];
ByteBuffer bb = iter.next();
int size = bb.limit() - bb.position();
ByteBuffer buffer = byteBuffers.get(i);
int size = buffer.limit() - buffer.position();
try
{
vbo.bind();
vbo.uploadBuffer(bb, size / LodUtil.LOD_VERTEX_FORMAT.getByteSize(), method, FULL_SIZED_BUFFER);
vbo.uploadBuffer(buffer, size / LodUtil.LOD_VERTEX_FORMAT.getByteSize(), method, FULL_SIZED_BUFFER);
}
catch (Exception e)
{
@@ -222,24 +196,6 @@ public class ColumnRenderBuffer implements AutoCloseable
LOGGER.error("Failed to upload buffer: ", e);
}
if (MBPerMS > 0)
{
// upload buffers over an extended period of time
// to hopefully prevent stuttering.
remainingMS += size * MBPerMS;
if (remainingMS >= TimeUnit.NANOSECONDS.convert(1000 / 60, TimeUnit.MILLISECONDS))
{
if (remainingMS > MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS)
{
remainingMS = MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS;
}
Thread.sleep(remainingMS / 1000000, (int) (remainingMS % 1000000));
remainingMS = 0;
}
}
vboIndex++;
}
@@ -318,9 +274,9 @@ public class ColumnRenderBuffer implements AutoCloseable
//==============//
// misc methods //
//==============//
//================//
// helper methods //
//================//
/** can be used when debugging */
public boolean hasNonNullVbos() { return this.vbos != null || this.vbosTransparent != null; }
@@ -366,6 +322,35 @@ public class ColumnRenderBuffer implements AutoCloseable
}
}
public static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize)
{
if (vbos.length == newSize)
{
return vbos;
}
GLVertexBuffer[] newVbos = new GLVertexBuffer[newSize];
System.arraycopy(vbos, 0, newVbos, 0, Math.min(vbos.length, newSize));
if (newSize < vbos.length)
{
for (int i = newSize; i < vbos.length; i++)
{
if (vbos[i] != null)
{
vbos[i].close();
}
}
}
return newVbos;
}
//================//
// base overrides //
//================//
/**
* This method is called when object is no longer in use.
* Called either after uploadBuffers() returned false (On buffer Upload
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.config.Config;
@@ -26,15 +27,16 @@ import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -59,21 +61,21 @@ public class ColumnRenderBufferBuilder
// vbo building //
//==============//
public static CompletableFuture<ColumnRenderBuffer> buildAndUploadBuffersAsync(
public static CompletableFuture<LodQuadBuilder> buildBuffersAsync(
IDhClientLevel clientLevel,
ColumnRenderSource renderSource, ColumnRenderSource[] adjData)
ColumnRenderSource renderSource, ColumnRenderSource[] adjData, boolean[] isSameDetailLevel
)
{
ThreadPoolExecutor bufferBuilderExecutor = ThreadPoolUtil.getBufferBuilderExecutor();
ThreadPoolExecutor bufferUploaderExecutor = ThreadPoolUtil.getBufferUploaderExecutor();
if ((bufferBuilderExecutor == null || bufferBuilderExecutor.isTerminated()) ||
(bufferUploaderExecutor == null || bufferUploaderExecutor.isTerminated()))
if (bufferBuilderExecutor == null || bufferBuilderExecutor.isTerminated())
{
// one or more of the thread pools has been shut down
CompletableFuture<ColumnRenderBuffer> future = new CompletableFuture<>();
CompletableFuture<LodQuadBuilder> future = new CompletableFuture<>();
future.cancel(true);
return future;
}
try
{
return CompletableFuture.supplyAsync(() ->
@@ -81,16 +83,8 @@ public class ColumnRenderBufferBuilder
try
{
boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
long builderStartTime = System.currentTimeMillis();
LodQuadBuilder builder = new LodQuadBuilder(enableTransparency, clientLevel.getClientLevelWrapper());
makeLodRenderData(builder, renderSource, adjData);
long builderEndTime = System.currentTimeMillis();
long buildMs = builderEndTime - builderStartTime;
//LOGGER.debug("RenderRegion end QuadBuild @ " + renderSource.pos + " took: " + buildMs);
makeLodRenderData(builder, renderSource, clientLevel, adjData, isSameDetailLevel);
return builder;
}
catch (UncheckedInterruptedException e)
@@ -99,20 +93,58 @@ public class ColumnRenderBufferBuilder
}
catch (Throwable e3)
{
LOGGER.error("\"LodNodeBufferBuilder\" was unable to build quads: ", e3);
LOGGER.error("LodNodeBufferBuilder was unable to build quads for pos ["+DhSectionPos.toString(renderSource.pos)+"], error: ["+ e3.getMessage()+"].", e3);
throw e3;
}
}, bufferBuilderExecutor)
.thenApplyAsync((quadBuilder) ->
}, bufferBuilderExecutor);
}
catch (RejectedExecutionException ignore)
{
// the thread pool was probably shut down because it's size is being changed, just wait a sec and it should be back
CompletableFuture<LodQuadBuilder> future = new CompletableFuture<>();
future.cancel(true);
return future;
}
}
/** @link adjData should be null for adjacent sections that cross detail level boundaries */
public static CompletableFuture<ColumnRenderBuffer> uploadBuffersAsync(
IDhClientLevel clientLevel,
long pos,
LodQuadBuilder quadBuilder
)
{
// TODO put into a single future/thread so it can be easily canceled
ThreadPoolExecutor bufferUploaderExecutor = ThreadPoolUtil.getBufferUploaderExecutor();
if (bufferUploaderExecutor == null || bufferUploaderExecutor.isTerminated())
{
// one or more of the thread pools has been shut down
CompletableFuture<ColumnRenderBuffer> future = new CompletableFuture<>();
future.cancel(true);
return future;
}
try
{
return CompletableFuture.supplyAsync(() ->
{
try
{
ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(renderSource.pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(renderSource.pos)));
ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(pos)));
try
{
buffer.uploadBuffer(quadBuilder, GLProxy.getInstance().getGpuUploadMethod());
LodUtil.assertTrue(buffer.buffersUploaded);
return buffer;
if (buffer.buffersUploaded)
{
return buffer;
}
else
{
buffer.close();
return null;
}
}
catch (Exception e)
{
@@ -126,35 +158,38 @@ public class ColumnRenderBufferBuilder
}
catch (Throwable e3)
{
LOGGER.error("LodNodeBufferBuilder was unable to upload buffer: " + e3.getMessage(), e3);
LOGGER.error("LodNodeBufferBuilder was unable to upload buffer for pos ["+DhSectionPos.toString(pos)+"], error: [" + e3.getMessage() + "].", e3);
throw e3;
}
}, bufferUploaderExecutor);
}
catch (RejectedExecutionException ignore)
{
// the thread pool was probably shut down because it's size is being changed, just wait a sec and it should be back
// shouldn't happen, but just in case
CompletableFuture<ColumnRenderBuffer> future = new CompletableFuture<>();
future.cancel(true);
return future;
}
}
private static void makeLodRenderData(LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, ColumnRenderSource[] adjRegions)
private static void makeLodRenderData(
LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, IDhClientLevel clientLevel,
ColumnRenderSource[] adjRegions, boolean[] isSameDetailLevel)
{
// Variable initialization
EDhApiDebugRendering debugMode = Config.Client.Advanced.Debugging.debugRendering.get();
//=============//
// debug check //
//=============//
// can be used to limit which section positions are build and thus, rendered
// useful when debugging a specific section
boolean enableColumnBufferLimit = Config.Client.Advanced.Debugging.columnBuilderDebugEnable.get();
if (enableColumnBufferLimit)
boolean columnBuilderDebugEnabled = Config.Client.Advanced.Debugging.columnBuilderDebugEnable.get();
if (columnBuilderDebugEnabled)
{
if (DhSectionPos.getDetailLevel(renderSource.pos) == Config.Client.Advanced.Debugging.columnBuilderDebugDetailLevel.get()
&& DhSectionPos.getX(renderSource.pos) == Config.Client.Advanced.Debugging.columnBuilderDebugXPos.get()
&& DhSectionPos.getZ(renderSource.pos) == Config.Client.Advanced.Debugging.columnBuilderDebugZPos.get())
{
int test = 0;
int breakpoint = 0;
}
else
{
@@ -162,24 +197,22 @@ public class ColumnRenderBufferBuilder
}
}
byte detailLevel = renderSource.getDataDetailLevel();
for (int x = 0; x < ColumnRenderSource.SECTION_SIZE; x++)
//===================//
// build each column //
//===================//
byte thisDetailLevel = renderSource.getDataDetailLevel();
for (int relX = 0; relX < ColumnRenderSource.SECTION_SIZE; relX++)
{
for (int z = 0; z < ColumnRenderSource.SECTION_SIZE; z++)
for (int relZ = 0; relZ < ColumnRenderSource.SECTION_SIZE; relZ++)
{
// TODO make a config for this
// can be uncommented to limit the buffer building to a specific
// relative position in this section.
// useful for debugging a single column's rendering
// if (x != 0 || (z != 0 && z != 1))
// {
// continue;
// }
// stop the builder if requested
UncheckedInterruptedException.throwIfInterrupted();
ColumnArrayView columnRenderData = renderSource.getVerticalDataPointView(x, z);
// ignore empty/null columns
ColumnArrayView columnRenderData = renderSource.getVerticalDataPointView(relX, relZ);
if (columnRenderData.size() == 0
|| !RenderDataPointUtil.doesDataPointExist(columnRenderData.get(0))
|| RenderDataPointUtil.isVoid(columnRenderData.get(0)))
@@ -187,43 +220,66 @@ public class ColumnRenderBufferBuilder
continue;
}
ColumnRenderSource.DebugSourceFlag debugSourceFlag = renderSource.debugGetFlag(x, z);
ColumnArrayView[][] adjColumnViews = new ColumnArrayView[4][];
// We extract the adj data in the four cardinal direction
// we first reset the adjShadeDisabled. This is used to disable the shade on the
// border when we have transparent block like water or glass
// to avoid having a "darker border" underground
// Arrays.fill(adjShadeDisabled, false);
// We check every adj block in each direction
//=============//
// debug limit //
//=============//
// If the adj block is rendered in the same region and with same detail
// and is positioned in a place that is not going to be rendered by vanilla game
// then we can set this position as adj
// We avoid cases where the adjPosition is in player chunk while the position is
// not
// to always have a wall underwater
// can be used to limit the buffer building to a specific relative position.
// useful for debugging a single column
if (columnBuilderDebugEnabled)
{
int wantedX = Config.Client.Advanced.Debugging.columnBuilderDebugXRow.get();
if (wantedX >= 0 && relX != wantedX)
{
continue;
}
int wantedZ = Config.Client.Advanced.Debugging.columnBuilderDebugZRow.get();
if (wantedZ >= 0 && relZ != wantedZ)
{
continue;
}
}
//==================================//
// get adjacent render data columns //
//==================================//
ColumnArrayView[] adjColumnViews = new ColumnArrayView[EDhDirection.ADJ_DIRECTIONS.length];
for (EDhDirection lodDirection : EDhDirection.ADJ_DIRECTIONS)
{
try
{
int xAdj = x + lodDirection.getNormal().x;
int zAdj = z + lodDirection.getNormal().z;
boolean isCrossRegionBoundary =
int xAdj = relX + lodDirection.getNormal().x;
int zAdj = relZ + lodDirection.getNormal().z;
boolean isCrossRenderSourceBoundary =
(xAdj < 0 || xAdj >= ColumnRenderSource.SECTION_SIZE) ||
(zAdj < 0 || zAdj >= ColumnRenderSource.SECTION_SIZE);
(zAdj < 0 || zAdj >= ColumnRenderSource.SECTION_SIZE);
ColumnRenderSource adjRenderSource;
byte adjDetailLevel;
//we check if the detail of the adjPos is equal to the correct one (region border fix)
//or if the detail is wrong by 1 value (region+circle border fix)
if (isCrossRegionBoundary)
//=========================//
// get the adjacent render //
// source if present //
//=========================//
if (!isCrossRenderSourceBoundary)
{
//we compute at which detail that position should be rendered
// the adjacent position is inside this same render source
adjRenderSource = renderSource;
adjDetailLevel = thisDetailLevel;
}
else
{
// the adjacent position is outside this render source
// skip empty sections
adjRenderSource = adjRegions[lodDirection.ordinal() - 2];
if (adjRenderSource == null)
{
@@ -231,67 +287,70 @@ public class ColumnRenderBufferBuilder
}
adjDetailLevel = adjRenderSource.getDataDetailLevel();
if (adjDetailLevel != detailLevel)
{
//TODO: Implement this
}
else
if (adjDetailLevel == thisDetailLevel)
{
// if the adjacent position is outside this render source,
// wrap the position around so it's inside the adjacent source
if (xAdj < 0)
{
xAdj += ColumnRenderSource.SECTION_SIZE;
}
if (xAdj >= ColumnRenderSource.SECTION_SIZE)
{
xAdj -= ColumnRenderSource.SECTION_SIZE;
}
if (zAdj < 0)
{
zAdj += ColumnRenderSource.SECTION_SIZE;
if (xAdj >= ColumnRenderSource.SECTION_SIZE)
xAdj -= ColumnRenderSource.SECTION_SIZE;
}
if (zAdj >= ColumnRenderSource.SECTION_SIZE)
{
zAdj -= ColumnRenderSource.SECTION_SIZE;
}
}
}
else
{
adjRenderSource = renderSource;
adjDetailLevel = detailLevel;
}
if (adjDetailLevel < detailLevel - 1 || adjDetailLevel > detailLevel + 1)
{
continue;
}
if (adjDetailLevel == detailLevel || adjDetailLevel > detailLevel)
{
adjColumnViews[lodDirection.ordinal() - 2] = new ColumnArrayView[1];
adjColumnViews[lodDirection.ordinal() - 2][0] = adjRenderSource.getVerticalDataPointView(xAdj, zAdj);
}
else
{
adjColumnViews[lodDirection.ordinal() - 2] = new ColumnArrayView[2];
adjColumnViews[lodDirection.ordinal() - 2][0] = adjRenderSource.getVerticalDataPointView(xAdj, zAdj);
adjColumnViews[lodDirection.ordinal() - 2][1] = adjRenderSource.getVerticalDataPointView(
xAdj + (lodDirection.getAxis() == EDhDirection.Axis.X ? 0 : 1),
zAdj + (lodDirection.getAxis() == EDhDirection.Axis.Z ? 0 : 1));
}
//========================//
// get the adjacent views //
//========================//
// the old logic handled additional cases, but they never appeared to fire,
// so just these two cases should be fine
LodUtil.assertTrue(adjDetailLevel == thisDetailLevel || adjDetailLevel > thisDetailLevel);
adjColumnViews[lodDirection.ordinal() - 2] = adjRenderSource.getVerticalDataPointView(xAdj, zAdj);
}
catch (RuntimeException e)
{
EVENT_LOGGER.warn("Failed to get adj data for [" + detailLevel + ":" + x + "," + z + "] at [" + lodDirection + "], Error: "+e.getMessage(), e);
EVENT_LOGGER.warn("Failed to get adj data for relative pos: [" + thisDetailLevel + ":" + relX + "," + relZ + "] at [" + lodDirection + "], Error: "+e.getMessage(), e);
}
} // for adjacent directions
//==========================//
// build this render column //
//==========================//
ColumnRenderSource.DebugSourceFlag debugSourceFlag = renderSource.debugGetFlag(relX, relZ);
// We render every vertical lod present in this position
// We only stop when we find a block that is void or non-existing block
for (int i = 0; i < columnRenderData.size(); i++)
{
// TODO make a config for this
// can be uncommented to limit which vertical LOD is generated
// if (i != 0)
// {
// continue;
// }
if (Config.Client.Advanced.Debugging.columnBuilderDebugEnable.get())
{
int wantedColumnIndex = Config.Client.Advanced.Debugging.columnBuilderDebugColumnIndex.get();
if (wantedColumnIndex >= 0 && i != wantedColumnIndex)
{
continue;
}
}
long data = columnRenderData.get(i);
// If the data is not render-able (Void or non-existing) we stop since there is
@@ -304,8 +363,12 @@ public class ColumnRenderBufferBuilder
long topDataPoint = (i - 1) >= 0 ? columnRenderData.get(i - 1) : RenderDataPointUtil.EMPTY_DATA;
long bottomDataPoint = (i + 1) < columnRenderData.size() ? columnRenderData.get(i + 1) : RenderDataPointUtil.EMPTY_DATA;
CubicLodTemplate.addLodToBuffer(data, topDataPoint, bottomDataPoint, adjColumnViews, detailLevel,
x, z, quadBuilder, debugMode, debugSourceFlag);
addLodToBuffer(
clientLevel,
data, topDataPoint, bottomDataPoint,
adjColumnViews, isSameDetailLevel,
thisDetailLevel, relX, relZ,
quadBuilder, debugSourceFlag);
}
}// for z
@@ -313,33 +376,147 @@ public class ColumnRenderBufferBuilder
quadBuilder.finalizeData();
}
//=================//
// vbo interaction //
//=================//
public static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize)
private static void addLodToBuffer(
IDhClientLevel clientLevel,
long data, long topData, long bottomData,
ColumnArrayView[] adjColumnViews, boolean[] isSameDetailLevel,
byte detailLevel, int renderSourceOffsetPosX, int renderSourceOffsetPosZ,
LodQuadBuilder quadBuilder, ColumnRenderSource.DebugSourceFlag debugSource)
{
if (vbos.length == newSize)
long sectionPos = DhSectionPos.encode(detailLevel, renderSourceOffsetPosX, renderSourceOffsetPosZ);
short width = (short) BitShiftUtil.powerOfTwo(detailLevel);
short x = (short) DhSectionPos.getMinCornerBlockX(sectionPos);
short yMin = RenderDataPointUtil.getYMin(data);
short z = (short) DhSectionPos.getMinCornerBlockZ(sectionPos);
short ySize = (short) (RenderDataPointUtil.getYMax(data) - yMin);
if (ySize == 0)
{
return vbos;
return;
}
else if (ySize < 0)
{
throw new IllegalArgumentException("Negative y size for the data! Data: [" + RenderDataPointUtil.toString(data) + "].");
}
GLVertexBuffer[] newVbos = new GLVertexBuffer[newSize];
System.arraycopy(vbos, 0, newVbos, 0, Math.min(vbos.length, newSize));
if (newSize < vbos.length)
byte blockMaterialId = RenderDataPointUtil.getBlockMaterialId(data);
int color;
boolean fullBright = false;
EDhApiDebugRendering debugging = Config.Client.Advanced.Debugging.debugRendering.get();
switch (debugging)
{
for (int i = newSize; i < vbos.length; i++)
case OFF:
{
if (vbos[i] != null)
float saturationMultiplier = Config.Client.Advanced.Graphics.AdvancedGraphics.saturationMultiplier.get().floatValue();
float brightnessMultiplier = Config.Client.Advanced.Graphics.AdvancedGraphics.brightnessMultiplier.get().floatValue();
if (saturationMultiplier == 1.0 && brightnessMultiplier == 1.0)
{
vbos[i].close();
color = RenderDataPointUtil.getColor(data);
}
else
{
float[] ahsv = ColorUtil.argbToAhsv(RenderDataPointUtil.getColor(data));
color = ColorUtil.ahsvToArgb(ahsv[0], ahsv[1], ahsv[2] * saturationMultiplier, ahsv[3] * brightnessMultiplier);
}
break;
}
case SHOW_DETAIL:
{
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel];
fullBright = true;
break;
}
case SHOW_BLOCK_MATERIAL:
{
switch (EDhApiBlockMaterial.getFromIndex(blockMaterialId))
{
case UNKNOWN:
case AIR: // shouldn't normally be rendered, but just in case
color = ColorUtil.HOT_PINK;
break;
case LEAVES:
color = ColorUtil.DARK_GREEN;
break;
case STONE:
color = ColorUtil.GRAY;
break;
case WOOD:
color = ColorUtil.BROWN;
break;
case METAL:
color = ColorUtil.DARK_GRAY;
break;
case DIRT:
color = ColorUtil.LIGHT_BROWN;
break;
case LAVA:
color = ColorUtil.ORANGE;
break;
case DEEPSLATE:
color = ColorUtil.BLACK;
break;
case SNOW:
color = ColorUtil.WHITE;
break;
case SAND:
color = ColorUtil.TAN;
break;
case TERRACOTTA:
color = ColorUtil.DARK_ORANGE;
break;
case NETHER_STONE:
color = ColorUtil.DARK_RED;
break;
case WATER:
color = ColorUtil.BLUE;
break;
case GRASS:
color = ColorUtil.GREEN;
break;
case ILLUMINATED:
color = ColorUtil.YELLOW;
break;
default:
// undefined color
color = ColorUtil.CYAN;
break;
}
fullBright = true;
break;
}
case SHOW_OVERLAPPING_QUADS:
{
color = ColorUtil.WHITE;
fullBright = true;
break;
}
case SHOW_RENDER_SOURCE_FLAG:
{
color = debugSource == null ? ColorUtil.RED : debugSource.color;
fullBright = true;
break;
}
default:
throw new IllegalArgumentException("Unknown debug mode: " + debugging);
}
return newVbos;
ColumnBox.addBoxQuadsToBuilder(
quadBuilder, clientLevel,
width, ySize, width,
x, yMin, z,
color,
blockMaterialId,
RenderDataPointUtil.getLightSky(data),
fullBright ? 15 : RenderDataPointUtil.getLightBlock(data),
topData, bottomData, adjColumnViews, isSameDetailLevel);
}
}

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