Compare commits
225 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 894a2dbe7d | |||
| 172e7560fd | |||
| cd5ff8ce35 | |||
| 6e717a383d | |||
| bef873b875 | |||
| 331d75a3bc | |||
| cec6438602 | |||
| f5e0c112e3 | |||
| 899c4aca91 | |||
| a4ac483e5b | |||
| 723f67ea0c | |||
| 4575701bd4 | |||
| 7cfcfb0695 | |||
| f33bfa1d69 | |||
| a3c1f1563d | |||
| 361d197c5e | |||
| 04379691bc | |||
| e1ca398b8f | |||
| f34e67e6bb | |||
| aad095ca1a | |||
| 950c951c2d | |||
| 37eaa2656a | |||
| 945853d014 | |||
| 6bb38ad500 | |||
| 980086c533 | |||
| c8f1154831 | |||
| 9196480e50 | |||
| f0506d28e5 | |||
| 8ecd5dd9cb | |||
| c83140a2d0 | |||
| 3b600ce800 | |||
| 7f874b4dc5 | |||
| 23e857a20d | |||
| 2298ef0e0d | |||
| 7470455e50 | |||
| 614884c29e | |||
| b41e54e6a6 | |||
| 2cc6c8d840 | |||
| a613540b6a | |||
| c89fcb094a | |||
| 10014d3729 | |||
| 88ff9e7cde | |||
| 459bc5fc0c | |||
| 3826c83d89 | |||
| 382917033a | |||
| 37381896bc | |||
| 22f4757aae | |||
| 57cd143009 | |||
| 031742a951 | |||
| a2e2559b26 | |||
| 6e0071a046 | |||
| 352a59838d | |||
| 63371e8111 | |||
| 383ab1121d | |||
| c9a7527bcb | |||
| 7d4d899226 | |||
| 169429fe48 | |||
| 94304eb055 | |||
| 4a40e19b34 | |||
| 22422321ab | |||
| eafaf2b4cd | |||
| 281c85c283 | |||
| 8cecdebc05 | |||
| cba55aa643 | |||
| f7c5b5725f | |||
| 07b495d48c | |||
| b5d938475a | |||
| f8b127a85d | |||
| a216ba364d | |||
| 6d0ec33316 | |||
| 35681fe9a5 | |||
| e560ddf3dc | |||
| 77fffb1a9b | |||
| cf74d17e1b | |||
| a0e7bb94c6 | |||
| e10617ac19 | |||
| cd2b5fd668 | |||
| 336e8b99ec | |||
| d8bac9df8c | |||
| 1ff4a56e2d | |||
| 7bdafa28cc | |||
| 03e11fbe3e | |||
| 74d193d294 | |||
| 46e3b98b62 | |||
| 2d323cef39 | |||
| 8846ca5b5e | |||
| f310f1b316 | |||
| 10d9282df7 | |||
| a751507f19 | |||
| b4b7738aa6 | |||
| 8fd37d72c7 | |||
| f09818e564 | |||
| bdd0fbe5ae | |||
| ddbad36d8a | |||
| ae829cbe3e | |||
| 5ef6cb2e0d | |||
| a1a45f50bf | |||
| 42675abef1 | |||
| 08f0f8ee17 | |||
| 4e1155f8a7 | |||
| 4bc7cf1e86 | |||
| 1491487328 | |||
| 750d8b7bc3 | |||
| b59d505725 | |||
| 2cce71cf28 | |||
| 964f5feb4b | |||
| 9835af0845 | |||
| 81f921623e | |||
| dc5968b0b5 | |||
| 43392ca0e4 | |||
| 88d6092153 | |||
| 0ddf1dd640 | |||
| ddd475d537 | |||
| 543c3ffc54 | |||
| cf97c08aaf | |||
| ea0a62b6a3 | |||
| 57f3d44326 | |||
| e7eb2ff9cc | |||
| 6a3261394f | |||
| d41af88494 | |||
| e83864fd02 | |||
| 5722a92dc1 | |||
| 5249377199 | |||
| b3a20fb938 | |||
| 74627fdf90 | |||
| bbe5ae9b7c | |||
| 63b6365128 | |||
| ed9cf526cd | |||
| cb0c294df6 | |||
| 661f286b77 | |||
| 8c5cd7f11f | |||
| b64df318ce | |||
| a1c85d91fc | |||
| ce95dfb3d1 | |||
| cbfd4f9de3 | |||
| 5d50775932 | |||
| 18ad241025 | |||
| 4b98882b41 | |||
| 3ec9bfca1b | |||
| 6f7c46e086 | |||
| 7726335413 | |||
| f3d8a749fd | |||
| f11e9a142f | |||
| 72c601b2db | |||
| 4cdc6c9632 | |||
| 3b9962d7dc | |||
| d926d11d3c | |||
| 9fb5182b78 | |||
| a1950ebccc | |||
| 8f9caa5d9a | |||
| c537084d07 | |||
| 9f195231db | |||
| 805429722f | |||
| 5b67f60e6f | |||
| 6557a1a7bd | |||
| a62ee8b3cf | |||
| 7714569251 | |||
| 8b9e48d4d3 | |||
| 94e3426779 | |||
| 6b13e9141c | |||
| bf00a23499 | |||
| 569a4b16b4 | |||
| 8ec22189fd | |||
| 9d539c4766 | |||
| fada27257c | |||
| c55880bcb9 | |||
| cbadfab62a | |||
| c435b55576 | |||
| 69e3e2bca3 | |||
| b20cbab012 | |||
| ce4c5db5df | |||
| 6d2912e320 | |||
| e14e122b6c | |||
| dcd16cb84b | |||
| 98183a4e75 | |||
| 4ec1dea1ba | |||
| 031bf754e8 | |||
| c081b6c57c | |||
| 6413e17e4b | |||
| 01c1dbb146 | |||
| 9e0046ba83 | |||
| 150d929a45 | |||
| d5074feda2 | |||
| 4741e25349 | |||
| c3f99835db | |||
| 996621887c | |||
| 6f931c66bf | |||
| 024176f97c | |||
| 968bc9addc | |||
| e83f7bd62e | |||
| c5787d0ff2 | |||
| 30076f1b60 | |||
| 34fdae1c78 | |||
| dd7d7733f0 | |||
| fd1944eb26 | |||
| 64fce77a0a | |||
| d569ae4052 | |||
| 5f8eceee8d | |||
| b49988af9e | |||
| 46ba2630f1 | |||
| 02a1445732 | |||
| 8bdc4fe779 | |||
| 28de6f93af | |||
| 20394068b2 | |||
| 599340c4e8 | |||
| e6c985a189 | |||
| 244d960ec0 | |||
| eea2155e20 | |||
| 8f6ff8c943 | |||
| 97554ccae6 | |||
| c866fbfbfd | |||
| 0c5c4f3a74 | |||
| 0f04453134 | |||
| a734bb6a69 | |||
| dc687f70ae | |||
| e15ffe10c9 | |||
| 3d999a1749 | |||
| 47391028d8 | |||
| b8e03a2144 | |||
| 2a39a4cdc4 | |||
| 89012711ce | |||
| 9b93125936 | |||
| 113c0f227f | |||
| 28e230a2db | |||
| bb5dd248d8 |
@@ -27,3 +27,6 @@ forge*changelog.txt
|
||||
|
||||
# Sqlite databases
|
||||
*.sqlite
|
||||
*.sqlite-journal
|
||||
*.sqlite-shm
|
||||
*.sqlite-wal
|
||||
|
||||
-56
@@ -1,56 +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.enums.config;
|
||||
|
||||
/**
|
||||
* CONSTANT <br>
|
||||
* FREQUENT <br>
|
||||
* NORMAL <br>
|
||||
* RARE <br> <br>
|
||||
*
|
||||
* Determines how fast the buffers should be regenerated
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-25-2021
|
||||
*/
|
||||
@Deprecated // not currently in use, if the config this enum represents is re-implemented, the deprecated flag can be removed
|
||||
public enum EBufferRebuildTimes
|
||||
{
|
||||
CONSTANT(0, 0, 0, 1),
|
||||
|
||||
FREQUENT(1000, 500, 2500, 1),
|
||||
|
||||
NORMAL(2000, 1000, 5000, 4),
|
||||
|
||||
RARE(5000, 2000, 10000, 16);
|
||||
|
||||
public final int playerMoveTimeout;
|
||||
public final int renderedChunkTimeout;
|
||||
public final int chunkChangeTimeout;
|
||||
public final int playerMoveDistance;
|
||||
|
||||
EBufferRebuildTimes(int playerMoveTimeout, int renderedChunkTimeout, int chunkChangeTimeout, int playerMoveDistance)
|
||||
{
|
||||
this.playerMoveTimeout = playerMoveTimeout;
|
||||
this.renderedChunkTimeout = renderedChunkTimeout;
|
||||
this.chunkChangeTimeout = chunkChangeTimeout;
|
||||
this.playerMoveDistance = playerMoveDistance;
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -23,9 +23,10 @@ package com.seibel.distanthorizons.api.enums.config;
|
||||
* NONE, <br>
|
||||
* NON_COLLIDING, <br>
|
||||
*
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum EBlocksToAvoid
|
||||
public enum EDhApiBlocksToAvoid
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
@@ -36,6 +37,6 @@ public enum EBlocksToAvoid
|
||||
|
||||
public final boolean noCollision;
|
||||
|
||||
EBlocksToAvoid(boolean noCollision) { this.noCollision = noCollision; }
|
||||
EDhApiBlocksToAvoid(boolean noCollision) { this.noCollision = noCollision; }
|
||||
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.enums.config;
|
||||
|
||||
/**
|
||||
* UNCOMPRESSED <br>
|
||||
* LZ4 <br>
|
||||
* ZSTD <br>
|
||||
* XZ <br><br>
|
||||
*
|
||||
* Note: speed and compression ratios are examples
|
||||
* and should only be used for estimated comparisons.
|
||||
*
|
||||
* @version 2024-3-16
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
public enum EDhApiDataCompressionMode
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
// when removing items up the API major version
|
||||
|
||||
/**
|
||||
* Should only be used internally and for unit testing. <br><br>
|
||||
*
|
||||
* Read Speed: 1.64 MS / DTO <br>
|
||||
* Write Speed: 12.44 MS / DTO <br>
|
||||
* Compression ratio: 1.0 <br>
|
||||
*/
|
||||
@DisallowSelectingViaConfigGui
|
||||
UNCOMPRESSED(0),
|
||||
|
||||
/**
|
||||
* Extremely fast (often faster than uncompressed), but generally poor compression. <br><br>
|
||||
*
|
||||
* Read Speed: 1.85 MS / DTO <br>
|
||||
* Write Speed: 9.46 MS / DTO <br>
|
||||
* Compression ratio: 0.3638 <br>
|
||||
*/
|
||||
LZ4(1),
|
||||
|
||||
/*
|
||||
* Decent speed and good compression. <br><br>
|
||||
*
|
||||
* Read Speed: 11.78 MS / DTO <br>
|
||||
* Write Speed: 16.76 MS / DTO <br>
|
||||
* Compression ratio: 0.2199 <br>
|
||||
*/
|
||||
//@Deprecated
|
||||
//Z_STD(2),
|
||||
|
||||
/**
|
||||
* Extremely slow, but very good compression. <br><br>
|
||||
*
|
||||
* Read Speed: 12.25 MS / DTO <br>
|
||||
* Write Speed: 490.07 MS / DTO <br>
|
||||
* Compression ratio: 0.1242 <br>
|
||||
*/
|
||||
LZMA2(3);
|
||||
|
||||
|
||||
|
||||
/** More stable than using the ordinal of the enum */
|
||||
public final byte value;
|
||||
|
||||
EDhApiDataCompressionMode(int value) { this.value = (byte) value; }
|
||||
|
||||
|
||||
/** @throws IllegalArgumentException if the value doesn't map to a value */
|
||||
public static EDhApiDataCompressionMode getFromValue(byte value) throws IllegalArgumentException
|
||||
{
|
||||
EDhApiDataCompressionMode[] enumList = EDhApiDataCompressionMode.values();
|
||||
for (int i = 0; i < enumList.length; i++)
|
||||
{
|
||||
if (enumList[i].value == value)
|
||||
{
|
||||
return enumList[i];
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("No compression mode with the value ["+value+"]");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+3
-2
@@ -20,9 +20,10 @@
|
||||
package com.seibel.distanthorizons.api.enums.config;
|
||||
|
||||
/**
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum EGLErrorHandlingMode
|
||||
public enum EDhApiGLErrorHandlingMode
|
||||
{
|
||||
IGNORE,
|
||||
LOG,
|
||||
+3
-2
@@ -20,9 +20,10 @@
|
||||
package com.seibel.distanthorizons.api.enums.config;
|
||||
|
||||
/**
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum EGlProfileMode
|
||||
public enum EDhApiGlProfileMode
|
||||
{
|
||||
CORE,
|
||||
COMPAT,
|
||||
+4
-4
@@ -28,10 +28,10 @@ package com.seibel.distanthorizons.api.enums.config;
|
||||
*
|
||||
* @author Leetom
|
||||
* @author James Seibel
|
||||
* @version 2022-7-2
|
||||
* @since API 1.0.0
|
||||
* @version 2024-4-6
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
public enum EGpuUploadMethod
|
||||
public enum EDhApiGpuUploadMethod
|
||||
{
|
||||
/** Picks the best option based on the GPU the user has. */
|
||||
AUTO(false, false),
|
||||
@@ -60,7 +60,7 @@ public enum EGpuUploadMethod
|
||||
public final boolean useEarlyMapping;
|
||||
public final boolean useBufferStorage;
|
||||
|
||||
EGpuUploadMethod(boolean useEarlyMapping, boolean useBufferStorage)
|
||||
EDhApiGpuUploadMethod(boolean useEarlyMapping, boolean useBufferStorage)
|
||||
{
|
||||
this.useEarlyMapping = useEarlyMapping;
|
||||
this.useBufferStorage = useBufferStorage;
|
||||
+9
-9
@@ -20,21 +20,21 @@
|
||||
package com.seibel.distanthorizons.api.enums.config;
|
||||
|
||||
/**
|
||||
* DISTANT_HORIZONS, <br>
|
||||
* MINECRAFT,
|
||||
* AS_GRASS <br>
|
||||
* FADE_TO_DIRT <br>
|
||||
* AS_DIRT <br>
|
||||
*
|
||||
* @author Leetom
|
||||
* @version 2023-6-7
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-7
|
||||
*/
|
||||
public enum ELightGenerationMode
|
||||
public enum EDhApiGrassSideRendering
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
// when removing items up the API major version
|
||||
|
||||
DISTANT_HORIZONS,
|
||||
|
||||
MINECRAFT
|
||||
AS_GRASS,
|
||||
FADE_TO_DIRT,
|
||||
AS_DIRT;
|
||||
|
||||
}
|
||||
+7
-7
@@ -26,9 +26,10 @@ package com.seibel.distanthorizons.api.enums.config;
|
||||
* HIGH <br>
|
||||
* UNLIMITED <br>
|
||||
*
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum EHorizontalQuality
|
||||
public enum EDhApiHorizontalQuality
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
@@ -42,17 +43,16 @@ public enum EHorizontalQuality
|
||||
LOWEST(2.0f, 4),
|
||||
LOW(2.0f, 8),
|
||||
MEDIUM(2.0f, 12),
|
||||
HIGH(2.2f, 24),
|
||||
EXTREME(2.4f, 64),
|
||||
|
||||
UNLIMITED(-1, -1);
|
||||
HIGH(2.2f, 16),
|
||||
EXTREME(2.4f, 32),
|
||||
;
|
||||
|
||||
|
||||
|
||||
public final double quadraticBase;
|
||||
public final int distanceUnitInBlocks;
|
||||
|
||||
EHorizontalQuality(double quadraticBase, int distanceUnitInBlocks)
|
||||
EDhApiHorizontalQuality(double quadraticBase, int distanceUnitInBlocks)
|
||||
{
|
||||
this.quadraticBase = quadraticBase;
|
||||
this.distanceUnitInBlocks = distanceUnitInBlocks;
|
||||
+16
-10
@@ -20,27 +20,33 @@
|
||||
package com.seibel.distanthorizons.api.enums.config;
|
||||
|
||||
/**
|
||||
* MINECRAFT <br>
|
||||
* OLD_LIGHTING <br>
|
||||
* NONE <br>
|
||||
* AUTO <br>
|
||||
* ENABLED <br>
|
||||
* DISABLED <br>
|
||||
*
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum ELodShading
|
||||
public enum EDhApiLodShading
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
// when removing items up the API major version
|
||||
|
||||
/** Uses Minecraft's shading for LODs */
|
||||
MINECRAFT,
|
||||
/**
|
||||
* Simulates Minecraft's shading.
|
||||
* Uses Minecraft's shading for LODs. <Br>
|
||||
* This means if Minecraft's shading is disabled DH's shading will be as well.
|
||||
*/
|
||||
AUTO,
|
||||
|
||||
/**
|
||||
* Simulates Minecraft's shading. <Br>
|
||||
* This is most useful for shaders that disable Minecraft's shading
|
||||
* but still require shading on LODs.
|
||||
*/
|
||||
OLD_LIGHTING,
|
||||
ENABLED,
|
||||
|
||||
/** LODs will have no shading */
|
||||
NONE;
|
||||
DISABLED;
|
||||
|
||||
}
|
||||
+7
-3
@@ -22,9 +22,10 @@ package com.seibel.distanthorizons.api.enums.config;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
/**
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum ELoggerMode
|
||||
public enum EDhApiLoggerMode
|
||||
{
|
||||
DISABLED(Level.OFF, Level.OFF),
|
||||
LOG_ALL_TO_FILE(Level.ALL, Level.OFF),
|
||||
@@ -40,11 +41,14 @@ public enum ELoggerMode
|
||||
LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE(Level.INFO, Level.WARN),
|
||||
LOG_ERROR_TO_CHAT_AND_INFO_TO_FILE(Level.INFO, Level.ERROR),
|
||||
;
|
||||
|
||||
public final Level levelForFile;
|
||||
public final Level levelForChat;
|
||||
ELoggerMode(Level levelForFile, Level levelForChat)
|
||||
|
||||
EDhApiLoggerMode(Level levelForFile, Level levelForChat)
|
||||
{
|
||||
this.levelForFile = levelForFile;
|
||||
this.levelForChat = levelForChat;
|
||||
}
|
||||
|
||||
}
|
||||
+13
-13
@@ -33,10 +33,10 @@ import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author Leonardo Amato
|
||||
* @version 2023-6-14
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum EMaxHorizontalResolution
|
||||
public enum EDhApiMaxHorizontalResolution
|
||||
{
|
||||
/** render 256 LODs for each chunk */
|
||||
BLOCK(16, 0),
|
||||
@@ -84,12 +84,12 @@ public enum EMaxHorizontalResolution
|
||||
* 2nd dimension: An array of all LodDetails that are less than or <br>
|
||||
* equal to that detailLevel
|
||||
*/
|
||||
private static EMaxHorizontalResolution[][] lowerDetailArrays;
|
||||
private static EDhApiMaxHorizontalResolution[][] lowerDetailArrays;
|
||||
|
||||
|
||||
|
||||
|
||||
EMaxHorizontalResolution(int newLengthCount, int newDetailLevel)
|
||||
EDhApiMaxHorizontalResolution(int newLengthCount, int newDetailLevel)
|
||||
{
|
||||
this.detailLevel = (byte) newDetailLevel;
|
||||
this.dataPointLengthCount = newLengthCount;
|
||||
@@ -129,20 +129,20 @@ public enum EMaxHorizontalResolution
|
||||
* Returns an array of all LodDetails that have a detail level
|
||||
* that is less than or equal to the given LodDetail
|
||||
*/
|
||||
public static EMaxHorizontalResolution[] getSelfAndLowerDetails(EMaxHorizontalResolution detail)
|
||||
public static EDhApiMaxHorizontalResolution[] getSelfAndLowerDetails(EDhApiMaxHorizontalResolution detail)
|
||||
{
|
||||
if (lowerDetailArrays == null)
|
||||
{
|
||||
// run first time setup
|
||||
lowerDetailArrays = new EMaxHorizontalResolution[EMaxHorizontalResolution.values().length][];
|
||||
lowerDetailArrays = new EDhApiMaxHorizontalResolution[EDhApiMaxHorizontalResolution.values().length][];
|
||||
|
||||
// go through each LodDetail
|
||||
for (EMaxHorizontalResolution currentDetail : EMaxHorizontalResolution.values())
|
||||
for (EDhApiMaxHorizontalResolution currentDetail : EDhApiMaxHorizontalResolution.values())
|
||||
{
|
||||
ArrayList<EMaxHorizontalResolution> lowerDetails = new ArrayList<>();
|
||||
ArrayList<EDhApiMaxHorizontalResolution> lowerDetails = new ArrayList<>();
|
||||
|
||||
// find the details lower than currentDetail
|
||||
for (EMaxHorizontalResolution compareDetail : EMaxHorizontalResolution.values())
|
||||
for (EDhApiMaxHorizontalResolution compareDetail : EDhApiMaxHorizontalResolution.values())
|
||||
{
|
||||
if (currentDetail.detailLevel <= compareDetail.detailLevel)
|
||||
{
|
||||
@@ -154,7 +154,7 @@ public enum EMaxHorizontalResolution
|
||||
Collections.sort(lowerDetails);
|
||||
Collections.reverse(lowerDetails);
|
||||
|
||||
lowerDetailArrays[currentDetail.detailLevel] = lowerDetails.toArray(new EMaxHorizontalResolution[lowerDetails.size()]);
|
||||
lowerDetailArrays[currentDetail.detailLevel] = lowerDetails.toArray(new EDhApiMaxHorizontalResolution[lowerDetails.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,9 +162,9 @@ public enum EMaxHorizontalResolution
|
||||
}
|
||||
|
||||
/** Returns what detail level should be used at a given distance and maxDistance. */
|
||||
public static EMaxHorizontalResolution getDetailForDistance(EMaxHorizontalResolution maxDetailLevel, int distance, int maxDistance)
|
||||
public static EDhApiMaxHorizontalResolution getDetailForDistance(EDhApiMaxHorizontalResolution maxDetailLevel, int distance, int maxDistance)
|
||||
{
|
||||
EMaxHorizontalResolution[] lowerDetails = getSelfAndLowerDetails(maxDetailLevel);
|
||||
EDhApiMaxHorizontalResolution[] lowerDetails = getSelfAndLowerDetails(maxDetailLevel);
|
||||
int distanceBetweenDetails = maxDistance / lowerDetails.length;
|
||||
int index = MathUtil.clamp(0, distance / distanceBetweenDetails, lowerDetails.length - 1);
|
||||
|
||||
+7
-3
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.api.enums.config;
|
||||
|
||||
/**
|
||||
* NAME_ONLY, <br>
|
||||
* IP_ONLY, <br>
|
||||
* NAME_IP, <br>
|
||||
* NAME_IP_PORT, <br>
|
||||
* NAME_IP_PORT_MC_VERSION, <br> <br>
|
||||
@@ -29,10 +30,10 @@ package com.seibel.distanthorizons.api.enums.config;
|
||||
* Determines how the multiplayer folders should be named.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-7-1
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum EServerFolderNameMode
|
||||
public enum EDhApiServerFolderNameMode
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
@@ -42,6 +43,9 @@ public enum EServerFolderNameMode
|
||||
/** Only use the server name */
|
||||
NAME_ONLY,
|
||||
|
||||
/** Only use the server IP */
|
||||
IP_ONLY,
|
||||
|
||||
/**
|
||||
* {SERVER_NAME} IP {IP} <br>
|
||||
* Example: Minecraft Server IP 192.168.1.40
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.seibel.distanthorizons.api.enums.config;
|
||||
|
||||
/**
|
||||
* STABLE, <br>
|
||||
* NIGHTLY, <br><br>
|
||||
*
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum EDhApiUpdateBranch
|
||||
{
|
||||
STABLE,
|
||||
NIGHTLY
|
||||
}
|
||||
+3
-2
@@ -28,10 +28,11 @@ package com.seibel.distanthorizons.api.enums.config;
|
||||
* the vanilla Minecraft terrain.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-6-30
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
@Deprecated // not currently in use, if the config this enum represents is re-implemented, the deprecated flag can be removed
|
||||
public enum EVanillaOverdraw
|
||||
public enum EDhApiVanillaOverdraw
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
+11
-9
@@ -29,23 +29,25 @@ import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
||||
* EXTREME <br>
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 2023-2-5
|
||||
* @since API 1.0.0
|
||||
* @version 2024-4-6
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
public enum EVerticalQuality
|
||||
public enum EDhApiVerticalQuality
|
||||
{
|
||||
HEIGHT_MAP(new int[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}),
|
||||
LOW(new int[]{4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1}),
|
||||
MEDIUM(new int[]{6, 4, 3, 2, 2, 1, 1, 1, 1, 1, 1}),
|
||||
HIGH(new int[]{8, 6, 4, 2, 2, 2, 2, 1, 1, 1, 1}),
|
||||
EXTREME(new int[]{16, 8, 4, 2, 2, 2, 2, 1, 1, 1, 1});
|
||||
HEIGHT_MAP( new int[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}),
|
||||
LOW( new int[]{4, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1}),
|
||||
MEDIUM( new int[]{6, 4, 3, 3, 3, 3, 3, 2, 2, 2, 1}),
|
||||
HIGH( new int[]{16, 8, 4, 3, 3, 3, 3, 3, 3, 3, 1}),
|
||||
VERY_HIGH( new int[]{32, 16, 8, 4, 4, 3, 3, 3, 3, 3, 1}),
|
||||
EXTREME( new int[]{64, 32, 8, 4, 4, 3, 3, 3, 3, 3, 1}),
|
||||
PIXEL_ART( new int[]{512, 64, 16, 8, 4, 3, 3, 3, 3, 3, 1});
|
||||
|
||||
/** represents how many LODs can be rendered in a single vertical slice */
|
||||
public final int[] maxVerticalData;
|
||||
|
||||
|
||||
|
||||
EVerticalQuality(int[] maxVerticalData) { this.maxVerticalData = maxVerticalData; }
|
||||
EDhApiVerticalQuality(int[] maxVerticalData) { this.maxVerticalData = maxVerticalData; }
|
||||
|
||||
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.enums.config;
|
||||
|
||||
/**
|
||||
* MERGE_SAME_BLOCKS <br>
|
||||
* VISUALLY_EQUAL <br><br>
|
||||
*
|
||||
* @version 2024-3-27
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
public enum EDhApiWorldCompressionMode
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
// when removing items up the API major version
|
||||
|
||||
/**
|
||||
* Every block/biome change is recorded in the database. <br>
|
||||
* This is what DH 2.0 and 2.0.1 all used by default and will store a lot of data.
|
||||
*/
|
||||
MERGE_SAME_BLOCKS(0),
|
||||
|
||||
/**
|
||||
* Only visible block/biome changes are recorded in the database. <Br>
|
||||
* Hidden blocks (IE ores) are ignored.
|
||||
*/
|
||||
VISUALLY_EQUAL(1);
|
||||
|
||||
|
||||
|
||||
/** More stable than using the ordinal of the enum */
|
||||
public final byte value;
|
||||
|
||||
EDhApiWorldCompressionMode(int value) { this.value = (byte) value; }
|
||||
|
||||
|
||||
public static EDhApiWorldCompressionMode getFromValue(byte value)
|
||||
{
|
||||
EDhApiWorldCompressionMode[] enumList = EDhApiWorldCompressionMode.values();
|
||||
for (int i = 0; i < enumList.length; i++)
|
||||
{
|
||||
if (enumList[i].value == value)
|
||||
{
|
||||
return enumList[i];
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("No lossy compression mode with the value ["+value+"]");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-48
@@ -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.enums.config;
|
||||
|
||||
/**
|
||||
* AUTO <br>
|
||||
* Near_First <br>
|
||||
* Far_First <br> <br>
|
||||
*
|
||||
* Determines which LODs should have priority when generating
|
||||
* outside the normal view distance.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 12-1-2021
|
||||
*/
|
||||
@Deprecated // not currently in use, if the config this enum represents is re-implemented, the deprecated flag can be removed
|
||||
public enum EGenerationPriority
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
// when removing items up the API major version
|
||||
|
||||
/** NEAR_FIRST when connected to servers and BALANCED when on single player */
|
||||
AUTO,
|
||||
|
||||
NEAR_FIRST,
|
||||
|
||||
BALANCED,
|
||||
|
||||
FAR_FIRST
|
||||
}
|
||||
-51
@@ -1,51 +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.enums.config;
|
||||
|
||||
/**
|
||||
* NONE <br>
|
||||
* LIGHT <br>
|
||||
* MEDIUM <br>
|
||||
* HEAVY <br>
|
||||
*
|
||||
* CUSTOM <br>
|
||||
*
|
||||
* @since API 1.0.0
|
||||
* @deprecated will be removed when DH updates to MC 1.21 <br>
|
||||
* After removal a float value will be used to control overdraw instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public enum EOverdrawPrevention
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
// when removing items up the API major version
|
||||
|
||||
NONE,
|
||||
LIGHT,
|
||||
MEDIUM,
|
||||
HEAVY,
|
||||
|
||||
/**
|
||||
* Should not be passed in. <br>
|
||||
* Is returned if the overdraw value doesn't match any of the enums defined here.
|
||||
*/
|
||||
CUSTOM;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.seibel.distanthorizons.api.enums.config;
|
||||
|
||||
public enum EUpdateBranch
|
||||
{
|
||||
STABLE,
|
||||
NIGHTLY
|
||||
}
|
||||
+3
-2
@@ -30,9 +30,10 @@ import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui
|
||||
* HIGH, <br>
|
||||
* EXTREME, <br>
|
||||
*
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum EQualityPreset
|
||||
public enum EDhApiQualityPreset
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
+3
-4
@@ -29,9 +29,10 @@ import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui
|
||||
* BALANCED, <br>
|
||||
* AGGRESSIVE, <br>
|
||||
*
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum EThreadPreset
|
||||
public enum EDhApiThreadPreset
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
@@ -44,8 +45,6 @@ public enum EThreadPreset
|
||||
LOW_IMPACT,
|
||||
BALANCED,
|
||||
AGGRESSIVE,
|
||||
|
||||
// temporarily removed due to stability concerns
|
||||
I_PAID_FOR_THE_WHOLE_CPU,
|
||||
|
||||
}
|
||||
+5
-11
@@ -28,10 +28,10 @@ package com.seibel.distanthorizons.api.enums.rendering;
|
||||
*
|
||||
* @author Leetom
|
||||
* @author James Seibel
|
||||
* @version 2023-6-7
|
||||
* @since API 1.0.0
|
||||
* @version 2024-4-6
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
public enum EDebugRendering
|
||||
public enum EDhApiDebugRendering
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
@@ -44,10 +44,6 @@ public enum EDebugRendering
|
||||
/** LOD colors are based on their detail */
|
||||
SHOW_DETAIL,
|
||||
|
||||
@Deprecated
|
||||
/** LOD colors are based on their gen mode. */
|
||||
SHOW_GENMODE,
|
||||
|
||||
/** Block Materials are often used by Iris shaders to determine how LODs should be rendered */
|
||||
SHOW_BLOCK_MATERIAL,
|
||||
|
||||
@@ -58,7 +54,7 @@ public enum EDebugRendering
|
||||
SHOW_RENDER_SOURCE_FLAG;
|
||||
|
||||
|
||||
public static EDebugRendering next(EDebugRendering type)
|
||||
public static EDhApiDebugRendering next(EDhApiDebugRendering type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
@@ -75,7 +71,7 @@ public enum EDebugRendering
|
||||
}
|
||||
}
|
||||
|
||||
public static EDebugRendering previous(EDebugRendering type)
|
||||
public static EDhApiDebugRendering previous(EDhApiDebugRendering type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
@@ -84,8 +80,6 @@ public enum EDebugRendering
|
||||
case SHOW_RENDER_SOURCE_FLAG:
|
||||
return SHOW_OVERLAPPING_QUADS;
|
||||
case SHOW_OVERLAPPING_QUADS:
|
||||
return SHOW_GENMODE;
|
||||
case SHOW_GENMODE:
|
||||
return SHOW_DETAIL;
|
||||
default:
|
||||
return OFF;
|
||||
+4
-3
@@ -24,10 +24,10 @@ package com.seibel.distanthorizons.api.enums.rendering;
|
||||
* USE_SKY_COLOR, <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-6-9
|
||||
* @since API 1.0.0
|
||||
* @version 2024-4-6
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
public enum EFogColorMode
|
||||
public enum EDhApiFogColorMode
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items: up the API minor version
|
||||
@@ -44,4 +44,5 @@ public enum EFogColorMode
|
||||
* https://www.curseforge.com/minecraft/mc-mods/clear-skies-forge-port
|
||||
*/
|
||||
USE_SKY_COLOR,
|
||||
|
||||
}
|
||||
+2
-2
@@ -25,10 +25,10 @@ package com.seibel.distanthorizons.api.enums.rendering;
|
||||
* FOG_DISABLED <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @since API 1.1.0
|
||||
* @version 2022-6-2
|
||||
* @since API 1.0.0
|
||||
*/
|
||||
public enum EFogDrawMode
|
||||
public enum EDhApiFogDrawMode
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
+3
-3
@@ -26,9 +26,9 @@ package com.seibel.distanthorizons.api.enums.rendering;
|
||||
*
|
||||
* @author Leetom
|
||||
* @version 2022-6-30
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
public enum EFogFalloff
|
||||
public enum EDhApiFogFalloff
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
@@ -38,5 +38,5 @@ public enum EFogFalloff
|
||||
LINEAR,
|
||||
EXPONENTIAL,
|
||||
EXPONENTIAL_SQUARED,
|
||||
// TEXTURE_BASED, // TODO: Impl this
|
||||
|
||||
}
|
||||
+3
-3
@@ -32,10 +32,10 @@ package com.seibel.distanthorizons.api.enums.rendering;
|
||||
* AVERAGE <br>
|
||||
*
|
||||
* @author Leetom
|
||||
* @version 2022-4-14
|
||||
* @since API 1.0.0
|
||||
* @version 2024-4-6
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
public enum EHeightFogMixMode
|
||||
public enum EDhApiHeightFogMixMode
|
||||
{
|
||||
BASIC,
|
||||
IGNORE_HEIGHT,
|
||||
+4
-4
@@ -28,10 +28,10 @@ package com.seibel.distanthorizons.api.enums.rendering;
|
||||
* ABOVE_AND_BELOW_SET_HEIGHT, <br>
|
||||
*
|
||||
* @author Leetom
|
||||
* @version 6-30-2022
|
||||
* @since API 1.0.0
|
||||
* @version 2024-4-6
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
public enum EHeightFogMode
|
||||
public enum EDhApiHeightFogMode
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
@@ -49,7 +49,7 @@ public enum EHeightFogMode
|
||||
public final boolean above;
|
||||
public final boolean below;
|
||||
|
||||
EHeightFogMode(boolean basedOnCamera, boolean above, boolean below)
|
||||
EDhApiHeightFogMode(boolean basedOnCamera, boolean above, boolean below)
|
||||
{
|
||||
this.basedOnCamera = basedOnCamera;
|
||||
this.above = above;
|
||||
+5
-5
@@ -24,10 +24,10 @@ package com.seibel.distanthorizons.api.enums.rendering;
|
||||
* Debug <br>
|
||||
* Disabled <br>
|
||||
*
|
||||
* @version 2022-6-2
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum ERendererMode
|
||||
public enum EDhApiRendererMode
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
@@ -40,7 +40,7 @@ public enum ERendererMode
|
||||
|
||||
|
||||
/** Used by the config GUI to cycle through the available rendering options */
|
||||
public static ERendererMode next(ERendererMode type)
|
||||
public static EDhApiRendererMode next(EDhApiRendererMode type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
@@ -54,7 +54,7 @@ public enum ERendererMode
|
||||
}
|
||||
|
||||
/** Used by the config GUI to cycle through the available rendering options */
|
||||
public static ERendererMode previous(ERendererMode type)
|
||||
public static EDhApiRendererMode previous(EDhApiRendererMode type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
+4
-3
@@ -24,9 +24,10 @@ package com.seibel.distanthorizons.api.enums.rendering;
|
||||
* FAKE, <br>
|
||||
* COMPLETE, <br>
|
||||
*
|
||||
* @since API 1.0.0
|
||||
* @since API 1.1.0
|
||||
* @version 2024-4-6
|
||||
*/
|
||||
public enum ETransparency
|
||||
public enum EDhApiTransparency
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
@@ -40,7 +41,7 @@ public enum ETransparency
|
||||
public final boolean transparencyEnabled;
|
||||
public final boolean fakeTransparencyEnabled;
|
||||
|
||||
ETransparency(boolean transparencyEnabled, boolean fakeTransparencyEnabled)
|
||||
EDhApiTransparency(boolean transparencyEnabled, boolean fakeTransparencyEnabled)
|
||||
{
|
||||
this.transparencyEnabled = transparencyEnabled;
|
||||
this.fakeTransparencyEnabled = fakeTransparencyEnabled;
|
||||
@@ -1,40 +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.enums.rendering;
|
||||
|
||||
/**
|
||||
* NEAR, <br>
|
||||
* FAR, <br>
|
||||
* NEAR_AND_FAR <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-6-2
|
||||
* @since API 1.0.0
|
||||
*/
|
||||
public enum EFogDistance
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
// when removing items up the API major version
|
||||
|
||||
NEAR,
|
||||
FAR,
|
||||
NEAR_AND_FAR
|
||||
}
|
||||
-1
@@ -19,7 +19,6 @@
|
||||
|
||||
package com.seibel.distanthorizons.api.interfaces.config.both;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.ELightGenerationMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
|
||||
|
||||
-3
@@ -19,9 +19,6 @@
|
||||
|
||||
package com.seibel.distanthorizons.api.interfaces.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogColorMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogDistance;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogDrawMode;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.api.interfaces.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDebugRendering;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
|
||||
|
||||
@@ -33,7 +33,7 @@ import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
|
||||
public interface IDhApiDebuggingConfig extends IDhApiConfigGroup
|
||||
{
|
||||
/** Can be used to debug the standard fake chunk rendering. */
|
||||
IDhApiConfigValue<EDebugRendering> debugRendering();
|
||||
IDhApiConfigValue<EDhApiDebugRendering> debugRendering();
|
||||
|
||||
/** If enabled debug keybindings can be used. */
|
||||
IDhApiConfigValue<Boolean> debugKeybindings();
|
||||
|
||||
+2
-3
@@ -19,8 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.api.interfaces.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogFalloff;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.*;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
|
||||
@@ -64,7 +63,7 @@ public interface IDhApiFarFogConfig extends IDhApiConfigGroup
|
||||
IDhApiConfigValue<Double> farFogMaxThickness();
|
||||
|
||||
/** Defines how the fog changes in thickness. */
|
||||
IDhApiConfigValue<EFogFalloff> farFogFalloff();
|
||||
IDhApiConfigValue<EDhApiFogFalloff> farFogFalloff();
|
||||
|
||||
/** Defines the fog density. */
|
||||
IDhApiConfigValue<Double> farFogDensity();
|
||||
|
||||
+4
-9
@@ -19,10 +19,8 @@
|
||||
|
||||
package com.seibel.distanthorizons.api.interfaces.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogColorMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogDistance;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogDrawMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.*;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogDrawMode;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
|
||||
@@ -58,14 +56,11 @@ public interface IDhApiFogConfig extends IDhApiConfigGroup
|
||||
// basic fog settings //
|
||||
//====================//
|
||||
|
||||
/** Defines at what distance fog is rendered on fake chunks. */
|
||||
IDhApiConfigValue<EFogDistance> distance();
|
||||
|
||||
/** Should be used to enable/disable fog rendering. */
|
||||
IDhApiConfigValue<EFogDrawMode> drawMode();
|
||||
IDhApiConfigValue<EDhApiFogDrawMode> drawMode();
|
||||
|
||||
/** Can be used to enable support with mods that change vanilla MC's fog color. */
|
||||
IDhApiConfigValue<EFogColorMode> color();
|
||||
IDhApiConfigValue<EDhApiFogColorMode> color();
|
||||
|
||||
/**
|
||||
* If enabled attempts to disable vanilla MC's fog on real chunks. <br>
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.api.interfaces.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EGpuUploadMethod;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
|
||||
@@ -34,7 +34,7 @@ public interface IDhApiGpuBuffersConfig extends IDhApiConfigGroup
|
||||
{
|
||||
|
||||
/** Defines how geometry data is uploaded to the GPU. */
|
||||
IDhApiConfigValue<EGpuUploadMethod> gpuUploadMethod();
|
||||
IDhApiConfigValue<EDhApiGpuUploadMethod> gpuUploadMethod();
|
||||
|
||||
/**
|
||||
* Defines how long we should wait after uploading one
|
||||
|
||||
+9
-21
@@ -20,9 +20,8 @@
|
||||
package com.seibel.distanthorizons.api.interfaces.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.*;
|
||||
import com.seibel.distanthorizons.api.enums.config.*;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.ERendererMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.ETransparency;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
|
||||
|
||||
@@ -68,7 +67,7 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup
|
||||
*
|
||||
* Changing this config also changes {@link IDhApiGraphicsConfig#renderingEnabled()}'s value.
|
||||
*/
|
||||
IDhApiConfigValue<ERendererMode> renderingMode();
|
||||
IDhApiConfigValue<EDhApiRendererMode> renderingMode();
|
||||
|
||||
|
||||
|
||||
@@ -77,18 +76,18 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup
|
||||
//==================//
|
||||
|
||||
/** Defines how detailed fake chunks are in the horizontal direction */
|
||||
IDhApiConfigValue<EMaxHorizontalResolution> maxHorizontalResolution();
|
||||
IDhApiConfigValue<EDhApiMaxHorizontalResolution> maxHorizontalResolution();
|
||||
|
||||
/** Defines how detailed fake chunks are in the vertical direction */
|
||||
IDhApiConfigValue<EVerticalQuality> verticalQuality();
|
||||
IDhApiConfigValue<EDhApiVerticalQuality> verticalQuality();
|
||||
|
||||
/** Modifies the quadratic function fake chunks use for horizontal quality drop-off. */
|
||||
IDhApiConfigValue<EHorizontalQuality> horizontalQuality();
|
||||
IDhApiConfigValue<EDhApiHorizontalQuality> horizontalQuality();
|
||||
|
||||
IDhApiConfigValue<ETransparency> transparency();
|
||||
IDhApiConfigValue<EDhApiTransparency> transparency();
|
||||
|
||||
/** Defines what blocks won't be rendered as LODs. */
|
||||
IDhApiConfigValue<EBlocksToAvoid> blocksToAvoid();
|
||||
IDhApiConfigValue<EDhApiBlocksToAvoid> blocksToAvoid();
|
||||
|
||||
/**
|
||||
* Defines if the color of avoided blocks will color the block below them. <Br>
|
||||
@@ -112,17 +111,6 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup
|
||||
// advanced graphic settings //
|
||||
//===========================//
|
||||
|
||||
/**
|
||||
* Sets the distance used by the near clip plane to reduce
|
||||
* overdraw. <br>
|
||||
* Disabling this reduces holes in the world due to the near clip plane
|
||||
* being too close to the camera and the terrain not being covered by vanilla terrain.
|
||||
*
|
||||
* @deprecated Use {@link IDhApiGraphicsConfig#overdrawPreventionRadius()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
IDhApiConfigValue<EOverdrawPrevention> overdrawPrevention();
|
||||
|
||||
/**
|
||||
* Sets the radius used by the near clip shader to reduce
|
||||
* overdraw. <br>
|
||||
@@ -172,7 +160,7 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup
|
||||
*
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
IDhApiConfigValue<ELodShading> lodShading();
|
||||
IDhApiConfigValue<EDhApiLodShading> lodShading();
|
||||
|
||||
/**
|
||||
* Sets whether LODs outside the view frustum culling will
|
||||
|
||||
+6
-6
@@ -19,9 +19,9 @@
|
||||
|
||||
package com.seibel.distanthorizons.api.interfaces.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogFalloff;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EHeightFogMixMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EHeightFogMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMode;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
|
||||
@@ -40,10 +40,10 @@ public interface IDhApiHeightFogConfig extends IDhApiConfigGroup
|
||||
{
|
||||
|
||||
/** Defines how the height fog mixes. */
|
||||
IDhApiConfigValue<EHeightFogMixMode> heightFogMixMode();
|
||||
IDhApiConfigValue<EDhApiHeightFogMixMode> heightFogMixMode();
|
||||
|
||||
/** Defines how the height fog is drawn relative to the camera or world. */
|
||||
IDhApiConfigValue<EHeightFogMode> heightFogMode();
|
||||
IDhApiConfigValue<EDhApiHeightFogMode> heightFogMode();
|
||||
|
||||
/**
|
||||
* Defines the height fog's base height if {@link IDhApiHeightFogConfig#heightFogMode()}
|
||||
@@ -64,7 +64,7 @@ public interface IDhApiHeightFogConfig extends IDhApiConfigGroup
|
||||
IDhApiConfigValue<Double> heightFogMaxThickness();
|
||||
|
||||
/** Defines how the height fog changes in thickness. */
|
||||
IDhApiConfigValue<EFogFalloff> heightFogFalloff();
|
||||
IDhApiConfigValue<EDhApiFogFalloff> heightFogFalloff();
|
||||
|
||||
/** Defines the height fog's density. */
|
||||
IDhApiConfigValue<Double> heightFogDensity();
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@
|
||||
package com.seibel.distanthorizons.api.interfaces.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.enums.config.EServerFolderNameMode;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiServerFolderNameMode;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
|
||||
|
||||
/**
|
||||
@@ -37,7 +37,7 @@ public interface IDhApiMultiplayerConfig extends IDhApiConfigGroup
|
||||
* Defines how multiplayer server folders are named. <br>
|
||||
* Note: Changing this while connected to a multiplayer world will cause undefined behavior!
|
||||
*/
|
||||
IDhApiConfigValue<EServerFolderNameMode> folderSavingMode();
|
||||
IDhApiConfigValue<EDhApiServerFolderNameMode> folderSavingMode();
|
||||
|
||||
/**
|
||||
* Defines the necessary similarity (as a percent) that two potential levels
|
||||
|
||||
+5
-5
@@ -23,16 +23,16 @@ import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
|
||||
|
||||
/**
|
||||
* Called before Distant Horizons starts rendering a buffer. <br>
|
||||
* This event cannot be cancelled, use {@link DhApiBeforeRenderEvent} if you want to cancel rendering.
|
||||
* Called whenever Distant Horizons (re)creates
|
||||
* the color and depth textures it renders to. <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2023-1-23
|
||||
* @version 2024-3-2
|
||||
* @since API 1.1.0
|
||||
*/
|
||||
public abstract class DhApiScreenResizeEvent implements IDhApiEvent<DhApiScreenResizeEvent.EventParam>
|
||||
public abstract class DhApiColorDepthTextureCreatedEvent implements IDhApiEvent<DhApiColorDepthTextureCreatedEvent.EventParam>
|
||||
{
|
||||
/** Fired immediately before Distant Horizons renders any transparent buffers. */
|
||||
/** Fired before Distant Horizons creates. */
|
||||
public abstract void onResize(DhApiEventParam<EventParam> event);
|
||||
|
||||
|
||||
+5
-3
@@ -40,7 +40,8 @@ public class DhApiTerrainDataPoint
|
||||
*/
|
||||
public final byte detailLevel;
|
||||
|
||||
public final int lightLevel;
|
||||
public final int blockLightLevel;
|
||||
public final int skyLightLevel;
|
||||
public final int topYBlockPos;
|
||||
public final int bottomYBlockPos;
|
||||
|
||||
@@ -49,11 +50,12 @@ public class DhApiTerrainDataPoint
|
||||
|
||||
|
||||
|
||||
public DhApiTerrainDataPoint(byte detailLevel, int lightLevel, int topYBlockPos, int bottomYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper)
|
||||
public DhApiTerrainDataPoint(byte detailLevel, int blockLightLevel, int skyLightLevel, int topYBlockPos, int bottomYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
|
||||
this.lightLevel = lightLevel;
|
||||
this.blockLightLevel = blockLightLevel;
|
||||
this.skyLightLevel = skyLightLevel;
|
||||
this.topYBlockPos = topYBlockPos;
|
||||
this.bottomYBlockPos = bottomYBlockPos;
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ public final class ModInfo
|
||||
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.0.2-a-dev";
|
||||
public static final String VERSION = "2.0.4-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");
|
||||
|
||||
|
||||
+6
-6
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.coreapi.util.converters;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.ERendererMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.config.IConverter;
|
||||
|
||||
/**
|
||||
@@ -28,17 +28,17 @@ import com.seibel.distanthorizons.coreapi.interfaces.config.IConverter;
|
||||
* @author James Seibel
|
||||
* @version 2022-6-30
|
||||
*/
|
||||
public class RenderModeEnabledConverter implements IConverter<ERendererMode, Boolean>
|
||||
public class RenderModeEnabledConverter implements IConverter<EDhApiRendererMode, Boolean>
|
||||
{
|
||||
|
||||
@Override public ERendererMode convertToCoreType(Boolean renderingEnabled)
|
||||
@Override public EDhApiRendererMode convertToCoreType(Boolean renderingEnabled)
|
||||
{
|
||||
return renderingEnabled ? ERendererMode.DEFAULT : ERendererMode.DISABLED;
|
||||
return renderingEnabled ? EDhApiRendererMode.DEFAULT : EDhApiRendererMode.DISABLED;
|
||||
}
|
||||
|
||||
@Override public Boolean convertToApiType(ERendererMode renderingMode)
|
||||
@Override public Boolean convertToApiType(EDhApiRendererMode renderingMode)
|
||||
{
|
||||
return renderingMode == ERendererMode.DEFAULT;
|
||||
return renderingMode == EDhApiRendererMode.DEFAULT;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ import org.junit.Test;
|
||||
* @author James Seibel
|
||||
* @version 2022-9-5
|
||||
*/
|
||||
public class ExampleTest
|
||||
public class ExampleApiTest
|
||||
{
|
||||
|
||||
@Test
|
||||
@@ -0,0 +1,63 @@
|
||||
plugins {
|
||||
id "java"
|
||||
id "com.github.johnrengelman.shadow" version '7.1.2' apply false // Set this to true if you're using the standalone Core project
|
||||
}
|
||||
|
||||
apply plugin: "application"
|
||||
|
||||
application {
|
||||
mainClass.set("com.seibel.distanthorizons.core.jar.JarMain")
|
||||
}
|
||||
|
||||
configurations {
|
||||
shadowedArtifact // Used by DH to specify that we want to implement the shadowed core JAR file instead of the regular JAR file
|
||||
shade
|
||||
implementation.extendsFrom shade
|
||||
}
|
||||
|
||||
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
|
||||
|
||||
// Set the OS lwjgl is using to the current os
|
||||
project.ext.lwjglNatives = "natives-" + os.toFamilyName()
|
||||
|
||||
dependencies { // All of these dependencies are in Vanilla Minecraft, but we need to depend on it as we arent importing Minecraft in the core
|
||||
// Imports most of lwjgl's libraries (well, only the ones that we need)
|
||||
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}") // TODO: Use Minecraft's version for lwjgl_version (which changes in nearly every version) instead of a hard defined version for all versions
|
||||
|
||||
// REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use
|
||||
implementation "org.lwjgl:lwjgl"
|
||||
implementation "org.lwjgl:lwjgl-assimp"
|
||||
implementation "org.lwjgl:lwjgl-glfw"
|
||||
implementation "org.lwjgl:lwjgl-openal"
|
||||
implementation "org.lwjgl:lwjgl-opengl"
|
||||
implementation "org.lwjgl:lwjgl-stb"
|
||||
implementation "org.lwjgl:lwjgl-tinyfd"
|
||||
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
|
||||
|
||||
// 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
|
||||
implementation("org.jetbrains:annotations:16.0.2")
|
||||
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
||||
implementation("com.google.common:google-collect:0.5")
|
||||
implementation("com.google.guava:guava:31.1-jre")
|
||||
|
||||
}
|
||||
|
||||
artifacts {
|
||||
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()
|
||||
}
|
||||
@@ -26,17 +26,14 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.world.DhApiWorldProxy;
|
||||
import com.seibel.distanthorizons.core.api.external.methods.config.DhApiConfig;
|
||||
import com.seibel.distanthorizons.core.api.external.methods.data.DhApiTerrainDataRepo;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.loader.CompleteFullDataSourceLoader;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.loader.HighDetailIncompleteFullDataSourceLoader;
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.loader.LowDetailIncompleteFullDataSourceLoader;
|
||||
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
|
||||
//import io.netty.buffer.ByteBuf;
|
||||
import net.jpountz.lz4.LZ4Compressor;
|
||||
import net.jpountz.lz4.LZ4FrameOutputStream;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.tukaani.xz.XZOutputStream;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.awt.*;
|
||||
|
||||
/** Handles first time Core setup. */
|
||||
public class Initializer
|
||||
@@ -50,13 +47,17 @@ public class Initializer
|
||||
{
|
||||
// if any library isn't present in the jar its class
|
||||
// will throw an error (not an exception)
|
||||
Class<?> compressor = LZ4Compressor.class;
|
||||
Class<?> fastCompressor = LZ4FrameOutputStream.class;
|
||||
Class<?> smallCompressor = XZOutputStream.class;
|
||||
//Class<?> networking = ByteBuf.class;
|
||||
Class<?> toml = com.electronwill.nightconfig.core.Config.class;
|
||||
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
|
||||
}
|
||||
catch (NoClassDefFoundError e)
|
||||
catch (Throwable e)
|
||||
{
|
||||
LOGGER.fatal("Critical programmer error: One or more libraries aren't present. Error: [" + e.getMessage() + "].");
|
||||
// throwing here should crash the game, notifying the developer that something is wrong
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -76,10 +77,16 @@ public class Initializer
|
||||
}
|
||||
|
||||
|
||||
|
||||
CompleteFullDataSourceLoader unused2 = new CompleteFullDataSourceLoader(); // Auto register into the loader system
|
||||
HighDetailIncompleteFullDataSourceLoader unused3 = new HighDetailIncompleteFullDataSourceLoader(); // Auto register
|
||||
LowDetailIncompleteFullDataSourceLoader unused4 = new LowDetailIncompleteFullDataSourceLoader(); // Auto register
|
||||
// attempt to setup 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
|
||||
DhApi.Delayed.configs = DhApiConfig.INSTANCE;
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiDebuggingConfig;
|
||||
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDebugRendering;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
|
||||
|
||||
public class DhApiDebuggingConfig implements IDhApiDebuggingConfig
|
||||
{
|
||||
@@ -33,8 +33,8 @@ public class DhApiDebuggingConfig implements IDhApiDebuggingConfig
|
||||
|
||||
|
||||
|
||||
public IDhApiConfigValue<EDebugRendering> debugRendering()
|
||||
{ return new DhApiConfigValue<EDebugRendering, EDebugRendering>(Config.Client.Advanced.Debugging.debugRendering); }
|
||||
public IDhApiConfigValue<EDhApiDebugRendering> debugRendering()
|
||||
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRendering); }
|
||||
|
||||
public IDhApiConfigValue<Boolean> debugKeybindings()
|
||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.enableDebugKeybindings); }
|
||||
|
||||
+3
-3
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.api.external.methods.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogFalloff;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFarFogConfig;
|
||||
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
|
||||
@@ -50,8 +50,8 @@ public class DhApiFarFogConfig implements IDhApiFarFogConfig
|
||||
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogMax); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EFogFalloff> farFogFalloff()
|
||||
{ return new DhApiConfigValue<EFogFalloff, EFogFalloff>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogFalloff); }
|
||||
public IDhApiConfigValue<EDhApiFogFalloff> farFogFalloff()
|
||||
{ return new DhApiConfigValue<EDhApiFogFalloff, EDhApiFogFalloff>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.farFogFalloff); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Double> farFogDensity()
|
||||
|
||||
+4
-9
@@ -19,9 +19,8 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.api.external.methods.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogColorMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogDistance;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogDrawMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogDrawMode;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFarFogConfig;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFogConfig;
|
||||
@@ -53,15 +52,11 @@ public class DhApiFogConfig implements IDhApiFogConfig
|
||||
//====================//
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EFogDistance> distance()
|
||||
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.distance); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EFogDrawMode> drawMode()
|
||||
public IDhApiConfigValue<EDhApiFogDrawMode> drawMode()
|
||||
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.drawMode); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EFogColorMode> color()
|
||||
public IDhApiConfigValue<EDhApiFogColorMode> color()
|
||||
{ return new DhApiConfigValue<>(Config.Client.Advanced.Graphics.Fog.colorMode); }
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ 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.EGpuUploadMethod;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
|
||||
|
||||
public class DhApiGpuBuffersConfig implements IDhApiGpuBuffersConfig
|
||||
{
|
||||
@@ -33,7 +33,7 @@ public class DhApiGpuBuffersConfig implements IDhApiGpuBuffersConfig
|
||||
|
||||
|
||||
|
||||
public IDhApiConfigValue<EGpuUploadMethod> gpuUploadMethod()
|
||||
public IDhApiConfigValue<EDhApiGpuUploadMethod> gpuUploadMethod()
|
||||
{ return new DhApiConfigValue<>(Config.Client.Advanced.GpuBuffers.gpuUploadMethod); }
|
||||
|
||||
public IDhApiConfigValue<Integer> gpuUploadPerMegabyteInMilliseconds()
|
||||
|
||||
+16
-21
@@ -20,14 +20,14 @@
|
||||
package com.seibel.distanthorizons.core.api.external.methods.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.*;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.ETransparency;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiAmbientOcclusionConfig;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFogConfig;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiGraphicsConfig;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiNoiseTextureConfig;
|
||||
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.ERendererMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
|
||||
public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
|
||||
@@ -61,8 +61,8 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
|
||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.quickEnableRendering); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<ERendererMode> renderingMode()
|
||||
{ return new DhApiConfigValue<ERendererMode, ERendererMode>(Config.Client.Advanced.Debugging.rendererMode); }
|
||||
public IDhApiConfigValue<EDhApiRendererMode> renderingMode()
|
||||
{ return new DhApiConfigValue<EDhApiRendererMode, EDhApiRendererMode>(Config.Client.Advanced.Debugging.rendererMode); }
|
||||
|
||||
|
||||
|
||||
@@ -71,24 +71,24 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
|
||||
//==================//
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EMaxHorizontalResolution> maxHorizontalResolution()
|
||||
{ return new DhApiConfigValue<EMaxHorizontalResolution, EMaxHorizontalResolution>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution); }
|
||||
public IDhApiConfigValue<EDhApiMaxHorizontalResolution> maxHorizontalResolution()
|
||||
{ return new DhApiConfigValue<EDhApiMaxHorizontalResolution, EDhApiMaxHorizontalResolution>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EVerticalQuality> verticalQuality()
|
||||
{ return new DhApiConfigValue<EVerticalQuality, EVerticalQuality>(Config.Client.Advanced.Graphics.Quality.verticalQuality); }
|
||||
public IDhApiConfigValue<EDhApiVerticalQuality> verticalQuality()
|
||||
{ return new DhApiConfigValue<EDhApiVerticalQuality, EDhApiVerticalQuality>(Config.Client.Advanced.Graphics.Quality.verticalQuality); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EHorizontalQuality> horizontalQuality()
|
||||
{ return new DhApiConfigValue<EHorizontalQuality, EHorizontalQuality>(Config.Client.Advanced.Graphics.Quality.horizontalQuality); }
|
||||
public IDhApiConfigValue<EDhApiHorizontalQuality> horizontalQuality()
|
||||
{ return new DhApiConfigValue<EDhApiHorizontalQuality, EDhApiHorizontalQuality>(Config.Client.Advanced.Graphics.Quality.horizontalQuality); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<ETransparency> transparency()
|
||||
{ return new DhApiConfigValue<ETransparency, ETransparency>(Config.Client.Advanced.Graphics.Quality.transparency); }
|
||||
public IDhApiConfigValue<EDhApiTransparency> transparency()
|
||||
{ return new DhApiConfigValue<EDhApiTransparency, EDhApiTransparency>(Config.Client.Advanced.Graphics.Quality.transparency); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EBlocksToAvoid> blocksToAvoid()
|
||||
{ return new DhApiConfigValue<EBlocksToAvoid, EBlocksToAvoid>(Config.Client.Advanced.Graphics.Quality.blocksToIgnore); }
|
||||
public IDhApiConfigValue<EDhApiBlocksToAvoid> blocksToAvoid()
|
||||
{ return new DhApiConfigValue<EDhApiBlocksToAvoid, EDhApiBlocksToAvoid>(Config.Client.Advanced.Graphics.Quality.blocksToIgnore); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Boolean> tintWithAvoidedBlocks()
|
||||
@@ -105,11 +105,6 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
|
||||
// advanced graphic settings //
|
||||
//===========================//
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public IDhApiConfigValue<EOverdrawPrevention> overdrawPrevention()
|
||||
{ return new DhApiConfigValue<EOverdrawPrevention, EOverdrawPrevention>(Config.Client.Advanced.Graphics.AdvancedGraphics.overdrawPreventionPreset); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Double> overdrawPreventionRadius()
|
||||
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.AdvancedGraphics.overdrawPrevention); }
|
||||
@@ -143,8 +138,8 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
|
||||
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.AdvancedGraphics.lodBias); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<ELodShading> lodShading()
|
||||
{ return new DhApiConfigValue<ELodShading, ELodShading>(Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading); }
|
||||
public IDhApiConfigValue<EDhApiLodShading> lodShading()
|
||||
{ return new DhApiConfigValue<EDhApiLodShading, EDhApiLodShading>(Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Boolean> disableFrustumCulling()
|
||||
|
||||
+9
-9
@@ -19,9 +19,9 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.api.external.methods.config.client;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EFogFalloff;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EHeightFogMixMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EHeightFogMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMode;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiHeightFogConfig;
|
||||
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
|
||||
@@ -36,12 +36,12 @@ public class DhApiHeightFogConfig implements IDhApiHeightFogConfig
|
||||
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EHeightFogMixMode> heightFogMixMode()
|
||||
{ return new DhApiConfigValue<EHeightFogMixMode, EHeightFogMixMode>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMixMode); }
|
||||
public IDhApiConfigValue<EDhApiHeightFogMixMode> heightFogMixMode()
|
||||
{ return new DhApiConfigValue<EDhApiHeightFogMixMode, EDhApiHeightFogMixMode>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMixMode); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EHeightFogMode> heightFogMode()
|
||||
{ return new DhApiConfigValue<EHeightFogMode, EHeightFogMode>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMode); }
|
||||
public IDhApiConfigValue<EDhApiHeightFogMode> heightFogMode()
|
||||
{ return new DhApiConfigValue<EDhApiHeightFogMode, EDhApiHeightFogMode>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMode); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Double> heightFogBaseHeight()
|
||||
@@ -64,8 +64,8 @@ public class DhApiHeightFogConfig implements IDhApiHeightFogConfig
|
||||
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogMax); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<EFogFalloff> heightFogFalloff()
|
||||
{ return new DhApiConfigValue<EFogFalloff, EFogFalloff>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogFalloff); }
|
||||
public IDhApiConfigValue<EDhApiFogFalloff> heightFogFalloff()
|
||||
{ return new DhApiConfigValue<EDhApiFogFalloff, EDhApiFogFalloff>(Config.Client.Advanced.Graphics.Fog.AdvancedFog.HeightFog.heightFogFalloff); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Double> heightFogDensity()
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiMultiplayerConfig;
|
||||
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.api.enums.config.EServerFolderNameMode;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiServerFolderNameMode;
|
||||
|
||||
public class DhApiMultiplayerConfig implements IDhApiMultiplayerConfig
|
||||
{
|
||||
@@ -33,8 +33,8 @@ public class DhApiMultiplayerConfig implements IDhApiMultiplayerConfig
|
||||
|
||||
|
||||
|
||||
public IDhApiConfigValue<EServerFolderNameMode> folderSavingMode()
|
||||
{ return new DhApiConfigValue<EServerFolderNameMode, EServerFolderNameMode>(Config.Client.Advanced.Multiplayer.serverFolderNameMode); }
|
||||
public IDhApiConfigValue<EDhApiServerFolderNameMode> folderSavingMode()
|
||||
{ return new DhApiConfigValue<EDhApiServerFolderNameMode, EDhApiServerFolderNameMode>(Config.Client.Advanced.Multiplayer.serverFolderNameMode); }
|
||||
|
||||
public IDhApiConfigValue<Double> multiverseSimilarityRequirement()
|
||||
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent); }
|
||||
|
||||
-1
@@ -19,7 +19,6 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.api.external.methods.config.common;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.ELightGenerationMode;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
|
||||
import com.seibel.distanthorizons.api.interfaces.config.both.IDhApiWorldGenerationConfig;
|
||||
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
|
||||
|
||||
+12
-11
@@ -27,8 +27,7 @@ import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataRepo;
|
||||
import com.seibel.distanthorizons.api.objects.math.DhApiVec3i;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
@@ -47,6 +46,7 @@ import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
|
||||
import com.seibel.distanthorizons.coreapi.util.math.Vec3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -205,26 +205,26 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
byte sectionDetailLevel = (byte) (requestedDetailLevel + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||
|
||||
// get the positions for this request
|
||||
DhSectionPos sectionPos = requestedColumnPos.getSectionPosWithSectionDetailLevel(sectionDetailLevel);
|
||||
long sectionPos = requestedColumnPos.getSectionPosWithSectionDetailLevel(sectionDetailLevel);
|
||||
DhLodPos relativePos = requestedColumnPos.getDhSectionRelativePositionForDetailLevel();
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// attempt to get/generate the data source for this section
|
||||
IFullDataSource dataSource = level.getFileHandler().getAsync(sectionPos).get();
|
||||
FullDataSourceV2 dataSource = level.getFullDataProvider().getAsync(sectionPos).get();
|
||||
if (dataSource == null)
|
||||
{
|
||||
return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + sectionPos + "].");
|
||||
return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "].");
|
||||
}
|
||||
else
|
||||
{
|
||||
// attempt to get the LOD data from the data source
|
||||
FullDataPointIdMap mapping = dataSource.getMapping();
|
||||
SingleColumnFullDataAccessor dataColumn = dataSource.tryGet(relativePos.x, relativePos.z);
|
||||
FullDataPointIdMap mapping = dataSource.mapping;
|
||||
LongArrayList dataColumn = dataSource.get(relativePos.x, relativePos.z);
|
||||
if (dataColumn != null)
|
||||
{
|
||||
int dataColumnIndexCount = dataColumn.getSingleLength();
|
||||
int dataColumnIndexCount = dataColumn.size();
|
||||
DhApiTerrainDataPoint[] returnArray = new DhApiTerrainDataPoint[dataColumnIndexCount];
|
||||
long dataPoint;
|
||||
|
||||
@@ -235,7 +235,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
// search for a datapoint that contains the block y position
|
||||
for (int i = 0; i < dataColumnIndexCount; i++)
|
||||
{
|
||||
dataPoint = dataColumn.getSingle(i);
|
||||
dataPoint = dataColumn.getLong(i);
|
||||
|
||||
if (!getSpecificYCoordinate)
|
||||
{
|
||||
@@ -290,7 +290,8 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
int topY = bottomY + height;
|
||||
|
||||
return new DhApiTerrainDataPoint(detailLevel,
|
||||
FullDataPointUtil.getLight(dataPoint), topY, bottomY,
|
||||
FullDataPointUtil.getBlockLight(dataPoint), FullDataPointUtil.getSkyLight(dataPoint),
|
||||
topY, bottomY,
|
||||
blockState, biomeWrapper);
|
||||
}
|
||||
|
||||
@@ -460,7 +461,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
|
||||
// this will throw a cast exception if the chunk object array isn't correct
|
||||
IChunkWrapper chunk = SingletonInjector.INSTANCE.get(IWrapperFactory.class).createChunkWrapper(chunkObjectArray);
|
||||
dhLevel.updateChunkAsync(chunk);
|
||||
SharedApi.INSTANCE.applyChunkUpdate(chunk, dhLevel.getLevelWrapper(), true);
|
||||
|
||||
|
||||
return DhApiResult.createSuccess();
|
||||
|
||||
@@ -21,7 +21,6 @@ package com.seibel.distanthorizons.core.api.internal;
|
||||
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
|
||||
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiFramebuffer;
|
||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
|
||||
@@ -32,10 +31,9 @@ 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.coreapi.DependencyInjection.OverrideInjector;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDebugRendering;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.ERendererMode;
|
||||
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.logging.ConfigBasedLogger;
|
||||
import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger;
|
||||
@@ -54,10 +52,11 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -81,6 +80,9 @@ public class ClientApi
|
||||
public static final long SPAM_LOGGER_FLUSH_NS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
|
||||
|
||||
private boolean configOverrideReminderPrinted = false;
|
||||
|
||||
private final Queue<String> chatMessageQueueForNextFrame = new LinkedBlockingQueue<>();
|
||||
|
||||
public boolean rendererDisabledBecauseOfExceptions = false;
|
||||
|
||||
private long lastFlushNanoTime = 0;
|
||||
@@ -179,7 +181,7 @@ public class ClientApi
|
||||
// can happen on certain multiverse servers
|
||||
return;
|
||||
}
|
||||
LOGGER.info("Unloading client level [" + level + "].");
|
||||
LOGGER.info("Unloading client level [" + level + "]-["+level.getDimensionType().getDimensionName()+"].");
|
||||
|
||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||
if (world != null)
|
||||
@@ -218,7 +220,7 @@ public class ClientApi
|
||||
|
||||
|
||||
|
||||
LOGGER.info("Loading " + (isServerCommunication ? "Multiverse" : "") + " client level [" + level + "].");
|
||||
LOGGER.info("Loading " + (isServerCommunication ? "Multiverse" : "") + " client level [" + level + "]-["+level.getDimensionType().getDimensionName()+"].");
|
||||
|
||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||
if (world != null)
|
||||
@@ -473,14 +475,28 @@ public class ClientApi
|
||||
{
|
||||
// logging //
|
||||
|
||||
// dev build
|
||||
if (ModInfo.IS_DEV_BUILD && !this.configOverrideReminderPrinted && MC.playerExists())
|
||||
{
|
||||
this.configOverrideReminderPrinted = true;
|
||||
|
||||
// remind the user that this is a development build
|
||||
MC.sendChatMessage(ModInfo.READABLE_NAME + " experimental build " + ModInfo.VERSION);
|
||||
MC.sendChatMessage("You are running an unsupported version of Distant Horizons!");
|
||||
MC.sendChatMessage("Distant Horizons nightly/unstable build, version: [" + ModInfo.VERSION+"].");
|
||||
MC.sendChatMessage("Issues may occur with this version.");
|
||||
MC.sendChatMessage("Here be dragons!");
|
||||
MC.sendChatMessage("");
|
||||
}
|
||||
|
||||
// generic messages
|
||||
while (!this.chatMessageQueueForNextFrame.isEmpty())
|
||||
{
|
||||
String message = this.chatMessageQueueForNextFrame.poll();
|
||||
if (message == null)
|
||||
{
|
||||
// done to prevent potential null pointers
|
||||
message = "";
|
||||
}
|
||||
MC.sendChatMessage(message);
|
||||
}
|
||||
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
@@ -547,7 +563,7 @@ public class ClientApi
|
||||
|
||||
if (!renderingDeferredLayer)
|
||||
{
|
||||
if (Config.Client.Advanced.Debugging.rendererMode.get() == ERendererMode.DEFAULT)
|
||||
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
|
||||
{
|
||||
this.renderingCancelledForThisFrame = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderEvent.class, renderEventParam);
|
||||
if (!this.renderingCancelledForThisFrame)
|
||||
@@ -560,7 +576,7 @@ public class ClientApi
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, renderEventParam);
|
||||
}
|
||||
}
|
||||
else if (Config.Client.Advanced.Debugging.rendererMode.get() == ERendererMode.DEBUG)
|
||||
else if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEBUG)
|
||||
{
|
||||
profiler.push("Render Debug");
|
||||
ClientApi.testRenderer.render();
|
||||
@@ -617,12 +633,12 @@ public class ClientApi
|
||||
|
||||
if (glfwKey == GLFW.GLFW_KEY_F8)
|
||||
{
|
||||
Config.Client.Advanced.Debugging.debugRendering.set(EDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get()));
|
||||
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());
|
||||
}
|
||||
else if (glfwKey == GLFW.GLFW_KEY_F6)
|
||||
{
|
||||
Config.Client.Advanced.Debugging.rendererMode.set(ERendererMode.next(Config.Client.Advanced.Debugging.rendererMode.get()));
|
||||
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());
|
||||
}
|
||||
else if (glfwKey == GLFW.GLFW_KEY_P)
|
||||
@@ -632,5 +648,10 @@ public class ClientApi
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues the given message to appear in chat the next valid frame.
|
||||
* Useful for queueing up messages that may be triggered before the user has loaded into the world.
|
||||
*/
|
||||
public void showChatMessageNextFrame(String chatMessage) { this.chatMessageQueueForNextFrame.add(chatMessage); }
|
||||
|
||||
}
|
||||
|
||||
@@ -38,11 +38,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* This holds the methods that should be called by the host mod loader (Fabric,
|
||||
* Forge, etc.). Specifically server events.
|
||||
|
||||
@@ -32,7 +32,7 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.Pair;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPools;
|
||||
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.IMinecraftRenderWrapper;
|
||||
@@ -46,6 +46,7 @@ import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/** Contains code and variables used by both {@link ClientApi} and {@link ServerApi} */
|
||||
@@ -102,13 +103,15 @@ public class SharedApi
|
||||
// access the MC level at inappropriate times, which can cause exceptions
|
||||
if (currentWorld != null)
|
||||
{
|
||||
ThreadPools.setupThreadPools();
|
||||
ThreadPoolUtil.setupThreadPools();
|
||||
}
|
||||
else
|
||||
{
|
||||
ThreadPools.shutdownThreadPools();
|
||||
ThreadPoolUtil.shutdownThreadPools();
|
||||
DebugRenderer.clearRenderables();
|
||||
MC_RENDER.clearTargetFrameBuffer();
|
||||
// needs to be closed on world shutdown to clear out un-processed chunks
|
||||
UPDATING_CHUNK_POS_SET.clear();
|
||||
|
||||
// recommend that the garbage collector cleans up any objects from the old world and thread pools
|
||||
System.gc();
|
||||
@@ -285,77 +288,81 @@ public class SharedApi
|
||||
private static void bakeChunkLightingAndSendToLevelAsync(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 = ThreadPools.getLightPopulatorExecutor();
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getLightPopulatorExecutor();
|
||||
if (executor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
executor.execute(() ->
|
||||
try
|
||||
{
|
||||
LOGGER.trace(chunkWrapper.getChunkPos() + " " + executor.getActiveCount() + " / " + executor.getQueue().size() + " - " + executor.getCompletedTaskCount());
|
||||
|
||||
try
|
||||
executor.execute(() ->
|
||||
{
|
||||
// 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())
|
||||
//LOGGER.trace(chunkWrapper.getChunkPos() + " " + executor.getActiveCount() + " / " + executor.getQueue().size() + " - " + executor.getCompletedTaskCount());
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
// 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())
|
||||
{
|
||||
// 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
|
||||
|
||||
ArrayList<IChunkWrapper> nearbyChunkList;
|
||||
if (neighbourChunkList != null)
|
||||
{
|
||||
nearbyChunkList = neighbourChunkList;
|
||||
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
|
||||
{
|
||||
nearbyChunkList = new ArrayList<>(1);
|
||||
nearbyChunkList.add(chunkWrapper);
|
||||
// generate the chunk's lighting, using neighboring chunks if present
|
||||
|
||||
ArrayList<IChunkWrapper> nearbyChunkList;
|
||||
if (neighbourChunkList != null)
|
||||
{
|
||||
nearbyChunkList = neighbourChunkList;
|
||||
}
|
||||
else
|
||||
{
|
||||
nearbyChunkList = new ArrayList<>(1);
|
||||
nearbyChunkList.add(chunkWrapper);
|
||||
}
|
||||
|
||||
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? 15 : 0);
|
||||
}
|
||||
|
||||
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? 15 : 0);
|
||||
dhLevel.updateChunkAsync(chunkWrapper);
|
||||
}
|
||||
|
||||
dhLevel.updateChunkAsync(chunkWrapper);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Unexpected error when updating chunk at pos: ["+chunkWrapper.getChunkPos()+"]", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// the LOD chunk has finished being updated
|
||||
int updateTimeoutInSec = Config.Client.Advanced.LodBuilding.minTimeBetweenChunkUpdatesInSeconds.get();
|
||||
if (updateTimeoutInSec != 0)
|
||||
catch (Exception e)
|
||||
{
|
||||
// prevent updating this chunk again until the timeout finishes
|
||||
CHUNK_UPDATE_TIMER.schedule(new TimerTask()
|
||||
LOGGER.error("Unexpected error when updating chunk at pos: [" + chunkWrapper.getChunkPos() + "]", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// the LOD chunk has finished being updated
|
||||
int updateTimeoutInSec = Config.Client.Advanced.LodBuilding.minTimeBetweenChunkUpdatesInSeconds.get();
|
||||
if (updateTimeoutInSec != 0)
|
||||
{
|
||||
@Override
|
||||
public void run() { UPDATING_CHUNK_POS_SET.remove(chunkWrapper.getChunkPos()); }
|
||||
}, updateTimeoutInSec * 1000L);
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
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 */ }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -37,7 +37,9 @@ import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
@@ -72,8 +74,8 @@ public class Config
|
||||
|
||||
public static ConfigLinkedEntry quickLodChunkRenderDistance = new ConfigLinkedEntry(Advanced.Graphics.Quality.lodChunkRenderDistanceRadius);
|
||||
|
||||
public static ConfigEntry<EQualityPreset> qualityPresetSetting = new ConfigEntry.Builder<EQualityPreset>()
|
||||
.set(EQualityPreset.MEDIUM) // the default value is set via the listener when accessed
|
||||
public static ConfigEntry<EDhApiQualityPreset> qualityPresetSetting = new ConfigEntry.Builder<EDhApiQualityPreset>()
|
||||
.set(EDhApiQualityPreset.MEDIUM) // the default value is set via the listener when accessed
|
||||
.comment(""
|
||||
+ "Changing this setting will modify a number of different settings that will change the \n"
|
||||
+ "visual fidelity of the rendered LODs.\n"
|
||||
@@ -84,8 +86,8 @@ public class Config
|
||||
.addListener(RenderQualityPresetConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EThreadPreset> threadPresetSetting = new ConfigEntry.Builder<EThreadPreset>()
|
||||
.set(EThreadPreset.LOW_IMPACT) // the default value is set via the listener when accessed
|
||||
public static ConfigEntry<EDhApiThreadPreset> threadPresetSetting = new ConfigEntry.Builder<EDhApiThreadPreset>()
|
||||
.set(EDhApiThreadPreset.LOW_IMPACT) // the default value is set via the listener when accessed
|
||||
.comment(""
|
||||
+ "Changing this setting will modify a number of different settings that will change \n"
|
||||
+ "the load that Distant Horizons is allowed to put on your CPU. \n"
|
||||
@@ -138,20 +140,20 @@ public class Config
|
||||
|
||||
public static class Quality
|
||||
{
|
||||
public static ConfigEntry<EMaxHorizontalResolution> maxHorizontalResolution = new ConfigEntry.Builder<EMaxHorizontalResolution>()
|
||||
.set(EMaxHorizontalResolution.BLOCK)
|
||||
public static ConfigEntry<EDhApiMaxHorizontalResolution> maxHorizontalResolution = new ConfigEntry.Builder<EDhApiMaxHorizontalResolution>()
|
||||
.set(EDhApiMaxHorizontalResolution.BLOCK)
|
||||
.comment(""
|
||||
+ "What is the maximum detail LODs should be drawn at? \n"
|
||||
+ "Higher settings will increase memory and GPU usage. \n"
|
||||
+ "\n"
|
||||
+ EMaxHorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
|
||||
+ EMaxHorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
|
||||
+ EMaxHorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
|
||||
+ EMaxHorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
|
||||
+ EMaxHorizontalResolution.BLOCK + ": render 256 LODs for each Chunk (width of one block). \n"
|
||||
+ EDhApiMaxHorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
|
||||
+ EDhApiMaxHorizontalResolution.BLOCK + ": render 256 LODs for each Chunk (width of one block). \n"
|
||||
+ "\n"
|
||||
+ "Lowest Quality: " + EMaxHorizontalResolution.CHUNK + "\n"
|
||||
+ "Highest Quality: " + EMaxHorizontalResolution.BLOCK)
|
||||
+ "Lowest Quality: " + EDhApiMaxHorizontalResolution.CHUNK + "\n"
|
||||
+ "Highest Quality: " + EDhApiMaxHorizontalResolution.BLOCK)
|
||||
.setPerformance(EConfigEntryPerformance.MEDIUM)
|
||||
.build();
|
||||
|
||||
@@ -161,21 +163,21 @@ public class Config
|
||||
.setPerformance(EConfigEntryPerformance.HIGH)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EVerticalQuality> verticalQuality = new ConfigEntry.Builder<EVerticalQuality>()
|
||||
.set(EVerticalQuality.MEDIUM)
|
||||
public static ConfigEntry<EDhApiVerticalQuality> verticalQuality = new ConfigEntry.Builder<EDhApiVerticalQuality>()
|
||||
.set(EDhApiVerticalQuality.MEDIUM)
|
||||
.comment(""
|
||||
+ "This indicates how well LODs will represent \n"
|
||||
+ "overhangs, caves, floating islands, etc. \n"
|
||||
+ "Higher options will make the world more accurate, but"
|
||||
+ "will increase memory and GPU usage. \n"
|
||||
+ "\n"
|
||||
+ "Lowest Quality: " + EVerticalQuality.HEIGHT_MAP + "\n"
|
||||
+ "Highest Quality: " + EVerticalQuality.EXTREME)
|
||||
+ "Lowest Quality: " + EDhApiVerticalQuality.HEIGHT_MAP + "\n"
|
||||
+ "Highest Quality: " + EDhApiVerticalQuality.EXTREME)
|
||||
.setPerformance(EConfigEntryPerformance.VERY_HIGH)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EHorizontalQuality> horizontalQuality = new ConfigEntry.Builder<EHorizontalQuality>()
|
||||
.set(EHorizontalQuality.MEDIUM)
|
||||
public static ConfigEntry<EDhApiHorizontalQuality> horizontalQuality = new ConfigEntry.Builder<EDhApiHorizontalQuality>()
|
||||
.set(EDhApiHorizontalQuality.MEDIUM)
|
||||
.comment(""
|
||||
+ "This indicates how quickly LODs decrease in quality the further away they are. \n"
|
||||
+ "Higher settings will render higher quality fake chunks farther away, \n"
|
||||
@@ -183,25 +185,25 @@ public class Config
|
||||
.setPerformance(EConfigEntryPerformance.MEDIUM)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<ETransparency> transparency = new ConfigEntry.Builder<ETransparency>()
|
||||
.set(ETransparency.COMPLETE)
|
||||
public static ConfigEntry<EDhApiTransparency> transparency = new ConfigEntry.Builder<EDhApiTransparency>()
|
||||
.set(EDhApiTransparency.COMPLETE)
|
||||
.comment(""
|
||||
+ "How should LOD transparency be handled. \n"
|
||||
+ "\n"
|
||||
+ ETransparency.COMPLETE + ": LODs will render transparent. \n"
|
||||
+ ETransparency.FAKE + ": LODs will be opaque, but shaded to match the blocks underneath. \n"
|
||||
+ ETransparency.DISABLED + ": LODs will be opaque. \n"
|
||||
+ EDhApiTransparency.COMPLETE + ": LODs will render transparent. \n"
|
||||
+ EDhApiTransparency.FAKE + ": LODs will be opaque, but shaded to match the blocks underneath. \n"
|
||||
+ EDhApiTransparency.DISABLED + ": LODs will be opaque. \n"
|
||||
+ "")
|
||||
.setPerformance(EConfigEntryPerformance.MEDIUM)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EBlocksToAvoid> blocksToIgnore = new ConfigEntry.Builder<EBlocksToAvoid>()
|
||||
.set(EBlocksToAvoid.NON_COLLIDING)
|
||||
public static ConfigEntry<EDhApiBlocksToAvoid> blocksToIgnore = new ConfigEntry.Builder<EDhApiBlocksToAvoid>()
|
||||
.set(EDhApiBlocksToAvoid.NON_COLLIDING)
|
||||
.comment(""
|
||||
+ "What blocks shouldn't be rendered as LODs? \n"
|
||||
+ "\n"
|
||||
+ EBlocksToAvoid.NONE + ": Represent all blocks in the LODs \n"
|
||||
+ EBlocksToAvoid.NON_COLLIDING + ": Only represent solid blocks in the LODs (tall grass, torches, etc. won't count for a LOD's height) \n"
|
||||
+ EDhApiBlocksToAvoid.NONE + ": Represent all blocks in the LODs \n"
|
||||
+ 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)
|
||||
.build();
|
||||
@@ -234,33 +236,27 @@ public class Config
|
||||
|
||||
public static class Fog
|
||||
{
|
||||
public static ConfigEntry<EFogDrawMode> drawMode = new ConfigEntry.Builder<EFogDrawMode>()
|
||||
.set(EFogDrawMode.FOG_ENABLED)
|
||||
public static ConfigEntry<EDhApiFogDrawMode> drawMode = new ConfigEntry.Builder<EDhApiFogDrawMode>()
|
||||
.set(EDhApiFogDrawMode.FOG_ENABLED)
|
||||
.comment(""
|
||||
+ "When should fog be drawn? \n"
|
||||
+ "\n"
|
||||
+ EFogDrawMode.USE_OPTIFINE_SETTING + ": Use whatever Fog setting Optifine is using.\n"
|
||||
+ "If Optifine isn't installed this defaults to " + EFogDrawMode.FOG_ENABLED + ". \n"
|
||||
+ EFogDrawMode.FOG_ENABLED + ": Never draw fog on the LODs \n"
|
||||
+ EFogDrawMode.FOG_DISABLED + ": Always draw fast fog on the LODs \n"
|
||||
+ EDhApiFogDrawMode.USE_OPTIFINE_SETTING + ": Use whatever Fog setting Optifine is using.\n"
|
||||
+ "If Optifine isn't installed this defaults to " + EDhApiFogDrawMode.FOG_ENABLED + ". \n"
|
||||
+ EDhApiFogDrawMode.FOG_ENABLED + ": Never draw fog on the LODs \n"
|
||||
+ EDhApiFogDrawMode.FOG_DISABLED + ": Always draw fast fog on the LODs \n"
|
||||
+ "\n"
|
||||
+ "Disabling fog will improve GPU performance.")
|
||||
.setPerformance(EConfigEntryPerformance.VERY_LOW)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EFogDistance> distance = new ConfigEntry.Builder<EFogDistance>()
|
||||
.set(EFogDistance.FAR)
|
||||
.comment("At what distance should Fog be drawn on the LODs?")
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EFogColorMode> colorMode = new ConfigEntry.Builder<EFogColorMode>()
|
||||
.set(EFogColorMode.USE_WORLD_FOG_COLOR)
|
||||
public static ConfigEntry<EDhApiFogColorMode> colorMode = new ConfigEntry.Builder<EDhApiFogColorMode>()
|
||||
.set(EDhApiFogColorMode.USE_WORLD_FOG_COLOR)
|
||||
.comment(""
|
||||
+ "What color should fog use? \n"
|
||||
+ "\n"
|
||||
+ EFogColorMode.USE_WORLD_FOG_COLOR + ": Use the world's fog color. \n"
|
||||
+ EFogColorMode.USE_SKY_COLOR + ": Use the sky's color.")
|
||||
+ EDhApiFogColorMode.USE_WORLD_FOG_COLOR + ": Use the world's fog color. \n"
|
||||
+ EDhApiFogColorMode.USE_SKY_COLOR + ": Use the sky's color.")
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.build();
|
||||
|
||||
@@ -320,14 +316,14 @@ public class Config
|
||||
+ "1.0: Fully opaque fog.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EFogFalloff> farFogFalloff = new ConfigEntry.Builder<EFogFalloff>()
|
||||
.set(EFogFalloff.EXPONENTIAL_SQUARED)
|
||||
public static ConfigEntry<EDhApiFogFalloff> farFogFalloff = new ConfigEntry.Builder<EDhApiFogFalloff>()
|
||||
.set(EDhApiFogFalloff.EXPONENTIAL_SQUARED)
|
||||
.comment(""
|
||||
+ "How should the fog thickness should be calculated? \n"
|
||||
+ "\n"
|
||||
+ EFogFalloff.LINEAR + ": Linear based on distance (will ignore 'density')\n"
|
||||
+ EFogFalloff.EXPONENTIAL + ": 1/(e^(distance*density)) \n"
|
||||
+ EFogFalloff.EXPONENTIAL_SQUARED + ": 1/(e^((distance*density)^2)")
|
||||
+ EDhApiFogFalloff.LINEAR + ": Linear based on distance (will ignore 'density')\n"
|
||||
+ EDhApiFogFalloff.EXPONENTIAL + ": 1/(e^(distance*density)) \n"
|
||||
+ EDhApiFogFalloff.EXPONENTIAL_SQUARED + ": 1/(e^((distance*density)^2)")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Double> farFogDensity = new ConfigEntry.Builder<Double>()
|
||||
@@ -343,37 +339,37 @@ public class Config
|
||||
|
||||
public static class HeightFog
|
||||
{
|
||||
public static ConfigEntry<EHeightFogMixMode> heightFogMixMode = new ConfigEntry.Builder<EHeightFogMixMode>()
|
||||
.set(EHeightFogMixMode.BASIC)
|
||||
public static ConfigEntry<EDhApiHeightFogMixMode> heightFogMixMode = new ConfigEntry.Builder<EDhApiHeightFogMixMode>()
|
||||
.set(EDhApiHeightFogMixMode.BASIC)
|
||||
.comment(""
|
||||
+ "How should height effect the fog thickness? \n"
|
||||
+ "Note: height fog is combined with the other fog settings. \n"
|
||||
+ "\n"
|
||||
+ EHeightFogMixMode.BASIC + ": No special height fog effect. Fog is calculated based on camera distance \n"
|
||||
+ EHeightFogMixMode.IGNORE_HEIGHT + ": Ignore height completely. Fog is only calculated with horizontal distance \n"
|
||||
+ EHeightFogMixMode.ADDITION + ": heightFog + farFog \n"
|
||||
+ EHeightFogMixMode.MAX + ": max(heightFog, farFog) \n"
|
||||
+ EHeightFogMixMode.MULTIPLY + ": heightFog * farFog \n"
|
||||
+ EHeightFogMixMode.INVERSE_MULTIPLY + ": 1 - (1-heightFog) * (1-farFog) \n"
|
||||
+ EHeightFogMixMode.LIMITED_ADDITION + ": farFog + max(farFog, heightFog) \n"
|
||||
+ EHeightFogMixMode.MULTIPLY_ADDITION + ": farFog + farFog * heightFog \n"
|
||||
+ EHeightFogMixMode.INVERSE_MULTIPLY_ADDITION + ": farFog + 1 - (1-heightFog) * (1-farFog) \n"
|
||||
+ EHeightFogMixMode.AVERAGE + ": farFog*0.5 + heightFog*0.5 \n"
|
||||
+ EDhApiHeightFogMixMode.BASIC + ": No special height fog effect. Fog is calculated based on camera distance \n"
|
||||
+ EDhApiHeightFogMixMode.IGNORE_HEIGHT + ": Ignore height completely. Fog is only calculated with horizontal distance \n"
|
||||
+ EDhApiHeightFogMixMode.ADDITION + ": heightFog + farFog \n"
|
||||
+ EDhApiHeightFogMixMode.MAX + ": max(heightFog, farFog) \n"
|
||||
+ EDhApiHeightFogMixMode.MULTIPLY + ": heightFog * farFog \n"
|
||||
+ EDhApiHeightFogMixMode.INVERSE_MULTIPLY + ": 1 - (1-heightFog) * (1-farFog) \n"
|
||||
+ EDhApiHeightFogMixMode.LIMITED_ADDITION + ": farFog + max(farFog, heightFog) \n"
|
||||
+ EDhApiHeightFogMixMode.MULTIPLY_ADDITION + ": farFog + farFog * heightFog \n"
|
||||
+ EDhApiHeightFogMixMode.INVERSE_MULTIPLY_ADDITION + ": farFog + 1 - (1-heightFog) * (1-farFog) \n"
|
||||
+ EDhApiHeightFogMixMode.AVERAGE + ": farFog*0.5 + heightFog*0.5 \n"
|
||||
+ "\n"
|
||||
+ "Note: height fog settings are ignored if '" + EHeightFogMixMode.BASIC + "' or '" + EHeightFogMixMode.IGNORE_HEIGHT + "' are selected.")
|
||||
+ "Note: height fog settings are ignored if '" + EDhApiHeightFogMixMode.BASIC + "' or '" + EDhApiHeightFogMixMode.IGNORE_HEIGHT + "' are selected.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EHeightFogMode> heightFogMode = new ConfigEntry.Builder<EHeightFogMode>()
|
||||
.set(EHeightFogMode.ABOVE_AND_BELOW_CAMERA)
|
||||
public static ConfigEntry<EDhApiHeightFogMode> heightFogMode = new ConfigEntry.Builder<EDhApiHeightFogMode>()
|
||||
.set(EDhApiHeightFogMode.ABOVE_AND_BELOW_CAMERA)
|
||||
.comment(""
|
||||
+ "Where should the height fog start? \n"
|
||||
+ "\n"
|
||||
+ EHeightFogMode.ABOVE_CAMERA + ": Height fog starts at the camera and goes towards the sky \n"
|
||||
+ EHeightFogMode.BELOW_CAMERA + ": Height fog starts at the camera and goes towards the void \n"
|
||||
+ EHeightFogMode.ABOVE_AND_BELOW_CAMERA + ": Height fog starts from the camera to goes towards both the sky and void \n"
|
||||
+ EHeightFogMode.ABOVE_SET_HEIGHT + ": Height fog starts from a set height and goes towards the sky \n"
|
||||
+ EHeightFogMode.BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards the void \n"
|
||||
+ EHeightFogMode.ABOVE_AND_BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards both the sky and void")
|
||||
+ EDhApiHeightFogMode.ABOVE_CAMERA + ": Height fog starts at the camera and goes towards the sky \n"
|
||||
+ EDhApiHeightFogMode.BELOW_CAMERA + ": Height fog starts at the camera and goes towards the void \n"
|
||||
+ EDhApiHeightFogMode.ABOVE_AND_BELOW_CAMERA + ": Height fog starts from the camera to goes towards both the sky and void \n"
|
||||
+ EDhApiHeightFogMode.ABOVE_SET_HEIGHT + ": Height fog starts from a set height and goes towards the sky \n"
|
||||
+ EDhApiHeightFogMode.BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards the void \n"
|
||||
+ EDhApiHeightFogMode.ABOVE_AND_BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards both the sky and void")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Double> heightFogBaseHeight = new ConfigEntry.Builder<Double>()
|
||||
@@ -417,14 +413,14 @@ public class Config
|
||||
+ "1.0: Fully opaque fog.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EFogFalloff> heightFogFalloff = new ConfigEntry.Builder<EFogFalloff>()
|
||||
.set(EFogFalloff.EXPONENTIAL_SQUARED)
|
||||
public static ConfigEntry<EDhApiFogFalloff> heightFogFalloff = new ConfigEntry.Builder<EDhApiFogFalloff>()
|
||||
.set(EDhApiFogFalloff.EXPONENTIAL_SQUARED)
|
||||
.comment(""
|
||||
+ "How should the height fog thickness should be calculated? \n"
|
||||
+ "\n"
|
||||
+ EFogFalloff.LINEAR + ": Linear based on height (will ignore 'density')\n"
|
||||
+ EFogFalloff.EXPONENTIAL + ": 1/(e^(height*density)) \n"
|
||||
+ EFogFalloff.EXPONENTIAL_SQUARED + ": 1/(e^((height*density)^2)")
|
||||
+ EDhApiFogFalloff.LINEAR + ": Linear based on height (will ignore 'density')\n"
|
||||
+ EDhApiFogFalloff.EXPONENTIAL + ": 1/(e^(height*density)) \n"
|
||||
+ EDhApiFogFalloff.EXPONENTIAL_SQUARED + ": 1/(e^((height*density)^2)")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Double> heightFogDensity = new ConfigEntry.Builder<Double>()
|
||||
@@ -534,18 +530,6 @@ public class Config
|
||||
|
||||
public static class AdvancedGraphics
|
||||
{
|
||||
/**
|
||||
* @deprecated Use overdrawPrevention instead, will be removed when DH updates to MC 1.21 <br>
|
||||
* After removal a float value will be used to control overdraw instead. <br>
|
||||
*/
|
||||
@Deprecated
|
||||
public static ConfigEntry<EOverdrawPrevention> overdrawPreventionPreset = new ConfigEntry.Builder<EOverdrawPrevention>()
|
||||
.set(EOverdrawPrevention.MEDIUM)
|
||||
.comment("")
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_API)
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Double> overdrawPrevention = new ConfigEntry.Builder<Double>()
|
||||
.setMinDefaultMax(0.0, 0.4, 1.0)
|
||||
.comment(""
|
||||
@@ -561,21 +545,6 @@ public class Config
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.build();
|
||||
|
||||
@Deprecated // TODO remove failed experiment
|
||||
public static ConfigEntry<Boolean> seamlessOverdraw = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment(""
|
||||
+ "Buggy experimental option that will attempt to match up \n"
|
||||
+ "Distant Horizons' and Minecraft's near/far clip planes, \n"
|
||||
+ "reducing overdraw. \n"
|
||||
+ "\n"
|
||||
+ "Only functional on Fabric.\n"
|
||||
+ "Works best with an overdraw prevention setting of " + EOverdrawPrevention.MEDIUM + " or higher \n"
|
||||
+ " and cave culling is disabled. \n"
|
||||
+ "")
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.build();
|
||||
|
||||
// move into "shader compatibility"
|
||||
public static ConfigEntry<Double> brightnessMultiplier = new ConfigEntry.Builder<Double>() // TODO: Make this a float (the ClassicConfigGUI doesnt support floats)
|
||||
.set(1.0)
|
||||
@@ -641,15 +610,15 @@ public class Config
|
||||
+ "If set to 0 the mod wont overwrite vanilla's default (which so happens to also be 0)")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<ELodShading> lodShading = new ConfigEntry.Builder<ELodShading>()
|
||||
.set(ELodShading.MINECRAFT)
|
||||
public static ConfigEntry<EDhApiLodShading> lodShading = new ConfigEntry.Builder<EDhApiLodShading>()
|
||||
.set(EDhApiLodShading.AUTO)
|
||||
.comment(""
|
||||
+ "How should LODs be shaded? \n"
|
||||
+ "\n"
|
||||
+ ELodShading.MINECRAFT + ": Uses the same side shading as vanilla Minecraft blocks. \n"
|
||||
+ ELodShading.OLD_LIGHTING + ": Simulates Minecraft's block shading for LODs. \n"
|
||||
+ EDhApiLodShading.AUTO + ": Uses the same side shading as vanilla Minecraft blocks. \n"
|
||||
+ EDhApiLodShading.ENABLED + ": Simulates Minecraft's block shading for LODs. \n"
|
||||
+ " Can be used to force LOD shading when using some shaders. \n"
|
||||
+ ELodShading.NONE + ": All LOD sides will be rendered with the same brightness. \n"
|
||||
+ EDhApiLodShading.DISABLED + ": All LOD sides will be rendered with the same brightness. \n"
|
||||
+ "")
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.build();
|
||||
@@ -676,6 +645,17 @@ public class Config
|
||||
+ "Disable this if shadows render incorrectly.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiGrassSideRendering> grassSideRendering = new ConfigEntry.Builder<EDhApiGrassSideRendering>()
|
||||
.set(EDhApiGrassSideRendering.FADE_TO_DIRT)
|
||||
.comment(""
|
||||
+ "How should the sides and bottom of grass block LODs render? \n"
|
||||
+ "\n"
|
||||
+ EDhApiGrassSideRendering.AS_GRASS + ": all sides of dirt LOD's render using the top (green) color. \n"
|
||||
+ EDhApiGrassSideRendering.FADE_TO_DIRT + ": sides fade from grass to dirt. \n"
|
||||
+ EDhApiGrassSideRendering.AS_DIRT + ": sides render entirely as dirt. \n"
|
||||
+ "")
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@@ -743,36 +723,6 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
// deprecated and not implemented, can be made public if we ever re-implement it
|
||||
@Deprecated
|
||||
private static ConfigEntry<EGenerationPriority> generationPriority = new ConfigEntry.Builder<EGenerationPriority>()
|
||||
.set(EGenerationPriority.NEAR_FIRST)
|
||||
.comment(""
|
||||
+ "In what priority should fake chunks be generated outside the vanilla render distance? \n"
|
||||
+ "\n"
|
||||
+ EGenerationPriority.FAR_FIRST + " \n"
|
||||
+ "Fake chunks are generated from lowest to highest detail \n"
|
||||
+ " with a priority for far away regions. \n"
|
||||
+ "This fills in the world fastest, but you will have large low detail \n"
|
||||
+ " blocks for a while while the generation happens. \n"
|
||||
+ "\n"
|
||||
+ EGenerationPriority.NEAR_FIRST + " \n"
|
||||
+ "Fake chunks are generated around the player \n"
|
||||
+ " in a spiral, similar to vanilla minecraft. \n"
|
||||
+ "Best used when on a server since we can't generate \n"
|
||||
+ " fake chunks. \n"
|
||||
+ "\n"
|
||||
+ EGenerationPriority.BALANCED + " \n"
|
||||
+ "A mix between " + EGenerationPriority.NEAR_FIRST + "and" + EGenerationPriority.FAR_FIRST + ". \n"
|
||||
+ "First prioritise completing nearby highest detail chunks, \n"
|
||||
+ " then focus on filling in the low detail areas away from the player. \n"
|
||||
+ "\n"
|
||||
+ EGenerationPriority.AUTO + " \n"
|
||||
+ "Uses " + EGenerationPriority.BALANCED + " when on a single player world \n"
|
||||
+ " and " + EGenerationPriority.NEAR_FIRST + " when connected to a server.")
|
||||
.setPerformance(EConfigEntryPerformance.NONE)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
public static class LodBuilding
|
||||
@@ -801,19 +751,75 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiDataCompressionMode> dataCompression = new ConfigEntry.Builder<EDhApiDataCompressionMode>()
|
||||
.set(EDhApiDataCompressionMode.LZMA2)
|
||||
.comment(""
|
||||
+ "What algorithm should be used to compress new LOD data? \n"
|
||||
+ "This setting will only affect new or updated LOD data, \n"
|
||||
+ "any data already generated when this setting is changed will be\n"
|
||||
+ "unaffected until it needs to be re-written to the database.\n"
|
||||
+ "\n"
|
||||
+ EDhApiDataCompressionMode.UNCOMPRESSED + " \n"
|
||||
+ "Should only be used for testing, is worse in every way vs ["+EDhApiDataCompressionMode.LZ4+"].\n"
|
||||
+ "Expected Compression Ratio: 1.0\n"
|
||||
+ "Estimated average DTO read speed: 1.64 milliseconds\n"
|
||||
+ "Estimated average DTO write speed: 12.44 milliseconds\n"
|
||||
+ "\n"
|
||||
+ EDhApiDataCompressionMode.LZ4 + " \n"
|
||||
+ "A good option if you're CPU limited and have plenty of hard drive space.\n"
|
||||
+ "Expected Compression Ratio: 0.36\n"
|
||||
+ "Estimated average DTO read speed: 1.85 ms\n"
|
||||
+ "Estimated average DTO write speed: 9.46 ms\n"
|
||||
+ "\n"
|
||||
+ EDhApiDataCompressionMode.LZMA2 + " \n"
|
||||
+ "Slow but very good compression.\n"
|
||||
+ "Expected Compression Ratio: 0.14\n"
|
||||
+ "Estimated average DTO read speed: 11.89 ms\n"
|
||||
+ "Estimated average DTO write speed: 192.01 ms\n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiWorldCompressionMode> worldCompression = new ConfigEntry.Builder<EDhApiWorldCompressionMode>()
|
||||
.set(EDhApiWorldCompressionMode.VISUALLY_EQUAL)
|
||||
.comment(""
|
||||
+ "How should block data be compressed when creating LOD data? \n"
|
||||
+ "This setting will only affect new or updated LOD data, \n"
|
||||
+ "any data already generated when this setting is changed will be\n"
|
||||
+ "unaffected until it is modified or re-loaded.\n"
|
||||
+ "\n"
|
||||
+ EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS + " \n"
|
||||
+ "Every block/biome change is recorded in the database. \n"
|
||||
+ "This is what DH 2.0 and 2.0.1 all used by default and will store a lot of data. \n"
|
||||
+ "Expected Compression Ratio: 1.0\n"
|
||||
+ "\n"
|
||||
+ EDhApiWorldCompressionMode.VISUALLY_EQUAL + " \n"
|
||||
+ "Only visible block/biome changes are recorded in the database. \n"
|
||||
+ "Hidden blocks (IE ores) are ignored. \n"
|
||||
+ "Expected Compression Ratio: 0.7\n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> showMigrationChatWarning = new ConfigEntry.Builder<Boolean>()
|
||||
.set(true)
|
||||
.comment(""
|
||||
+ "Determines if a message should be displayed in the chat when LOD migration starts. \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
public static class Multiplayer
|
||||
{
|
||||
public static ConfigEntry<EServerFolderNameMode> serverFolderNameMode = new ConfigEntry.Builder<EServerFolderNameMode>()
|
||||
.set(EServerFolderNameMode.NAME_ONLY)
|
||||
public static ConfigEntry<EDhApiServerFolderNameMode> serverFolderNameMode = new ConfigEntry.Builder<EDhApiServerFolderNameMode>()
|
||||
.set(EDhApiServerFolderNameMode.NAME_ONLY)
|
||||
.comment(""
|
||||
+ "How should multiplayer save folders should be named? \n"
|
||||
+ "\n"
|
||||
+ EServerFolderNameMode.NAME_ONLY + ": Example: \"Minecraft Server\" \n"
|
||||
+ EServerFolderNameMode.NAME_IP + ": Example: \"Minecraft Server IP 192.168.1.40\" \n"
|
||||
+ EServerFolderNameMode.NAME_IP_PORT + ": Example: \"Minecraft Server IP 192.168.1.40:25565\""
|
||||
+ EServerFolderNameMode.NAME_IP_PORT_MC_VERSION + ": Example: \"Minecraft Server IP 192.168.1.40:25565 GameVersion 1.16.5\"")
|
||||
+ EDhApiServerFolderNameMode.NAME_ONLY + ": Example: \"Minecraft Server\" \n"
|
||||
+ EDhApiServerFolderNameMode.IP_ONLY + ": Example: \"192.168.1.40\" \n"
|
||||
+ EDhApiServerFolderNameMode.NAME_IP + ": Example: \"Minecraft Server IP 192.168.1.40\" \n"
|
||||
+ EDhApiServerFolderNameMode.NAME_IP_PORT + ": Example: \"Minecraft Server IP 192.168.1.40:25565\""
|
||||
+ EDhApiServerFolderNameMode.NAME_IP_PORT_MC_VERSION + ": Example: \"Minecraft Server IP 192.168.1.40:25565 GameVersion 1.16.5\"")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Double> multiverseSimilarityRequiredPercent = new ConfigEntry.Builder<Double>()
|
||||
@@ -907,7 +913,7 @@ public class Config
|
||||
ThreadPresetConfigEventHandler.getFileHandlerDefaultThreadCount(),
|
||||
Runtime.getRuntime().availableProcessors())
|
||||
.comment(""
|
||||
+ "How many threads should be used when reading in LOD data from disk? \n"
|
||||
+ "How many threads should be used when reading/writing LOD data to/from disk? \n"
|
||||
+ "\n"
|
||||
+ "Increasing this number will cause LODs to load in faster, \n"
|
||||
+ "but may cause lag when loading a new world or when \n"
|
||||
@@ -920,6 +926,31 @@ public class Config
|
||||
.comment(THREAD_RUN_TIME_RATIO_NOTE)
|
||||
.build();
|
||||
|
||||
public static final ConfigEntry<Integer> numberOfUpdatePropagatorThreads = new ConfigEntry.Builder<Integer>()
|
||||
.setMinDefaultMax(1,
|
||||
ThreadPresetConfigEventHandler.getUpdatePropagatorDefaultThreadCount(),
|
||||
Runtime.getRuntime().availableProcessors())
|
||||
.comment(""
|
||||
+ "How many threads should be used when applying LOD updates? \n"
|
||||
+ "An LOD update is the operation of down-sampling a high detail LOD \n"
|
||||
+ "into a lower detail one.\n"
|
||||
+ "\n"
|
||||
+ "This config can have a much higher number of threads \n"
|
||||
+ "assigned and much lower run time ratio vs other thread pools \n"
|
||||
+ "because the amount of time any particular thread may run is relatively low.\n"
|
||||
+ "\n"
|
||||
+ "This is because LOD updating only only partially thread safe, \n"
|
||||
+ "so between 40% and 60% of the time a given thread may end up \n"
|
||||
+ "waiting on another thread to finish updating the same LOD it also wants\n"
|
||||
+ "to work on.\n"
|
||||
+ "\n"
|
||||
+ THREAD_NOTE)
|
||||
.build();
|
||||
public static final ConfigEntry<Double> runTimeRatioForUpdatePropagatorThreads = new ConfigEntry.Builder<Double>()
|
||||
.setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getUpdatePropagatorDefaultRunTimeRatio(), 1.0)
|
||||
.comment(THREAD_RUN_TIME_RATIO_NOTE)
|
||||
.build();
|
||||
|
||||
public static final ConfigEntry<Integer> numberOfLodBuilderThreads = new ConfigEntry.Builder<Integer>()
|
||||
.setMinDefaultMax(1,
|
||||
ThreadPresetConfigEventHandler.getLodBuilderDefaultThreadCount(),
|
||||
@@ -950,27 +981,27 @@ public class Config
|
||||
|
||||
public static class GpuBuffers
|
||||
{
|
||||
public static ConfigEntry<EGpuUploadMethod> gpuUploadMethod = new ConfigEntry.Builder<EGpuUploadMethod>()
|
||||
.set(EGpuUploadMethod.AUTO)
|
||||
public static ConfigEntry<EDhApiGpuUploadMethod> gpuUploadMethod = new ConfigEntry.Builder<EDhApiGpuUploadMethod>()
|
||||
.set(EDhApiGpuUploadMethod.AUTO)
|
||||
.comment(""
|
||||
+ "What method should be used to upload geometry to the GPU? \n"
|
||||
+ "\n"
|
||||
+ EGpuUploadMethod.AUTO + ": Picks the best option based on the GPU you have. \n"
|
||||
+ EDhApiGpuUploadMethod.AUTO + ": Picks the best option based on the GPU you have. \n"
|
||||
+ "\n"
|
||||
+ EGpuUploadMethod.BUFFER_STORAGE + ": Default if OpenGL 4.5 is supported. \n"
|
||||
+ EDhApiGpuUploadMethod.BUFFER_STORAGE + ": Default if OpenGL 4.5 is supported. \n"
|
||||
+ " Fast rendering, no stuttering. \n"
|
||||
+ "\n"
|
||||
+ EGpuUploadMethod.SUB_DATA + ": Backup option for NVIDIA. \n"
|
||||
+ EDhApiGpuUploadMethod.SUB_DATA + ": Backup option for NVIDIA. \n"
|
||||
+ " Fast rendering but may stutter when uploading. \n"
|
||||
+ "\n"
|
||||
+ EGpuUploadMethod.BUFFER_MAPPING + ": Slow rendering but won't stutter when uploading. \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"
|
||||
+ EGpuUploadMethod.DATA + ": Fast rendering but will stutter when uploading. \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"
|
||||
@@ -1003,15 +1034,6 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
// deprecated and not implemented, can be made public if we ever re-implement it
|
||||
@Deprecated
|
||||
private static ConfigEntry<EBufferRebuildTimes> rebuildTimes = new ConfigEntry.Builder<EBufferRebuildTimes>()
|
||||
.set(EBufferRebuildTimes.NORMAL)
|
||||
.comment(""
|
||||
+ "How frequently should vertex buffers (geometry) be rebuilt and sent to the GPU? \n"
|
||||
+ "Higher settings may cause stuttering, but will prevent holes in the world")
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
public static class AutoUpdater
|
||||
@@ -1030,9 +1052,9 @@ public class Config
|
||||
+ "Should Distant Horizons silently, automatically download and install new versions?")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EUpdateBranch> updateBranch = new ConfigEntry.Builder<EUpdateBranch>()
|
||||
public static ConfigEntry<EDhApiUpdateBranch> updateBranch = new ConfigEntry.Builder<EDhApiUpdateBranch>()
|
||||
.set(
|
||||
ModInfo.IS_DEV_BUILD? EUpdateBranch.NIGHTLY: EUpdateBranch.STABLE // If it's already a nightly build, then download the nightly build ofc
|
||||
ModInfo.IS_DEV_BUILD? EDhApiUpdateBranch.NIGHTLY: EDhApiUpdateBranch.STABLE // If it's already a nightly build, then download the nightly build ofc
|
||||
)
|
||||
.comment(""
|
||||
+ " If DH should use the nightly (provided by Gitlab), or stable (provided by Modrinth) build")
|
||||
@@ -1043,64 +1065,64 @@ public class Config
|
||||
{
|
||||
// TODO add change all option
|
||||
// TODO default to error chat and info file
|
||||
public static ConfigEntry<ELoggerMode> logWorldGenEvent = new ConfigEntry.Builder<ELoggerMode>()
|
||||
.set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
|
||||
public static ConfigEntry<EDhApiLoggerMode> logWorldGenEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
|
||||
.set(EDhApiLoggerMode.LOG_WARNING_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.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<ELoggerMode> logWorldGenPerformance = new ConfigEntry.Builder<ELoggerMode>()
|
||||
.set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_FILE)
|
||||
public static ConfigEntry<EDhApiLoggerMode> logWorldGenPerformance = new ConfigEntry.Builder<EDhApiLoggerMode>()
|
||||
.set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE)
|
||||
.comment(""
|
||||
+ "If enabled, the mod will log performance about the world generation process. \n"
|
||||
+ "This can be useful for debugging.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<ELoggerMode> logWorldGenLoadEvent = new ConfigEntry.Builder<ELoggerMode>()
|
||||
.set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_FILE)
|
||||
public static ConfigEntry<EDhApiLoggerMode> logWorldGenLoadEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
|
||||
.set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE)
|
||||
.comment(""
|
||||
+ "If enabled, the mod will log information about the world generation process. \n"
|
||||
+ "This can be useful for debugging.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<ELoggerMode> logLodBuilderEvent = new ConfigEntry.Builder<ELoggerMode>()
|
||||
.set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
|
||||
public static ConfigEntry<EDhApiLoggerMode> logLodBuilderEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
|
||||
.set(EDhApiLoggerMode.LOG_WARNING_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.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<ELoggerMode> logRendererBufferEvent = new ConfigEntry.Builder<ELoggerMode>()
|
||||
.set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
|
||||
public static ConfigEntry<EDhApiLoggerMode> logRendererBufferEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
|
||||
.set(EDhApiLoggerMode.LOG_WARNING_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<ELoggerMode> logRendererGLEvent = new ConfigEntry.Builder<ELoggerMode>()
|
||||
.set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
|
||||
public static ConfigEntry<EDhApiLoggerMode> logRendererGLEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
|
||||
.set(EDhApiLoggerMode.LOG_WARNING_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<ELoggerMode> logFileReadWriteEvent = new ConfigEntry.Builder<ELoggerMode>()
|
||||
.set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
|
||||
public static ConfigEntry<EDhApiLoggerMode> logFileReadWriteEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
|
||||
.set(EDhApiLoggerMode.LOG_WARNING_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.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<ELoggerMode> logFileSubDimEvent = new ConfigEntry.Builder<ELoggerMode>()
|
||||
.set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
|
||||
public static ConfigEntry<EDhApiLoggerMode> logFileSubDimEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
|
||||
.set(EDhApiLoggerMode.LOG_WARNING_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.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<ELoggerMode> logNetworkEvent = new ConfigEntry.Builder<ELoggerMode>()
|
||||
.set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
|
||||
public static ConfigEntry<EDhApiLoggerMode> logNetworkEvent = new ConfigEntry.Builder<EDhApiLoggerMode>()
|
||||
.set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
|
||||
.comment(""
|
||||
+ "If enabled, the mod will log information about network operations. \n"
|
||||
+ "This can be useful for debugging.")
|
||||
@@ -1110,25 +1132,25 @@ public class Config
|
||||
|
||||
public static class Debugging
|
||||
{
|
||||
public static ConfigEntry<ERendererMode> rendererMode = new ConfigEntry.Builder<ERendererMode>()
|
||||
.set(ERendererMode.DEFAULT)
|
||||
public static ConfigEntry<EDhApiRendererMode> rendererMode = new ConfigEntry.Builder<EDhApiRendererMode>()
|
||||
.set(EDhApiRendererMode.DEFAULT)
|
||||
.comment(""
|
||||
+ "What renderer is active? \n"
|
||||
+ "\n"
|
||||
+ ERendererMode.DEFAULT + ": Default lod renderer \n"
|
||||
+ ERendererMode.DEBUG + ": Debug testing renderer \n"
|
||||
+ ERendererMode.DISABLED + ": Disable rendering")
|
||||
+ EDhApiRendererMode.DEFAULT + ": Default lod renderer \n"
|
||||
+ EDhApiRendererMode.DEBUG + ": Debug testing renderer \n"
|
||||
+ EDhApiRendererMode.DISABLED + ": Disable rendering")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDebugRendering> debugRendering = new ConfigEntry.Builder<EDebugRendering>()
|
||||
.set(EDebugRendering.OFF)
|
||||
public static ConfigEntry<EDhApiDebugRendering> debugRendering = new ConfigEntry.Builder<EDhApiDebugRendering>()
|
||||
.set(EDhApiDebugRendering.OFF)
|
||||
.comment(""
|
||||
+ "Should specialized colors/rendering modes be used? \n"
|
||||
+ "\n"
|
||||
+ EDebugRendering.OFF + ": LODs will be drawn with their normal colors. \n"
|
||||
+ EDebugRendering.SHOW_DETAIL + ": LODs' color will be based on their detail level. \n"
|
||||
+ EDebugRendering.SHOW_BLOCK_MATERIAL + ": LODs' color will be based on their material. \n"
|
||||
+ EDebugRendering.SHOW_OVERLAPPING_QUADS + ": LODs will be drawn with total white, but overlapping quads will be drawn with red. \n"
|
||||
+ EDhApiDebugRendering.OFF + ": LODs will be drawn with their normal colors. \n"
|
||||
+ EDhApiDebugRendering.SHOW_DETAIL + ": LODs' color will be based on their detail level. \n"
|
||||
+ EDhApiDebugRendering.SHOW_BLOCK_MATERIAL + ": LODs' color will be based on their material. \n"
|
||||
+ EDhApiDebugRendering.SHOW_OVERLAPPING_QUADS + ": LODs will be drawn with total white, but overlapping quads will be drawn with red. \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
@@ -1165,6 +1187,14 @@ public class Config
|
||||
+ "Useful for debugging shaders")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> showOverlappingQuadErrors = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment(""
|
||||
+ "If true overlapping quads will be rendered as bright red for easy identification. \n"
|
||||
+ "If false the quads will be rendered normally. \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
// Note: This will reset on game restart, and should have a warning on the tooltip
|
||||
public static ConfigEntry<Boolean> allowUnsafeValues = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
@@ -1222,19 +1252,14 @@ public class Config
|
||||
.comment("Render LOD section status?")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> showFullDataFileStatus = new ConfigEntry.Builder<Boolean>()
|
||||
public static ConfigEntry<Boolean> showQuadTreeRenderStatus = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment("Render full data file status?")
|
||||
.comment("Render Quad Tree Rendering status?")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> showFullDataFileSampling = new ConfigEntry.Builder<Boolean>()
|
||||
public static ConfigEntry<Boolean> showFullDataUpdateStatus = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment("Render full data file sampling progress?")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> showRenderDataFileStatus = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment("Render render data file status?")
|
||||
.comment("Render full data update/lock status?")
|
||||
.build();
|
||||
|
||||
}
|
||||
@@ -1254,15 +1279,15 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EGLErrorHandlingMode> glErrorHandlingMode = new ConfigEntry.Builder<EGLErrorHandlingMode>()
|
||||
.set(ModInfo.IS_DEV_BUILD ? EGLErrorHandlingMode.LOG : EGLErrorHandlingMode.IGNORE)
|
||||
public static ConfigEntry<EDhApiGLErrorHandlingMode> glErrorHandlingMode = new ConfigEntry.Builder<EDhApiGLErrorHandlingMode>()
|
||||
.set(ModInfo.IS_DEV_BUILD ? EDhApiGLErrorHandlingMode.LOG : EDhApiGLErrorHandlingMode.IGNORE)
|
||||
.comment(""
|
||||
+ "Defines how OpenGL errors are handled. \n"
|
||||
+ "May incorrectly catch OpenGL errors thrown by other mods. \n"
|
||||
+ "\n"
|
||||
+ EGLErrorHandlingMode.IGNORE + ": Do nothing. \n"
|
||||
+ EGLErrorHandlingMode.LOG + ": write an error to the log. \n"
|
||||
+ EGLErrorHandlingMode.LOG_THROW + ": write to the log and throw an exception. \n"
|
||||
+ EDhApiGLErrorHandlingMode.IGNORE + ": Do nothing. \n"
|
||||
+ EDhApiGLErrorHandlingMode.LOG + ": write an error to the log. \n"
|
||||
+ EDhApiGLErrorHandlingMode.LOG_THROW + ": write to the log and throw an exception. \n"
|
||||
+ " Warning: this should only be enabled when debugging the LOD renderer \n"
|
||||
+ " as it may break Minecraft's renderer when an exception is thrown. \n"
|
||||
+ "")
|
||||
@@ -1295,14 +1320,14 @@ public class Config
|
||||
"")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EGlProfileMode> glProfileMode = new ConfigEntry.Builder<EGlProfileMode>()
|
||||
.set(EGlProfileMode.CORE)
|
||||
public static ConfigEntry<EDhApiGlProfileMode> glProfileMode = new ConfigEntry.Builder<EDhApiGlProfileMode>()
|
||||
.set(EDhApiGlProfileMode.CORE)
|
||||
.comment("" +
|
||||
"Can be changed if you experience crashing when loading into a world.\n" +
|
||||
"\n" +
|
||||
"Defines the OpenGL context type Distant Horizon's will create. \n" +
|
||||
"Generally this should be left as ["+EGlProfileMode.CORE+"] unless there is an issue with your GPU driver. \n" +
|
||||
"Possible values: ["+ StringUtil.join("],[", EGlProfileMode.values())+"] \n" +
|
||||
"Generally this should be left as ["+ EDhApiGlProfileMode.CORE+"] unless there is an issue with your GPU driver. \n" +
|
||||
"Possible values: ["+ StringUtil.join("],[", EDhApiGlProfileMode.values())+"] \n" +
|
||||
"")
|
||||
.build();
|
||||
public static ConfigEntry<Boolean> enableGlForwardCompatibilityMode = new ConfigEntry.Builder<Boolean>()
|
||||
@@ -1381,10 +1406,20 @@ public class Config
|
||||
.set(new HashMap<String, String>())
|
||||
.build();
|
||||
|
||||
public static ConfigUIButton uiButtonTest = new ConfigUIButton(() -> { new Thread(() -> {
|
||||
System.setProperty("java.awt.headless", "false"); // Required to make it work
|
||||
JOptionPane.showMessageDialog(null, "Button pressed!", "UITester dialog", JOptionPane.INFORMATION_MESSAGE);
|
||||
});});
|
||||
public static ConfigUIButton uiButtonTest = new ConfigUIButton(() ->
|
||||
{
|
||||
new Thread(() ->
|
||||
{
|
||||
if (!GraphicsEnvironment.isHeadless())
|
||||
{
|
||||
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();
|
||||
|
||||
@@ -1440,23 +1475,12 @@ public class Config
|
||||
{
|
||||
complicatedListenerSetupComplete = true;
|
||||
|
||||
try
|
||||
{
|
||||
// listener can only be added after the config has finished initializing
|
||||
Client.Advanced.Graphics.AdvancedGraphics.overdrawPreventionPreset.addListener(OverdrawPreventionPresetConfigEventHandler.INSTANCE);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Unexpected exception when running config delayed listener setup. Error: [" + e.getMessage() + "].", e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// TODO automatically get all instances of AbstractPresetConfigEventHandler and fire "setUiOnlyConfigValues"
|
||||
ThreadPresetConfigEventHandler.INSTANCE.setUiOnlyConfigValues();
|
||||
RenderQualityPresetConfigEventHandler.INSTANCE.setUiOnlyConfigValues();
|
||||
QuickRenderToggleConfigEventHandler.INSTANCE.setUiOnlyConfigValues();
|
||||
OverdrawPreventionPresetConfigEventHandler.INSTANCE.setUiOnlyConfigValues();
|
||||
RenderCacheConfigEventHandler.getInstance();
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
-16
@@ -20,24 +20,8 @@
|
||||
package com.seibel.distanthorizons.core.config.eventHandlers;
|
||||
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.api.enums.config.EHorizontalQuality;
|
||||
import com.seibel.distanthorizons.api.enums.config.EMaxHorizontalResolution;
|
||||
import com.seibel.distanthorizons.api.enums.config.EVerticalQuality;
|
||||
import com.seibel.distanthorizons.api.enums.config.quickOptions.EQualityPreset;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.ETransparency;
|
||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions;
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.presets.AbstractPresetConfigEventHandler;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class DebugColumnConfigEventHandler implements IConfigListener
|
||||
{
|
||||
|
||||
+5
-5
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.config.eventHandlers;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.ERendererMode;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
|
||||
@@ -28,15 +28,15 @@ public class QuickRenderToggleConfigEventHandler
|
||||
public static QuickRenderToggleConfigEventHandler INSTANCE = new QuickRenderToggleConfigEventHandler();
|
||||
|
||||
private final ConfigChangeListener<Boolean> quickRenderChangeListener;
|
||||
private final ConfigChangeListener<ERendererMode> rendererModeChangeListener;
|
||||
private final ConfigChangeListener<EDhApiRendererMode> rendererModeChangeListener;
|
||||
|
||||
|
||||
|
||||
/** 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() ? ERendererMode.DEFAULT : ERendererMode.DISABLED); });
|
||||
this.rendererModeChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Debugging.rendererMode, (val) -> { Config.Client.quickEnableRendering.set(Config.Client.Advanced.Debugging.rendererMode.get() != ERendererMode.DISABLED); });
|
||||
this.quickRenderChangeListener = new ConfigChangeListener<>(Config.Client.quickEnableRendering, (val) -> { Config.Client.Advanced.Debugging.rendererMode.set(Config.Client.quickEnableRendering.get() ? EDhApiRendererMode.DEFAULT : EDhApiRendererMode.DISABLED); });
|
||||
this.rendererModeChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Debugging.rendererMode, (val) -> { Config.Client.quickEnableRendering.set(Config.Client.Advanced.Debugging.rendererMode.get() != EDhApiRendererMode.DISABLED); });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,7 +45,7 @@ public class QuickRenderToggleConfigEventHandler
|
||||
*/
|
||||
public void setUiOnlyConfigValues()
|
||||
{
|
||||
Config.Client.quickEnableRendering.set(Config.Client.Advanced.Debugging.rendererMode.get() != ERendererMode.DISABLED);
|
||||
Config.Client.quickEnableRendering.set(Config.Client.Advanced.Debugging.rendererMode.get() != EDhApiRendererMode.DISABLED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
-11
@@ -20,13 +20,10 @@
|
||||
package com.seibel.distanthorizons.core.config.eventHandlers;
|
||||
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.api.enums.config.EBlocksToAvoid;
|
||||
import com.seibel.distanthorizons.api.enums.config.ELodShading;
|
||||
import com.seibel.distanthorizons.api.enums.config.EMaxHorizontalResolution;
|
||||
import com.seibel.distanthorizons.api.enums.config.EVerticalQuality;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.ETransparency;
|
||||
import com.seibel.distanthorizons.api.enums.config.*;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||
|
||||
@@ -45,15 +42,18 @@ public class RenderCacheConfigEventHandler
|
||||
|
||||
|
||||
// previous values used to check if a watched setting was actually modified
|
||||
private final ConfigChangeListener<EMaxHorizontalResolution> horizontalResolutionChangeListener;
|
||||
private final ConfigChangeListener<EVerticalQuality> verticalQualityChangeListener;
|
||||
private final ConfigChangeListener<ETransparency> transparencyChangeListener;
|
||||
private final ConfigChangeListener<EBlocksToAvoid> blocksToIgnoreChangeListener;
|
||||
private final ConfigChangeListener<EDhApiMaxHorizontalResolution> horizontalResolutionChangeListener;
|
||||
private final ConfigChangeListener<EDhApiVerticalQuality> verticalQualityChangeListener;
|
||||
private final ConfigChangeListener<EDhApiTransparency> transparencyChangeListener;
|
||||
private final ConfigChangeListener<EDhApiBlocksToAvoid> blocksToIgnoreChangeListener;
|
||||
private final ConfigChangeListener<Boolean> tintWithAvoidedBlocksChangeListener;
|
||||
|
||||
private final ConfigChangeListener<Double> brightnessMultiplierChangeListener;
|
||||
private final ConfigChangeListener<Double> saturationMultiplierChangeListener;
|
||||
private final ConfigChangeListener<ELodShading> lodShadingChangeListener;
|
||||
private final ConfigChangeListener<EDhApiLodShading> lodShadingChangeListener;
|
||||
private final ConfigChangeListener<EDhApiGrassSideRendering> grassSideChangeListener;
|
||||
|
||||
private final ConfigChangeListener<EDhApiDebugRendering> debugRenderingChangeListener;
|
||||
|
||||
/** how long to wait in milliseconds before applying the config changes */
|
||||
private static final long TIMEOUT_IN_MS = 4_000L;
|
||||
@@ -83,6 +83,9 @@ public class RenderCacheConfigEventHandler
|
||||
this.brightnessMultiplierChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.AdvancedGraphics.brightnessMultiplier, (newValue) -> this.refreshRenderDataAfterTimeout());
|
||||
this.saturationMultiplierChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.AdvancedGraphics.saturationMultiplier, (newValue) -> this.refreshRenderDataAfterTimeout());
|
||||
this.lodShadingChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading, (newValue) -> this.refreshRenderDataAfterTimeout());
|
||||
this.grassSideChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.AdvancedGraphics.grassSideRendering, (newValue) -> this.refreshRenderDataAfterTimeout());
|
||||
|
||||
this.debugRenderingChangeListener = new ConfigChangeListener<>(Config.Client.Advanced.Debugging.debugRendering, (newValue) -> this.refreshRenderDataAfterTimeout());
|
||||
|
||||
}
|
||||
|
||||
|
||||
-86
@@ -1,86 +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.config.eventHandlers.presets;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EMaxHorizontalResolution;
|
||||
import com.seibel.distanthorizons.api.enums.config.EOverdrawPrevention;
|
||||
import com.seibel.distanthorizons.api.enums.config.quickOptions.EQualityPreset;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class OverdrawPreventionPresetConfigEventHandler extends AbstractPresetConfigEventHandler<EOverdrawPrevention>
|
||||
{
|
||||
public static final OverdrawPreventionPresetConfigEventHandler INSTANCE = new OverdrawPreventionPresetConfigEventHandler();
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
|
||||
private final ConfigEntryWithPresetOptions<EOverdrawPrevention, Double> overdrawPrevention = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.AdvancedGraphics.overdrawPrevention,
|
||||
new HashMap<EOverdrawPrevention, Double>()
|
||||
{{
|
||||
this.put(EOverdrawPrevention.HEAVY, 0.6);
|
||||
this.put(EOverdrawPrevention.MEDIUM, 0.4);
|
||||
this.put(EOverdrawPrevention.LIGHT, 0.25);
|
||||
this.put(EOverdrawPrevention.NONE, 0.0);
|
||||
}});
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
/** private since we only ever need one handler at a time */
|
||||
private OverdrawPreventionPresetConfigEventHandler()
|
||||
{
|
||||
// add each config used by this preset
|
||||
this.configList.add(this.overdrawPrevention);
|
||||
|
||||
|
||||
for (ConfigEntryWithPresetOptions<EOverdrawPrevention, ?> 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(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// enum getters //
|
||||
//==============//
|
||||
|
||||
@Override
|
||||
protected IConfigEntry<EOverdrawPrevention> getPresetConfigEntry() { return Config.Client.Advanced.Graphics.AdvancedGraphics.overdrawPreventionPreset; }
|
||||
|
||||
@Override
|
||||
protected List<EOverdrawPrevention> getPresetEnumList() { return Arrays.asList(EOverdrawPrevention.values()); }
|
||||
@Override
|
||||
protected EOverdrawPrevention getCustomPresetEnum() { return EOverdrawPrevention.CUSTOM; }
|
||||
|
||||
}
|
||||
+45
-45
@@ -19,11 +19,11 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.config.eventHandlers.presets;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EHorizontalQuality;
|
||||
import com.seibel.distanthorizons.api.enums.config.EMaxHorizontalResolution;
|
||||
import com.seibel.distanthorizons.api.enums.config.EVerticalQuality;
|
||||
import com.seibel.distanthorizons.api.enums.config.quickOptions.EQualityPreset;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.ETransparency;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiHorizontalQuality;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiMaxHorizontalResolution;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiVerticalQuality;
|
||||
import com.seibel.distanthorizons.api.enums.config.quickOptions.EDhApiQualityPreset;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
@@ -33,57 +33,57 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigEventHandler<EQualityPreset>
|
||||
public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigEventHandler<EDhApiQualityPreset>
|
||||
{
|
||||
public static final RenderQualityPresetConfigEventHandler INSTANCE = new RenderQualityPresetConfigEventHandler();
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
|
||||
private final ConfigEntryWithPresetOptions<EQualityPreset, EMaxHorizontalResolution> drawResolution = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution,
|
||||
new HashMap<EQualityPreset, EMaxHorizontalResolution>()
|
||||
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiMaxHorizontalResolution> drawResolution = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution,
|
||||
new HashMap<EDhApiQualityPreset, EDhApiMaxHorizontalResolution>()
|
||||
{{
|
||||
this.put(EQualityPreset.MINIMUM, EMaxHorizontalResolution.TWO_BLOCKS);
|
||||
this.put(EQualityPreset.LOW, EMaxHorizontalResolution.BLOCK);
|
||||
this.put(EQualityPreset.MEDIUM, EMaxHorizontalResolution.BLOCK);
|
||||
this.put(EQualityPreset.HIGH, EMaxHorizontalResolution.BLOCK);
|
||||
this.put(EQualityPreset.EXTREME, EMaxHorizontalResolution.BLOCK);
|
||||
this.put(EDhApiQualityPreset.MINIMUM, EDhApiMaxHorizontalResolution.TWO_BLOCKS);
|
||||
this.put(EDhApiQualityPreset.LOW, EDhApiMaxHorizontalResolution.BLOCK);
|
||||
this.put(EDhApiQualityPreset.MEDIUM, EDhApiMaxHorizontalResolution.BLOCK);
|
||||
this.put(EDhApiQualityPreset.HIGH, EDhApiMaxHorizontalResolution.BLOCK);
|
||||
this.put(EDhApiQualityPreset.EXTREME, EDhApiMaxHorizontalResolution.BLOCK);
|
||||
}});
|
||||
private final ConfigEntryWithPresetOptions<EQualityPreset, EVerticalQuality> verticalQuality = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.verticalQuality,
|
||||
new HashMap<EQualityPreset, EVerticalQuality>()
|
||||
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiVerticalQuality> verticalQuality = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.verticalQuality,
|
||||
new HashMap<EDhApiQualityPreset, EDhApiVerticalQuality>()
|
||||
{{
|
||||
this.put(EQualityPreset.MINIMUM, EVerticalQuality.HEIGHT_MAP);
|
||||
this.put(EQualityPreset.LOW, EVerticalQuality.LOW);
|
||||
this.put(EQualityPreset.MEDIUM, EVerticalQuality.MEDIUM);
|
||||
this.put(EQualityPreset.HIGH, EVerticalQuality.HIGH);
|
||||
this.put(EQualityPreset.EXTREME, EVerticalQuality.EXTREME);
|
||||
this.put(EDhApiQualityPreset.MINIMUM, EDhApiVerticalQuality.HEIGHT_MAP);
|
||||
this.put(EDhApiQualityPreset.LOW, EDhApiVerticalQuality.LOW);
|
||||
this.put(EDhApiQualityPreset.MEDIUM, EDhApiVerticalQuality.MEDIUM);
|
||||
this.put(EDhApiQualityPreset.HIGH, EDhApiVerticalQuality.HIGH);
|
||||
this.put(EDhApiQualityPreset.EXTREME, EDhApiVerticalQuality.EXTREME);
|
||||
}});
|
||||
private final ConfigEntryWithPresetOptions<EQualityPreset, EHorizontalQuality> horizontalQuality = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.horizontalQuality,
|
||||
new HashMap<EQualityPreset, EHorizontalQuality>()
|
||||
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiHorizontalQuality> horizontalQuality = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.horizontalQuality,
|
||||
new HashMap<EDhApiQualityPreset, EDhApiHorizontalQuality>()
|
||||
{{
|
||||
this.put(EQualityPreset.MINIMUM, EHorizontalQuality.LOWEST);
|
||||
this.put(EQualityPreset.LOW, EHorizontalQuality.LOW);
|
||||
this.put(EQualityPreset.MEDIUM, EHorizontalQuality.MEDIUM);
|
||||
this.put(EQualityPreset.HIGH, EHorizontalQuality.HIGH);
|
||||
this.put(EQualityPreset.EXTREME, EHorizontalQuality.EXTREME);
|
||||
this.put(EDhApiQualityPreset.MINIMUM, EDhApiHorizontalQuality.LOWEST);
|
||||
this.put(EDhApiQualityPreset.LOW, EDhApiHorizontalQuality.LOW);
|
||||
this.put(EDhApiQualityPreset.MEDIUM, EDhApiHorizontalQuality.MEDIUM);
|
||||
this.put(EDhApiQualityPreset.HIGH, EDhApiHorizontalQuality.HIGH);
|
||||
this.put(EDhApiQualityPreset.EXTREME, EDhApiHorizontalQuality.EXTREME);
|
||||
}});
|
||||
private final ConfigEntryWithPresetOptions<EQualityPreset, ETransparency> transparency = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.transparency,
|
||||
new HashMap<EQualityPreset, ETransparency>()
|
||||
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, EDhApiTransparency> transparency = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Quality.transparency,
|
||||
new HashMap<EDhApiQualityPreset, EDhApiTransparency>()
|
||||
{{
|
||||
this.put(EQualityPreset.MINIMUM, ETransparency.DISABLED);
|
||||
this.put(EQualityPreset.LOW, ETransparency.DISABLED); // should be fake if/when fake is fixed
|
||||
this.put(EQualityPreset.MEDIUM, ETransparency.COMPLETE);
|
||||
this.put(EQualityPreset.HIGH, ETransparency.COMPLETE);
|
||||
this.put(EQualityPreset.EXTREME, ETransparency.COMPLETE);
|
||||
this.put(EDhApiQualityPreset.MINIMUM, EDhApiTransparency.DISABLED);
|
||||
this.put(EDhApiQualityPreset.LOW, EDhApiTransparency.DISABLED); // should be fake if/when fake is fixed
|
||||
this.put(EDhApiQualityPreset.MEDIUM, EDhApiTransparency.COMPLETE);
|
||||
this.put(EDhApiQualityPreset.HIGH, EDhApiTransparency.COMPLETE);
|
||||
this.put(EDhApiQualityPreset.EXTREME, EDhApiTransparency.COMPLETE);
|
||||
}});
|
||||
private final ConfigEntryWithPresetOptions<EQualityPreset, Boolean> ssaoEnabled = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Ssao.enabled,
|
||||
new HashMap<EQualityPreset, Boolean>()
|
||||
private final ConfigEntryWithPresetOptions<EDhApiQualityPreset, Boolean> ssaoEnabled = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.Graphics.Ssao.enabled,
|
||||
new HashMap<EDhApiQualityPreset, Boolean>()
|
||||
{{
|
||||
this.put(EQualityPreset.MINIMUM, false);
|
||||
this.put(EQualityPreset.LOW, false);
|
||||
this.put(EQualityPreset.MEDIUM, true);
|
||||
this.put(EQualityPreset.HIGH, true);
|
||||
this.put(EQualityPreset.EXTREME, true);
|
||||
this.put(EDhApiQualityPreset.MINIMUM, false);
|
||||
this.put(EDhApiQualityPreset.LOW, false);
|
||||
this.put(EDhApiQualityPreset.MEDIUM, true);
|
||||
this.put(EDhApiQualityPreset.HIGH, true);
|
||||
this.put(EDhApiQualityPreset.EXTREME, true);
|
||||
}});
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
|
||||
this.configList.add(this.ssaoEnabled);
|
||||
|
||||
|
||||
for (ConfigEntryWithPresetOptions<EQualityPreset, ?> config : this.configList)
|
||||
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(); });
|
||||
@@ -117,11 +117,11 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
|
||||
//==============//
|
||||
|
||||
@Override
|
||||
protected IConfigEntry<EQualityPreset> getPresetConfigEntry() { return Config.Client.qualityPresetSetting; }
|
||||
protected IConfigEntry<EDhApiQualityPreset> getPresetConfigEntry() { return Config.Client.qualityPresetSetting; }
|
||||
|
||||
@Override
|
||||
protected List<EQualityPreset> getPresetEnumList() { return Arrays.asList(EQualityPreset.values()); }
|
||||
protected List<EDhApiQualityPreset> getPresetEnumList() { return Arrays.asList(EDhApiQualityPreset.values()); }
|
||||
@Override
|
||||
protected EQualityPreset getCustomPresetEnum() { return EQualityPreset.CUSTOM; }
|
||||
protected EDhApiQualityPreset getCustomPresetEnum() { return EDhApiQualityPreset.CUSTOM; }
|
||||
|
||||
}
|
||||
|
||||
+74
-49
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.config.eventHandlers.presets;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.quickOptions.EThreadPreset;
|
||||
import com.seibel.distanthorizons.api.enums.config.quickOptions.EDhApiThreadPreset;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions;
|
||||
@@ -32,7 +32,7 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHandler<EThreadPreset>
|
||||
public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHandler<EDhApiThreadPreset>
|
||||
{
|
||||
public static final ThreadPresetConfigEventHandler INSTANCE = new ThreadPresetConfigEventHandler();
|
||||
|
||||
@@ -42,68 +42,90 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
|
||||
|
||||
|
||||
public static int getWorldGenDefaultThreadCount() { return getThreadCountByPercent(0.15); }
|
||||
private final ConfigEntryWithPresetOptions<EThreadPreset, Integer> worldGenThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads,
|
||||
new HashMap<EThreadPreset, Integer>()
|
||||
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Integer> worldGenThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads,
|
||||
new HashMap<EDhApiThreadPreset, Integer>()
|
||||
{{
|
||||
this.put(EThreadPreset.MINIMAL_IMPACT, 1);
|
||||
this.put(EThreadPreset.LOW_IMPACT, getWorldGenDefaultThreadCount());
|
||||
this.put(EThreadPreset.BALANCED, getThreadCountByPercent(0.25));
|
||||
this.put(EThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.5));
|
||||
this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
|
||||
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1);
|
||||
this.put(EDhApiThreadPreset.LOW_IMPACT, getWorldGenDefaultThreadCount());
|
||||
this.put(EDhApiThreadPreset.BALANCED, getThreadCountByPercent(0.25));
|
||||
this.put(EDhApiThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.5));
|
||||
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
|
||||
}});
|
||||
public static double getWorldGenDefaultRunTimeRatio() { return LOW_THREAD_COUNT_CPU ? 0.5 : 0.75; }
|
||||
private final ConfigEntryWithPresetOptions<EThreadPreset, Double> worldGenRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForWorldGenerationThreads,
|
||||
new HashMap<EThreadPreset, Double>()
|
||||
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Double> worldGenRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForWorldGenerationThreads,
|
||||
new HashMap<EDhApiThreadPreset, Double>()
|
||||
{{
|
||||
this.put(EThreadPreset.MINIMAL_IMPACT, LOW_THREAD_COUNT_CPU ? 0.1 : 0.25);
|
||||
this.put(EThreadPreset.LOW_IMPACT, getWorldGenDefaultRunTimeRatio());
|
||||
this.put(EThreadPreset.BALANCED, LOW_THREAD_COUNT_CPU ? 0.5 : 0.75);
|
||||
this.put(EThreadPreset.AGGRESSIVE, LOW_THREAD_COUNT_CPU ? 0.75 : 1.0);
|
||||
this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
|
||||
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, LOW_THREAD_COUNT_CPU ? 0.1 : 0.25);
|
||||
this.put(EDhApiThreadPreset.LOW_IMPACT, getWorldGenDefaultRunTimeRatio());
|
||||
this.put(EDhApiThreadPreset.BALANCED, LOW_THREAD_COUNT_CPU ? 0.5 : 0.75);
|
||||
this.put(EDhApiThreadPreset.AGGRESSIVE, LOW_THREAD_COUNT_CPU ? 0.75 : 1.0);
|
||||
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
|
||||
}});
|
||||
|
||||
|
||||
public static int getFileHandlerDefaultThreadCount() { return getThreadCountByPercent(0.1); }
|
||||
private final ConfigEntryWithPresetOptions<EThreadPreset, Integer> fileHandlerThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfFileHandlerThreads,
|
||||
new HashMap<EThreadPreset, Integer>()
|
||||
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Integer> fileHandlerThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfFileHandlerThreads,
|
||||
new HashMap<EDhApiThreadPreset, Integer>()
|
||||
{{
|
||||
this.put(EThreadPreset.MINIMAL_IMPACT, 1);
|
||||
this.put(EThreadPreset.LOW_IMPACT, getFileHandlerDefaultThreadCount());
|
||||
this.put(EThreadPreset.BALANCED, getThreadCountByPercent(0.2));
|
||||
this.put(EThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.2));
|
||||
this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
|
||||
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1);
|
||||
this.put(EDhApiThreadPreset.LOW_IMPACT, getFileHandlerDefaultThreadCount());
|
||||
this.put(EDhApiThreadPreset.BALANCED, getThreadCountByPercent(0.2));
|
||||
this.put(EDhApiThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.2));
|
||||
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
|
||||
}});
|
||||
public static double getFileHandlerDefaultRunTimeRatio() { return 0.5; }
|
||||
private final ConfigEntryWithPresetOptions<EThreadPreset, Double> fileHandlerRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForFileHandlerThreads,
|
||||
new HashMap<EThreadPreset, Double>()
|
||||
public static double getFileHandlerDefaultRunTimeRatio() { return 0.75; }
|
||||
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Double> fileHandlerRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForFileHandlerThreads,
|
||||
new HashMap<EDhApiThreadPreset, Double>()
|
||||
{{
|
||||
this.put(EThreadPreset.MINIMAL_IMPACT, 0.25);
|
||||
this.put(EThreadPreset.LOW_IMPACT, getFileHandlerDefaultRunTimeRatio());
|
||||
this.put(EThreadPreset.BALANCED, 0.75);
|
||||
this.put(EThreadPreset.AGGRESSIVE, 1.0);
|
||||
this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
|
||||
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.50);
|
||||
this.put(EDhApiThreadPreset.LOW_IMPACT, getFileHandlerDefaultRunTimeRatio());
|
||||
this.put(EDhApiThreadPreset.BALANCED, 1.0);
|
||||
this.put(EDhApiThreadPreset.AGGRESSIVE, 1.0);
|
||||
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
|
||||
}});
|
||||
|
||||
|
||||
public static int getUpdatePropagatorDefaultThreadCount() { return getThreadCountByPercent(0.25); }
|
||||
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Integer> UpdatePropagatorThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfUpdatePropagatorThreads,
|
||||
new HashMap<EDhApiThreadPreset, Integer>()
|
||||
{{
|
||||
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1);
|
||||
this.put(EDhApiThreadPreset.LOW_IMPACT, getUpdatePropagatorDefaultThreadCount());
|
||||
this.put(EDhApiThreadPreset.BALANCED, getThreadCountByPercent(0.5));
|
||||
this.put(EDhApiThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.75));
|
||||
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
|
||||
}});
|
||||
public static double getUpdatePropagatorDefaultRunTimeRatio() { return 0.5; }
|
||||
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Double> UpdatePropagatorRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForUpdatePropagatorThreads,
|
||||
new HashMap<EDhApiThreadPreset, Double>()
|
||||
{{
|
||||
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.25);
|
||||
this.put(EDhApiThreadPreset.LOW_IMPACT, getUpdatePropagatorDefaultRunTimeRatio());
|
||||
this.put(EDhApiThreadPreset.BALANCED, 0.75);
|
||||
this.put(EDhApiThreadPreset.AGGRESSIVE, 1.0);
|
||||
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
|
||||
}});
|
||||
|
||||
|
||||
public static int getLodBuilderDefaultThreadCount() { return getThreadCountByPercent(0.1); }
|
||||
private final ConfigEntryWithPresetOptions<EThreadPreset, Integer> lodBuilderThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads,
|
||||
new HashMap<EThreadPreset, Integer>()
|
||||
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Integer> lodBuilderThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads,
|
||||
new HashMap<EDhApiThreadPreset, Integer>()
|
||||
{{
|
||||
this.put(EThreadPreset.MINIMAL_IMPACT, 1);
|
||||
this.put(EThreadPreset.LOW_IMPACT, getLodBuilderDefaultThreadCount());
|
||||
this.put(EThreadPreset.BALANCED, getThreadCountByPercent(0.2));
|
||||
this.put(EThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.4));
|
||||
this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
|
||||
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1);
|
||||
this.put(EDhApiThreadPreset.LOW_IMPACT, getLodBuilderDefaultThreadCount());
|
||||
this.put(EDhApiThreadPreset.BALANCED, getThreadCountByPercent(0.2));
|
||||
this.put(EDhApiThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.4));
|
||||
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
|
||||
}});
|
||||
public static double getLodBuilderDefaultRunTimeRatio() { return LOW_THREAD_COUNT_CPU ? 0.25 : 0.5; }
|
||||
private final ConfigEntryWithPresetOptions<EThreadPreset, Double> lodBuilderRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForLodBuilderThreads,
|
||||
new HashMap<EThreadPreset, Double>()
|
||||
private final ConfigEntryWithPresetOptions<EDhApiThreadPreset, Double> lodBuilderRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForLodBuilderThreads,
|
||||
new HashMap<EDhApiThreadPreset, Double>()
|
||||
{{
|
||||
this.put(EThreadPreset.MINIMAL_IMPACT, 0.1);
|
||||
this.put(EThreadPreset.LOW_IMPACT, getLodBuilderDefaultRunTimeRatio());
|
||||
this.put(EThreadPreset.BALANCED, LOW_THREAD_COUNT_CPU ? 0.5 : 0.75);
|
||||
this.put(EThreadPreset.AGGRESSIVE, 1.0);
|
||||
this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
|
||||
this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.1);
|
||||
this.put(EDhApiThreadPreset.LOW_IMPACT, getLodBuilderDefaultRunTimeRatio());
|
||||
this.put(EDhApiThreadPreset.BALANCED, LOW_THREAD_COUNT_CPU ? 0.5 : 0.75);
|
||||
this.put(EDhApiThreadPreset.AGGRESSIVE, 1.0);
|
||||
this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
|
||||
}});
|
||||
|
||||
|
||||
@@ -122,11 +144,14 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
|
||||
this.configList.add(this.fileHandlerThreadCount);
|
||||
this.configList.add(this.fileHandlerRunTime);
|
||||
|
||||
this.configList.add(this.UpdatePropagatorThreadCount);
|
||||
this.configList.add(this.UpdatePropagatorRunTime);
|
||||
|
||||
this.configList.add(this.lodBuilderThreadCount);
|
||||
this.configList.add(this.lodBuilderRunTime);
|
||||
|
||||
|
||||
for (ConfigEntryWithPresetOptions<EThreadPreset, ?> config : this.configList)
|
||||
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(); });
|
||||
@@ -171,11 +196,11 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
|
||||
//==============//
|
||||
|
||||
@Override
|
||||
protected IConfigEntry<EThreadPreset> getPresetConfigEntry() { return Config.Client.threadPresetSetting; }
|
||||
protected IConfigEntry<EDhApiThreadPreset> getPresetConfigEntry() { return Config.Client.threadPresetSetting; }
|
||||
|
||||
@Override
|
||||
protected List<EThreadPreset> getPresetEnumList() { return Arrays.asList(EThreadPreset.values()); }
|
||||
protected List<EDhApiThreadPreset> getPresetEnumList() { return Arrays.asList(EDhApiThreadPreset.values()); }
|
||||
@Override
|
||||
protected EThreadPreset getCustomPresetEnum() { return EThreadPreset.CUSTOM; }
|
||||
protected EDhApiThreadPreset getCustomPresetEnum() { return EDhApiThreadPreset.CUSTOM; }
|
||||
|
||||
}
|
||||
|
||||
+2
-1
@@ -37,7 +37,8 @@ public class JavaScreenHandlerScreen extends AbstractScreen
|
||||
|
||||
static
|
||||
{
|
||||
// Required to run this
|
||||
// Needs to be called before any Swing code is called, otherwise
|
||||
// Swing will get stuck thinking it's headless
|
||||
System.setProperty("java.awt.headless", "false");
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.config.gui;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EGpuUploadMethod;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLState;
|
||||
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
|
||||
@@ -76,7 +76,7 @@ public class OpenGLConfigScreen extends AbstractScreen
|
||||
buffer.rewind();
|
||||
GLVertexBuffer vbo = new GLVertexBuffer(false);
|
||||
vbo.bind();
|
||||
vbo.uploadBuffer(buffer, 4, EGpuUploadMethod.DATA, vertices.length * Float.BYTES);
|
||||
vbo.uploadBuffer(buffer, 4, EDhApiGpuUploadMethod.DATA, vertices.length * Float.BYTES);
|
||||
return vbo;
|
||||
}
|
||||
|
||||
|
||||
-159
@@ -1,159 +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.dataObjects.fullData;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class FullDataDownSampler
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
public static CompletableFuture<IFullDataSource> createDownSamplingFuture(DhSectionPos newTarget, IFullDataSourceProvider provider)
|
||||
{
|
||||
// TODO: Make this future somehow run with lowest priority (to ensure ram usage stays low)
|
||||
return createDownSamplingFuture(CompleteFullDataSource.createEmpty(newTarget), provider);
|
||||
}
|
||||
|
||||
public static CompletableFuture<IFullDataSource> createDownSamplingFuture(CompleteFullDataSource target, IFullDataSourceProvider provider)
|
||||
{
|
||||
int sectionSizeNeeded = 1 << target.getDataDetailLevel();
|
||||
|
||||
ArrayList<CompletableFuture<IFullDataSource>> futures;
|
||||
DhLodPos basePos = target.getSectionPos().getSectionBBoxPos().getCornerLodPos(CompleteFullDataSource.SECTION_SIZE_OFFSET);
|
||||
|
||||
|
||||
if (sectionSizeNeeded <= CompleteFullDataSource.SECTION_SIZE_OFFSET)
|
||||
{
|
||||
futures = new ArrayList<>(sectionSizeNeeded * sectionSizeNeeded);
|
||||
for (int xOffset = 0; xOffset < sectionSizeNeeded; xOffset++)
|
||||
{
|
||||
for (int zOffset = 0; zOffset < sectionSizeNeeded; zOffset++)
|
||||
{
|
||||
CompletableFuture<IFullDataSource> future = provider.getAsync(new DhSectionPos(
|
||||
CompleteFullDataSource.SECTION_SIZE_OFFSET, basePos.x + xOffset, basePos.z + zOffset));
|
||||
future = future.whenComplete((source, ex) -> {
|
||||
if (ex == null && source != null && source instanceof CompleteFullDataSource)
|
||||
{
|
||||
downSample(target, (CompleteFullDataSource) source);
|
||||
}
|
||||
else if (ex != null)
|
||||
{
|
||||
LOGGER.error("Error while down sampling", ex);
|
||||
}
|
||||
});
|
||||
futures.add(future);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
futures = new ArrayList<>(CompleteFullDataSource.WIDTH * CompleteFullDataSource.WIDTH);
|
||||
int multiplier = sectionSizeNeeded / CompleteFullDataSource.WIDTH;
|
||||
for (int xOffset = 0; xOffset < CompleteFullDataSource.WIDTH; xOffset++)
|
||||
{
|
||||
for (int zOffset = 0; zOffset < CompleteFullDataSource.WIDTH; zOffset++)
|
||||
{
|
||||
CompletableFuture<IFullDataSource> future = provider.getAsync(new DhSectionPos(
|
||||
CompleteFullDataSource.SECTION_SIZE_OFFSET, basePos.x + xOffset * multiplier, basePos.z + zOffset * multiplier));
|
||||
future = future.whenComplete((source, ex) -> {
|
||||
if (ex == null && source != null && source instanceof CompleteFullDataSource)
|
||||
{
|
||||
downSample(target, (CompleteFullDataSource) source);
|
||||
}
|
||||
else if (ex != null)
|
||||
{
|
||||
LOGGER.error("Error while down sampling", ex);
|
||||
}
|
||||
});
|
||||
futures.add(future);
|
||||
}
|
||||
}
|
||||
}
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApply(v -> target);
|
||||
}
|
||||
|
||||
public static void downSample(CompleteFullDataSource target, CompleteFullDataSource source)
|
||||
{
|
||||
LodUtil.assertTrue(target.getSectionPos().overlapsExactly(source.getSectionPos()));
|
||||
LodUtil.assertTrue(target.getDataDetailLevel() > source.getDataDetailLevel());
|
||||
|
||||
byte detailDiff = (byte) (target.getDataDetailLevel() - source.getDataDetailLevel());
|
||||
DhSectionPos trgPos = target.getSectionPos();
|
||||
DhSectionPos srcPos = source.getSectionPos();
|
||||
|
||||
if (detailDiff >= CompleteFullDataSource.SECTION_SIZE_OFFSET)
|
||||
{
|
||||
// The source occupies only 1 datapoint in the target
|
||||
// FIXME: TEMP method for down-sampling: take only the corner column
|
||||
int sourceSectionPerTargetData = 1 << (detailDiff - CompleteFullDataSource.SECTION_SIZE_OFFSET);
|
||||
if (srcPos.getX() % sourceSectionPerTargetData != 0 || srcPos.getZ() % sourceSectionPerTargetData != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DhLodPos trgOffset = trgPos.getMinCornerLodPos(target.getDataDetailLevel());
|
||||
DhLodPos srcOffset = srcPos.getSectionBBoxPos().convertToDetailLevel(target.getDataDetailLevel());
|
||||
int offsetX = trgOffset.x - srcOffset.x;
|
||||
int offsetZ = trgOffset.z - srcOffset.z;
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetX < CompleteFullDataSource.WIDTH
|
||||
&& offsetZ >= 0 && offsetZ < CompleteFullDataSource.WIDTH);
|
||||
target.markNotEmpty();
|
||||
source.get(0, 0).deepCopyTo(target.get(offsetX, offsetZ));
|
||||
|
||||
}
|
||||
else if (detailDiff > 0)
|
||||
{
|
||||
// The source occupies multiple data-points in the target
|
||||
int srcDataPerTrgData = 1 << detailDiff;
|
||||
int overlappedTrgDataSize = CompleteFullDataSource.WIDTH / srcDataPerTrgData;
|
||||
|
||||
DhLodPos trgOffset = trgPos.getMinCornerLodPos(target.getDataDetailLevel());
|
||||
DhLodPos srcOffset = srcPos.getSectionBBoxPos().getCornerLodPos(target.getDataDetailLevel());
|
||||
int offsetX = trgOffset.x - srcOffset.x;
|
||||
int offsetZ = trgOffset.z - srcOffset.z;
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetX < CompleteFullDataSource.WIDTH
|
||||
&& offsetZ >= 0 && offsetZ < CompleteFullDataSource.WIDTH);
|
||||
target.markNotEmpty();
|
||||
|
||||
for (int ox = 0; ox < overlappedTrgDataSize; ox++)
|
||||
{
|
||||
for (int oz = 0; oz < overlappedTrgDataSize; oz++)
|
||||
{
|
||||
SingleColumnFullDataAccessor column = target.get(ox + offsetX, oz + offsetZ);
|
||||
column.downsampleFrom(source.subView(srcDataPerTrgData, ox * srcDataPerTrgData, oz * srcDataPerTrgData));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LodUtil.assertNotReach();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+208
-42
@@ -21,25 +21,33 @@ package com.seibel.distanthorizons.core.dataObjects.fullData;
|
||||
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* WARNING: This is not THREAD-SAFE!
|
||||
* <p>
|
||||
* Used to map a numerical IDs to a Biome/BlockState pair.
|
||||
*
|
||||
* WARNING: This is not THREAD-SAFE! <br><br>
|
||||
*
|
||||
* Used to map a numerical IDs to a Biome/BlockState pair. <br><br>
|
||||
*
|
||||
* TODO the serializing of this map might be really big
|
||||
* since it stringifies every block and biome name, which is quite bulky.
|
||||
* It might be worth while to have a biome and block ID that then both get mapped
|
||||
* to the data point ID to reduce file size.
|
||||
* And/or it would be good to dynamically remove IDs that aren't currently in use.
|
||||
*
|
||||
* @author Leetom
|
||||
*/
|
||||
public class FullDataPointIdMap
|
||||
@@ -55,12 +63,11 @@ public class FullDataPointIdMap
|
||||
private static final String BLOCK_STATE_SEPARATOR_STRING = "_DH-BSW_";
|
||||
|
||||
|
||||
// FIXME: Improve performance maybe?
|
||||
/** used when the data point map is running normally */
|
||||
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||
|
||||
/** should only be used for debugging */
|
||||
private DhSectionPos pos;
|
||||
private long pos;
|
||||
|
||||
/** The index should be the same as the Entry's ID */
|
||||
private final ArrayList<Entry> entryList = new ArrayList<>();
|
||||
@@ -72,7 +79,7 @@ public class FullDataPointIdMap
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public FullDataPointIdMap(DhSectionPos pos) { this.pos = pos; }
|
||||
public FullDataPointIdMap(long pos) { this.pos = pos; }
|
||||
|
||||
|
||||
|
||||
@@ -112,8 +119,11 @@ public class FullDataPointIdMap
|
||||
|
||||
/** @return -1 if the list is empty */
|
||||
public int getMaxValidId() { return this.entryList.size() - 1; }
|
||||
public int size() { return this.entryList.size(); }
|
||||
|
||||
public DhSectionPos getPos() { return this.pos; }
|
||||
public boolean isEmpty() { return this.entryList.isEmpty(); }
|
||||
|
||||
public long getPos() { return this.pos; }
|
||||
|
||||
|
||||
|
||||
@@ -125,7 +135,7 @@ public class FullDataPointIdMap
|
||||
* If an entry with the given values already exists nothing will
|
||||
* be added but the existing item's ID will still be returned.
|
||||
*/
|
||||
public int addIfNotPresentAndGetId(IBiomeWrapper biome, IBlockStateWrapper blockState) { return this.addIfNotPresentAndGetId(new Entry(biome, blockState), true); }
|
||||
public int addIfNotPresentAndGetId(IBiomeWrapper biome, IBlockStateWrapper blockState) { return this.addIfNotPresentAndGetId(Entry.getEntry(biome, blockState), true); }
|
||||
/** @param useWriteLocks should only be false if this method is already in a write lock to prevent unlocking at the wrong time */
|
||||
private int addIfNotPresentAndGetId(Entry biomeBlockStateEntry, boolean useWriteLocks)
|
||||
{
|
||||
@@ -162,22 +172,84 @@ public class FullDataPointIdMap
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds each entry from the given map to this map.
|
||||
*
|
||||
* @return an array of each added entry's ID in this map in order
|
||||
*/
|
||||
public int[] mergeAndReturnRemappedEntityIds(FullDataPointIdMap target)
|
||||
/** allows for adding duplicate {@link Entry} */
|
||||
private void add(Entry biomeBlockStateEntry, boolean useWriteLocks)
|
||||
{
|
||||
try
|
||||
{
|
||||
LOGGER.trace("merging {" + this.pos + ", " + this.entryList.size() + "} and {" + target.pos + ", " + target.entryList.size() + "}");
|
||||
if (useWriteLocks)
|
||||
{
|
||||
this.readWriteLock.writeLock().lock();
|
||||
}
|
||||
|
||||
target.readWriteLock.readLock().lock();
|
||||
|
||||
int id = this.entryList.size();
|
||||
this.entryList.add(biomeBlockStateEntry);
|
||||
this.idMap.put(biomeBlockStateEntry, id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (useWriteLocks)
|
||||
{
|
||||
this.readWriteLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds every {@link Entry} from inputMap into this map. <br>
|
||||
* Allows duplicate entries. <br><br>
|
||||
*
|
||||
* Allowing duplicate entries should be done if a datasource is just being read in and
|
||||
* a merge step isn't being done afterwards. If duplicates are removed it may cause
|
||||
* the ID's to get out of sync since everything will be shifted down after the removed
|
||||
* ID(s).
|
||||
*/
|
||||
public void addAll(FullDataPointIdMap inputMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
//LOGGER.trace("adding {" + this.pos + ", " + this.entryList.size() + "} and {" + inputMap.pos + ", " + inputMap.entryList.size() + "}");
|
||||
|
||||
inputMap.readWriteLock.readLock().lock();
|
||||
this.readWriteLock.writeLock().lock();
|
||||
|
||||
ArrayList<Entry> entriesToMerge = target.entryList;
|
||||
ArrayList<Entry> entriesToMerge = inputMap.entryList;
|
||||
for (int i = 0; i < entriesToMerge.size(); i++)
|
||||
{
|
||||
Entry entity = entriesToMerge.get(i);
|
||||
this.add(entity, false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.readWriteLock.writeLock().unlock();
|
||||
inputMap.readWriteLock.readLock().unlock();
|
||||
|
||||
//LOGGER.trace("finished merging {" + this.pos + ", " + this.entryList.size() + "} and {" + inputMap.pos + ", " + inputMap.entryList.size() + "}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds each entry from the given map to this map. <br><br>
|
||||
*
|
||||
* Note: when using this function be careful about re-mapping the
|
||||
* same data source multiple times.
|
||||
* Doing so may cause indexOutOfBounds issues.
|
||||
*
|
||||
* @return an array of each added entry's ID in this map in order
|
||||
*/
|
||||
public int[] mergeAndReturnRemappedEntityIds(FullDataPointIdMap inputMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
//LOGGER.trace("merging {" + this.pos + ", " + this.entryList.size() + "} and {" + inputMap.pos + ", " + inputMap.entryList.size() + "}");
|
||||
|
||||
inputMap.readWriteLock.readLock().lock();
|
||||
this.readWriteLock.writeLock().lock();
|
||||
|
||||
ArrayList<Entry> entriesToMerge = inputMap.entryList;
|
||||
int[] remappedEntryIds = new int[entriesToMerge.size()];
|
||||
for (int i = 0; i < entriesToMerge.size(); i++)
|
||||
{
|
||||
@@ -191,14 +263,14 @@ public class FullDataPointIdMap
|
||||
finally
|
||||
{
|
||||
this.readWriteLock.writeLock().unlock();
|
||||
target.readWriteLock.readLock().unlock();
|
||||
inputMap.readWriteLock.readLock().unlock();
|
||||
|
||||
LOGGER.trace("finished merging {" + this.pos + ", " + this.entryList.size() + "} and {" + target.pos + ", " + target.entryList.size() + "}");
|
||||
//LOGGER.trace("finished merging {" + this.pos + ", " + this.entryList.size() + "} and {" + inputMap.pos + ", " + inputMap.entryList.size() + "}");
|
||||
}
|
||||
}
|
||||
|
||||
/** Should only be used if this map is going to be reused, otherwise bad things will happen. */
|
||||
public void clear(DhSectionPos pos)
|
||||
public void clear(long pos)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.entryList.clear();
|
||||
@@ -244,14 +316,19 @@ public class FullDataPointIdMap
|
||||
finally
|
||||
{
|
||||
this.readWriteLock.readLock().unlock();
|
||||
LOGGER.trace("serialize " + this.pos + " " + this.entryList.size());
|
||||
//LOGGER.trace("serialize " + this.pos + " " + this.entryList.size());
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a new IdBiomeBlockStateMap from the given UTF formatted stream */
|
||||
public static FullDataPointIdMap deserialize(DhDataInputStream inputStream, DhSectionPos pos, ILevelWrapper levelWrapper) throws IOException, InterruptedException
|
||||
public static FullDataPointIdMap deserialize(DhDataInputStream inputStream, long pos, ILevelWrapper levelWrapper) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
int entityCount = inputStream.readInt();
|
||||
if (entityCount < 0)
|
||||
{
|
||||
throw new DataCorruptedException("FullDataPointIdMap deserialize entry count should have a number greater than or equal to 0, returned value ["+entityCount+"].");
|
||||
}
|
||||
|
||||
|
||||
// only used when debugging
|
||||
HashMap<String, FullDataPointIdMap.Entry> dataPointEntryBySerialization = new HashMap<>();
|
||||
@@ -259,6 +336,13 @@ public class FullDataPointIdMap
|
||||
FullDataPointIdMap newMap = new FullDataPointIdMap(pos);
|
||||
for (int i = 0; i < entityCount; i++)
|
||||
{
|
||||
// necessary to prevent issues with deserializing objects after the level has been closed
|
||||
if (Thread.interrupted())
|
||||
{
|
||||
throw new InterruptedException(FullDataPointIdMap.class.getSimpleName() + " task interrupted.");
|
||||
}
|
||||
|
||||
|
||||
String entryString = inputStream.readUTF();
|
||||
Entry newEntry = Entry.deserialize(entryString, levelWrapper);
|
||||
newMap.entryList.add(newEntry);
|
||||
@@ -277,7 +361,11 @@ public class FullDataPointIdMap
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.trace("deserialized " + pos + " " + newMap.entryList.size() + "-" + entityCount);
|
||||
if (newMap.size() != entityCount)
|
||||
{
|
||||
// if the mappings are out of sync then the LODs will render incorrectly due to IDs being wrong
|
||||
LodUtil.assertNotReach("ID maps failed to deserialize for pos: ["+ DhSectionPos.toString(pos)+"], incorrect entity count. Expected count ["+entityCount+"], actual count ["+newMap.size()+"]");
|
||||
}
|
||||
|
||||
return newMap;
|
||||
}
|
||||
@@ -312,15 +400,81 @@ public class FullDataPointIdMap
|
||||
{
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
|
||||
private static final Int2ReferenceOpenHashMap<ArrayList<Entry>> ENTRY_POOL = new Int2ReferenceOpenHashMap<>();
|
||||
/** lock is necessary since {@link Int2ReferenceOpenHashMap} isn't concurrent and concurrent threads can cause infinite loops */
|
||||
private static final ReentrantReadWriteLock ENTRY_POOL_LOCK = new ReentrantReadWriteLock();
|
||||
|
||||
public final IBiomeWrapper biome;
|
||||
public final IBlockStateWrapper blockState;
|
||||
|
||||
private Integer hashCode = null;
|
||||
|
||||
|
||||
// constructor //
|
||||
|
||||
public Entry(IBiomeWrapper biome, IBlockStateWrapper blockState)
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public static Entry getEntry(IBiomeWrapper biome, IBlockStateWrapper blockState)
|
||||
{
|
||||
int entryHash = getHashCode(biome, blockState);
|
||||
|
||||
// try getting the existing entry
|
||||
try
|
||||
{
|
||||
ENTRY_POOL_LOCK.readLock().lock();
|
||||
|
||||
// check if an entry already exists
|
||||
ArrayList<Entry> entryList = ENTRY_POOL.get(entryHash);
|
||||
if (entryList != null)
|
||||
{
|
||||
// at least one entry exists with this hash code
|
||||
for (int i = 0; i < entryList.size(); i++)
|
||||
{
|
||||
Entry entry = entryList.get(i);
|
||||
if (entry.biome.equals(biome) && entry.blockState.equals(blockState))
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
// if we got here, then there was a hash collision and this entry wasn't present in the array
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ENTRY_POOL_LOCK.readLock().unlock();
|
||||
}
|
||||
|
||||
|
||||
// no entry exists,
|
||||
// create a new one
|
||||
try
|
||||
{
|
||||
ENTRY_POOL_LOCK.writeLock().lock();
|
||||
|
||||
ArrayList<Entry> entryList = ENTRY_POOL.get(entryHash);
|
||||
if (entryList == null)
|
||||
{
|
||||
// no entries exist for this hash code
|
||||
|
||||
// we assume that hash collisions should basically never happen,
|
||||
// so the array starts with an initial capacity of 1.
|
||||
// However, since collisions will eventually happen, using an arrayList prevents unexpected bugs caused by collisions.
|
||||
entryList = new ArrayList<>(1);
|
||||
ENTRY_POOL.put(entryHash, entryList);
|
||||
}
|
||||
|
||||
Entry newEntry = new Entry(biome, blockState);
|
||||
entryList.add(newEntry);
|
||||
return newEntry;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ENTRY_POOL_LOCK.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
private Entry(IBiomeWrapper biome, IBlockStateWrapper blockState)
|
||||
{
|
||||
this.biome = biome;
|
||||
this.blockState = blockState;
|
||||
@@ -328,15 +482,29 @@ public class FullDataPointIdMap
|
||||
|
||||
|
||||
|
||||
// methods //
|
||||
//===========//
|
||||
// overrides //
|
||||
//===========//
|
||||
|
||||
public static int getHashCode(Entry entry) { return getHashCode(entry.biome, entry.blockState); }
|
||||
public static int getHashCode(IBiomeWrapper biome, IBlockStateWrapper blockState)
|
||||
{
|
||||
final int prime = 31;
|
||||
|
||||
int result = 1;
|
||||
// the biome and blockstate hashcode should be already calculated by the time
|
||||
// we get here, so this operation should be very fast
|
||||
result = prime * result + (biome == null ? 0 : biome.hashCode());
|
||||
result = prime * result + (blockState == null ? 0 : blockState.hashCode());
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
// cache the hash code to improve speed
|
||||
if (this.hashCode == null)
|
||||
{
|
||||
this.hashCode = this.serialize().hashCode();
|
||||
this.hashCode = getHashCode(this);
|
||||
}
|
||||
|
||||
return this.hashCode;
|
||||
@@ -361,25 +529,23 @@ public class FullDataPointIdMap
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// (de)serializing //
|
||||
//=================//
|
||||
|
||||
public String serialize() { return this.biome.getSerialString() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.getSerialString(); }
|
||||
|
||||
public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException
|
||||
public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, DataCorruptedException
|
||||
{
|
||||
String[] stringArray = str.split(BLOCK_STATE_SEPARATOR_STRING);
|
||||
if (stringArray.length != 2)
|
||||
{
|
||||
throw new IOException("Failed to deserialize BiomeBlockStateEntry");
|
||||
throw new DataCorruptedException("Failed to deserialize BiomeBlockStateEntry");
|
||||
}
|
||||
|
||||
// necessary to prevent issues with deserializing objects after the level has been closed
|
||||
if (Thread.interrupted())
|
||||
{
|
||||
throw new InterruptedException(FullDataPointIdMap.class.getSimpleName() + " task interrupted.");
|
||||
}
|
||||
|
||||
IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapper(stringArray[0], levelWrapper);
|
||||
IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1], levelWrapper);
|
||||
return new Entry(biome, blockState);
|
||||
IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapperOrGetDefault(stringArray[0], levelWrapper);
|
||||
IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapperOrGetDefault(stringArray[1], levelWrapper);
|
||||
return Entry.getEntry(biome, blockState);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-79
@@ -1,79 +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.dataObjects.fullData.accessor;
|
||||
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
|
||||
/**
|
||||
* A more specific version of {@link FullDataArrayAccessor}
|
||||
* that only contains full data for a single chunk.
|
||||
*
|
||||
* @see FullDataPointUtil
|
||||
*/
|
||||
public class ChunkSizedFullDataAccessor extends FullDataArrayAccessor
|
||||
{
|
||||
public final DhChunkPos chunkPos;
|
||||
public final DhSectionPos sectionPos;
|
||||
|
||||
// TODO replace this var with LodUtil.BLOCK_DETAIL_LEVEL
|
||||
public final byte detailLevel = LodUtil.BLOCK_DETAIL_LEVEL;
|
||||
|
||||
|
||||
|
||||
public ChunkSizedFullDataAccessor(DhChunkPos chunkPos)
|
||||
{
|
||||
super(new FullDataPointIdMap(new DhSectionPos(chunkPos)),
|
||||
new long[LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH][0],
|
||||
LodUtil.CHUNK_WIDTH);
|
||||
|
||||
this.chunkPos = chunkPos;
|
||||
// TODO the fact this is using a LodUtil detail level instead of the DhSectionPos detail level may cause confusion and trouble down the line
|
||||
this.sectionPos = new DhSectionPos(LodUtil.CHUNK_DETAIL_LEVEL, this.chunkPos.x, this.chunkPos.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setSingleColumn(long[] data, int xRelative, int zRelative) { this.dataArrays[xRelative * LodUtil.CHUNK_WIDTH + zRelative] = data; }
|
||||
|
||||
public long nonEmptyCount()
|
||||
{
|
||||
long count = 0;
|
||||
for (long[] data : this.dataArrays)
|
||||
{
|
||||
if (data.length != 0)
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public long emptyCount() { return (LodUtil.CHUNK_WIDTH * LodUtil.CHUNK_WIDTH) - this.nonEmptyCount(); }
|
||||
|
||||
public DhSectionPos getSectionPos() { return this.sectionPos; }
|
||||
|
||||
@Override
|
||||
public String toString() { return this.chunkPos + " " + this.nonEmptyCount(); }
|
||||
|
||||
}
|
||||
-189
@@ -1,189 +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.dataObjects.fullData.accessor;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.LowDetailIncompleteFullDataSource;
|
||||
|
||||
/**
|
||||
* Contains Full Data points and basic methods for getting and setting them. <br>
|
||||
* Can be used standalone or as the base for Full data sources.
|
||||
*
|
||||
* @see CompleteFullDataSource
|
||||
* @see LowDetailIncompleteFullDataSource
|
||||
*/
|
||||
public class FullDataArrayAccessor implements IFullDataAccessor
|
||||
{
|
||||
protected final FullDataPointIdMap mapping;
|
||||
|
||||
/** A flattened 2D array (for the X and Z directions) containing an array for the Y direction. */
|
||||
protected final long[][] dataArrays;
|
||||
|
||||
/** measured in data points */
|
||||
protected final int width;
|
||||
/** measured in data points */
|
||||
protected final int dataWidth;
|
||||
|
||||
/** index offset used when getting/setting data in {@link FullDataArrayAccessor#dataArrays}. */
|
||||
protected final int offset;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public FullDataArrayAccessor(FullDataPointIdMap mapping, long[][] dataArrays, int width)
|
||||
{
|
||||
if (dataArrays.length != width * width)
|
||||
{
|
||||
throw new IllegalArgumentException("tried constructing dataArrayView with invalid input!");
|
||||
}
|
||||
|
||||
this.dataArrays = dataArrays;
|
||||
this.width = width;
|
||||
this.dataWidth = width;
|
||||
this.mapping = mapping;
|
||||
this.offset = 0;
|
||||
}
|
||||
|
||||
public FullDataArrayAccessor(FullDataArrayAccessor source, int width, int offsetX, int offsetZ)
|
||||
{
|
||||
if (source.width < width || source.width < width + offsetX || source.width < width + offsetZ)
|
||||
{
|
||||
throw new IllegalArgumentException("tried constructing dataArrayView subview with invalid input!");
|
||||
}
|
||||
|
||||
this.dataArrays = source.dataArrays;
|
||||
this.width = width;
|
||||
this.dataWidth = source.dataWidth;
|
||||
this.mapping = source.mapping;
|
||||
this.offset = source.offset + offsetX * this.dataWidth + offsetZ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public FullDataArrayAccessor subView(int width, int xOffset, int zOffset) { return new FullDataArrayAccessor(this, width, xOffset, zOffset); }
|
||||
|
||||
/** WARNING: This will potentially share the underlying array object! */
|
||||
public void shadowCopyTo(FullDataArrayAccessor target)
|
||||
{
|
||||
if (target.width != this.width)
|
||||
{
|
||||
throw new IllegalArgumentException("Target view must have same size as this view");
|
||||
}
|
||||
|
||||
|
||||
if (target.mapping.equals(this.mapping))
|
||||
{
|
||||
for (int x = 0; x < this.width; x++)
|
||||
{
|
||||
System.arraycopy(this.dataArrays, this.offset + x * this.dataWidth,
|
||||
target.dataArrays, target.offset + x * target.dataWidth, this.width);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int[] remappedIds = target.mapping.mergeAndReturnRemappedEntityIds(this.mapping);
|
||||
for (int x = 0; x < this.width; x++)
|
||||
{
|
||||
for (int z = 0; z < this.width; z++)
|
||||
{
|
||||
long[] currentData = this.dataArrays[this.offset + x * this.dataWidth + z];
|
||||
// may be null if no data exists for this column yet
|
||||
if (currentData != null)
|
||||
{
|
||||
long[] newData = new long[currentData.length]; // TODO what to do if null?
|
||||
for (int dataPointIndex = 0; dataPointIndex < newData.length; dataPointIndex++)
|
||||
{
|
||||
newData[dataPointIndex] = FullDataPointUtil.remap(remappedIds, currentData[dataPointIndex]);
|
||||
}
|
||||
|
||||
target.dataArrays[target.offset + x * target.dataWidth + z] = newData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a higher detail {@link FullDataArrayAccessor}'s and converts the data to a lower detail level.
|
||||
*
|
||||
* @param incomingFullDataAccessor must be larger than this {@link FullDataArrayAccessor} and its width must a power of two larger (example: this.width = 4, other.width = 8)
|
||||
*/
|
||||
public void downsampleFrom(FullDataArrayAccessor incomingFullDataAccessor)
|
||||
{
|
||||
// validate that the incoming data isn't smaller than this accessor
|
||||
LodUtil.assertTrue(incomingFullDataAccessor.width >= this.width && incomingFullDataAccessor.width % this.width == 0);
|
||||
|
||||
int dataPointsPerWidthUnit = incomingFullDataAccessor.width / this.width;
|
||||
for (int xOffset = 0; xOffset < this.width; xOffset++)
|
||||
{
|
||||
for (int zOffset = 0; zOffset < this.width; zOffset++)
|
||||
{
|
||||
FullDataArrayAccessor subView = incomingFullDataAccessor.subView(dataPointsPerWidthUnit,
|
||||
xOffset * dataPointsPerWidthUnit,
|
||||
zOffset * dataPointsPerWidthUnit);
|
||||
|
||||
SingleColumnFullDataAccessor column = this.get(xOffset, zOffset);
|
||||
column.downsampleFrom(subView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public FullDataPointIdMap getMapping() { return this.mapping; }
|
||||
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor get(int index) { return this.get(index / this.width, index % this.width); }
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor get(int relativeX, int relativeZ)
|
||||
{
|
||||
int dataArrayIndex = (relativeX * this.width) + relativeZ + this.offset;
|
||||
if (dataArrayIndex >= this.dataArrays.length)
|
||||
{
|
||||
LodUtil.assertNotReach(
|
||||
"FullDataArrayAccessor.get() called with a relative position that is outside the data source. \n" +
|
||||
"source width: [" + this.width + "] source offset: [" + this.offset + "]\n" +
|
||||
"given relative pos X: [" + relativeX + "] Z: [" + relativeZ + "]\n" +
|
||||
"dataArrays.length: [" + this.dataArrays.length + "] dataArrayIndex: [" + dataArrayIndex + "].");
|
||||
}
|
||||
|
||||
return new SingleColumnFullDataAccessor(this.mapping, this.dataArrays, dataArrayIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int width() { return this.width; }
|
||||
|
||||
}
|
||||
-77
@@ -1,77 +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.dataObjects.fullData.accessor;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Contains raw full data points, which must be interpreted by the {@link FullDataPointUtil}. <br>
|
||||
* Often used by {@link IFullDataSource}'s.
|
||||
*
|
||||
* @see IFullDataSource
|
||||
* @see FullDataArrayAccessor
|
||||
* @see FullDataPointUtil
|
||||
*/
|
||||
public interface IFullDataAccessor
|
||||
{
|
||||
FullDataPointIdMap getMapping();
|
||||
|
||||
/** generally used for iterating through the whole data set */
|
||||
SingleColumnFullDataAccessor get(int index);
|
||||
SingleColumnFullDataAccessor get(int relativeX, int relativeZ);
|
||||
|
||||
/** measured in full data points */
|
||||
int width();
|
||||
|
||||
/**
|
||||
* Creates a new {@link IFullDataAccessor} with the given width and starting at the given X and Z offsets. <br>
|
||||
* The returned object will use the same underlining data structure (IE memory addresses) as the source {@link IFullDataAccessor}.
|
||||
*/
|
||||
IFullDataAccessor subView(int width, int xOffset, int zOffset);
|
||||
|
||||
|
||||
|
||||
|
||||
/** Returns an iterator that goes over each data column */
|
||||
default Iterator<SingleColumnFullDataAccessor> iterator()
|
||||
{
|
||||
return new Iterator<SingleColumnFullDataAccessor>()
|
||||
{
|
||||
private int index = 0;
|
||||
private final int size = width() * width();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() { return this.index < this.size; }
|
||||
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor next()
|
||||
{
|
||||
LodUtil.assertTrue(this.hasNext(), "No more data to iterate!");
|
||||
return get(this.index++);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
-182
@@ -1,182 +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.dataObjects.fullData.accessor;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
|
||||
/**
|
||||
* Represents a single column of Full LOD data.
|
||||
*
|
||||
* @see FullDataPointUtil
|
||||
*/
|
||||
public class SingleColumnFullDataAccessor implements IFullDataAccessor
|
||||
{
|
||||
/**
|
||||
* A flattened 2D array (for the X and Z directions) containing an array for the Y direction.
|
||||
* TODO the flattened array is probably to reduce garbage collection overhead, but is doing it this way worth while? Having a 3D array would be much easier to understand
|
||||
*
|
||||
* @see FullDataArrayAccessor#dataArrays
|
||||
*/
|
||||
private final long[][] dataArrays;
|
||||
/** indicates what index of the {@link SingleColumnFullDataAccessor#dataArrays} is used by this accessor */
|
||||
private final int dataArrayIndex;
|
||||
private final FullDataPointIdMap mapping;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public SingleColumnFullDataAccessor(FullDataPointIdMap mapping, long[][] dataArrays, int dataArrayIndex)
|
||||
{
|
||||
this.dataArrays = dataArrays;
|
||||
this.dataArrayIndex = dataArrayIndex;
|
||||
this.mapping = mapping;
|
||||
|
||||
LodUtil.assertTrue(this.dataArrayIndex < this.dataArrays.length, "dataArrays.length [" + this.dataArrays.length + "] is less than the dataArrayIndex [" + this.dataArrayIndex + "].");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
/** @return true if any data exists in this column. */
|
||||
public boolean doesColumnExist()
|
||||
{
|
||||
long[] dataColumn = this.dataArrays[this.dataArrayIndex];
|
||||
return dataColumn != null && dataColumn.length != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FullDataPointIdMap getMapping() { return this.mapping; }
|
||||
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor get(int index)
|
||||
{
|
||||
if (index != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Only contains 1 column of full data!");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor get(int relativeX, int relativeZ)
|
||||
{
|
||||
if (relativeX != 0 || relativeZ != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Only contains 1 column of full data!");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @return the entire array of raw full data points. */
|
||||
public long[] getRaw() { return this.dataArrays[this.dataArrayIndex]; }
|
||||
|
||||
public long getSingle(int yIndex) { return this.dataArrays[this.dataArrayIndex][yIndex]; }
|
||||
public void setSingle(int yIndex, long fullDataPoint) { this.dataArrays[this.dataArrayIndex][yIndex] = fullDataPoint; }
|
||||
|
||||
public void setNew(long[] newArray) { this.dataArrays[this.dataArrayIndex] = newArray; }
|
||||
|
||||
/** @return how many data points are in this column */
|
||||
public int getSingleLength()
|
||||
{
|
||||
long[] singleDataArray = this.dataArrays[this.dataArrayIndex];
|
||||
return singleDataArray != null ? singleDataArray.length : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int width() { return 1; }
|
||||
|
||||
@Override
|
||||
public IFullDataAccessor subView(int width, int xOffset, int zOffset)
|
||||
{
|
||||
if (width != 1 || xOffset != 1 || zOffset != 1)
|
||||
{
|
||||
throw new IllegalArgumentException("Getting invalid range of subView from SingleColumnFullDataAccessor!");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** WARNING: This may potentially share the underlying array objects! */
|
||||
public void shadowCopyTo(SingleColumnFullDataAccessor target)
|
||||
{
|
||||
if (target.mapping.equals(this.mapping))
|
||||
{
|
||||
target.dataArrays[target.dataArrayIndex] = this.dataArrays[this.dataArrayIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
int[] remappedEntryIds = target.mapping.mergeAndReturnRemappedEntityIds(this.mapping);
|
||||
long[] sourceData = this.dataArrays[this.dataArrayIndex];
|
||||
long[] newData = new long[sourceData.length];
|
||||
|
||||
for (int i = 0; i < newData.length; i++)
|
||||
{
|
||||
newData[i] = FullDataPointUtil.remap(remappedEntryIds, sourceData[i]);
|
||||
}
|
||||
target.dataArrays[target.dataArrayIndex] = newData;
|
||||
}
|
||||
}
|
||||
|
||||
/** Copies both ID data and mapping data. */
|
||||
public void deepCopyTo(SingleColumnFullDataAccessor target)
|
||||
{
|
||||
if (target.mapping.equals(this.mapping))
|
||||
{
|
||||
System.arraycopy(this.dataArrays[this.dataArrayIndex], 0, target.dataArrays[target.dataArrayIndex], 0, this.dataArrays[this.dataArrayIndex].length);
|
||||
}
|
||||
else
|
||||
{
|
||||
int[] remappedEntryIds = target.mapping.mergeAndReturnRemappedEntityIds(this.mapping);
|
||||
long[] sourceData = this.dataArrays[this.dataArrayIndex];
|
||||
// FIXME sourceData.length != 0 may not be a good solution and may end up breaking issues down the line, but fixes exceptions being fired here
|
||||
if (sourceData != null && sourceData.length != 0)
|
||||
{
|
||||
long[] newData = new long[sourceData.length];
|
||||
for (int i = 0; i < newData.length; i++)
|
||||
{
|
||||
newData[i] = FullDataPointUtil.remap(remappedEntryIds, sourceData[i]);
|
||||
}
|
||||
target.dataArrays[target.dataArrayIndex] = newData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces this column's data with data from the input {@link IFullDataAccessor}. <br>
|
||||
* This is used to convert higher detail LOD data to lower detail LOD data.
|
||||
*/
|
||||
public void downsampleFrom(IFullDataAccessor source)
|
||||
{
|
||||
//TODO: average the data instead of just picking the first column
|
||||
SingleColumnFullDataAccessor firstColumn = source.get(0);
|
||||
firstColumn.deepCopyTo(this);
|
||||
}
|
||||
|
||||
}
|
||||
-209
@@ -1,209 +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.dataObjects.fullData.loader;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.DataSourceDto;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public abstract class AbstractFullDataSourceLoader
|
||||
{
|
||||
public static final HashMultimap<Class<? extends IFullDataSource>, AbstractFullDataSourceLoader> LOADER_REGISTRY = HashMultimap.create();
|
||||
public static final HashMap<String, Class<? extends IFullDataSource>> DATATYPE_REGISTRY = new HashMap<>();
|
||||
|
||||
|
||||
public final Class<? extends IFullDataSource> fullDataSourceClass;
|
||||
|
||||
public final String datatype;
|
||||
public final byte[] loaderSupportedVersions;
|
||||
|
||||
/** used when pooling data sources */
|
||||
private final ArrayList<IFullDataSource> cachedSources = new ArrayList<>();
|
||||
private final ReentrantLock cacheLock = new ReentrantLock();
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public AbstractFullDataSourceLoader(Class<? extends IFullDataSource> fullDataSourceClass, String datatype, byte[] loaderSupportedVersions)
|
||||
{
|
||||
this.datatype = datatype;
|
||||
this.loaderSupportedVersions = loaderSupportedVersions;
|
||||
Arrays.sort(loaderSupportedVersions); // sort to allow fast access
|
||||
this.fullDataSourceClass = fullDataSourceClass;
|
||||
|
||||
if (DATATYPE_REGISTRY.containsKey(datatype) && DATATYPE_REGISTRY.get(datatype) != fullDataSourceClass)
|
||||
{
|
||||
throw new IllegalArgumentException("Loader for datatype: [" + datatype + "] already registered with different class: "
|
||||
+ DATATYPE_REGISTRY.get(datatype) + " != " + fullDataSourceClass);
|
||||
}
|
||||
|
||||
Set<AbstractFullDataSourceLoader> loaders = LOADER_REGISTRY.get(fullDataSourceClass);
|
||||
if (loaders.stream().anyMatch(other ->
|
||||
{
|
||||
// see if any loaderSupportsVersion conflicts with this one
|
||||
for (byte otherVer : other.loaderSupportedVersions)
|
||||
{
|
||||
if (Arrays.binarySearch(loaderSupportedVersions, otherVer) >= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
throw new IllegalArgumentException("Loader for class " + fullDataSourceClass + " that supports one of the version in "
|
||||
+ Arrays.toString(loaderSupportedVersions) + " already registered!");
|
||||
}
|
||||
|
||||
DATATYPE_REGISTRY.put(datatype, fullDataSourceClass);
|
||||
LOADER_REGISTRY.put(fullDataSourceClass, this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// loader getters //
|
||||
//================//
|
||||
|
||||
public static AbstractFullDataSourceLoader getLoader(String dataType, byte dataVersion)
|
||||
{
|
||||
return LOADER_REGISTRY.get(DATATYPE_REGISTRY.get(dataType)).stream()
|
||||
.filter(loader -> Arrays.binarySearch(loader.loaderSupportedVersions, dataVersion) >= 0)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static AbstractFullDataSourceLoader getLoader(Class<? extends IFullDataSource> clazz, byte dataVersion)
|
||||
{
|
||||
return LOADER_REGISTRY.get(clazz).stream()
|
||||
.filter(loader -> Arrays.binarySearch(loader.loaderSupportedVersions, dataVersion) >= 0)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// abstract methods //
|
||||
//==================//
|
||||
|
||||
protected abstract IFullDataSource createEmptyDataSource(DhSectionPos pos);
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// data loading //
|
||||
//==============//
|
||||
|
||||
/** Should be used in conjunction with {@link AbstractFullDataSourceLoader#returnPooledDataSource} to return the pooled sources. */
|
||||
public IFullDataSource loadTemporaryDataSource(DataSourceDto dto, IDhLevel level) throws IOException, InterruptedException
|
||||
{
|
||||
IFullDataSource dataSource = this.tryGetPooledSource();
|
||||
if (dataSource != null)
|
||||
{
|
||||
dataSource.repopulateFromStream(dto, dto.getInputStream(), level);
|
||||
}
|
||||
else
|
||||
{
|
||||
dataSource = this.loadDataSource(dto, level);
|
||||
}
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can return null if any of the requirements aren't met.
|
||||
*
|
||||
* @throws InterruptedException if the loader thread is interrupted, generally happens when the level is shutting down
|
||||
*/
|
||||
public IFullDataSource loadDataSource(DataSourceDto dto, IDhLevel level) throws IOException, InterruptedException
|
||||
{
|
||||
IFullDataSource dataSource = this.createEmptyDataSource(dto.pos);
|
||||
dataSource.populateFromStream(dto, dto.getInputStream(), level);
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// data source pooling //
|
||||
//=====================//
|
||||
|
||||
/** @return null if no pooled source exists */
|
||||
public IFullDataSource tryGetPooledSource()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.cacheLock.lock();
|
||||
|
||||
int index = this.cachedSources.size() - 1;
|
||||
if (index == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.cachedSources.remove(index);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Doesn't have to be called, if a data source isn't returned, nothing will be leaked.
|
||||
* It just means a new source must be constructed next time {@link AbstractFullDataSourceLoader#tryGetPooledSource} is called.
|
||||
*/
|
||||
public void returnPooledDataSource(IFullDataSource dataSource)
|
||||
{
|
||||
if (dataSource == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (dataSource.getClass() != this.fullDataSourceClass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (this.cachedSources.size() > 25)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this.cacheLock.lock();
|
||||
this.cachedSources.add(dataSource);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.cacheLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-33
@@ -1,33 +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.dataObjects.fullData.loader;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource;
|
||||
|
||||
public class CompleteFullDataSourceLoader extends AbstractFullDataSourceLoader
|
||||
{
|
||||
public CompleteFullDataSourceLoader() { super(CompleteFullDataSource.class, CompleteFullDataSource.DATA_TYPE_NAME, new byte[]{CompleteFullDataSource.DATA_FORMAT_VERSION}); }
|
||||
|
||||
@Override
|
||||
protected IFullDataSource createEmptyDataSource(DhSectionPos pos) { return CompleteFullDataSource.createEmpty(pos); }
|
||||
|
||||
}
|
||||
-33
@@ -1,33 +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.dataObjects.fullData.loader;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.HighDetailIncompleteFullDataSource;
|
||||
|
||||
public class HighDetailIncompleteFullDataSourceLoader extends AbstractFullDataSourceLoader
|
||||
{
|
||||
public HighDetailIncompleteFullDataSourceLoader() { super(HighDetailIncompleteFullDataSource.class, HighDetailIncompleteFullDataSource.DATA_TYPE_NAME, new byte[]{HighDetailIncompleteFullDataSource.DATA_FORMAT_VERSION}); }
|
||||
|
||||
@Override
|
||||
protected IFullDataSource createEmptyDataSource(DhSectionPos pos) { return HighDetailIncompleteFullDataSource.createEmpty(pos); }
|
||||
|
||||
}
|
||||
-33
@@ -1,33 +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.dataObjects.fullData.loader;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.LowDetailIncompleteFullDataSource;
|
||||
|
||||
public class LowDetailIncompleteFullDataSourceLoader extends AbstractFullDataSourceLoader
|
||||
{
|
||||
public LowDetailIncompleteFullDataSourceLoader() { super(LowDetailIncompleteFullDataSource.class, LowDetailIncompleteFullDataSource.DATA_TYPE_NAME, new byte[]{LowDetailIncompleteFullDataSource.DATA_FORMAT_VERSION}); }
|
||||
|
||||
@Override
|
||||
protected IFullDataSource createEmptyDataSource(DhSectionPos pos) { return LowDetailIncompleteFullDataSource.createEmpty(pos); }
|
||||
|
||||
}
|
||||
-447
@@ -1,447 +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.dataObjects.fullData.sources;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.FullDataArrayAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IStreamableFullDataSource;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.DataSourceDto;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This data source contains every datapoint over its given {@link DhSectionPos}.
|
||||
*
|
||||
* @see FullDataPointUtil
|
||||
* @see LowDetailIncompleteFullDataSource
|
||||
* @see HighDetailIncompleteFullDataSource
|
||||
*/
|
||||
public class CompleteFullDataSource extends FullDataArrayAccessor implements IFullDataSource, IStreamableFullDataSource<IStreamableFullDataSource.FullDataSourceSummaryData, long[][]>
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static final byte SECTION_SIZE_OFFSET = DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
/** measured in dataPoints */
|
||||
public static final int WIDTH = BitShiftUtil.powerOfTwo(SECTION_SIZE_OFFSET);
|
||||
|
||||
public static final byte DATA_FORMAT_VERSION = 3;
|
||||
public static final String DATA_TYPE_NAME = "CompleteFullDataSource";
|
||||
@Override
|
||||
public String getDataTypeName() { return DATA_TYPE_NAME; }
|
||||
|
||||
private DhSectionPos sectionPos;
|
||||
|
||||
private boolean isEmpty = true;
|
||||
public EDhApiWorldGenerationStep worldGenStep = EDhApiWorldGenerationStep.EMPTY;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static CompleteFullDataSource createEmpty(DhSectionPos pos) { return new CompleteFullDataSource(pos); }
|
||||
private CompleteFullDataSource(DhSectionPos sectionPos)
|
||||
{
|
||||
super(new FullDataPointIdMap(sectionPos), new long[WIDTH * WIDTH][0], WIDTH);
|
||||
this.sectionPos = sectionPos;
|
||||
}
|
||||
|
||||
public CompleteFullDataSource(DhSectionPos pos, FullDataPointIdMap mapping, long[][] data)
|
||||
{
|
||||
super(mapping, data, WIDTH);
|
||||
LodUtil.assertTrue(data.length == WIDTH * WIDTH);
|
||||
|
||||
this.sectionPos = pos;
|
||||
this.isEmpty = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// stream handling //
|
||||
//=================//
|
||||
|
||||
@Override
|
||||
public void writeSourceSummaryInfo(IDhLevel level, DhDataOutputStream outputStream) throws IOException
|
||||
{
|
||||
outputStream.writeInt(this.getDataDetailLevel());
|
||||
outputStream.writeInt(this.width);
|
||||
outputStream.writeInt(level.getMinY());
|
||||
outputStream.writeByte(this.worldGenStep.value);
|
||||
|
||||
}
|
||||
@Override
|
||||
public FullDataSourceSummaryData readSourceSummaryInfo(DataSourceDto dto, DhDataInputStream inputStream, IDhLevel level) throws IOException
|
||||
{
|
||||
int dataDetail = inputStream.readInt();
|
||||
if (dataDetail != dto.dataDetailLevel)
|
||||
{
|
||||
throw new IOException(LodUtil.formatLog("Data level mismatch. Expected: ["+dto.dataDetailLevel+"], found ["+dataDetail+"]."));
|
||||
}
|
||||
|
||||
int width = inputStream.readInt();
|
||||
if (width != WIDTH)
|
||||
{
|
||||
throw new IOException(LodUtil.formatLog("Section width mismatch: " + width + " != " + WIDTH + " (Currently only 1 section width is supported)"));
|
||||
}
|
||||
|
||||
int minY = inputStream.readInt();
|
||||
if (minY != level.getMinY())
|
||||
{
|
||||
LOGGER.warn("Data minY mismatch: " + minY + " != " + level.getMinY() + ". Will ignore data's y level");
|
||||
}
|
||||
|
||||
byte worldGenByte = inputStream.readByte();
|
||||
EDhApiWorldGenerationStep worldGenStep = EDhApiWorldGenerationStep.fromValue(worldGenByte);
|
||||
if (worldGenStep == null)
|
||||
{
|
||||
worldGenStep = EDhApiWorldGenerationStep.SURFACE;
|
||||
LOGGER.warn("Missing WorldGenStep, defaulting to: " + worldGenStep.name());
|
||||
}
|
||||
|
||||
|
||||
return new FullDataSourceSummaryData(width, worldGenStep);
|
||||
}
|
||||
public void setSourceSummaryData(FullDataSourceSummaryData summaryData)
|
||||
{
|
||||
this.worldGenStep = summaryData.worldGenStep;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean writeDataPoints(DhDataOutputStream outputStream) throws IOException
|
||||
{
|
||||
if (this.isEmpty())
|
||||
{
|
||||
outputStream.writeInt(IFullDataSource.NO_DATA_FLAG_BYTE);
|
||||
return false;
|
||||
}
|
||||
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
|
||||
|
||||
|
||||
// Data array length
|
||||
for (int x = 0; x < this.width; x++)
|
||||
{
|
||||
for (int z = 0; z < this.width; z++)
|
||||
{
|
||||
outputStream.writeInt(this.get(x, z).getSingleLength());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Data array content (only on non-empty columns)
|
||||
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
for (int x = 0; x < this.width; x++)
|
||||
{
|
||||
for (int z = 0; z < this.width; z++)
|
||||
{
|
||||
SingleColumnFullDataAccessor columnAccessor = this.get(x, z);
|
||||
if (columnAccessor.doesColumnExist())
|
||||
{
|
||||
long[] dataPointArray = columnAccessor.getRaw();
|
||||
for (long dataPoint : dataPointArray)
|
||||
{
|
||||
outputStream.writeLong(dataPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public long[][] readDataPoints(DataSourceDto dto, int width, DhDataInputStream dataInputStream) throws IOException
|
||||
{
|
||||
// Data array length
|
||||
int dataPresentFlag = dataInputStream.readInt();
|
||||
if (dataPresentFlag == IFullDataSource.NO_DATA_FLAG_BYTE)
|
||||
{
|
||||
// Section is empty
|
||||
return null;
|
||||
}
|
||||
else if (dataPresentFlag != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("Invalid file format. Data Points guard byte expected: (no data) [" + IFullDataSource.NO_DATA_FLAG_BYTE + "] or (data present) [" + IFullDataSource.DATA_GUARD_BYTE + "], but found [" + dataPresentFlag + "].");
|
||||
}
|
||||
|
||||
|
||||
|
||||
long[][] dataPointArrays;
|
||||
if (this.width == width) // attempt to use the existing dataArrays if possible
|
||||
{
|
||||
dataPointArrays = this.dataArrays;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataPointArrays = new long[width * width][];
|
||||
}
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
int requestedArrayLength = dataInputStream.readInt();
|
||||
int arrayIndex = x * width + z;
|
||||
|
||||
// attempt to use the existing dataArrays if possible
|
||||
if (dataPointArrays[arrayIndex] == null || dataPointArrays[arrayIndex].length != requestedArrayLength)
|
||||
{
|
||||
dataPointArrays[arrayIndex] = new long[requestedArrayLength];
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear the existing array to prevent any data leakage
|
||||
Arrays.fill(dataPointArrays[arrayIndex], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// check if the array start flag is present
|
||||
int arrayStartFlag = dataInputStream.readInt();
|
||||
if (arrayStartFlag != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("invalid data length end guard");
|
||||
}
|
||||
|
||||
for (int xz = 0; xz < dataPointArrays.length; xz++) // x and z are combined
|
||||
{
|
||||
if (dataPointArrays[xz].length != 0)
|
||||
{
|
||||
for (int y = 0; y < dataPointArrays[xz].length; y++)
|
||||
{
|
||||
dataPointArrays[xz][y] = dataInputStream.readLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return dataPointArrays;
|
||||
}
|
||||
@Override
|
||||
public void setDataPoints(long[][] dataPoints)
|
||||
{
|
||||
LodUtil.assertTrue(this.dataArrays.length == dataPoints.length, "Data point array length mismatch.");
|
||||
|
||||
this.isEmpty = false;
|
||||
System.arraycopy(dataPoints, 0, this.dataArrays, 0, dataPoints.length);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeIdMappings(DhDataOutputStream outputStream) throws IOException
|
||||
{
|
||||
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
this.mapping.serialize(outputStream);
|
||||
}
|
||||
@Override
|
||||
public FullDataPointIdMap readIdMappings(long[][] dataPoints, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException
|
||||
{
|
||||
int guardByte = inputStream.readInt();
|
||||
if (guardByte != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("Invalid data content end guard for ID mapping");
|
||||
}
|
||||
|
||||
return FullDataPointIdMap.deserialize(inputStream, this.sectionPos, levelWrapper);
|
||||
}
|
||||
@Override
|
||||
public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); }
|
||||
|
||||
|
||||
|
||||
//======//
|
||||
// data //
|
||||
//======//
|
||||
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor tryGet(int relativeX, int relativeZ) { return this.get(relativeX, relativeZ); }
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor getOrCreate(int relativeX, int relativeZ) { return this.get(relativeX, relativeZ); }
|
||||
|
||||
@Override
|
||||
public void update(ChunkSizedFullDataAccessor chunkDataView)
|
||||
{
|
||||
LodUtil.assertTrue(this.sectionPos.overlapsExactly(chunkDataView.getSectionPos()));
|
||||
if (this.getDataDetailLevel() == LodUtil.BLOCK_DETAIL_LEVEL)
|
||||
{
|
||||
DhBlockPos2D chunkBlockPos = new DhBlockPos2D(chunkDataView.chunkPos.x * LodUtil.CHUNK_WIDTH, chunkDataView.chunkPos.z * LodUtil.CHUNK_WIDTH);
|
||||
DhBlockPos2D blockOffset = chunkBlockPos.subtract(this.sectionPos.getMinCornerLodPos().getCornerBlockPos());
|
||||
LodUtil.assertTrue(blockOffset.x >= 0 && blockOffset.x < WIDTH && blockOffset.z >= 0 && blockOffset.z < WIDTH);
|
||||
this.isEmpty = false;
|
||||
|
||||
chunkDataView.shadowCopyTo(this.subView(LodUtil.CHUNK_WIDTH, blockOffset.x, blockOffset.z));
|
||||
|
||||
// DEBUG ASSERTION
|
||||
{
|
||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
||||
{
|
||||
SingleColumnFullDataAccessor column = this.get(x + blockOffset.x, z + blockOffset.z);
|
||||
LodUtil.assertTrue(column.doesColumnExist());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.getDataDetailLevel() < LodUtil.CHUNK_DETAIL_LEVEL)
|
||||
{
|
||||
int dataPerFull = 1 << this.getDataDetailLevel();
|
||||
int fullSize = LodUtil.CHUNK_WIDTH / dataPerFull;
|
||||
DhLodPos dataOffset = chunkDataView.getSectionPos().getMinCornerLodPos(this.getDataDetailLevel());
|
||||
DhLodPos baseOffset = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel());
|
||||
|
||||
int offsetX = dataOffset.x - baseOffset.x;
|
||||
int offsetZ = dataOffset.z - baseOffset.z;
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetX < WIDTH && offsetZ >= 0 && offsetZ < WIDTH);
|
||||
|
||||
this.isEmpty = false;
|
||||
for (int xOffset = 0; xOffset < fullSize; xOffset++)
|
||||
{
|
||||
for (int zOffset = 0; zOffset < fullSize; zOffset++)
|
||||
{
|
||||
SingleColumnFullDataAccessor column = this.get(xOffset + offsetX, zOffset + offsetZ);
|
||||
column.downsampleFrom(chunkDataView.subView(dataPerFull, xOffset * dataPerFull, zOffset * dataPerFull));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (this.getDataDetailLevel() >= LodUtil.CHUNK_DETAIL_LEVEL)
|
||||
{
|
||||
//FIXME: TEMPORARY
|
||||
int chunkPerFull = 1 << (this.getDataDetailLevel() - LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
if (chunkDataView.chunkPos.x % chunkPerFull != 0 || chunkDataView.chunkPos.z % chunkPerFull != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DhLodPos baseOffset = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel());
|
||||
DhSectionPos dataOffset = chunkDataView.getSectionPos().convertNewToDetailLevel(this.getDataDetailLevel());
|
||||
|
||||
int offsetX = dataOffset.getX() - baseOffset.x;
|
||||
int offsetZ = dataOffset.getZ() - baseOffset.z;
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetX < WIDTH && offsetZ >= 0 && offsetZ < WIDTH);
|
||||
|
||||
this.isEmpty = false;
|
||||
chunkDataView.get(0, 0).deepCopyTo(this.get(offsetX, offsetZ));
|
||||
}
|
||||
else
|
||||
{
|
||||
LodUtil.assertNotReach();
|
||||
//TODO
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
/** Returns whether data at the given posToWrite can effect the target region file at posToTest. */
|
||||
public static boolean firstDataPosCanAffectSecond(DhSectionPos posToWrite, DhSectionPos posToTest)
|
||||
{
|
||||
if (!posToWrite.overlapsExactly(posToTest))
|
||||
{
|
||||
// the testPosition is outside the writePosition
|
||||
return false;
|
||||
}
|
||||
else if (posToTest.getDetailLevel() > posToWrite.getDetailLevel())
|
||||
{
|
||||
// the testPosition is larger (aka is less detailed) than the writePosition,
|
||||
// more detailed sections shouldn't be updated by lower detail sections
|
||||
return false;
|
||||
}
|
||||
else if (posToWrite.getDetailLevel() - posToTest.getDetailLevel() <= SECTION_SIZE_OFFSET)
|
||||
{
|
||||
// if the difference in detail levels is very large, the posToWrite
|
||||
// may be skipped, due to how we sample large detail levels by only
|
||||
// getting the corners.
|
||||
|
||||
// In this case the difference isn't very large, so return true
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the difference in detail levels is very large,
|
||||
// check if the posToWrite is in a corner of posToTest
|
||||
byte sectPerData = (byte) BitShiftUtil.powerOfTwo(posToWrite.getDetailLevel() - posToTest.getDetailLevel() - SECTION_SIZE_OFFSET);
|
||||
LodUtil.assertTrue(sectPerData != 0);
|
||||
return posToTest.getX() % sectPerData == 0 && posToTest.getZ() % sectPerData == 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// setters and getters //
|
||||
//=====================//
|
||||
|
||||
@Override
|
||||
public DhSectionPos getSectionPos() { return this.sectionPos; }
|
||||
|
||||
@Override
|
||||
public void resizeDataStructuresForRepopulation(DhSectionPos pos)
|
||||
{
|
||||
// no data structures need to be changed, only the source's position
|
||||
this.sectionPos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getDataDetailLevel() { return (byte) (this.sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET); }
|
||||
|
||||
@Override
|
||||
public byte getDataFormatVersion() { return DATA_FORMAT_VERSION; }
|
||||
|
||||
@Override
|
||||
public EDhApiWorldGenerationStep getWorldGenStep() { return this.worldGenStep; }
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() { return this.isEmpty; }
|
||||
@Override
|
||||
public void markNotEmpty() { this.isEmpty = false; }
|
||||
|
||||
@Override
|
||||
public int getWidthInDataPoints() { return this.width; }
|
||||
|
||||
}
|
||||
+410
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* 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.dataObjects.fullData.sources;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.file.IDataSource;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV1DTO;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Formerly "CompleteFullDataSource". <br>
|
||||
* Should be fully populated, containing 1 data point for each column. <br><br>
|
||||
*
|
||||
* Replaced by {@link FullDataSourceV2}.
|
||||
*
|
||||
* @see FullDataPointUtil
|
||||
* @see FullDataSourceV2
|
||||
*/
|
||||
public class FullDataSourceV1 implements IDataSource<IDhLevel>
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static final byte SECTION_SIZE_OFFSET = DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
/** measured in dataPoints */
|
||||
public static final int WIDTH = BitShiftUtil.powerOfTwo(SECTION_SIZE_OFFSET);
|
||||
|
||||
public static final byte DATA_FORMAT_VERSION = 3;
|
||||
/** never used but should stay here. */
|
||||
public static final String DATA_TYPE_NAME = "CompleteFullDataSource";
|
||||
|
||||
/**
|
||||
* This is the byte put between different sections in the binary save file.
|
||||
* The presence and absence of this byte indicates if the file is correctly formatted.
|
||||
*/
|
||||
private static final int DATA_GUARD_BYTE = 0xFFFFFFFF;
|
||||
/** indicates the binary save file represents an empty data source */
|
||||
private static final int NO_DATA_FLAG_BYTE = 0x00000001;
|
||||
|
||||
|
||||
public final FullDataPointIdMap mapping;
|
||||
public EDhApiWorldGenerationStep worldGenStep = EDhApiWorldGenerationStep.EMPTY;
|
||||
|
||||
|
||||
/** A flattened 2D array (for the X and Z directions) containing an array for the Y direction. */
|
||||
private final long[][] dataArrays;
|
||||
|
||||
private long sectionPos;
|
||||
|
||||
private boolean isEmpty = true;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static FullDataSourceV1 createEmpty(long pos) { return new FullDataSourceV1(pos); }
|
||||
private FullDataSourceV1(long sectionPos)
|
||||
{
|
||||
this.dataArrays = new long[WIDTH * WIDTH][0];
|
||||
this.mapping = new FullDataPointIdMap(sectionPos);
|
||||
this.sectionPos = sectionPos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//======//
|
||||
// data //
|
||||
//======//
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean update(FullDataSourceV2 dataSource, IDhLevel level) { throw new UnsupportedOperationException("Deprecated"); }
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// setters and getters //
|
||||
//=====================//
|
||||
|
||||
@Override
|
||||
public Long getKey() { return this.sectionPos; }
|
||||
|
||||
@Override
|
||||
public Long getPos() { return this.sectionPos; }
|
||||
|
||||
public void resizeDataStructuresForRepopulation(long pos)
|
||||
{
|
||||
// no data structures need to be changed, only the source's position
|
||||
this.sectionPos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.sectionPos) - SECTION_SIZE_OFFSET); }
|
||||
|
||||
public boolean isEmpty() { return this.isEmpty; }
|
||||
|
||||
|
||||
public long[] get(int index) { return this.get(index / WIDTH, index % WIDTH); }
|
||||
public long[] get(int relativeX, int relativeZ)
|
||||
{
|
||||
int dataArrayIndex = (relativeX * WIDTH) + relativeZ;
|
||||
if (dataArrayIndex >= this.dataArrays.length)
|
||||
{
|
||||
LodUtil.assertNotReach(
|
||||
"FullDataArrayAccessor.get() called with a relative position that is outside the data source. \n" +
|
||||
"given relative pos X: [" + relativeX + "] Z: [" + relativeZ + "]\n" +
|
||||
"dataArrays.length: [" + this.dataArrays.length + "] dataArrayIndex: [" + dataArrayIndex + "].");
|
||||
}
|
||||
|
||||
return this.dataArrays[dataArrayIndex];
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// stream handling //
|
||||
//=================//
|
||||
|
||||
/**
|
||||
* Clears and then overwrites any data in this object with the data from the given file and stream.
|
||||
* This is expected to be used with an existing {@link FullDataSourceV1} and can be used in place of a constructor to reuse an existing {@link FullDataSourceV1} object.
|
||||
*/
|
||||
public void repopulateFromStream(FullDataSourceV1DTO dto, DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
// clear/overwrite the old data
|
||||
this.resizeDataStructuresForRepopulation(dto.pos);
|
||||
this.mapping.clear(dto.pos);
|
||||
|
||||
// set the new data
|
||||
this.populateFromStream(dto, inputStream, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites any data in this object with the data from the given file and stream.
|
||||
* This is expected to be used with an empty {@link FullDataSourceV1} and functions similar to a constructor.
|
||||
*/
|
||||
public void populateFromStream(FullDataSourceV1DTO dto, DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
FullDataSourceSummaryData summaryData = this.readSourceSummaryInfo(dto, inputStream, level);
|
||||
this.setSourceSummaryData(summaryData);
|
||||
|
||||
|
||||
long[][] dataPoints = this.readDataPoints(summaryData.dataWidth, inputStream);
|
||||
if (dataPoints == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.setDataPoints(dataPoints);
|
||||
|
||||
|
||||
FullDataPointIdMap mapping = this.readIdMappings(inputStream, level.getLevelWrapper());
|
||||
this.setIdMapping(mapping);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// low level stream methods //
|
||||
|
||||
/** unused, just here for reference as to how the data was written */
|
||||
@Deprecated
|
||||
public void writeSourceSummaryInfo(IDhLevel level, DhDataOutputStream outputStream) throws IOException
|
||||
{
|
||||
outputStream.writeInt(this.getDataDetailLevel());
|
||||
outputStream.writeInt(WIDTH);
|
||||
outputStream.writeInt(level.getMinY());
|
||||
outputStream.writeByte(this.worldGenStep.value);
|
||||
|
||||
}
|
||||
public FullDataSourceSummaryData readSourceSummaryInfo(FullDataSourceV1DTO dto, DhDataInputStream inputStream, IDhLevel level) throws IOException
|
||||
{
|
||||
int dataDetail = inputStream.readInt();
|
||||
if (dataDetail != dto.dataDetailLevel)
|
||||
{
|
||||
throw new IOException(LodUtil.formatLog("Data level mismatch. Expected: ["+dto.dataDetailLevel+"], found ["+dataDetail+"]."));
|
||||
}
|
||||
|
||||
int width = inputStream.readInt();
|
||||
if (width != WIDTH)
|
||||
{
|
||||
throw new IOException(LodUtil.formatLog("Section width mismatch: " + width + " != " + WIDTH + " (Currently only 1 section width is supported)"));
|
||||
}
|
||||
|
||||
int minY = inputStream.readInt();
|
||||
if (minY != level.getMinY())
|
||||
{
|
||||
LOGGER.warn("Data minY mismatch: " + minY + " != " + level.getMinY() + ". Will ignore data's y level");
|
||||
}
|
||||
|
||||
byte worldGenByte = inputStream.readByte();
|
||||
EDhApiWorldGenerationStep worldGenStep = EDhApiWorldGenerationStep.fromValue(worldGenByte);
|
||||
if (worldGenStep == null)
|
||||
{
|
||||
worldGenStep = EDhApiWorldGenerationStep.SURFACE;
|
||||
LOGGER.warn("Missing WorldGenStep, defaulting to: " + worldGenStep.name());
|
||||
}
|
||||
|
||||
|
||||
return new FullDataSourceSummaryData(width, worldGenStep);
|
||||
}
|
||||
public void setSourceSummaryData(FullDataSourceSummaryData summaryData) { this.worldGenStep = summaryData.worldGenStep; }
|
||||
|
||||
/** unused, just here for reference as to how the data was written */
|
||||
@Deprecated
|
||||
public boolean writeDataPoints(DhDataOutputStream outputStream) throws IOException
|
||||
{
|
||||
if (this.isEmpty())
|
||||
{
|
||||
outputStream.writeInt(NO_DATA_FLAG_BYTE);
|
||||
return false;
|
||||
}
|
||||
outputStream.writeInt(DATA_GUARD_BYTE);
|
||||
|
||||
|
||||
|
||||
// Data array length
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < WIDTH; z++)
|
||||
{
|
||||
outputStream.writeInt(this.get(x, z).length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Data array content (only on non-empty columns)
|
||||
outputStream.writeInt(DATA_GUARD_BYTE);
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < WIDTH; z++)
|
||||
{
|
||||
long[] dataColumn = this.get(x, z);
|
||||
if (dataColumn != null)
|
||||
{
|
||||
for (long dataPoint : dataColumn)
|
||||
{
|
||||
outputStream.writeLong(dataPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
public long[][] readDataPoints(int width, DhDataInputStream dataInputStream) throws IOException
|
||||
{
|
||||
// Data array length
|
||||
int dataPresentFlag = dataInputStream.readInt();
|
||||
if (dataPresentFlag == NO_DATA_FLAG_BYTE)
|
||||
{
|
||||
// Section is empty
|
||||
return null;
|
||||
}
|
||||
else if (dataPresentFlag != DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("Invalid file format. Data Points guard byte expected: (no data) [" + NO_DATA_FLAG_BYTE + "] or (data present) [" + DATA_GUARD_BYTE + "], but found [" + dataPresentFlag + "].");
|
||||
}
|
||||
|
||||
|
||||
|
||||
long[][] dataPointArrays;
|
||||
if (WIDTH == width) // attempt to use the existing dataArrays if possible
|
||||
{
|
||||
dataPointArrays = this.dataArrays;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataPointArrays = new long[width * width][];
|
||||
}
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
int requestedArrayLength = dataInputStream.readInt();
|
||||
int arrayIndex = x * width + z;
|
||||
|
||||
// attempt to use the existing dataArrays if possible
|
||||
if (dataPointArrays[arrayIndex] == null || dataPointArrays[arrayIndex].length != requestedArrayLength)
|
||||
{
|
||||
dataPointArrays[arrayIndex] = new long[requestedArrayLength];
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear the existing array to prevent any data leakage
|
||||
Arrays.fill(dataPointArrays[arrayIndex], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// check if the array start flag is present
|
||||
int arrayStartFlag = dataInputStream.readInt();
|
||||
if (arrayStartFlag != DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("invalid data length end guard");
|
||||
}
|
||||
|
||||
for (int xz = 0; xz < dataPointArrays.length; xz++) // x and z are combined
|
||||
{
|
||||
if (dataPointArrays[xz].length != 0)
|
||||
{
|
||||
for (int y = 0; y < dataPointArrays[xz].length; y++)
|
||||
{
|
||||
dataPointArrays[xz][y] = dataInputStream.readLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return dataPointArrays;
|
||||
}
|
||||
public void setDataPoints(long[][] dataPoints)
|
||||
{
|
||||
LodUtil.assertTrue(this.dataArrays.length == dataPoints.length, "Data point array length mismatch.");
|
||||
|
||||
this.isEmpty = false;
|
||||
System.arraycopy(dataPoints, 0, this.dataArrays, 0, dataPoints.length);
|
||||
}
|
||||
|
||||
|
||||
/** unused, just here for reference as to how the data was written */
|
||||
@Deprecated
|
||||
public void writeIdMappings(DhDataOutputStream outputStream) throws IOException
|
||||
{
|
||||
outputStream.writeInt(DATA_GUARD_BYTE);
|
||||
this.mapping.serialize(outputStream);
|
||||
}
|
||||
public FullDataPointIdMap readIdMappings(DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
int guardByte = inputStream.readInt();
|
||||
if (guardByte != DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("Invalid data content end guard for ID mapping");
|
||||
}
|
||||
|
||||
return FullDataPointIdMap.deserialize(inputStream, this.sectionPos, levelWrapper);
|
||||
}
|
||||
public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); }
|
||||
|
||||
|
||||
//==================//
|
||||
// override methods //
|
||||
//==================//
|
||||
|
||||
@Override
|
||||
public void close() throws Exception
|
||||
{ /* not currently needed */ }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
/**
|
||||
* This holds information that is relevant to the entire source and isn't stored in the data points. <br>
|
||||
* Example: minimum height, detail level, source type, etc.
|
||||
*/
|
||||
private static class FullDataSourceSummaryData
|
||||
{
|
||||
public final int dataWidth;
|
||||
public EDhApiWorldGenerationStep worldGenStep;
|
||||
|
||||
|
||||
public FullDataSourceSummaryData(int dataWidth, EDhApiWorldGenerationStep worldGenStep)
|
||||
{
|
||||
this.dataWidth = dataWidth;
|
||||
this.worldGenStep = worldGenStep;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+951
@@ -0,0 +1,951 @@
|
||||
/*
|
||||
* 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.dataObjects.fullData.sources;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiWorldCompressionMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.dataObjects.transformers.LodDataBuilder;
|
||||
import com.seibel.distanthorizons.core.file.AbstractDataSourceHandler;
|
||||
import com.seibel.distanthorizons.core.file.DataSourcePool;
|
||||
import com.seibel.distanthorizons.core.file.IDataSource;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This data source contains every datapoint over its given {@link DhSectionPos}. <br><br>
|
||||
*
|
||||
* @see FullDataPointUtil
|
||||
* @see FullDataSourceV1
|
||||
*/
|
||||
public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
/** useful for debugging, but can slow down update operations quite a bit due to being called so often. */
|
||||
private static final boolean RUN_UPDATE_DEV_VALIDATION = false;
|
||||
/**
|
||||
* If the data column order isn't correct
|
||||
* block lighting may appear broken
|
||||
* and/or certain detail level LODs may not appear at all.
|
||||
*/
|
||||
private static final boolean RUN_DATA_ORDER_VALIDATION = ModInfo.IS_DEV_BUILD;
|
||||
|
||||
/** measured in data columns */
|
||||
public static final int WIDTH = 64;
|
||||
|
||||
public static final byte DATA_FORMAT_VERSION = 1;
|
||||
|
||||
public static final DataSourcePool<FullDataSourceV2, IDhLevel> DATA_SOURCE_POOL = new DataSourcePool<>(FullDataSourceV2::createEmpty, FullDataSourceV2::prepPooledDataSource);
|
||||
|
||||
|
||||
|
||||
private int cachedHashCode = 0;
|
||||
|
||||
private long pos;
|
||||
@Override
|
||||
public Long getKey() { return this.pos; }
|
||||
|
||||
|
||||
public final FullDataPointIdMap mapping;
|
||||
|
||||
|
||||
public long lastModifiedUnixDateTime;
|
||||
public long createdUnixDateTime;
|
||||
|
||||
public int levelMinY;
|
||||
|
||||
/**
|
||||
* stores how far each column has been generated should start with {@link EDhApiWorldGenerationStep#EMPTY}
|
||||
* @see EDhApiWorldGenerationStep
|
||||
*/
|
||||
public byte[] columnGenerationSteps;
|
||||
/**
|
||||
* stores what world compression was used for each column.
|
||||
* @see EDhApiWorldCompressionMode
|
||||
*/
|
||||
public byte[] columnWorldCompressionMode;
|
||||
|
||||
/**
|
||||
* stored x/z, y <br>
|
||||
* The y data should be sorted from top to bottom <br>
|
||||
* TODO that ordering feels weird, it'd be nice to reverse that order, unfortunately
|
||||
* there's something in the render data logic that expects this order so we can't change it right now
|
||||
*/
|
||||
public LongArrayList[] dataPoints;
|
||||
|
||||
public boolean isEmpty;
|
||||
public boolean applyToParent = false;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static FullDataSourceV2 createEmpty(long pos) { return new FullDataSourceV2(pos); }
|
||||
private FullDataSourceV2(long pos)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.dataPoints = new LongArrayList[WIDTH * WIDTH];
|
||||
this.mapping = new FullDataPointIdMap(pos);
|
||||
this.isEmpty = true;
|
||||
|
||||
// doesn't need to be populated since nothing has been generated yet
|
||||
// the default value of all 0's is adequate
|
||||
this.columnGenerationSteps = new byte[WIDTH * WIDTH];
|
||||
this.columnWorldCompressionMode = new byte[WIDTH * WIDTH];
|
||||
}
|
||||
|
||||
public static FullDataSourceV2 createWithData(long pos, FullDataPointIdMap mapping, LongArrayList[] data, byte[] columnGenerationStep, byte[] columnWorldCompressionMode) { return new FullDataSourceV2(pos, mapping, data, columnGenerationStep, columnWorldCompressionMode); }
|
||||
private FullDataSourceV2(long pos, FullDataPointIdMap mapping, LongArrayList[] data, byte[] columnGenerationSteps, byte[] columnWorldCompressionMode)
|
||||
{
|
||||
LodUtil.assertTrue(data.length == WIDTH * WIDTH);
|
||||
|
||||
this.pos = pos;
|
||||
this.dataPoints = data;
|
||||
this.mapping = mapping;
|
||||
this.isEmpty = false;
|
||||
|
||||
this.columnGenerationSteps = columnGenerationSteps;
|
||||
this.columnWorldCompressionMode = columnWorldCompressionMode;
|
||||
}
|
||||
|
||||
public static FullDataSourceV2 createFromChunk(IChunkWrapper chunkWrapper) { return LodDataBuilder.createGeneratedDataSource(chunkWrapper); }
|
||||
|
||||
public static FullDataSourceV2 createFromLegacyDataSourceV1(FullDataSourceV1 legacyData)
|
||||
{
|
||||
if (FullDataSourceV1.WIDTH != WIDTH)
|
||||
{
|
||||
throw new UnsupportedOperationException(
|
||||
"Unable to convert ["+FullDataSourceV1.class.getSimpleName()+"] into ["+FullDataSourceV2.class.getSimpleName()+"]. " +
|
||||
"Data sources have different data point widths and no converter is present. " +
|
||||
"input width ["+ FullDataSourceV1.WIDTH+"], recipient width ["+WIDTH+"].");
|
||||
}
|
||||
|
||||
|
||||
// Note: this logic only works if the data point data is the same between both versions
|
||||
byte[] columnGenerationSteps = new byte[WIDTH * WIDTH];
|
||||
byte[] columnWorldCompressionMode = new byte[WIDTH * WIDTH];
|
||||
LongArrayList[] dataPoints = new LongArrayList[WIDTH * WIDTH];
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < WIDTH; z++)
|
||||
{
|
||||
long[] legacyDataColumn = legacyData.get(x, z);
|
||||
if (legacyDataColumn != null && legacyDataColumn.length != 0)
|
||||
{
|
||||
int index = relativePosToIndex(x, z);
|
||||
LongArrayList newDataColumn = new LongArrayList(legacyDataColumn);
|
||||
|
||||
|
||||
// convert the data point format
|
||||
boolean columnHasNonAirBlock = false;
|
||||
for (int i = 0; i < legacyDataColumn.length; i++)
|
||||
{
|
||||
long dataPoint = legacyDataColumn[i];
|
||||
|
||||
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
|
||||
blockLight = 0;
|
||||
}
|
||||
|
||||
dataPoint = FullDataPointUtil.setBlockLight(dataPoint, blockLight);
|
||||
newDataColumn.set(i, dataPoint);
|
||||
|
||||
|
||||
// check if this datapoint is air
|
||||
if (!columnHasNonAirBlock && !isAir)
|
||||
{
|
||||
columnHasNonAirBlock = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// save the converted data point
|
||||
ensureDataColumnOrder(newDataColumn);
|
||||
dataPoints[index] = newDataColumn;
|
||||
|
||||
// the old data sources didn't have a generation step written down
|
||||
// if the column has any data points, assume it's fully generated, otherwise assume it's empty
|
||||
columnGenerationSteps[index] = (columnHasNonAirBlock ? EDhApiWorldGenerationStep.LIGHT.value : EDhApiWorldGenerationStep.EMPTY.value);
|
||||
columnWorldCompressionMode[index] = EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FullDataSourceV2 fullDataSource = FullDataSourceV2.createWithData(legacyData.getPos(), legacyData.mapping, dataPoints, columnGenerationSteps, columnWorldCompressionMode);
|
||||
return fullDataSource;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//======//
|
||||
// data //
|
||||
//======//
|
||||
|
||||
public LongArrayList get(int relX, int relZ) throws IndexOutOfBoundsException { return this.dataPoints[relativePosToIndex(relX, relZ)]; }
|
||||
|
||||
@Override
|
||||
public boolean update(@NotNull FullDataSourceV2 inputDataSource, @Nullable IDhLevel level) { return this.update(inputDataSource); }
|
||||
public boolean update(@NotNull FullDataSourceV2 inputDataSource)
|
||||
{
|
||||
// don't try updating if the input is empty
|
||||
if (inputDataSource.mapping.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
byte thisDetailLevel = DhSectionPos.getDetailLevel(this.pos);
|
||||
byte inputDetailLevel = DhSectionPos.getDetailLevel(inputDataSource.pos);
|
||||
|
||||
|
||||
// determine the mapping changes necessary for the input to map onto this datasource
|
||||
int[] remappedIds = this.mapping.mergeAndReturnRemappedEntityIds(inputDataSource.mapping);
|
||||
|
||||
boolean dataChanged;
|
||||
if (inputDetailLevel == thisDetailLevel)
|
||||
{
|
||||
dataChanged = this.updateFromSameDetailLevel(inputDataSource, remappedIds);
|
||||
}
|
||||
else if (inputDetailLevel + 1 == thisDetailLevel)
|
||||
{
|
||||
dataChanged = this.updateFromOneBelowDetailLevel(inputDataSource, remappedIds);
|
||||
}
|
||||
else
|
||||
{
|
||||
// other detail levels aren't supported since it would be more difficult to maintain
|
||||
// and would lead to edge cases that don't necessarily need to be supported
|
||||
// (IE what do you do when the input is smaller than a single datapoint in the receiving data source?)
|
||||
// instead it's better to just percolate the updates up
|
||||
throw new UnsupportedOperationException("Unsupported data source update. Expected input detail level of ["+thisDetailLevel+"] or ["+(thisDetailLevel+1)+"], received detail level ["+inputDetailLevel+"].");
|
||||
}
|
||||
|
||||
// determine if this data source should be applied to its parent
|
||||
this.applyToParent = (dataChanged && DhSectionPos.getDetailLevel(this.pos) < AbstractDataSourceHandler.TOP_SECTION_DETAIL_LEVEL);
|
||||
|
||||
if (dataChanged)
|
||||
{
|
||||
// update the hash code
|
||||
this.generateHashCode();
|
||||
}
|
||||
|
||||
return dataChanged;
|
||||
}
|
||||
public boolean updateFromSameDetailLevel(FullDataSourceV2 inputDataSource, int[] remappedIds)
|
||||
{
|
||||
// both data sources should have the same detail level
|
||||
if (DhSectionPos.getDetailLevel(inputDataSource.pos) != DhSectionPos.getDetailLevel(this.pos))
|
||||
{
|
||||
throw new IllegalArgumentException("Both data sources must have the same detail level. Expected ["+ DhSectionPos.getDetailLevel(this.pos)+"], received ["+ DhSectionPos.getDetailLevel(inputDataSource.pos)+"].");
|
||||
}
|
||||
|
||||
// copy over everything from the input data source into this one
|
||||
// provided there is data to copy and the world generation step is the same or more complete
|
||||
boolean dataChanged = false;
|
||||
for (int x = 0; x < WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < WIDTH; z++)
|
||||
{
|
||||
int index = relativePosToIndex(x, z);
|
||||
|
||||
LongArrayList inputDataArray = inputDataSource.dataPoints[index];
|
||||
if (inputDataArray != null)
|
||||
{
|
||||
byte thisGenState = this.columnGenerationSteps[index];
|
||||
byte inputGenState = inputDataSource.columnGenerationSteps[index];
|
||||
|
||||
if (inputGenState != EDhApiWorldGenerationStep.EMPTY.value
|
||||
&& thisGenState <= inputGenState)
|
||||
{
|
||||
// check if the data changed
|
||||
if (this.dataPoints[index] == null)
|
||||
{
|
||||
// no data was present previously
|
||||
this.dataPoints[index] = new LongArrayList(inputDataArray);
|
||||
dataChanged = true;
|
||||
}
|
||||
else if (this.dataPoints[index].size() != inputDataArray.size())
|
||||
{
|
||||
// data is present, but the size is different
|
||||
dataChanged = true;
|
||||
}
|
||||
|
||||
int oldDataHash = 0;
|
||||
if (!dataChanged)
|
||||
{
|
||||
// some old data existed with the same length,
|
||||
// we'll have to compare the caches
|
||||
oldDataHash = this.dataPoints[index].hashCode();
|
||||
}
|
||||
|
||||
|
||||
// copy over the new data
|
||||
this.dataPoints[index].clear();
|
||||
this.dataPoints[index].addAll(inputDataArray);
|
||||
this.remapDataColumn(index, remappedIds);
|
||||
|
||||
if (RUN_DATA_ORDER_VALIDATION)
|
||||
{
|
||||
throwIfDataColumnInWrongOrder(inputDataSource.pos, this.dataPoints[index]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!dataChanged)
|
||||
{
|
||||
// check if the identical length data column hashes are the same
|
||||
// hashes need to be compared after the ID's have been remapped otherwise the ID's won't match even if the data is the same
|
||||
if (oldDataHash != this.dataPoints[index].hashCode())
|
||||
{
|
||||
// the hashes are different, something was changed
|
||||
dataChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.columnGenerationSteps[index] = inputGenState;
|
||||
// always overwrite the compression mode since we're replacing this column
|
||||
this.columnWorldCompressionMode[index] = inputDataSource.columnWorldCompressionMode[index];
|
||||
this.isEmpty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dataChanged;
|
||||
}
|
||||
public boolean updateFromOneBelowDetailLevel(FullDataSourceV2 inputDataSource, int[] remappedIds)
|
||||
{
|
||||
if (DhSectionPos.getDetailLevel(inputDataSource.pos) + 1 != DhSectionPos.getDetailLevel(this.pos))
|
||||
{
|
||||
throw new IllegalArgumentException("Input data source must be exactly 1 detail level below this data source. Expected [" + (DhSectionPos.getDetailLevel(this.pos) - 1) + "], received [" + DhSectionPos.getDetailLevel(inputDataSource.pos) + "].");
|
||||
}
|
||||
|
||||
// input is one detail level lower (higher detail)
|
||||
// so 2x2 input data points will be converted into 1 recipient data point
|
||||
|
||||
|
||||
// determine where in the input data source should be written to
|
||||
// since the input is one detail level below it will be one of this position's 4 children
|
||||
int minChildXPos = DhSectionPos.getX(DhSectionPos.getChildByIndex(this.pos, 0));
|
||||
int recipientOffsetX = (DhSectionPos.getX(inputDataSource.pos) == minChildXPos) ? 0 : (WIDTH / 2);
|
||||
int minChildZPos = DhSectionPos.getZ(DhSectionPos.getChildByIndex(this.pos, 0));
|
||||
int recipientOffsetZ = (DhSectionPos.getZ(inputDataSource.pos) == minChildZPos) ? 0 : (WIDTH / 2);
|
||||
|
||||
|
||||
|
||||
// merge the input's data points
|
||||
// into this data source's
|
||||
boolean dataChanged = false;
|
||||
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);
|
||||
|
||||
|
||||
// world gen //
|
||||
byte inputGenStep = determineMinWorldGenStepForTwoByTwoColumn(inputDataSource.columnGenerationSteps, x, z);
|
||||
this.columnGenerationSteps[recipientIndex] = inputGenStep;
|
||||
|
||||
|
||||
// world compression //
|
||||
byte worldCompressionMode = determineHighestWorldCompressionForTwoByTwoColumn(inputDataSource.columnWorldCompressionMode, x, z);
|
||||
this.columnWorldCompressionMode[recipientIndex] = worldCompressionMode;
|
||||
|
||||
|
||||
|
||||
// data points //
|
||||
LongArrayList mergedInputDataArray = mergeInputTwoByTwoDataColumn(inputDataSource, x, z);
|
||||
|
||||
// check if the data changed
|
||||
if (this.dataPoints[recipientIndex] == null)
|
||||
{
|
||||
// no data was present previously
|
||||
dataChanged = true;
|
||||
}
|
||||
else if (this.dataPoints[recipientIndex].size() != mergedInputDataArray.size())
|
||||
{
|
||||
// data is present, but the size is different
|
||||
dataChanged = true;
|
||||
}
|
||||
|
||||
int oldDataHash = 0;
|
||||
if (!dataChanged)
|
||||
{
|
||||
// some old data existed with the same length,
|
||||
// we'll have to compare the caches
|
||||
oldDataHash = this.dataPoints[recipientIndex].hashCode();
|
||||
}
|
||||
|
||||
|
||||
this.dataPoints[recipientIndex] = mergedInputDataArray;
|
||||
this.remapDataColumn(recipientIndex, remappedIds);
|
||||
|
||||
if (RUN_DATA_ORDER_VALIDATION)
|
||||
{
|
||||
throwIfDataColumnInWrongOrder(inputDataSource.pos, this.dataPoints[recipientIndex]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!dataChanged)
|
||||
{
|
||||
// check if the identical length data column hashes are the same
|
||||
// hashes need to be compared after the ID's have been remapped otherwise the ID's won't match even if the data is the same
|
||||
if (oldDataHash != this.dataPoints[recipientIndex].hashCode())
|
||||
{
|
||||
// the hashes are different, something was changed
|
||||
dataChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.isEmpty = false;
|
||||
}
|
||||
}
|
||||
|
||||
return dataChanged;
|
||||
}
|
||||
/**
|
||||
* The minimum value is used because we don't want to accidentally record that
|
||||
* something was generated when it wasn't.
|
||||
*/
|
||||
private static byte determineMinWorldGenStepForTwoByTwoColumn(byte[] columnGenerationSteps, int relX, int relZ)
|
||||
{
|
||||
// TODO merge similar logic with determineHighestWorldCompressionForTwoByTwoColumn
|
||||
byte minWorldGenStepValue = Byte.MAX_VALUE;
|
||||
for (int x = 0; x < 2; x++)
|
||||
{
|
||||
for (int z = 0; z < 2; z++)
|
||||
{
|
||||
int index = relativePosToIndex(x + relX, z + relZ);
|
||||
byte worldGenStepValue = columnGenerationSteps[index];
|
||||
minWorldGenStepValue = (byte) Math.min(minWorldGenStepValue, worldGenStepValue);
|
||||
}
|
||||
}
|
||||
return minWorldGenStepValue;
|
||||
}
|
||||
/**
|
||||
* The minimum value is used because we don't want to accidentally record that
|
||||
* something was generated when it wasn't.
|
||||
*/
|
||||
private static byte determineHighestWorldCompressionForTwoByTwoColumn(byte[] columnCompressionMode, int relX, int relZ)
|
||||
{
|
||||
// TODO merge similar logic with determineMinWorldGenStepForTwoByTwoColumn
|
||||
byte minWorldGenStepValue = Byte.MIN_VALUE;
|
||||
for (int x = 0; x < 2; x++)
|
||||
{
|
||||
for (int z = 0; z < 2; z++)
|
||||
{
|
||||
int index = relativePosToIndex(x + relX, z + relZ);
|
||||
byte worldGenStepValue = columnCompressionMode[index];
|
||||
minWorldGenStepValue = (byte) Math.max(minWorldGenStepValue, worldGenStepValue);
|
||||
}
|
||||
}
|
||||
return minWorldGenStepValue;
|
||||
}
|
||||
private static LongArrayList mergeInputTwoByTwoDataColumn(FullDataSourceV2 inputDataSource, int x, int z)
|
||||
{
|
||||
LongArrayList newColumnList = new LongArrayList();
|
||||
|
||||
// special numbers:
|
||||
// -2 = the column's height hasn't been determined yet
|
||||
// -1 = we've reached the end of the column
|
||||
int[] currentDatapointIndex = new int[] { -2, -2, -2, -2 };
|
||||
|
||||
int lastId = 0;
|
||||
byte lastBlockLight = 0;
|
||||
byte lastSkyLight = 0;
|
||||
int height = 0;
|
||||
int minY = 0;
|
||||
|
||||
|
||||
// these arrays will be reused quite often, so re-using them helps reduce some GC pressure
|
||||
long[] datapointsForYSlice = new long[4];
|
||||
int[] mergeIds = new int[4];
|
||||
int[] mergeBlockLights = new int[4];
|
||||
int[] mergeSkyLights = new int[4];
|
||||
|
||||
|
||||
for (int blockY = 0; blockY < RenderDataPointUtil.MAX_WORLD_Y_SIZE; blockY++, height++)
|
||||
{
|
||||
// if each column has reached the end of their data, nothing more needs to be done
|
||||
if (currentDatapointIndex[0] == -1
|
||||
&& currentDatapointIndex[1] == -1
|
||||
&& currentDatapointIndex[2] == -1
|
||||
&& currentDatapointIndex[3] == -1
|
||||
)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// scary double loop but,
|
||||
// this will only ever loop 4 times,
|
||||
// once for each of the 4 input columns
|
||||
Arrays.fill(datapointsForYSlice, 0L);
|
||||
int colIndex = 0;
|
||||
for (int inputX = x; inputX < x + 2; inputX++)
|
||||
{
|
||||
for (int inputZ = z; inputZ < z + 2; inputZ++, colIndex++)
|
||||
{
|
||||
// TODO throw an assertion if the column isn't in top-down order or just fix it...
|
||||
LongArrayList inputDataArray = inputDataSource.dataPoints[relativePosToIndex(inputX, inputZ)];
|
||||
if (inputDataArray == null || inputDataArray.size() == 0)
|
||||
{
|
||||
currentDatapointIndex[colIndex] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// determine the last index (the lowest data point) for each column
|
||||
if (currentDatapointIndex[colIndex] == -2)
|
||||
{
|
||||
currentDatapointIndex[colIndex] = inputDataArray.size() - 1;
|
||||
|
||||
if (RUN_DATA_ORDER_VALIDATION)
|
||||
{
|
||||
throwIfDataColumnInWrongOrder(inputDataSource.pos, inputDataArray);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int dataPointIndex = currentDatapointIndex[colIndex];
|
||||
if (dataPointIndex == -1)
|
||||
{
|
||||
// went over the end
|
||||
continue;
|
||||
}
|
||||
long datapoint = inputDataArray.getLong(dataPointIndex);
|
||||
|
||||
int datapointMinY = FullDataPointUtil.getBottomY(datapoint);
|
||||
int numbOfBlocksTall = FullDataPointUtil.getHeight(datapoint);
|
||||
int datapointMaxY = (datapointMinY + numbOfBlocksTall);
|
||||
|
||||
|
||||
// check if y position is inside this datapoint
|
||||
if (blockY < datapointMinY)
|
||||
{
|
||||
// this y-slice is below this datapoint, nothing can be added
|
||||
continue;
|
||||
}
|
||||
else if (blockY >= datapointMaxY)
|
||||
{
|
||||
// this y-slice is above the current datapoint,
|
||||
// try the next data point
|
||||
|
||||
int newDatapointIndex = currentDatapointIndex[colIndex] - 1;
|
||||
if (newDatapointIndex < 0)
|
||||
{
|
||||
// went to far, no additional data present
|
||||
newDatapointIndex = -1;
|
||||
}
|
||||
currentDatapointIndex[colIndex] = newDatapointIndex;
|
||||
|
||||
|
||||
// try again with the next data point
|
||||
inputZ--;
|
||||
colIndex--;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
datapointsForYSlice[colIndex] = datapoint;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Arrays.fill(mergeIds, 0);
|
||||
Arrays.fill(mergeBlockLights, 0);
|
||||
Arrays.fill(mergeSkyLights, 0);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
mergeIds[i] = FullDataPointUtil.getId(datapointsForYSlice[i]);
|
||||
mergeBlockLights[i] = FullDataPointUtil.getBlockLight(datapointsForYSlice[i]);
|
||||
mergeSkyLights[i] = FullDataPointUtil.getSkyLight(datapointsForYSlice[i]);
|
||||
}
|
||||
|
||||
|
||||
// determine the most common values for this slice
|
||||
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
|
||||
|| blockLight != lastBlockLight
|
||||
|| skyLight != lastSkyLight)
|
||||
{
|
||||
if (height != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
long datapoint = FullDataPointUtil.encode(lastId, height, minY, lastBlockLight, lastSkyLight);
|
||||
newColumnList.add(datapoint);
|
||||
}
|
||||
catch (DataCorruptedException e)
|
||||
{
|
||||
// shouldn't happen, (especially if validation is disabled) but just in case
|
||||
LOGGER.warn("Skipping corrupt datapoint for pos "+inputDataSource.pos+" at relative position ["+x+","+z+"] with data: ID["+lastId+"], Height["+height+"], minY["+minY+"], lastBlockLight["+lastBlockLight+"], lastSkyLight["+lastSkyLight+"].");
|
||||
}
|
||||
}
|
||||
|
||||
lastId = id;
|
||||
lastBlockLight = blockLight;
|
||||
lastSkyLight = skyLight;
|
||||
height = 0;
|
||||
minY = blockY;
|
||||
}
|
||||
}
|
||||
|
||||
// add the last slice if present
|
||||
if (height != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
newColumnList.add(FullDataPointUtil.encode(lastId, height, minY, lastBlockLight, lastSkyLight));
|
||||
}
|
||||
catch (DataCorruptedException e)
|
||||
{
|
||||
// shouldn't happen, (especially if validation is disabled) but just in case
|
||||
LOGGER.warn("Skipping corrupt datapoint for pos "+inputDataSource.pos+" at relative position ["+x+","+z+"] with data: ID["+lastId+"], Height["+height+"], minY["+minY+"], lastBlockLight["+lastBlockLight+"], lastSkyLight["+lastSkyLight+"].");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// flip the array if necessary
|
||||
// TODO why is this sometimes necessary? What did I (James) screw up that causes the mergedInputDataArray
|
||||
// to sometimes be in a different order? Is it potentially related to what detail level is coming in?
|
||||
ensureDataColumnOrder(newColumnList);
|
||||
|
||||
return newColumnList;
|
||||
}
|
||||
/**
|
||||
* Only update the ID once it's been added to this data source.
|
||||
* Updating the incoming data source will cause issues if it is applied
|
||||
* to anything else due to multiple remapping.
|
||||
*/
|
||||
private void remapDataColumn(int dataPointIndex, int[] remappedIds)
|
||||
{
|
||||
LongArrayList dataColumn = this.dataPoints[dataPointIndex];
|
||||
for (int i = 0; i < dataColumn.size(); i++)
|
||||
{
|
||||
dataColumn.set(i, FullDataPointUtil.remap(remappedIds, dataColumn.getLong(i)));
|
||||
}
|
||||
}
|
||||
private static boolean areDataColumnsDifferent(long[] oldDataArray, long[] newDataArray)
|
||||
{
|
||||
if (oldDataArray == null || oldDataArray.length != newDataArray.length)
|
||||
{
|
||||
// new data was added/removed
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if the new column data is different
|
||||
int oldArrayHash = Arrays.hashCode(oldDataArray);
|
||||
int newArrayHash = Arrays.hashCode(newDataArray);
|
||||
return (newArrayHash != oldArrayHash);
|
||||
}
|
||||
}
|
||||
/** @param mapping can be included to ignore air ID's, otherwise all 4 values are treated equally */
|
||||
private static int determineMostValueInColumnSlice(int[] sliceArray, @Nullable FullDataPointIdMap mapping)
|
||||
{
|
||||
if (RUN_UPDATE_DEV_VALIDATION)
|
||||
{
|
||||
LodUtil.assertTrue(sliceArray.length == 4, "Column Slice should only contain 4 values.");
|
||||
}
|
||||
|
||||
int value0 = sliceArray[0];
|
||||
int count0 = 0;
|
||||
int value1 = sliceArray[1];
|
||||
int count1 = 0;
|
||||
int value2 = sliceArray[2];
|
||||
int count2 = 0;
|
||||
int value3 = sliceArray[3];
|
||||
int count3 = 0;
|
||||
|
||||
// count the occurrences of each value
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int value = sliceArray[i];
|
||||
if (mapping != null && mapping.getBlockStateWrapper(value).isAir())
|
||||
{
|
||||
// always overwrite air to prevent holes in hollow structures
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
return value0;
|
||||
else if (maxCount == count1)
|
||||
return value1;
|
||||
else if (maxCount == count2)
|
||||
return value2;
|
||||
else
|
||||
return value3;
|
||||
}
|
||||
private static int determineAverageValueInColumnSlice(int[] sliceArray)
|
||||
{
|
||||
if (RUN_UPDATE_DEV_VALIDATION)
|
||||
{
|
||||
LodUtil.assertTrue(sliceArray.length == 4, "Column Slice should only contain 4 values.");
|
||||
}
|
||||
|
||||
|
||||
int value = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
value += sliceArray[i];
|
||||
}
|
||||
|
||||
value /= 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
/**
|
||||
* Usually this should just be used internally, but there may be instances
|
||||
* where the raw data arrays are available without the data source object.
|
||||
*/
|
||||
public static int relativePosToIndex(int relX, int relZ) throws IndexOutOfBoundsException
|
||||
{
|
||||
if (relX < 0 || relZ < 0 ||
|
||||
relX > WIDTH || relZ > WIDTH)
|
||||
{
|
||||
throw new IndexOutOfBoundsException("Relative data source positions must be between [0] and ["+WIDTH+"] (inclusive) the relative pos: ["+relX+","+relZ+"] is outside of those boundaries.");
|
||||
}
|
||||
|
||||
return (relX * WIDTH) + relZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception if the given
|
||||
* full data column array is in the wrong order
|
||||
* IE if the first data point is the lowest and the last data point is the highest.
|
||||
* Data columns should be in reverse order, IE the first data point should be the highest data point.
|
||||
*
|
||||
* @see FullDataSourceV2#dataPoints
|
||||
*/
|
||||
public static void throwIfDataColumnInWrongOrder(long pos, LongArrayList dataArray) throws IllegalStateException
|
||||
{
|
||||
long firstDataPoint = dataArray.getLong(0);
|
||||
int firstBottomY = FullDataPointUtil.getBottomY(firstDataPoint);
|
||||
|
||||
long lastDataPoint = dataArray.getLong(dataArray.size() - 1);
|
||||
int lastBottomY = FullDataPointUtil.getBottomY(lastDataPoint);
|
||||
|
||||
if (firstBottomY < lastBottomY)
|
||||
{
|
||||
throw new IllegalStateException("Incorrect data point order at pos: ["+ DhSectionPos.toString(pos)+"], first datapoint bottom Y ["+firstBottomY+"], last datapoint bottom Y ["+lastBottomY+"].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the given data column is in the correct Y order, specifically
|
||||
* top-to-bottom.
|
||||
*/
|
||||
private static void ensureDataColumnOrder(LongArrayList dataColumn)
|
||||
{
|
||||
long firstDataPoint = dataColumn.getLong(0);
|
||||
int firstBottomY = FullDataPointUtil.getBottomY(firstDataPoint);
|
||||
|
||||
long lastDataPoint = dataColumn.getLong(dataColumn.size() - 1);
|
||||
int lastBottomY = FullDataPointUtil.getBottomY(lastDataPoint);
|
||||
|
||||
if (firstBottomY < lastBottomY)
|
||||
{
|
||||
// reverse the array so index 0 is the highest,
|
||||
// this is necessary for later logic
|
||||
// source: https://stackoverflow.com/questions/2137755/how-do-i-reverse-an-int-array-in-java
|
||||
for(int i = 0; i < dataColumn.size() / 2; i++)
|
||||
{
|
||||
long temp = dataColumn.getLong(i);
|
||||
dataColumn.set(i, dataColumn.getLong(dataColumn.size() - i - 1));
|
||||
dataColumn.set(dataColumn.size() - i - 1, temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// pooling //
|
||||
//=========//
|
||||
|
||||
private static void prepPooledDataSource(long pos, boolean clearData, FullDataSourceV2 dataSource)
|
||||
{
|
||||
dataSource.pos = pos;
|
||||
|
||||
if (clearData)
|
||||
{
|
||||
dataSource.mapping.clear(pos);
|
||||
|
||||
for (int i = 0; i < dataSource.dataPoints.length; i++)
|
||||
{
|
||||
if (dataSource.dataPoints[i] != null)
|
||||
{
|
||||
dataSource.dataPoints[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
Arrays.fill(dataSource.columnGenerationSteps, (byte) 0);
|
||||
Arrays.fill(dataSource.columnWorldCompressionMode, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// setters and getters //
|
||||
//=====================//
|
||||
|
||||
@Override
|
||||
public Long getPos() { return this.pos; }
|
||||
|
||||
@Override
|
||||
public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); }
|
||||
|
||||
public EDhApiWorldGenerationStep getWorldGenStepAtRelativePos(int relX, int relZ)
|
||||
{
|
||||
int index = relativePosToIndex(relX, relZ);
|
||||
return EDhApiWorldGenerationStep.fromValue(this.columnGenerationSteps[index]);
|
||||
}
|
||||
|
||||
public void setSingleColumn(LongArrayList longArray, int relX, int relZ, EDhApiWorldGenerationStep worldGenStep, EDhApiWorldCompressionMode worldCompressionMode)
|
||||
{
|
||||
int index = relativePosToIndex(relX, relZ);
|
||||
this.dataPoints[index] = longArray;
|
||||
this.columnGenerationSteps[index] = worldGenStep.value;
|
||||
this.columnWorldCompressionMode[index] = worldCompressionMode.value;
|
||||
|
||||
|
||||
if (RUN_UPDATE_DEV_VALIDATION)
|
||||
{
|
||||
// validate the incoming ID's
|
||||
int maxValidId = this.mapping.getMaxValidId();
|
||||
for (int i = 0; i < longArray.size(); i++)
|
||||
{
|
||||
long dataPoint = longArray.getLong(i);
|
||||
int id = FullDataPointUtil.getId(dataPoint);
|
||||
if (id > maxValidId)
|
||||
{
|
||||
LodUtil.assertNotReach("Column set with higher than possible ID. ID [" + id + "], max valid ID [" + maxValidId + "].");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public String toString() { return DhSectionPos.toString(this.pos); }
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
if (this.cachedHashCode == 0)
|
||||
{
|
||||
this.generateHashCode();
|
||||
}
|
||||
return this.cachedHashCode;
|
||||
}
|
||||
private void generateHashCode()
|
||||
{
|
||||
int result = DhSectionPos.hashCode(this.pos);
|
||||
result = 31 * result + Arrays.deepHashCode(this.dataPoints);
|
||||
result = 17 * result + Arrays.hashCode(this.columnGenerationSteps);
|
||||
result = 43 * result + Arrays.hashCode(this.columnWorldCompressionMode);
|
||||
|
||||
this.cachedHashCode = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (!(obj instanceof FullDataSourceV2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FullDataSourceV2 other = (FullDataSourceV2) obj;
|
||||
|
||||
if (other.pos != this.pos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception
|
||||
{
|
||||
DATA_SOURCE_POOL.returnPooledDataSource(this);
|
||||
}
|
||||
|
||||
}
|
||||
-583
@@ -1,583 +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.dataObjects.fullData.sources;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.FullDataArrayAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IStreamableFullDataSource;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.DataSourceDto;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* Used for small incomplete LOD blocks.<br>
|
||||
* Handles incomplete full data with a detail level equal to or lower than
|
||||
* {@link HighDetailIncompleteFullDataSource#MAX_SECTION_DETAIL}. <br><br>
|
||||
*
|
||||
* Compared to other {@link IIncompleteFullDataSource}'s, this object doesn't extend {@link FullDataArrayAccessor},
|
||||
* instead it contains several "sections" of data, represented by {@link FullDataArrayAccessor}s. <br><br>
|
||||
*
|
||||
* Formerly "SparseFullDataSource".
|
||||
*
|
||||
* @see LowDetailIncompleteFullDataSource
|
||||
* @see CompleteFullDataSource
|
||||
* @see FullDataPointUtil
|
||||
*/
|
||||
public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSource, IStreamableFullDataSource<IStreamableFullDataSource.FullDataSourceSummaryData, long[][][]>
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
// TODO James would like to rename, comment, and potentially remove some of these constants.
|
||||
// But he doesn't currently have the understanding to do so.
|
||||
public static final byte SPARSE_UNIT_DETAIL = LodUtil.CHUNK_DETAIL_LEVEL;
|
||||
public static final byte SPARSE_UNIT_SIZE = (byte) BitShiftUtil.powerOfTwo(SPARSE_UNIT_DETAIL);
|
||||
|
||||
public static final byte SECTION_SIZE_OFFSET = DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
public static final int SECTION_SIZE = (byte) BitShiftUtil.powerOfTwo(SECTION_SIZE_OFFSET);
|
||||
/** aka max detail level */
|
||||
public static final byte MAX_SECTION_DETAIL = SECTION_SIZE_OFFSET + SPARSE_UNIT_DETAIL;
|
||||
|
||||
public static final byte DATA_FORMAT_VERSION = 3;
|
||||
public static final String DATA_TYPE_NAME = "HighDetailIncompleteFullDataSource";
|
||||
@Override
|
||||
public String getDataTypeName() { return DATA_TYPE_NAME; }
|
||||
|
||||
|
||||
protected final FullDataPointIdMap mapping;
|
||||
private DhSectionPos sectionPos;
|
||||
private FullDataArrayAccessor[] sparseData;
|
||||
private DhLodPos chunkPos;
|
||||
|
||||
public int sectionCount;
|
||||
public int dataPointsPerSection;
|
||||
public boolean isEmpty = true;
|
||||
public EDhApiWorldGenerationStep worldGenStep = EDhApiWorldGenerationStep.EMPTY;
|
||||
private boolean isPromoted = false;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static HighDetailIncompleteFullDataSource createEmpty(DhSectionPos pos) { return new HighDetailIncompleteFullDataSource(pos); }
|
||||
private HighDetailIncompleteFullDataSource(DhSectionPos sectionPos)
|
||||
{
|
||||
LodUtil.assertTrue(sectionPos.getDetailLevel() > SPARSE_UNIT_DETAIL);
|
||||
LodUtil.assertTrue(sectionPos.getDetailLevel() <= MAX_SECTION_DETAIL);
|
||||
|
||||
this.sectionPos = sectionPos;
|
||||
this.sectionCount = BitShiftUtil.powerOfTwo(sectionPos.getDetailLevel() - SPARSE_UNIT_DETAIL);
|
||||
this.dataPointsPerSection = SECTION_SIZE / this.sectionCount;
|
||||
|
||||
this.sparseData = new FullDataArrayAccessor[this.sectionCount * this.sectionCount];
|
||||
this.chunkPos = sectionPos.getMinCornerLodPos(SPARSE_UNIT_DETAIL);
|
||||
this.mapping = new FullDataPointIdMap(sectionPos);
|
||||
}
|
||||
|
||||
protected HighDetailIncompleteFullDataSource(DhSectionPos sectionPos, FullDataPointIdMap mapping, FullDataArrayAccessor[] data)
|
||||
{
|
||||
LodUtil.assertTrue(sectionPos.getDetailLevel() > SPARSE_UNIT_DETAIL);
|
||||
LodUtil.assertTrue(sectionPos.getDetailLevel() <= MAX_SECTION_DETAIL);
|
||||
|
||||
this.sectionPos = sectionPos;
|
||||
this.sectionCount = 1 << (byte) (sectionPos.getDetailLevel() - SPARSE_UNIT_DETAIL);
|
||||
this.dataPointsPerSection = SECTION_SIZE / this.sectionCount;
|
||||
|
||||
LodUtil.assertTrue(this.sectionCount * this.sectionCount == data.length);
|
||||
this.sparseData = data;
|
||||
this.chunkPos = sectionPos.getMinCornerLodPos(SPARSE_UNIT_DETAIL);
|
||||
this.isEmpty = false;
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// stream handling //
|
||||
//=================//
|
||||
|
||||
|
||||
@Override
|
||||
public void writeSourceSummaryInfo(IDhLevel level, DhDataOutputStream dataOutputStream) throws IOException
|
||||
{
|
||||
dataOutputStream.writeShort(this.getDataDetailLevel());
|
||||
dataOutputStream.writeShort(SPARSE_UNIT_DETAIL);
|
||||
dataOutputStream.writeInt(SECTION_SIZE);
|
||||
dataOutputStream.writeInt(level.getMinY());
|
||||
dataOutputStream.writeByte(this.worldGenStep.value);
|
||||
|
||||
}
|
||||
@Override
|
||||
public FullDataSourceSummaryData readSourceSummaryInfo(DataSourceDto dto, DhDataInputStream inputStream, IDhLevel level) throws IOException
|
||||
{
|
||||
LodUtil.assertTrue(dto.pos.getDetailLevel() > SPARSE_UNIT_DETAIL);
|
||||
LodUtil.assertTrue(dto.pos.getDetailLevel() <= MAX_SECTION_DETAIL);
|
||||
|
||||
int dataDetailLevel = inputStream.readShort();
|
||||
if (dataDetailLevel != dto.dataDetailLevel)
|
||||
{
|
||||
throw new IOException("Data level mismatch: ["+dataDetailLevel+"] != ["+dto.dataDetailLevel+"]");
|
||||
}
|
||||
|
||||
// confirm that the detail level is correct
|
||||
int sparseDetail = inputStream.readShort();
|
||||
if (sparseDetail != SPARSE_UNIT_DETAIL)
|
||||
{
|
||||
throw new IOException("Unexpected sparse detail level: ["+sparseDetail+"] != ["+SPARSE_UNIT_DETAIL+"]");
|
||||
}
|
||||
|
||||
// confirm the scale of the data points is correct
|
||||
int sectionSize = inputStream.readInt();
|
||||
if (sectionSize != SECTION_SIZE)
|
||||
{
|
||||
throw new IOException("Section size mismatch: ["+sectionSize+"] != ["+SECTION_SIZE+"] (Currently only 1 section size is supported)");
|
||||
}
|
||||
|
||||
int minY = inputStream.readInt();
|
||||
if (minY != level.getMinY())
|
||||
{
|
||||
LOGGER.warn("Data minY mismatch: [" + minY + "] != [" + level.getMinY() + "]. Will ignore data's y level");
|
||||
}
|
||||
|
||||
EDhApiWorldGenerationStep worldGenStep = EDhApiWorldGenerationStep.fromValue(inputStream.readByte());
|
||||
if (worldGenStep == null)
|
||||
{
|
||||
worldGenStep = EDhApiWorldGenerationStep.SURFACE;
|
||||
LOGGER.warn("Missing WorldGenStep, defaulting to: " + worldGenStep.name());
|
||||
}
|
||||
|
||||
|
||||
return new FullDataSourceSummaryData(-1, worldGenStep);
|
||||
}
|
||||
public void setSourceSummaryData(FullDataSourceSummaryData summaryData) { this.worldGenStep = summaryData.worldGenStep; }
|
||||
|
||||
|
||||
@Override
|
||||
public boolean writeDataPoints(DhDataOutputStream dataOutputStream) throws IOException
|
||||
{
|
||||
if (this.isEmpty)
|
||||
{
|
||||
dataOutputStream.writeInt(IFullDataSource.NO_DATA_FLAG_BYTE);
|
||||
return false;
|
||||
}
|
||||
dataOutputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
|
||||
|
||||
// sparse array existence bitset
|
||||
BitSet dataArrayIndexHasData = new BitSet(this.sparseData.length);
|
||||
for (int i = 0; i < this.sparseData.length; i++)
|
||||
{
|
||||
dataArrayIndexHasData.set(i, this.sparseData[i] != null);
|
||||
}
|
||||
byte[] bytes = dataArrayIndexHasData.toByteArray();
|
||||
dataOutputStream.writeInt(bytes.length);
|
||||
dataOutputStream.write(bytes);
|
||||
|
||||
|
||||
// Data array content (only non-empty data is written)
|
||||
dataOutputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
for (int dataArrayIndex = dataArrayIndexHasData.nextSetBit(0);
|
||||
dataArrayIndex >= 0;
|
||||
dataArrayIndex = dataArrayIndexHasData.nextSetBit(dataArrayIndex + 1))
|
||||
{
|
||||
// column data length
|
||||
FullDataArrayAccessor array = this.sparseData[dataArrayIndex];
|
||||
LodUtil.assertTrue(array != null);
|
||||
for (int x = 0; x < array.width(); x++)
|
||||
{
|
||||
for (int z = 0; z < array.width(); z++)
|
||||
{
|
||||
SingleColumnFullDataAccessor columnAccessor = array.get(x, z);
|
||||
int columnLength = 0;
|
||||
if (columnAccessor != null)
|
||||
{
|
||||
columnLength = columnAccessor.getSingleLength();
|
||||
}
|
||||
dataOutputStream.writeInt(columnLength);
|
||||
}
|
||||
}
|
||||
|
||||
// column data
|
||||
for (int x = 0; x < array.width(); x++)
|
||||
{
|
||||
for (int z = 0; z < array.width(); z++)
|
||||
{
|
||||
SingleColumnFullDataAccessor column = array.get(x, z);
|
||||
LodUtil.assertTrue(column.getMapping() == this.mapping); // the mappings must be exactly equal!
|
||||
|
||||
if (column.doesColumnExist())
|
||||
{
|
||||
long[] rawDataPoints = column.getRaw();
|
||||
for (long dataPoint : rawDataPoints)
|
||||
{
|
||||
dataOutputStream.writeLong(dataPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public long[][][] readDataPoints(DataSourceDto dto, int width, DhDataInputStream inputStream) throws IOException
|
||||
{
|
||||
// calculate the number of chunks and dataPoints based on the sparseDetail and sectionSize
|
||||
// TODO these values should be constant, should we still be calculating them like this?
|
||||
int chunks = BitShiftUtil.powerOfTwo(dto.pos.getDetailLevel() - SPARSE_UNIT_DETAIL);
|
||||
int dataPointsPerChunk = SECTION_SIZE / chunks;
|
||||
|
||||
|
||||
// check if this file has any data
|
||||
int dataPresentFlag = inputStream.readInt();
|
||||
if (dataPresentFlag == IFullDataSource.NO_DATA_FLAG_BYTE)
|
||||
{
|
||||
// this file is empty
|
||||
return null;
|
||||
}
|
||||
else if (dataPresentFlag != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
// the file format is incorrect
|
||||
throw new IOException("Invalid file format. Data Points guard byte expected: (no data) [" + IFullDataSource.NO_DATA_FLAG_BYTE + "] or (data present) [" + IFullDataSource.DATA_GUARD_BYTE + "], but found [" + dataPresentFlag + "].");
|
||||
}
|
||||
|
||||
|
||||
// get the number of columns (IE the bitSet from before)
|
||||
int numberOfDataColumns = inputStream.readInt();
|
||||
// validate the number of data columns
|
||||
int maxNumberOfDataColumns = (chunks * chunks / 8 + 64) * 2; // TODO what do these values represent?
|
||||
if (numberOfDataColumns < 0 || numberOfDataColumns > maxNumberOfDataColumns)
|
||||
{
|
||||
throw new IOException(LodUtil.formatLog("Sparse Flag BitSet size outside reasonable range: {} (expects {} to {})",
|
||||
numberOfDataColumns, 1, maxNumberOfDataColumns));
|
||||
}
|
||||
|
||||
// read in the presence of each data column
|
||||
byte[] bytes = new byte[numberOfDataColumns];
|
||||
inputStream.readFully(bytes, 0, numberOfDataColumns);
|
||||
BitSet dataArrayIndexHasData = BitSet.valueOf(bytes);
|
||||
|
||||
|
||||
|
||||
//====================//
|
||||
// Data array content //
|
||||
//====================//
|
||||
|
||||
// (only on non-empty columns)
|
||||
int dataArrayStartByte = inputStream.readInt();
|
||||
// confirm the column data is starting
|
||||
if (dataArrayStartByte != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
// the file format is incorrect
|
||||
throw new IOException("invalid data length end guard");
|
||||
}
|
||||
|
||||
|
||||
// read in each column that has data written to it
|
||||
long[][][] rawFullDataArrays = new long[chunks * chunks][][];
|
||||
for (int fullDataIndex = dataArrayIndexHasData.nextSetBit(0);
|
||||
fullDataIndex >= 0 && // TODO why does this happen?
|
||||
fullDataIndex < rawFullDataArrays.length;
|
||||
fullDataIndex = dataArrayIndexHasData.nextSetBit(fullDataIndex + 1))
|
||||
{
|
||||
long[][] dataColumn = new long[dataPointsPerChunk * dataPointsPerChunk][];
|
||||
|
||||
// get the column data lengths
|
||||
rawFullDataArrays[fullDataIndex] = dataColumn;
|
||||
for (int x = 0; x < dataColumn.length; x++)
|
||||
{
|
||||
// this should be zero if the column doesn't have any data
|
||||
int dataColumnLength = inputStream.readInt();
|
||||
dataColumn[x] = new long[dataColumnLength];
|
||||
}
|
||||
|
||||
// get the column data
|
||||
for (int x = 0; x < dataColumn.length; x++)
|
||||
{
|
||||
if (dataColumn[x].length != 0)
|
||||
{
|
||||
// read in the data columns
|
||||
for (int z = 0; z < dataColumn[x].length; z++)
|
||||
{
|
||||
dataColumn[x][z] = inputStream.readLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return rawFullDataArrays;
|
||||
}
|
||||
@Override
|
||||
public void setDataPoints(long[][][] dataPoints)
|
||||
{
|
||||
LodUtil.assertTrue(this.sparseData.length == dataPoints.length, "Data point array length mismatch.");
|
||||
|
||||
this.isEmpty = false;
|
||||
|
||||
|
||||
for (int arrayAccessorIndex = 0; arrayAccessorIndex < dataPoints.length; arrayAccessorIndex++)
|
||||
{
|
||||
if (dataPoints[arrayAccessorIndex] == null)
|
||||
{
|
||||
this.sparseData[arrayAccessorIndex] = null;
|
||||
}
|
||||
else if (this.sparseData[arrayAccessorIndex] == null)
|
||||
{
|
||||
int width = (int) Math.sqrt(dataPoints[arrayAccessorIndex].length);
|
||||
this.sparseData[arrayAccessorIndex] = new FullDataArrayAccessor(this.mapping, dataPoints[arrayAccessorIndex], width);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int dataPointColIndex = 0; dataPointColIndex < dataPoints[arrayAccessorIndex].length; dataPointColIndex++)
|
||||
{
|
||||
long[] incomingColumn = dataPoints[arrayAccessorIndex][dataPointColIndex];
|
||||
long[] destinationColumn = this.sparseData[arrayAccessorIndex].get(dataPointColIndex).getRaw();
|
||||
|
||||
// use the existing arrays if possible
|
||||
if (incomingColumn.length == destinationColumn.length)
|
||||
{
|
||||
System.arraycopy(incomingColumn, 0, destinationColumn, 0, incomingColumn.length);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.sparseData[arrayAccessorIndex].get(dataPointColIndex).setNew(incomingColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public FullDataPointIdMap readIdMappings(long[][][] dataPoints, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException
|
||||
{
|
||||
// mark the start of the ID data
|
||||
int idMappingStartByte = inputStream.readInt();
|
||||
if (idMappingStartByte != DATA_GUARD_BYTE)
|
||||
{
|
||||
// the file format is incorrect
|
||||
throw new IOException("invalid data content end guard");
|
||||
}
|
||||
|
||||
// deserialize the ID data
|
||||
return FullDataPointIdMap.deserialize(inputStream, this.sectionPos, levelWrapper);
|
||||
}
|
||||
@Override
|
||||
public void writeIdMappings(DhDataOutputStream dataOutputStream) throws IOException
|
||||
{
|
||||
dataOutputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
this.mapping.serialize(dataOutputStream);
|
||||
}
|
||||
@Override
|
||||
public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); }
|
||||
|
||||
|
||||
|
||||
//======//
|
||||
// data //
|
||||
//======//
|
||||
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor tryGet(int relativeX, int relativeZ) { return this.tryGetOrCreate(relativeX, relativeZ, false); }
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor getOrCreate(int relativeX, int relativeZ) { return this.tryGetOrCreate(relativeX, relativeZ, true); }
|
||||
private SingleColumnFullDataAccessor tryGetOrCreate(int relativeX, int relativeZ, boolean createIfMissing)
|
||||
{
|
||||
LodUtil.assertTrue(relativeX >= 0 && relativeX < SECTION_SIZE && relativeZ >= 0 && relativeZ < SECTION_SIZE);
|
||||
int chunkX = relativeX / this.dataPointsPerSection;
|
||||
int chunkZ = relativeZ / this.dataPointsPerSection;
|
||||
FullDataArrayAccessor accessor = this.sparseData[chunkX * this.sectionCount + chunkZ];
|
||||
if (accessor == null)
|
||||
{
|
||||
if (createIfMissing)
|
||||
{
|
||||
// create the missing data so the following get() will succeed
|
||||
accessor = new FullDataArrayAccessor(this.mapping, new long[this.dataPointsPerSection * this.dataPointsPerSection][], this.dataPointsPerSection);
|
||||
this.sparseData[chunkX * this.sectionCount + chunkZ] = accessor;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return accessor.get(relativeX % this.dataPointsPerSection, relativeZ % this.dataPointsPerSection);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public DhSectionPos getSectionPos() { return this.sectionPos; }
|
||||
|
||||
@Override
|
||||
public void resizeDataStructuresForRepopulation(DhSectionPos pos)
|
||||
{
|
||||
// update the position
|
||||
this.sectionPos = pos;
|
||||
this.sectionCount = BitShiftUtil.powerOfTwo(this.sectionPos.getDetailLevel() - SPARSE_UNIT_DETAIL);
|
||||
this.dataPointsPerSection = SECTION_SIZE / this.sectionCount;
|
||||
|
||||
this.chunkPos = this.sectionPos.getMinCornerLodPos(SPARSE_UNIT_DETAIL);
|
||||
|
||||
|
||||
// update the data container
|
||||
int dataPointCount = this.sectionCount * this.sectionCount;
|
||||
if (this.sparseData.length != dataPointCount)
|
||||
{
|
||||
this.sparseData = new FullDataArrayAccessor[this.sectionCount * this.sectionCount];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getDataDetailLevel() { return (byte) (this.sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET); }
|
||||
|
||||
@Override
|
||||
public byte getDataFormatVersion() { return DATA_FORMAT_VERSION; }
|
||||
|
||||
@Override
|
||||
public EDhApiWorldGenerationStep getWorldGenStep() { return this.worldGenStep; }
|
||||
|
||||
@Override
|
||||
public FullDataPointIdMap getMapping() { return this.mapping; }
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() { return this.isEmpty; }
|
||||
@Override
|
||||
public void markNotEmpty() { this.isEmpty = false; }
|
||||
|
||||
@Override
|
||||
public int getWidthInDataPoints() { return SECTION_SIZE; }
|
||||
|
||||
|
||||
private int calculateOffset(int chunkX, int chunkZ)
|
||||
{
|
||||
int offsetX = chunkX - this.chunkPos.x;
|
||||
int offsetZ = chunkZ - this.chunkPos.z;
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetZ >= 0 && offsetX < this.sectionCount && offsetZ < this.sectionCount);
|
||||
return offsetX * this.sectionCount + offsetZ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// data update //
|
||||
//=============//
|
||||
|
||||
@Override
|
||||
public void update(ChunkSizedFullDataAccessor chunkDataView)
|
||||
{
|
||||
int arrayOffset = this.calculateOffset(chunkDataView.chunkPos.x, chunkDataView.chunkPos.z);
|
||||
FullDataArrayAccessor newArray = new FullDataArrayAccessor(this.mapping, new long[this.dataPointsPerSection * this.dataPointsPerSection][], this.dataPointsPerSection);
|
||||
if (this.getDataDetailLevel() == chunkDataView.detailLevel)
|
||||
{
|
||||
chunkDataView.shadowCopyTo(newArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = this.dataPointsPerSection;
|
||||
int dataPerCount = SPARSE_UNIT_SIZE / this.dataPointsPerSection;
|
||||
|
||||
for (int xOffset = 0; xOffset < count; xOffset++)
|
||||
{
|
||||
for (int zOffset = 0; zOffset < count; zOffset++)
|
||||
{
|
||||
SingleColumnFullDataAccessor column = newArray.get(xOffset, zOffset);
|
||||
column.downsampleFrom(chunkDataView.subView(dataPerCount, xOffset * dataPerCount, zOffset * dataPerCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.isEmpty = false;
|
||||
this.sparseData[arrayOffset] = newArray;
|
||||
}
|
||||
|
||||
|
||||
// data sampling //
|
||||
|
||||
private void applyToFullDataSource(CompleteFullDataSource dataSource)
|
||||
{
|
||||
LodUtil.assertTrue(dataSource.getSectionPos().equals(this.sectionPos));
|
||||
LodUtil.assertTrue(dataSource.getDataDetailLevel() == this.getDataDetailLevel());
|
||||
for (int x = 0; x < this.sectionCount; x++)
|
||||
{
|
||||
for (int z = 0; z < this.sectionCount; z++)
|
||||
{
|
||||
FullDataArrayAccessor array = this.sparseData[x * this.sectionCount + z];
|
||||
if (array == null)
|
||||
continue;
|
||||
|
||||
// Otherwise, apply data to dataSource
|
||||
dataSource.markNotEmpty();
|
||||
FullDataArrayAccessor view = dataSource.subView(this.dataPointsPerSection, x * this.dataPointsPerSection, z * this.dataPointsPerSection);
|
||||
array.shadowCopyTo(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IFullDataSource tryPromotingToCompleteDataSource()
|
||||
{
|
||||
if (this.isEmpty)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
// promotion can only succeed if every data column is present
|
||||
for (FullDataArrayAccessor array : this.sparseData)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
this.isPromoted = true;
|
||||
CompleteFullDataSource fullDataSource = CompleteFullDataSource.createEmpty(this.sectionPos);
|
||||
this.applyToFullDataSource(fullDataSource);
|
||||
return fullDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenPromoted() { return this.isPromoted; }
|
||||
|
||||
}
|
||||
-436
@@ -1,436 +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.dataObjects.fullData.sources;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.FullDataArrayAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IStreamableFullDataSource;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.DataSourceDto;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* Used for large incomplete LOD blocks. <Br>
|
||||
* Handles incomplete full data with a detail level higher than
|
||||
* {@link HighDetailIncompleteFullDataSource#MAX_SECTION_DETAIL}. <br><br>
|
||||
*
|
||||
* Formerly "SpottyFullDataSource".
|
||||
*
|
||||
* @see HighDetailIncompleteFullDataSource
|
||||
* @see CompleteFullDataSource
|
||||
* @see FullDataPointUtil
|
||||
*/
|
||||
public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor implements IIncompleteFullDataSource, IStreamableFullDataSource<IStreamableFullDataSource.FullDataSourceSummaryData, LowDetailIncompleteFullDataSource.StreamDataPointContainer>
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static final byte SECTION_SIZE_OFFSET = DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
/** measured in dataPoints */
|
||||
public static final int WIDTH = BitShiftUtil.powerOfTwo(SECTION_SIZE_OFFSET);
|
||||
|
||||
public static final byte DATA_FORMAT_VERSION = 3;
|
||||
public static final String DATA_TYPE_NAME = "LowDetailIncompleteFullDataSource";
|
||||
@Override
|
||||
public String getDataTypeName() { return DATA_TYPE_NAME; }
|
||||
|
||||
private DhSectionPos sectionPos;
|
||||
|
||||
private final BitSet isColumnNotEmpty;
|
||||
|
||||
private boolean isEmpty = true;
|
||||
public EDhApiWorldGenerationStep worldGenStep = EDhApiWorldGenerationStep.EMPTY;
|
||||
private boolean isPromoted = false;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static LowDetailIncompleteFullDataSource createEmpty(DhSectionPos pos) { return new LowDetailIncompleteFullDataSource(pos); }
|
||||
private LowDetailIncompleteFullDataSource(DhSectionPos sectionPos)
|
||||
{
|
||||
super(new FullDataPointIdMap(sectionPos), new long[WIDTH * WIDTH][0], WIDTH);
|
||||
LodUtil.assertTrue(sectionPos.getDetailLevel() > HighDetailIncompleteFullDataSource.MAX_SECTION_DETAIL);
|
||||
|
||||
this.sectionPos = sectionPos;
|
||||
this.isColumnNotEmpty = new BitSet(WIDTH * WIDTH);
|
||||
this.worldGenStep = EDhApiWorldGenerationStep.EMPTY;
|
||||
}
|
||||
|
||||
private LowDetailIncompleteFullDataSource(DhSectionPos pos, FullDataPointIdMap mapping, EDhApiWorldGenerationStep worldGenStep, BitSet isColumnNotEmpty, long[][] data)
|
||||
{
|
||||
super(mapping, data, WIDTH);
|
||||
LodUtil.assertTrue(data.length == WIDTH * WIDTH);
|
||||
|
||||
this.sectionPos = pos;
|
||||
this.isColumnNotEmpty = isColumnNotEmpty;
|
||||
this.worldGenStep = worldGenStep;
|
||||
this.isEmpty = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// stream handling //
|
||||
//=================//
|
||||
|
||||
|
||||
@Override
|
||||
public void writeSourceSummaryInfo(IDhLevel level, DhDataOutputStream outputStream) throws IOException
|
||||
{
|
||||
outputStream.writeInt(this.getDataDetailLevel());
|
||||
outputStream.writeInt(this.width);
|
||||
outputStream.writeInt(level.getMinY());
|
||||
outputStream.writeByte(this.worldGenStep.value);
|
||||
|
||||
}
|
||||
@Override
|
||||
public FullDataSourceSummaryData readSourceSummaryInfo(DataSourceDto dto, DhDataInputStream inputStream, IDhLevel level) throws IOException
|
||||
{
|
||||
int dataDetailLevel = inputStream.readInt();
|
||||
if (dataDetailLevel != dto.dataDetailLevel)
|
||||
{
|
||||
throw new IOException(LodUtil.formatLog("Data level mismatch: " + dataDetailLevel + " != " + dto.dataDetailLevel));
|
||||
}
|
||||
|
||||
int width = inputStream.readInt();
|
||||
if (width != WIDTH)
|
||||
{
|
||||
throw new IOException(LodUtil.formatLog("Section size mismatch: " + width + " != " + WIDTH + " (Currently only 1 section size is supported)"));
|
||||
}
|
||||
|
||||
int minY = inputStream.readInt();
|
||||
if (minY != level.getMinY())
|
||||
{
|
||||
LOGGER.warn("Data minY mismatch: " + minY + " != " + level.getMinY() + ". Will ignore data's y level");
|
||||
}
|
||||
|
||||
EDhApiWorldGenerationStep worldGenStep = EDhApiWorldGenerationStep.fromValue(inputStream.readByte());
|
||||
if (worldGenStep == null)
|
||||
{
|
||||
worldGenStep = EDhApiWorldGenerationStep.SURFACE;
|
||||
LOGGER.warn("Missing WorldGenStep, defaulting to: " + worldGenStep.name());
|
||||
}
|
||||
|
||||
|
||||
return new FullDataSourceSummaryData(this.width, worldGenStep);
|
||||
}
|
||||
public void setSourceSummaryData(FullDataSourceSummaryData summaryData)
|
||||
{
|
||||
this.worldGenStep = summaryData.worldGenStep;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean writeDataPoints(DhDataOutputStream dataOutputStream) throws IOException
|
||||
{
|
||||
if (this.isEmpty)
|
||||
{
|
||||
dataOutputStream.writeInt(IFullDataSource.NO_DATA_FLAG_BYTE);
|
||||
return false;
|
||||
}
|
||||
dataOutputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
|
||||
|
||||
// data column presence
|
||||
byte[] bytes = this.isColumnNotEmpty.toByteArray();
|
||||
dataOutputStream.writeInt(bytes.length);
|
||||
dataOutputStream.write(bytes);
|
||||
|
||||
|
||||
// Data content
|
||||
dataOutputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
for (int i = this.isColumnNotEmpty.nextSetBit(0); i >= 0; i = this.isColumnNotEmpty.nextSetBit(i + 1))
|
||||
{
|
||||
dataOutputStream.writeByte(this.dataArrays[i].length);
|
||||
for (long dataPoint : this.dataArrays[i])
|
||||
{
|
||||
dataOutputStream.writeLong(dataPoint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public StreamDataPointContainer readDataPoints(DataSourceDto dto, int width, DhDataInputStream inputStream) throws IOException
|
||||
{
|
||||
// is source empty flag
|
||||
int dataPresentFlag = inputStream.readInt();
|
||||
if (dataPresentFlag == IFullDataSource.NO_DATA_FLAG_BYTE)
|
||||
{
|
||||
// Section is empty
|
||||
return null;
|
||||
}
|
||||
else if (dataPresentFlag != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("Invalid file format. Data Points guard byte expected: (no data) [" + IFullDataSource.NO_DATA_FLAG_BYTE + "] or (data present) [" + IFullDataSource.DATA_GUARD_BYTE + "], but found [" + dataPresentFlag + "].");
|
||||
}
|
||||
|
||||
|
||||
// data column presence
|
||||
int length = inputStream.readInt();
|
||||
if (length < 0 || length > (WIDTH * WIDTH / 8 + 64) * 2) // TODO replace magic numbers or comment what they mean
|
||||
{
|
||||
throw new IOException(LodUtil.formatLog("Spotty Flag BitSet size outside reasonable range: {} (expects {} to {})",
|
||||
length, 1, WIDTH * WIDTH / 8 + 63));
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[length];
|
||||
inputStream.readFully(bytes, 0, length);
|
||||
BitSet isColumnNotEmpty = BitSet.valueOf(bytes);
|
||||
|
||||
|
||||
|
||||
// Data array content
|
||||
long[][] dataPointArray = new long[WIDTH * WIDTH][];
|
||||
dataPresentFlag = inputStream.readInt();
|
||||
if (dataPresentFlag != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("invalid spotty flag end guard");
|
||||
}
|
||||
|
||||
for (int xz = isColumnNotEmpty.nextSetBit(0); xz >= 0; xz = isColumnNotEmpty.nextSetBit(xz + 1))
|
||||
{
|
||||
long[] array = new long[inputStream.readByte()];
|
||||
for (int y = 0; y < array.length; y++)
|
||||
{
|
||||
array[y] = inputStream.readLong();
|
||||
}
|
||||
dataPointArray[xz] = array;
|
||||
}
|
||||
|
||||
|
||||
return new StreamDataPointContainer(dataPointArray, isColumnNotEmpty);
|
||||
}
|
||||
@Override
|
||||
public void setDataPoints(StreamDataPointContainer streamDataPointContainer)
|
||||
{
|
||||
long[][] dataPoints = streamDataPointContainer.dataPoints;
|
||||
|
||||
// copy over the datapoints
|
||||
LodUtil.assertTrue(this.dataArrays.length == dataPoints.length, "Data point array length mismatch.");
|
||||
System.arraycopy(dataPoints, 0, this.dataArrays, 0, dataPoints.length);
|
||||
|
||||
// overwrite the bitset
|
||||
for (int i = 0; i < streamDataPointContainer.isColumnNotEmpty.length(); i++)
|
||||
{
|
||||
this.isColumnNotEmpty.set(i, streamDataPointContainer.isColumnNotEmpty.get(i));
|
||||
}
|
||||
|
||||
this.isEmpty = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void writeIdMappings(DhDataOutputStream outputStream) throws IOException
|
||||
{
|
||||
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
|
||||
this.mapping.serialize(outputStream);
|
||||
|
||||
}
|
||||
@Override
|
||||
public FullDataPointIdMap readIdMappings(StreamDataPointContainer streamDataPointContainer, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException
|
||||
{
|
||||
// Id mapping
|
||||
int dataPresentFlag = inputStream.readInt();
|
||||
if (dataPresentFlag != IFullDataSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("invalid ID mapping end guard");
|
||||
}
|
||||
return FullDataPointIdMap.deserialize(inputStream, this.sectionPos, levelWrapper);
|
||||
}
|
||||
@Override
|
||||
public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); }
|
||||
|
||||
|
||||
|
||||
//======//
|
||||
// data //
|
||||
//======//
|
||||
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor tryGet(int relativeX, int relativeZ) { return this.tryGetOrCreate(relativeX, relativeZ, false); }
|
||||
@Override
|
||||
public SingleColumnFullDataAccessor getOrCreate(int relativeX, int relativeZ) { return this.tryGetOrCreate(relativeX, relativeZ, true); }
|
||||
private SingleColumnFullDataAccessor tryGetOrCreate(int relativeX, int relativeZ, boolean createIfMissing)
|
||||
{
|
||||
int notEmptyIndex = relativeX * WIDTH + relativeZ;
|
||||
boolean columnEmpty = this.isColumnNotEmpty.get(notEmptyIndex);
|
||||
|
||||
// "create" the missing column if necessary
|
||||
if (columnEmpty && createIfMissing)
|
||||
{
|
||||
this.isColumnNotEmpty.set(notEmptyIndex, true);
|
||||
columnEmpty = false;
|
||||
}
|
||||
|
||||
return !columnEmpty ? this.get(relativeX, relativeZ) : null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// getters and setters //
|
||||
//=====================//
|
||||
|
||||
@Override
|
||||
public DhSectionPos getSectionPos() { return this.sectionPos; }
|
||||
|
||||
@Override
|
||||
public void resizeDataStructuresForRepopulation(DhSectionPos pos)
|
||||
{
|
||||
// no data structures need to be changed, only the source's position
|
||||
this.sectionPos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getDataDetailLevel() { return (byte) (this.sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET); }
|
||||
@Override
|
||||
public byte getDataFormatVersion() { return DATA_FORMAT_VERSION; }
|
||||
|
||||
@Override
|
||||
public EDhApiWorldGenerationStep getWorldGenStep() { return this.worldGenStep; }
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() { return this.isEmpty; }
|
||||
@Override
|
||||
public void markNotEmpty() { this.isEmpty = false; }
|
||||
|
||||
@Override
|
||||
public int getWidthInDataPoints() { return WIDTH; }
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// Data updating //
|
||||
//===============//
|
||||
|
||||
@Override
|
||||
public void update(ChunkSizedFullDataAccessor data)
|
||||
{
|
||||
LodUtil.assertTrue(this.sectionPos.overlapsExactly(data.getSectionPos()));
|
||||
|
||||
if (this.getDataDetailLevel() >= 4)
|
||||
{
|
||||
//FIXME: TEMPORARY
|
||||
int chunkPerFull = 1 << (this.getDataDetailLevel() - 4);
|
||||
if (data.chunkPos.x % chunkPerFull != 0 || data.chunkPos.z % chunkPerFull != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DhLodPos baseOffset = this.sectionPos.getMinCornerLodPos(this.getDataDetailLevel());
|
||||
DhSectionPos dataOffset = data.getSectionPos().convertNewToDetailLevel(this.getDataDetailLevel());
|
||||
int offsetX = dataOffset.getX() - baseOffset.x;
|
||||
int offsetZ = dataOffset.getZ() - baseOffset.z;
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetX < WIDTH && offsetZ >= 0 && offsetZ < WIDTH);
|
||||
this.isEmpty = false;
|
||||
|
||||
SingleColumnFullDataAccessor columnFullDataAccessor = this.get(offsetX, offsetZ);
|
||||
data.get(0, 0).deepCopyTo(columnFullDataAccessor);
|
||||
|
||||
this.isColumnNotEmpty.set(offsetX * WIDTH + offsetZ, columnFullDataAccessor.doesColumnExist());
|
||||
}
|
||||
else
|
||||
{
|
||||
LodUtil.assertNotReach();
|
||||
//TODO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFullDataSource tryPromotingToCompleteDataSource()
|
||||
{
|
||||
// promotion can only be completed if every column has data
|
||||
if (this.isEmpty)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
else if (this.isColumnNotEmpty.cardinality() != WIDTH * WIDTH)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
this.isPromoted = true;
|
||||
return new CompleteFullDataSource(this.sectionPos, this.mapping, this.dataArrays);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBeenPromoted() { return this.isPromoted; }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
/** used when reading the datapoints to and from the {@link IStreamableFullDataSource} */
|
||||
public static class StreamDataPointContainer
|
||||
{
|
||||
public long[][] dataPoints;
|
||||
public BitSet isColumnNotEmpty;
|
||||
|
||||
public StreamDataPointContainer(long[][] dataPoints, BitSet isColumnNotEmpty)
|
||||
{
|
||||
this.dataPoints = dataPoints;
|
||||
this.isColumnNotEmpty = isColumnNotEmpty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// unused //
|
||||
//========//
|
||||
|
||||
public static boolean neededForPosition(DhSectionPos posToWrite, DhSectionPos posToTest)
|
||||
{
|
||||
if (!posToWrite.overlapsExactly(posToTest))
|
||||
return false;
|
||||
if (posToTest.getDetailLevel() > posToWrite.getDetailLevel())
|
||||
return false;
|
||||
if (posToWrite.getDetailLevel() - posToTest.getDetailLevel() <= SECTION_SIZE_OFFSET)
|
||||
return true;
|
||||
byte sectPerData = (byte) (1 << (posToWrite.getDetailLevel() - posToTest.getDetailLevel() - SECTION_SIZE_OFFSET));
|
||||
return posToTest.getX() % sectPerData == 0 && posToTest.getZ() % sectPerData == 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-107
@@ -1,107 +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.dataObjects.fullData.sources.interfaces;
|
||||
|
||||
import com.seibel.distanthorizons.core.file.IDataSource;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.IFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
||||
import com.seibel.distanthorizons.core.sql.DataSourceDto;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Base for all Full Data Source objects. <br><br>
|
||||
*
|
||||
* Contains full DH data, methods related to file/stream reading/writing, and the data necessary to create {@link ColumnRenderSource}'s. <br>
|
||||
* {@link IFullDataSource}'s will either implement or contain {@link IFullDataAccessor}'s.
|
||||
*
|
||||
* @see IFullDataAccessor
|
||||
* @see IIncompleteFullDataSource
|
||||
* @see IStreamableFullDataSource
|
||||
*/
|
||||
public interface IFullDataSource extends IDataSource<IDhLevel>
|
||||
{
|
||||
/**
|
||||
* This is the byte put between different sections in the binary save file.
|
||||
* The presence and absence of this byte indicates if the file is correctly formatted.
|
||||
*/
|
||||
int DATA_GUARD_BYTE = 0xFFFFFFFF;
|
||||
/** indicates the binary save file represents an empty data source */
|
||||
int NO_DATA_FLAG_BYTE = 0x00000001;
|
||||
|
||||
|
||||
|
||||
default void update(ChunkSizedFullDataAccessor chunkData, IDhLevel level) { this.update(chunkData); }
|
||||
void update(ChunkSizedFullDataAccessor data);
|
||||
|
||||
boolean isEmpty();
|
||||
void markNotEmpty();
|
||||
|
||||
/** AKA; the max relative position that {@link IFullDataSource#tryGet(int, int)} can accept for either X or Z */
|
||||
int getWidthInDataPoints();
|
||||
|
||||
|
||||
|
||||
//======//
|
||||
// data //
|
||||
//======//
|
||||
|
||||
/**
|
||||
* Attempts to get the data column for the given relative x and z position.
|
||||
*
|
||||
* @return null if the data doesn't exist
|
||||
*/
|
||||
@Nullable
|
||||
SingleColumnFullDataAccessor tryGet(int relativeX, int relativeZ);
|
||||
/**
|
||||
* Attempts to get the data column for the given relative x and z position. <br>
|
||||
* If no data exists yet an empty data column will be created.
|
||||
*/
|
||||
SingleColumnFullDataAccessor getOrCreate(int relativeX, int relativeZ);
|
||||
|
||||
FullDataPointIdMap getMapping();
|
||||
|
||||
|
||||
|
||||
//=======================//
|
||||
// basic stream handling //
|
||||
//=======================//
|
||||
|
||||
/**
|
||||
* Should only be implemented by {@link IStreamableFullDataSource} to prevent potential stream read/write inconsistencies.
|
||||
*
|
||||
* @see IStreamableFullDataSource#populateFromStream(DataSourceDto, DhDataInputStream, IDhLevel)
|
||||
*/
|
||||
void populateFromStream(DataSourceDto dto, DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException;
|
||||
|
||||
/**
|
||||
* Should only be implemented by {@link IStreamableFullDataSource} to prevent potential stream read/write inconsistencies.
|
||||
*
|
||||
* @see IStreamableFullDataSource#repopulateFromStream(DataSourceDto, DhDataInputStream, IDhLevel)
|
||||
*/
|
||||
void repopulateFromStream(DataSourceDto dto, DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException;
|
||||
|
||||
}
|
||||
-86
@@ -1,86 +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.dataObjects.fullData.sources.interfaces;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
|
||||
public interface IIncompleteFullDataSource extends IFullDataSource
|
||||
{
|
||||
/**
|
||||
* Overwrites data in this object with non-null data from the input {@link IFullDataSource}. <br><br>
|
||||
*
|
||||
* This can be used to either merge same sized data sources or downsample to
|
||||
*/
|
||||
default void sampleFrom(IFullDataSource inputSource)
|
||||
{
|
||||
DhSectionPos inputPos = inputSource.getSectionPos();
|
||||
DhSectionPos thisPos = this.getSectionPos();
|
||||
LodUtil.assertTrue(inputPos.getDetailLevel() < thisPos.getDetailLevel(), "input data source at pos: ["+inputPos+"] has a lower detail level than this: ["+thisPos+"].");
|
||||
LodUtil.assertTrue(inputPos.overlapsExactly(this.getSectionPos()), "input source at pos: ["+inputPos+"] (converted to ["+inputPos.convertNewToDetailLevel(thisPos.getDetailLevel())+"]) doesn't overlap with this source's pos: ["+thisPos+"].");
|
||||
|
||||
if (inputSource.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.markNotEmpty();
|
||||
|
||||
DhLodPos baseOffset = thisPos.getMinCornerLodPos(this.getDataDetailLevel());
|
||||
DhSectionPos inputOffset = inputPos.convertNewToDetailLevel(this.getDataDetailLevel());
|
||||
int offsetX = inputOffset.getX() - baseOffset.x;
|
||||
int offsetZ = inputOffset.getZ() - baseOffset.z;
|
||||
|
||||
|
||||
int numberOfDataPointsToUpdate = this.getWidthInDataPoints() / thisPos.getWidthCountForLowerDetailedSection(inputSource.getSectionPos().getDetailLevel()); // can be 0 if the input source is significantly smaller than this data source
|
||||
// should be 1 at minimum, to prevent divide by zero errors (and because trying to get 0 or a fractional data point doesn't make any sense)
|
||||
numberOfDataPointsToUpdate = Math.max(1, numberOfDataPointsToUpdate);
|
||||
|
||||
|
||||
int inputFractionWidth = inputSource.getWidthInDataPoints() / numberOfDataPointsToUpdate;
|
||||
for (int x = 0; x < numberOfDataPointsToUpdate; x++)
|
||||
{
|
||||
for (int z = 0; z < numberOfDataPointsToUpdate; z++)
|
||||
{
|
||||
SingleColumnFullDataAccessor thisDataColumn = this.getOrCreate(offsetX + x, offsetZ + z);
|
||||
SingleColumnFullDataAccessor inputDataColumn = inputSource.tryGet(inputFractionWidth * x, inputFractionWidth * z);
|
||||
|
||||
if (inputDataColumn != null)
|
||||
{
|
||||
inputDataColumn.deepCopyTo(thisDataColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to convert this {@link IIncompleteFullDataSource} into a {@link CompleteFullDataSource}.
|
||||
*
|
||||
* @return a new {@link CompleteFullDataSource} if successful, returns itself if not.
|
||||
*/
|
||||
IFullDataSource tryPromotingToCompleteDataSource();
|
||||
|
||||
boolean hasBeenPromoted();
|
||||
|
||||
}
|
||||
-161
@@ -1,161 +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.dataObjects.fullData.sources.interfaces;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.DataSourceDto;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This interface holds the complete method list necessary for reading and writing a {@link IFullDataSource}
|
||||
* to and from data streams. <br><br>
|
||||
*
|
||||
* This interface's purpose is to reduce the chance of accidentally mismatching read/write operation data types or content by splitting
|
||||
* up each read/write method into small easy to understand chunks.
|
||||
*
|
||||
* @param <SummaryDataType> defines the object holding this data source's summary data, extends {@link IStreamableFullDataSource.FullDataSourceSummaryData}.
|
||||
* @param <DataContainerType> defines the object holding the data points, probably long[][] or long[][][].
|
||||
* {@link IStreamableFullDataSource#populateFromStream(DataSourceDto, DhDataInputStream, IDhLevel) populateFromStream}
|
||||
* for the full reasoning.
|
||||
*/
|
||||
public interface IStreamableFullDataSource<SummaryDataType extends IStreamableFullDataSource.FullDataSourceSummaryData, DataContainerType> extends IFullDataSource
|
||||
{
|
||||
|
||||
//=================//
|
||||
// stream handling //
|
||||
//=================//
|
||||
|
||||
/**
|
||||
* Clears and then overwrites any data in this object with the data from the given file and stream.
|
||||
* This is expected to be used with an existing {@link IStreamableFullDataSource} and can be used in place of a constructor to reuse an existing {@link IStreamableFullDataSource} object.
|
||||
*
|
||||
* @see IStreamableFullDataSource#populateFromStream
|
||||
*/
|
||||
@Override
|
||||
default void repopulateFromStream(DataSourceDto dto, DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException
|
||||
{
|
||||
// clear/overwrite the old data
|
||||
this.resizeDataStructuresForRepopulation(dto.pos);
|
||||
this.getMapping().clear(dto.pos);
|
||||
|
||||
// set the new data
|
||||
this.populateFromStream(dto, inputStream, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites any data in this object with the data from the given file and stream.
|
||||
* This is expected to be used with an empty {@link IStreamableFullDataSource} and functions similar to a constructor.
|
||||
*/
|
||||
@Override
|
||||
default void populateFromStream(DataSourceDto dto, DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException
|
||||
{
|
||||
SummaryDataType summaryData = this.readSourceSummaryInfo(dto, inputStream, level);
|
||||
this.setSourceSummaryData(summaryData);
|
||||
|
||||
|
||||
DataContainerType dataPoints = this.readDataPoints(dto, summaryData.dataWidth, inputStream);
|
||||
if (dataPoints == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.setDataPoints(dataPoints);
|
||||
|
||||
|
||||
FullDataPointIdMap mapping = this.readIdMappings(dataPoints, inputStream, level.getLevelWrapper());
|
||||
this.setIdMapping(mapping);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
default void writeToStream(DhDataOutputStream outputStream, IDhLevel level) throws IOException
|
||||
{
|
||||
this.writeSourceSummaryInfo(level, outputStream);
|
||||
|
||||
boolean hasData = this.writeDataPoints(outputStream);
|
||||
if (!hasData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.writeIdMappings(outputStream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Note: this should only be used if the data source is being reused. Normally data sources shouldn't change. */
|
||||
void resizeDataStructuresForRepopulation(DhSectionPos pos);
|
||||
|
||||
/**
|
||||
* Includes information about the source file that doesn't need to be saved in each data point. Like the source's size and y-level.
|
||||
*/
|
||||
void writeSourceSummaryInfo(IDhLevel level, DhDataOutputStream outputStream) throws IOException;
|
||||
/**
|
||||
* Confirms that the given {@link DataSourceDto} is valid for this {@link IStreamableFullDataSource}. <br>
|
||||
* This specifically checks any fields that should be set when the {@link IStreamableFullDataSource} was first constructed.
|
||||
*
|
||||
* @throws IOException if the {@link DataSourceDto} isn't valid for this object.
|
||||
*/
|
||||
SummaryDataType readSourceSummaryInfo(DataSourceDto dto, DhDataInputStream inputStream, IDhLevel level) throws IOException;
|
||||
void setSourceSummaryData(SummaryDataType summaryData);
|
||||
|
||||
|
||||
/** @return true if any data points were present and written, false if this object was empty */
|
||||
boolean writeDataPoints(DhDataOutputStream outputStream) throws IOException;
|
||||
/** @return null if no data points were present */
|
||||
DataContainerType readDataPoints(DataSourceDto dto, int width, DhDataInputStream inputStream) throws IOException;
|
||||
void setDataPoints(DataContainerType dataPoints);
|
||||
|
||||
|
||||
void writeIdMappings(DhDataOutputStream outputStream) throws IOException;
|
||||
FullDataPointIdMap readIdMappings(DataContainerType dataPoints, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException;
|
||||
void setIdMapping(FullDataPointIdMap mappings);
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
/**
|
||||
* This holds information that is relevant to the entire source and isn't stored in the data points. <br>
|
||||
* Example: minimum height, detail level, source type, etc.
|
||||
*/
|
||||
class FullDataSourceSummaryData
|
||||
{
|
||||
public final int dataWidth;
|
||||
public EDhApiWorldGenerationStep worldGenStep;
|
||||
|
||||
|
||||
public FullDataSourceSummaryData(int dataWidth, EDhApiWorldGenerationStep worldGenStep)
|
||||
{
|
||||
this.dataWidth = dataWidth;
|
||||
this.worldGenStep = worldGenStep;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+106
-327
@@ -20,27 +20,24 @@
|
||||
package com.seibel.distanthorizons.core.dataObjects.render;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.file.IDataSource;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRenderDataTransformer;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
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.DhSectionPos;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnQuadView;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.IColumnDataView;
|
||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
@@ -56,34 +53,20 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
public static final byte SECTION_SIZE_OFFSET = DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
public static final int SECTION_SIZE = BitShiftUtil.powerOfTwo(SECTION_SIZE_OFFSET);
|
||||
|
||||
public static final byte DATA_FORMAT_VERSION = 1;
|
||||
@Override
|
||||
public byte getDataFormatVersion() { return DATA_FORMAT_VERSION; }
|
||||
|
||||
public static final String DATA_NAME = "ColumnRenderSource";
|
||||
@Override
|
||||
public String getDataTypeName() { return DATA_NAME; }
|
||||
|
||||
/**
|
||||
* This is the byte put between different sections in the binary save file.
|
||||
* The presence and absence of this byte indicates if the file is correctly formatted.
|
||||
*/
|
||||
public static final int DATA_GUARD_BYTE = 0xFFFFFFFF;
|
||||
/** indicates the binary save file represents an empty data source */
|
||||
public static final int NO_DATA_FLAG_BYTE = 0x00000001;
|
||||
public static final DataSourcePool<ColumnRenderSource, IDhClientLevel> DATA_SOURCE_POOL = new DataSourcePool<>(ColumnRenderSource::createEmptyRenderSource, null /* data source prep/cleanup needs to be done outside the pool since it requires additional inputs */);
|
||||
|
||||
|
||||
|
||||
/** will be zero if an empty data source was created */
|
||||
public int verticalDataCount;
|
||||
public final DhSectionPos sectionPos;
|
||||
public final int yOffset;
|
||||
public long pos;
|
||||
public int yOffset;
|
||||
|
||||
public long[] renderDataContainer;
|
||||
public LongArrayList renderDataContainer;
|
||||
|
||||
public final DebugSourceFlag[] debugSourceFlags;
|
||||
|
||||
private boolean isEmpty = true;
|
||||
public EDhApiWorldGenerationStep worldGenStep;
|
||||
|
||||
public AtomicLong localVersion = new AtomicLong(0); // used to track changes to the data source, so that buffers can be updated when necessary
|
||||
|
||||
@@ -93,44 +76,53 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static ColumnRenderSource createEmptyRenderSource(DhSectionPos sectionPos) { return new ColumnRenderSource(sectionPos, 0, 0); }
|
||||
/**
|
||||
* This is separate from {@link DataSourcePool#getPooledSource(long, boolean)}
|
||||
* because we need to pass in a couple extra values,
|
||||
* specifically maxVerticalSize and yOffset.
|
||||
*/
|
||||
public static ColumnRenderSource getPooledRenderSource(long pos, int maxVerticalSize, int yOffset, boolean clearData)
|
||||
{
|
||||
ColumnRenderSource renderSource = DATA_SOURCE_POOL.getPooledSource(pos);
|
||||
|
||||
// set necessary properties
|
||||
renderSource.pos = pos;
|
||||
renderSource.verticalDataCount = maxVerticalSize;
|
||||
renderSource.yOffset = yOffset;
|
||||
|
||||
|
||||
// resize the array if necessary
|
||||
int dataArraySize = SECTION_SIZE * SECTION_SIZE * maxVerticalSize;
|
||||
renderSource.renderDataContainer.ensureCapacity(dataArraySize);
|
||||
while (renderSource.renderDataContainer.size() < dataArraySize)
|
||||
{
|
||||
renderSource.renderDataContainer.add(0);
|
||||
}
|
||||
|
||||
if (clearData)
|
||||
{
|
||||
Arrays.fill(renderSource.renderDataContainer.elements(), 0);
|
||||
Arrays.fill(renderSource.debugSourceFlags, null);
|
||||
}
|
||||
|
||||
return renderSource;
|
||||
}
|
||||
|
||||
|
||||
private static ColumnRenderSource createEmptyRenderSource(long sectionPos) { return new ColumnRenderSource(sectionPos, 0, 0); }
|
||||
/**
|
||||
* Creates an empty ColumnRenderSource.
|
||||
*
|
||||
* @param sectionPos the relative position of the container
|
||||
* @param pos the relative position of the container
|
||||
* @param maxVerticalSize the maximum vertical size of the container
|
||||
*/
|
||||
public ColumnRenderSource(DhSectionPos sectionPos, int maxVerticalSize, int yOffset)
|
||||
private ColumnRenderSource(long pos, int maxVerticalSize, int yOffset)
|
||||
{
|
||||
this.verticalDataCount = maxVerticalSize;
|
||||
this.renderDataContainer = new long[SECTION_SIZE * SECTION_SIZE * this.verticalDataCount];
|
||||
this.renderDataContainer = new LongArrayList(new long[SECTION_SIZE * SECTION_SIZE * this.verticalDataCount]);
|
||||
this.debugSourceFlags = new DebugSourceFlag[SECTION_SIZE * SECTION_SIZE];
|
||||
this.sectionPos = sectionPos;
|
||||
this.pos = pos;
|
||||
this.yOffset = yOffset;
|
||||
this.worldGenStep = EDhApiWorldGenerationStep.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ColumnRenderSource from the parsedColumnData.
|
||||
*
|
||||
* @throws IOException if the DataInputStream's detail level isn't what was expected
|
||||
*/
|
||||
public ColumnRenderSource(DhSectionPos sectionPos, ColumnRenderSourceLoader.ParsedColumnData parsedColumnData, IDhLevel level) throws IOException
|
||||
{
|
||||
if (sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET != parsedColumnData.detailLevel)
|
||||
{
|
||||
throw new IOException("Invalid data: detail level does not match");
|
||||
}
|
||||
|
||||
this.sectionPos = sectionPos;
|
||||
this.yOffset = level.getMinY();
|
||||
this.verticalDataCount = parsedColumnData.verticalSize;
|
||||
this.renderDataContainer = parsedColumnData.dataContainer;
|
||||
this.worldGenStep = parsedColumnData.worldGenStep;
|
||||
this.isEmpty = parsedColumnData.isEmpty;
|
||||
|
||||
this.debugSourceFlags = new DebugSourceFlag[SECTION_SIZE * SECTION_SIZE];
|
||||
this.fillDebugFlag(0, 0, SECTION_SIZE, SECTION_SIZE, DebugSourceFlag.FILE);
|
||||
}
|
||||
|
||||
|
||||
@@ -139,65 +131,7 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
// datapoint manipulation //
|
||||
//========================//
|
||||
|
||||
public void clearDataPoint(int posX, int posZ)
|
||||
{
|
||||
for (int verticalIndex = 0; verticalIndex < this.verticalDataCount; verticalIndex++)
|
||||
{
|
||||
this.renderDataContainer[posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount + verticalIndex] = RenderDataPointUtil.EMPTY_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setDataPoint(long data, int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
this.renderDataContainer[posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount + verticalIndex] = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean copyVerticalData(IColumnDataView newData, int posX, int posZ, boolean overwriteDataWithSameGenerationMode)
|
||||
{
|
||||
if (DO_SAFETY_CHECKS)
|
||||
{
|
||||
if (newData.size() != this.verticalDataCount)
|
||||
throw new IllegalArgumentException("newData size not the same as this column's vertical size");
|
||||
if (posX < 0 || posX >= SECTION_SIZE)
|
||||
throw new IllegalArgumentException("X position is out of bounds");
|
||||
if (posZ < 0 || posZ >= SECTION_SIZE)
|
||||
throw new IllegalArgumentException("Z position is out of bounds");
|
||||
}
|
||||
|
||||
int dataOffset = posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount;
|
||||
int compare = RenderDataPointUtil.compareDatapointPriority(newData.get(0), this.renderDataContainer[dataOffset]);
|
||||
if (overwriteDataWithSameGenerationMode)
|
||||
{
|
||||
if (compare < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (compare <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// copy the newData into this column's data
|
||||
newData.copyTo(this.renderDataContainer, dataOffset, newData.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public long getFirstDataPoint(int posX, int posZ) { return getDataPoint(posX, posZ, 0); }
|
||||
public long getDataPoint(int posX, int posZ, int verticalIndex) { return this.renderDataContainer[posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount + verticalIndex]; }
|
||||
|
||||
public long[] getVerticalDataPointArray(int posX, int posZ)
|
||||
{
|
||||
long[] result = new long[this.verticalDataCount];
|
||||
int index = posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount;
|
||||
System.arraycopy(this.renderDataContainer, index, result, 0, this.verticalDataCount);
|
||||
return result;
|
||||
}
|
||||
public long getDataPoint(int posX, int posZ, int verticalIndex) { return this.renderDataContainer.getLong(posX * SECTION_SIZE * this.verticalDataCount + posZ * this.verticalDataCount + verticalIndex); }
|
||||
|
||||
public ColumnArrayView getVerticalDataPointView(int posX, int posZ)
|
||||
{
|
||||
@@ -209,201 +143,70 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
public ColumnQuadView getFullQuadView() { return this.getQuadViewOverRange(0, 0, SECTION_SIZE, SECTION_SIZE); }
|
||||
public ColumnQuadView getQuadViewOverRange(int quadX, int quadZ, int quadXSize, int quadZSize) { return new ColumnQuadView(this.renderDataContainer, SECTION_SIZE, this.verticalDataCount, quadX, quadZ, quadXSize, quadZSize); }
|
||||
|
||||
public int getVerticalSize() { return this.verticalDataCount; }
|
||||
|
||||
|
||||
|
||||
//========================//
|
||||
// data update and output //
|
||||
//========================//
|
||||
//=============//
|
||||
// data update //
|
||||
//=============//
|
||||
|
||||
@Override
|
||||
public void writeToStream(DhDataOutputStream outputStream, IDhClientLevel level) throws IOException { this.writeToStream(outputStream); }
|
||||
public void writeToStream(DhDataOutputStream outputStream) throws IOException
|
||||
public boolean update(FullDataSourceV2 inputFullDataSource, IDhClientLevel level)
|
||||
{
|
||||
outputStream.flush();
|
||||
|
||||
outputStream.writeByte(this.getDataDetailLevel());
|
||||
outputStream.writeInt(this.verticalDataCount);
|
||||
|
||||
if (this.isEmpty)
|
||||
{
|
||||
// no data is present
|
||||
outputStream.writeByte(NO_DATA_FLAG_BYTE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// data is present
|
||||
outputStream.writeByte(DATA_GUARD_BYTE);
|
||||
outputStream.writeInt(this.yOffset);
|
||||
|
||||
// write the data for each column
|
||||
for (int xz = 0; xz < SECTION_SIZE * SECTION_SIZE; xz++)
|
||||
{
|
||||
for (int y = 0; y < this.verticalDataCount; y++)
|
||||
{
|
||||
long currentDatapoint = this.renderDataContainer[xz * this.verticalDataCount + y];
|
||||
outputStream.writeLong(Long.reverseBytes(currentDatapoint)); // the reverse bytes is necessary to ensure the data is read in correctly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputStream.writeByte(DATA_GUARD_BYTE);
|
||||
outputStream.writeByte(this.worldGenStep.value);
|
||||
|
||||
outputStream.flush();
|
||||
}
|
||||
|
||||
/** Overrides any data that has not been written directly using write(). Skips empty source dataPoints. */
|
||||
public void updateFromRenderSource(ColumnRenderSource renderSource)
|
||||
{
|
||||
// validate we are writing for the same location
|
||||
LodUtil.assertTrue(renderSource.sectionPos.equals(this.sectionPos));
|
||||
|
||||
// change the vertical size if necessary (this can happen if the vertical quality was changed in the config)
|
||||
this.clearAndChangeVerticalSize(renderSource.verticalDataCount);
|
||||
// validate both objects have the same number of dataPoints
|
||||
LodUtil.assertTrue(renderSource.verticalDataCount == this.verticalDataCount);
|
||||
|
||||
|
||||
if (renderSource.isEmpty)
|
||||
{
|
||||
// the source is empty, don't attempt to update anything
|
||||
return;
|
||||
}
|
||||
// the source isn't empty, this object won't be empty after the method finishes
|
||||
this.isEmpty = false;
|
||||
|
||||
localVersion.incrementAndGet();
|
||||
}
|
||||
/**
|
||||
* If the newVerticalSize is different than the current verticalSize,
|
||||
* this will delete any data currently in this object and re-size it. <Br>
|
||||
* Otherwise this method will do nothing.
|
||||
*/
|
||||
private void clearAndChangeVerticalSize(int newVerticalSize)
|
||||
{
|
||||
if (newVerticalSize != this.verticalDataCount)
|
||||
{
|
||||
this.verticalDataCount = newVerticalSize;
|
||||
this.renderDataContainer = new long[SECTION_SIZE * SECTION_SIZE * this.verticalDataCount];
|
||||
this.localVersion.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(ChunkSizedFullDataAccessor chunkDataView, IDhClientLevel level)
|
||||
{
|
||||
final String errorMessagePrefix = "Unable to complete fastWrite for RenderSource pos: [" + this.sectionPos + "] and chunk pos: [" + chunkDataView.chunkPos + "]. Error:";
|
||||
|
||||
final DhSectionPos renderSourcePos = this.getSectionPos();
|
||||
|
||||
final int sourceBlockX = renderSourcePos.getMinCornerLodPos().getCornerBlockPos().x;
|
||||
final int sourceBlockZ = renderSourcePos.getMinCornerLodPos().getCornerBlockPos().z;
|
||||
|
||||
// offset between the incoming chunk data and this render source
|
||||
final int blockOffsetX = (chunkDataView.chunkPos.x * LodUtil.CHUNK_WIDTH) - sourceBlockX;
|
||||
final int blockOffsetZ = (chunkDataView.chunkPos.z * LodUtil.CHUNK_WIDTH) - sourceBlockZ;
|
||||
|
||||
final int sourceDataPointBlockWidth = BitShiftUtil.powerOfTwo(this.getDataDetailLevel());
|
||||
final String errorMessagePrefix = "Unable to complete update for RenderSource pos: [" + this.pos + "] and pos: [" + inputFullDataSource.getPos() + "]. Error:";
|
||||
|
||||
boolean dataChanged = false;
|
||||
|
||||
if (chunkDataView.detailLevel == this.getDataDetailLevel())
|
||||
if (DhSectionPos.getDetailLevel(inputFullDataSource.getPos()) == DhSectionPos.getDetailLevel(this.pos))
|
||||
{
|
||||
this.markNotEmpty();
|
||||
// confirm the render source contains this chunk
|
||||
if (blockOffsetX < 0
|
||||
|| blockOffsetX + LodUtil.CHUNK_WIDTH > this.getWidthInDataPoints()
|
||||
|| blockOffsetZ < 0
|
||||
|| blockOffsetZ + LodUtil.CHUNK_WIDTH > this.getWidthInDataPoints())
|
||||
try
|
||||
{
|
||||
LOGGER.warn(errorMessagePrefix+"Data offset is out of bounds.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (Thread.interrupted())
|
||||
{
|
||||
LOGGER.warn(errorMessagePrefix+"write interrupted.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
||||
if (Thread.interrupted())
|
||||
{
|
||||
ColumnArrayView columnArrayView = this.getVerticalDataPointView(blockOffsetX + x, blockOffsetZ + z);
|
||||
int hash = columnArrayView.getDataHash();
|
||||
SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(x, z);
|
||||
FullDataToRenderDataTransformer.convertColumnData(level,
|
||||
sourceBlockX + sourceDataPointBlockWidth * (blockOffsetX + x),
|
||||
sourceBlockZ + sourceDataPointBlockWidth * (blockOffsetZ + z),
|
||||
columnArrayView, fullArrayView);
|
||||
dataChanged |= hash != columnArrayView.getDataHash();
|
||||
LOGGER.warn(errorMessagePrefix + "write interrupted.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DhBlockPos2D centerBlockPos = DhSectionPos.getCenterBlockPos(inputFullDataSource.getPos());
|
||||
int halfBlockWidth = DhSectionPos.getBlockWidth(inputFullDataSource.getPos()) / 2;
|
||||
DhBlockPos2D minBlockPos = new DhBlockPos2D(centerBlockPos.x - halfBlockWidth, centerBlockPos.z - halfBlockWidth);
|
||||
|
||||
for (int x = 0; x < FullDataSourceV2.WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < FullDataSourceV2.WIDTH; z++)
|
||||
{
|
||||
ColumnArrayView columnArrayView = this.getVerticalDataPointView(x, z);
|
||||
int columnHash = columnArrayView.getDataHash();
|
||||
|
||||
LongArrayList dataColumn = inputFullDataSource.get(x, z);
|
||||
EDhApiWorldGenerationStep worldGenStep = inputFullDataSource.getWorldGenStepAtRelativePos(x, z);
|
||||
if (dataColumn != null && worldGenStep != EDhApiWorldGenerationStep.EMPTY)
|
||||
{
|
||||
FullDataToRenderDataTransformer.convertColumnData(
|
||||
level, inputFullDataSource.mapping,
|
||||
minBlockPos.x + x,
|
||||
minBlockPos.z + z,
|
||||
columnArrayView, dataColumn);
|
||||
dataChanged |= columnHash != columnArrayView.getDataHash();
|
||||
|
||||
this.fillDebugFlag(x, z, 1, 1, ColumnRenderSource.DebugSourceFlag.DIRECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.fillDebugFlag(blockOffsetX, blockOffsetZ, LodUtil.CHUNK_WIDTH, LodUtil.CHUNK_WIDTH, ColumnRenderSource.DebugSourceFlag.DIRECT);
|
||||
}
|
||||
else if (chunkDataView.detailLevel < this.getDataDetailLevel() && this.getDataDetailLevel() <= chunkDataView.getSectionPos().getDetailLevel())
|
||||
{
|
||||
this.markNotEmpty();
|
||||
// multiple chunk data points converting to 1 column data point
|
||||
DhLodPos dataCornerPos = chunkDataView.getSectionPos().getMinCornerLodPos(chunkDataView.detailLevel);
|
||||
DhLodPos sourceCornerPos = renderSourcePos.getMinCornerLodPos(this.getDataDetailLevel());
|
||||
DhLodPos sourceStartingChangePos = dataCornerPos.convertToDetailLevel(this.getDataDetailLevel());
|
||||
int relStartX = Math.floorMod(sourceStartingChangePos.x, this.getWidthInDataPoints());
|
||||
int relStartZ = Math.floorMod(sourceStartingChangePos.z, this.getWidthInDataPoints());
|
||||
int dataToSourceScale = sourceCornerPos.getWidthAtDetail(chunkDataView.detailLevel);
|
||||
int columnsInChunk = chunkDataView.getSectionPos().getWidthCountForLowerDetailedSection(this.getDataDetailLevel());
|
||||
|
||||
for (int xOffset = 0; xOffset < columnsInChunk; xOffset++)
|
||||
catch (Exception e)
|
||||
{
|
||||
for (int zOffset = 0; zOffset < columnsInChunk; zOffset++)
|
||||
{
|
||||
int relSourceX = relStartX + xOffset;
|
||||
int relSourceZ = relStartZ + zOffset;
|
||||
ColumnArrayView columnArrayView = this.getVerticalDataPointView(relSourceX, relSourceZ);
|
||||
int hash = columnArrayView.getDataHash();
|
||||
SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(xOffset * dataToSourceScale, zOffset * dataToSourceScale);
|
||||
FullDataToRenderDataTransformer.convertColumnData(level,
|
||||
sourceBlockX + sourceDataPointBlockWidth * relSourceX,
|
||||
sourceBlockZ + sourceDataPointBlockWidth * relSourceZ,
|
||||
columnArrayView, fullArrayView);
|
||||
dataChanged |= hash != columnArrayView.getDataHash();
|
||||
}
|
||||
LOGGER.error(errorMessagePrefix + e.getMessage(), e);
|
||||
}
|
||||
this.fillDebugFlag(relStartX, relStartZ, columnsInChunk, columnsInChunk, ColumnRenderSource.DebugSourceFlag.DIRECT);
|
||||
}
|
||||
else if (chunkDataView.getSectionPos().getDetailLevel() < this.getDataDetailLevel())
|
||||
{
|
||||
// The entire chunk is being converted to a single column data point, possibly.
|
||||
DhLodPos dataCornerPos = chunkDataView.getSectionPos().getMinCornerLodPos(chunkDataView.detailLevel);
|
||||
DhLodPos sourceCornerPos = renderSourcePos.getMinCornerLodPos(this.getDataDetailLevel());
|
||||
DhLodPos sourceStartingChangePos = dataCornerPos.convertToDetailLevel(this.getDataDetailLevel());
|
||||
int chunksPerColumn = sourceStartingChangePos.getWidthAtDetail(chunkDataView.getSectionPos().getDetailLevel());
|
||||
if (chunkDataView.getSectionPos().getX() % chunksPerColumn != 0 || chunkDataView.getSectionPos().getZ() % chunksPerColumn != 0)
|
||||
{
|
||||
return; // not a multiple of the column size, so no change
|
||||
}
|
||||
int relStartX = Math.floorMod(sourceStartingChangePos.x, this.getWidthInDataPoints());
|
||||
int relStartZ = Math.floorMod(sourceStartingChangePos.z, this.getWidthInDataPoints());
|
||||
ColumnArrayView columnArrayView = this.getVerticalDataPointView(relStartX, relStartZ);
|
||||
int hash = columnArrayView.getDataHash();
|
||||
SingleColumnFullDataAccessor fullArrayView = chunkDataView.get(0, 0);
|
||||
FullDataToRenderDataTransformer.convertColumnData(level, dataCornerPos.x * sourceDataPointBlockWidth,
|
||||
dataCornerPos.z * sourceDataPointBlockWidth,
|
||||
columnArrayView, fullArrayView);
|
||||
dataChanged = hash != columnArrayView.getDataHash();
|
||||
this.fillDebugFlag(relStartX, relStartZ, 1, 1, ColumnRenderSource.DebugSourceFlag.DIRECT);
|
||||
}
|
||||
|
||||
|
||||
if (dataChanged)
|
||||
{
|
||||
this.localVersion.incrementAndGet();
|
||||
this.markNotEmpty();
|
||||
}
|
||||
|
||||
return dataChanged;
|
||||
}
|
||||
|
||||
|
||||
@@ -412,39 +215,11 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
// data helper methods //
|
||||
//=====================//
|
||||
|
||||
public boolean doesDataPointExist(int posX, int posZ) { return RenderDataPointUtil.doesDataPointExist(this.getFirstDataPoint(posX, posZ)); }
|
||||
|
||||
public void generateData(ColumnRenderSource lowerDataContainer, int posX, int posZ)
|
||||
{
|
||||
ColumnArrayView outputView = this.getVerticalDataPointView(posX, posZ);
|
||||
ColumnQuadView quadView = lowerDataContainer.getQuadViewOverRange(posX * 2, posZ * 2, 2, 2);
|
||||
outputView.mergeMultiDataFrom(quadView);
|
||||
}
|
||||
|
||||
public int getMaxLodCount() { return SECTION_SIZE * SECTION_SIZE * this.getVerticalSize(); }
|
||||
|
||||
public long getRoughRamUsageInBytes() { return (long) this.renderDataContainer.length * Long.BYTES; }
|
||||
|
||||
public DhSectionPos getSectionPos() { return this.sectionPos; }
|
||||
public Long getPos() { return this.pos; }
|
||||
@Override
|
||||
public String getPrimaryKeyString() { return this.sectionPos.serialize(); }
|
||||
public Long getKey() { return this.pos; }
|
||||
|
||||
public byte getDataDetailLevel() { return (byte) (this.sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET); }
|
||||
|
||||
@Override
|
||||
public EDhApiWorldGenerationStep getWorldGenStep() { return EDhApiWorldGenerationStep.EMPTY; }
|
||||
|
||||
/** @return how many data points wide this {@link ColumnRenderSource} is. */
|
||||
public int getWidthInDataPoints() { return BitShiftUtil.powerOfTwo(this.getDetailOffset()); }
|
||||
public byte getDetailOffset() { return SECTION_SIZE_OFFSET; }
|
||||
|
||||
public byte getRenderDataFormatVersion() { return DATA_FORMAT_VERSION; }
|
||||
|
||||
/**
|
||||
* Whether this object is still valid. If not, a new one should be created.
|
||||
* TODO this will be necessary for dedicated multiplayer support, if the server has newer data this section should no longer be valid
|
||||
*/
|
||||
public boolean isValid() { return true; }
|
||||
public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.pos) - SECTION_SIZE_OFFSET); }
|
||||
|
||||
public boolean isEmpty() { return this.isEmpty; }
|
||||
public void markNotEmpty() { this.isEmpty = false; }
|
||||
@@ -493,7 +268,6 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
this.debugSourceFlags[x * SECTION_SIZE + z] = flag;
|
||||
}
|
||||
}
|
||||
localVersion.incrementAndGet();
|
||||
}
|
||||
|
||||
public DebugSourceFlag debugGetFlag(int ox, int oz) { return this.debugSourceFlags[ox * SECTION_SIZE + oz]; }
|
||||
@@ -512,7 +286,7 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
String SUBDATA_DELIMITER = ",";
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
stringBuilder.append(this.sectionPos);
|
||||
stringBuilder.append(this.pos);
|
||||
stringBuilder.append(LINE_DELIMITER);
|
||||
|
||||
int size = 1;
|
||||
@@ -538,6 +312,12 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception
|
||||
{
|
||||
DATA_SOURCE_POOL.returnPooledDataSource(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
@@ -548,7 +328,6 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
{
|
||||
FULL(ColorUtil.BLUE),
|
||||
DIRECT(ColorUtil.WHITE),
|
||||
SPARSE(ColorUtil.YELLOW),
|
||||
FILE(ColorUtil.BROWN);
|
||||
|
||||
public final int color;
|
||||
|
||||
-171
@@ -1,171 +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.dataObjects.render;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.sql.DataSourceDto;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* Handles loading and parsing {@link DataSourceDto}s to create {@link ColumnRenderSource}s. <br><br>
|
||||
*
|
||||
* Please see the {@link ColumnRenderSourceLoader#loadRenderSource} method to see what
|
||||
* file versions this class can handle.
|
||||
*/
|
||||
public class ColumnRenderSourceLoader
|
||||
{
|
||||
public static ColumnRenderSourceLoader INSTANCE = new ColumnRenderSourceLoader();
|
||||
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
|
||||
private ColumnRenderSourceLoader() { }
|
||||
|
||||
|
||||
|
||||
public ColumnRenderSource loadRenderSource(DataSourceDto dto, DhDataInputStream inputStream, IDhLevel level) throws IOException
|
||||
{
|
||||
int dataFileVersion = dto.binaryDataFormatVersion;
|
||||
|
||||
switch (dataFileVersion)
|
||||
{
|
||||
case 1:
|
||||
ParsedColumnData parsedColumnData = readDataV1(inputStream, level.getMinY());
|
||||
return new ColumnRenderSource(dto.pos, parsedColumnData, level);
|
||||
default:
|
||||
throw new IOException("Invalid Data: The data version [" + dataFileVersion + "] is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//========================//
|
||||
// versioned file parsing //
|
||||
//========================//
|
||||
|
||||
/**
|
||||
* @param inputStream Expected format: 1st byte: detail level, 2nd byte: vertical size, 3rd byte on: column data
|
||||
* @throws IOException if there was an issue reading the stream
|
||||
*/
|
||||
private static ParsedColumnData readDataV1(DhDataInputStream inputStream, int expectedYOffset) throws IOException
|
||||
{
|
||||
// TODO move into ColumnRenderSource
|
||||
|
||||
byte detailLevel = inputStream.readByte();
|
||||
|
||||
int verticalDataCount = inputStream.readInt();
|
||||
if (verticalDataCount <= 0)
|
||||
{
|
||||
throw new IOException("Invalid data: vertical size must be 0 or greater");
|
||||
}
|
||||
|
||||
int maxNumberOfDataPoints = ColumnRenderSource.SECTION_SIZE * ColumnRenderSource.SECTION_SIZE * verticalDataCount;
|
||||
|
||||
|
||||
byte dataPresentFlag = inputStream.readByte();
|
||||
if (dataPresentFlag != ColumnRenderSource.NO_DATA_FLAG_BYTE && dataPresentFlag != ColumnRenderSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("Incorrect render file format. Expected either: NO_DATA_FLAG_BYTE [" + ColumnRenderSource.NO_DATA_FLAG_BYTE + "] or DATA_GUARD_BYTE [" + ColumnRenderSource.DATA_GUARD_BYTE + "], Found: [" + dataPresentFlag + "]");
|
||||
}
|
||||
else if (dataPresentFlag == ColumnRenderSource.NO_DATA_FLAG_BYTE)
|
||||
{
|
||||
// no data is present
|
||||
return new ParsedColumnData(detailLevel, verticalDataCount, EDhApiWorldGenerationStep.EMPTY, new long[maxNumberOfDataPoints], true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// data is present
|
||||
|
||||
int fileYOffset = inputStream.readInt();
|
||||
if (fileYOffset != expectedYOffset)
|
||||
{
|
||||
throw new IOException("Invalid data: yOffset is incorrect. Expected: [" + expectedYOffset + "], found: [" + fileYOffset + "].");
|
||||
}
|
||||
|
||||
|
||||
// read the column data
|
||||
byte[] rawByteData = new byte[maxNumberOfDataPoints * Long.BYTES];
|
||||
ByteBuffer columnDataByteBuffer = ByteBuffer.wrap(rawByteData).order(ByteOrder.LITTLE_ENDIAN);
|
||||
inputStream.readFully(rawByteData);
|
||||
|
||||
|
||||
// parse the column data
|
||||
long[] dataPoints = new long[maxNumberOfDataPoints];
|
||||
columnDataByteBuffer.asLongBuffer().get(dataPoints);
|
||||
|
||||
boolean isEmpty = true;
|
||||
for (long dataPoint : dataPoints)
|
||||
{
|
||||
if (dataPoint != 0)
|
||||
{
|
||||
isEmpty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
byte guardByteFlag = inputStream.readByte();
|
||||
if (guardByteFlag != ColumnRenderSource.DATA_GUARD_BYTE)
|
||||
{
|
||||
throw new IOException("invalid world gen step end guard");
|
||||
}
|
||||
EDhApiWorldGenerationStep worldGenStep = EDhApiWorldGenerationStep.fromValue(inputStream.readByte());
|
||||
if (worldGenStep == null)
|
||||
{
|
||||
LOGGER.warn("Missing WorldGenStep, defaulting to: " + EDhApiWorldGenerationStep.SURFACE.name());
|
||||
worldGenStep = EDhApiWorldGenerationStep.SURFACE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new ParsedColumnData(detailLevel, verticalDataCount, worldGenStep, dataPoints, isEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ParsedColumnData
|
||||
{
|
||||
public final byte detailLevel;
|
||||
public final int verticalSize;
|
||||
public final EDhApiWorldGenerationStep worldGenStep;
|
||||
public final long[] dataContainer;
|
||||
public final boolean isEmpty;
|
||||
|
||||
public ParsedColumnData(byte detailLevel, int verticalSize, EDhApiWorldGenerationStep worldGenStep, long[] dataContainer, boolean isEmpty)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
this.verticalSize = verticalSize;
|
||||
this.worldGenStep = worldGenStep;
|
||||
this.dataContainer = dataContainer;
|
||||
this.isEmpty = isEmpty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+11
-3
@@ -151,6 +151,7 @@ public final class BufferQuad
|
||||
{
|
||||
if (quad.hasError || this.hasError)
|
||||
return false;
|
||||
|
||||
// only merge quads that are in the same direction
|
||||
if (this.direction != quad.direction)
|
||||
return false;
|
||||
@@ -284,10 +285,17 @@ public final class BufferQuad
|
||||
if (thisPerpendicularCompareStartPos < otherPerpendicularCompareStartPos + otherPerpendicularCompareWidth)
|
||||
{
|
||||
// these quads are overlapping, they can't be merged
|
||||
//EVENT_LOGGER.warn("Overlapping quads detected!");
|
||||
quad.hasError = true;
|
||||
this.hasError = true;
|
||||
|
||||
// Overlapping quads appear to render correctly, why are we marking them as errored?
|
||||
// Is it possible the wrong quad will be extended thus the wrong color is rendered?
|
||||
// Or is that the height/depth might be wrong?
|
||||
if (Config.Client.Advanced.Debugging.showOverlappingQuadErrors.get())
|
||||
{
|
||||
quad.hasError = true;
|
||||
this.hasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+23
-16
@@ -22,7 +22,9 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
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;
|
||||
@@ -63,13 +65,12 @@ public class ColumnBox
|
||||
// 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)
|
||||
&& 0 == skyLight
|
||||
&& builder.skyLightCullingBelow > maxY
|
||||
&& (
|
||||
(RenderDataPointUtil.getAlpha(topData) < 255 && RenderDataPointUtil.getYMax(topData) >= builder.skyLightCullingBelow)
|
||||
|| (RenderDataPointUtil.getYMin(topData) >= builder.skyLightCullingBelow)
|
||||
|| !RenderDataPointUtil.doesDataPointExist(topData)
|
||||
)
|
||||
)
|
||||
{
|
||||
@@ -124,7 +125,7 @@ public class ColumnBox
|
||||
// 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, (byte) 15, blockLight);
|
||||
builder.addQuadAdj(EDhDirection.NORTH, x, minY, z, xSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||
}
|
||||
}
|
||||
else if (adjDataNorth.length == 1)
|
||||
@@ -151,7 +152,7 @@ public class ColumnBox
|
||||
if (adjDataSouth == null)
|
||||
{
|
||||
if (!isTransparent || overVoid)
|
||||
builder.addQuadAdj(EDhDirection.SOUTH, x, minY, maxZ, xSize, ySize, color, irisBlockMaterialId, (byte) 15, blockLight);
|
||||
builder.addQuadAdj(EDhDirection.SOUTH, x, minY, maxZ, xSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||
}
|
||||
else if (adjDataSouth.length == 1)
|
||||
{
|
||||
@@ -178,7 +179,7 @@ public class ColumnBox
|
||||
if (adjDataWest == null)
|
||||
{
|
||||
if (!isTransparent || overVoid)
|
||||
builder.addQuadAdj(EDhDirection.WEST, x, minY, z, zSize, ySize, color, irisBlockMaterialId, (byte) 15, blockLight);
|
||||
builder.addQuadAdj(EDhDirection.WEST, x, minY, z, zSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||
}
|
||||
else if (adjDataWest.length == 1)
|
||||
{
|
||||
@@ -204,7 +205,7 @@ public class ColumnBox
|
||||
if (adjData[EDhDirection.EAST.ordinal() - 2] == null)
|
||||
{
|
||||
if (!isTransparent || overVoid)
|
||||
builder.addQuadAdj(EDhDirection.EAST, maxX, minY, z, zSize, ySize, color, irisBlockMaterialId, (byte) 15, blockLight);
|
||||
builder.addQuadAdj(EDhDirection.EAST, maxX, minY, z, zSize, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||
}
|
||||
else if (adjDataEast.length == 1)
|
||||
{
|
||||
@@ -236,7 +237,7 @@ public class ColumnBox
|
||||
if (adjColumnView == null || 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, (byte) 15, blockLight);
|
||||
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, color, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, blockLight);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -393,7 +394,7 @@ public class ColumnBox
|
||||
// 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, (byte) 15, (byte) 15);
|
||||
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, ySize, debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -402,7 +403,7 @@ public class ColumnBox
|
||||
|
||||
if (adjYMax > yMin && debugOverlapColor != 0)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, (short) (adjYMax - yMin), debugOverlapColor, irisBlockMaterialId, (byte) 15, (byte) 15);
|
||||
builder.addQuadAdj(direction, x, yMin, z, horizontalWidth, (short) (adjYMax - yMin), debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
|
||||
}
|
||||
|
||||
// if this is the only face, use the yMax and break,
|
||||
@@ -448,7 +449,7 @@ public class ColumnBox
|
||||
// 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, (byte) 15, (byte) 15);
|
||||
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
|
||||
@@ -459,7 +460,7 @@ public class ColumnBox
|
||||
// _______&&: y < depth ______ < yMax
|
||||
if (debugOverlapColor != 0)
|
||||
{
|
||||
builder.addQuadAdj(direction, x, adjYMin, z, horizontalWidth, (short) (adjYMax - adjYMin), debugOverlapColor, irisBlockMaterialId, (byte) 15, (byte) 15);
|
||||
builder.addQuadAdj(direction, x, adjYMin, z, horizontalWidth, (short) (adjYMax - adjYMin), debugOverlapColor, irisBlockMaterialId, LodUtil.MAX_MC_LIGHT, LodUtil.MAX_MC_LIGHT);
|
||||
}
|
||||
|
||||
if (firstFace)
|
||||
@@ -474,6 +475,12 @@ public class ColumnBox
|
||||
throw new RuntimeException("Loop error");
|
||||
if (previousAdjDepth > adjYMax)
|
||||
{
|
||||
if (irisBlockMaterialId == IBlockStateWrapper.IrisBlockMaterial.GRASS)
|
||||
{
|
||||
// this LOD is underneath another, grass will never show here
|
||||
irisBlockMaterialId = IBlockStateWrapper.IrisBlockMaterial.DIRT;
|
||||
}
|
||||
|
||||
builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (previousAdjDepth - adjYMax), color, irisBlockMaterialId,
|
||||
RenderDataPointUtil.getLightSky(adjPoint), blockLight);
|
||||
}
|
||||
|
||||
+36
-22
@@ -25,14 +25,12 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.enums.EGLProxyContext;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.render.AbstractRenderBuffer;
|
||||
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.EGpuUploadMethod;
|
||||
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;
|
||||
@@ -46,13 +44,19 @@ import java.util.concurrent.*;
|
||||
*
|
||||
* @see ColumnRenderBufferBuilder
|
||||
*/
|
||||
public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
public class ColumnRenderBuffer implements AutoCloseable
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final IMinecraftClientWrapper minecraftClient = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
|
||||
private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000;
|
||||
|
||||
public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4; // TODO what does the 4 represent
|
||||
public static final int MAX_QUADS_PER_BUFFER = (1024 * 1024 * 1) / QUADS_BYTE_SIZE; // TODO what do these multiples represent?
|
||||
public static final int FULL_SIZED_BUFFER = MAX_QUADS_PER_BUFFER * QUADS_BYTE_SIZE;
|
||||
|
||||
|
||||
|
||||
|
||||
public final DhBlockPos pos;
|
||||
|
||||
@@ -61,17 +65,15 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
private GLVertexBuffer[] vbos;
|
||||
private GLVertexBuffer[] vbosTransparent;
|
||||
|
||||
private final DhSectionPos debugPos;
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public ColumnRenderBuffer(DhBlockPos pos, DhSectionPos debugPos)
|
||||
public ColumnRenderBuffer(DhBlockPos pos)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.debugPos = debugPos;
|
||||
this.vbos = new GLVertexBuffer[0];
|
||||
this.vbosTransparent = new GLVertexBuffer[0];
|
||||
}
|
||||
@@ -85,7 +87,7 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
//==================//
|
||||
|
||||
/** Should be run on a DH thread. */
|
||||
public void uploadBuffer(LodQuadBuilder builder, EGpuUploadMethod gpuUploadMethod) throws InterruptedException
|
||||
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.");
|
||||
|
||||
@@ -144,7 +146,7 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
|
||||
}
|
||||
}
|
||||
private void uploadBuffersUsingUploadMethod(LodQuadBuilder builder, EGpuUploadMethod gpuUploadMethod) throws InterruptedException
|
||||
private void uploadBuffersUsingUploadMethod(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) throws InterruptedException
|
||||
{
|
||||
if (gpuUploadMethod.useEarlyMapping)
|
||||
{
|
||||
@@ -160,7 +162,7 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
|
||||
|
||||
|
||||
private void uploadBuffersMapped(LodQuadBuilder builder, EGpuUploadMethod method)
|
||||
private void uploadBuffersMapped(LodQuadBuilder builder, EDhApiGpuUploadMethod method)
|
||||
{
|
||||
// opaque vbos //
|
||||
|
||||
@@ -196,7 +198,7 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadBuffersDirect(LodQuadBuilder builder, EGpuUploadMethod method) throws InterruptedException
|
||||
private void uploadBuffersDirect(LodQuadBuilder builder, EDhApiGpuUploadMethod method) throws InterruptedException
|
||||
{
|
||||
this.vbos = ColumnRenderBufferBuilder.resizeBuffer(this.vbos, builder.getCurrentNeededOpaqueVertexBufferCount());
|
||||
uploadBuffersDirect(this.vbos, builder.makeOpaqueVertexBuffers(), method);
|
||||
@@ -204,7 +206,7 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
this.vbosTransparent = ColumnRenderBufferBuilder.resizeBuffer(this.vbosTransparent, builder.getCurrentNeededTransparentVertexBufferCount());
|
||||
uploadBuffersDirect(this.vbosTransparent, builder.makeTransparentVertexBuffers(), method);
|
||||
}
|
||||
private static void uploadBuffersDirect(GLVertexBuffer[] vbos, Iterator<ByteBuffer> iter, EGpuUploadMethod method) throws InterruptedException
|
||||
private static void uploadBuffersDirect(GLVertexBuffer[] vbos, Iterator<ByteBuffer> iter, EDhApiGpuUploadMethod method) throws InterruptedException
|
||||
{
|
||||
long remainingMS = 0;
|
||||
long MBPerMS = Config.Client.Advanced.GpuBuffers.gpuUploadPerMegabyteInMilliseconds.get();
|
||||
@@ -275,7 +277,7 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
// render //
|
||||
//========//
|
||||
|
||||
@Override
|
||||
/** @return true if something was rendered, false otherwise */
|
||||
public boolean renderOpaque(LodRenderer renderContext, DhApiRenderParam renderEventParam)
|
||||
{
|
||||
boolean hasRendered = false;
|
||||
@@ -299,7 +301,7 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
return hasRendered;
|
||||
}
|
||||
|
||||
@Override
|
||||
/** @return true if something was rendered, false otherwise */
|
||||
public boolean renderTransparent(LodRenderer renderContext, DhApiRenderParam renderEventParam)
|
||||
{
|
||||
boolean hasRendered = false;
|
||||
@@ -341,20 +343,26 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
//==============//
|
||||
|
||||
/** can be used when debugging */
|
||||
public boolean hasNonEmptyBuffers()
|
||||
public boolean hasNonNullVbos() { return this.vbos != null || this.vbosTransparent != null; }
|
||||
|
||||
/** can be used when debugging */
|
||||
public int vboBufferCount()
|
||||
{
|
||||
for (GLVertexBuffer vertexBuffer : this.vbos)
|
||||
int count = 0;
|
||||
|
||||
if (this.vbos != null)
|
||||
{
|
||||
if (vertexBuffer != null && vertexBuffer.getSize() != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
count += this.vbos.length;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (this.vbosTransparent != null)
|
||||
{
|
||||
count += this.vbosTransparent.length;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debugDumpStats(StatsMap statsMap)
|
||||
{
|
||||
statsMap.incStat("RenderBuffers");
|
||||
@@ -378,6 +386,12 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when object is no longer in use.
|
||||
* Called either after uploadBuffers() returned false (On buffer Upload
|
||||
* thread), or by others when the object is not being used. (not in build,
|
||||
* upload, or render state).
|
||||
*/
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
|
||||
+43
-55
@@ -19,7 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDebugRendering;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
||||
@@ -27,18 +27,19 @@ 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.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
|
||||
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.Reference;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPools;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
@@ -63,12 +64,12 @@ public class ColumnRenderBufferBuilder
|
||||
// vbo building //
|
||||
//==============//
|
||||
|
||||
public static CompletableFuture<ColumnRenderBuffer> buildBuffersAsync(
|
||||
IDhClientLevel clientLevel, Reference<ColumnRenderBuffer> renderBufferRef,
|
||||
public static CompletableFuture<ColumnRenderBuffer> buildAndUploadBuffersAsync(
|
||||
IDhClientLevel clientLevel,
|
||||
ColumnRenderSource renderSource, ColumnRenderSource[] adjData)
|
||||
{
|
||||
ThreadPoolExecutor bufferBuilderExecutor = ThreadPools.getBufferBuilderExecutor();
|
||||
ThreadPoolExecutor bufferUploaderExecutor = ThreadPools.getBufferUploaderExecutor();
|
||||
ThreadPoolExecutor bufferBuilderExecutor = ThreadPoolUtil.getBufferBuilderExecutor();
|
||||
ThreadPoolExecutor bufferUploaderExecutor = ThreadPoolUtil.getBufferUploaderExecutor();
|
||||
if ((bufferBuilderExecutor == null || bufferBuilderExecutor.isTerminated()) ||
|
||||
(bufferUploaderExecutor == null || bufferUploaderExecutor.isTerminated()))
|
||||
{
|
||||
@@ -78,15 +79,27 @@ public class ColumnRenderBufferBuilder
|
||||
return future;
|
||||
}
|
||||
|
||||
//LOGGER.info("RenderRegion startBuild @ "+renderSource.sectionPos);
|
||||
return CompletableFuture.supplyAsync(() ->
|
||||
try
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
|
||||
|
||||
EVENT_LOGGER.trace("RenderRegion start QuadBuild @ " + renderSource.sectionPos);
|
||||
boolean enableSkyLightCulling = !clientLevel.getLevelWrapper().hasCeiling() && Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get();
|
||||
//EVENT_LOGGER.trace("RenderRegion start QuadBuild @ " + renderSource.sectionPos);
|
||||
boolean enableSkyLightCulling =
|
||||
Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get()
|
||||
&& (
|
||||
// dimensions with a ceiling will be all caves so we don't want cave culling
|
||||
!clientLevel.getLevelWrapper().hasCeiling()
|
||||
// the end has a lot of overhangs with 0 lighting above the void, which look broken with
|
||||
// the current cave culling logic (this could probably be improved, but just skipping it works best for now)
|
||||
&& !clientLevel.getLevelWrapper().getDimensionType().isTheEnd()
|
||||
// FIXME temporary fix
|
||||
// Cave culling is currently broken for any detail level above 0
|
||||
&& DhSectionPos.getDetailLevel(renderSource.pos) == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL
|
||||
);
|
||||
|
||||
int skyLightCullingBelow = Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight.get();
|
||||
// FIXME: Clamp also to the max world height.
|
||||
@@ -95,12 +108,12 @@ public class ColumnRenderBufferBuilder
|
||||
|
||||
long builderStartTime = System.currentTimeMillis();
|
||||
|
||||
LodQuadBuilder builder = new LodQuadBuilder(enableSkyLightCulling, (short) (skyLightCullingBelow - clientLevel.getMinY()), enableTransparency);
|
||||
LodQuadBuilder builder = new LodQuadBuilder(enableSkyLightCulling, (short) (skyLightCullingBelow - clientLevel.getMinY()), enableTransparency, clientLevel.getClientLevelWrapper());
|
||||
makeLodRenderData(builder, renderSource, adjData);
|
||||
|
||||
long builderEndTime = System.currentTimeMillis();
|
||||
long buildMs = builderEndTime - builderStartTime;
|
||||
LOGGER.debug("RenderRegion end QuadBuild @ " + renderSource.sectionPos + " took: " + buildMs);
|
||||
LOGGER.debug("RenderRegion end QuadBuild @ " + renderSource.pos + " took: " + buildMs);
|
||||
|
||||
return builder;
|
||||
}
|
||||
@@ -118,19 +131,11 @@ public class ColumnRenderBufferBuilder
|
||||
{
|
||||
try
|
||||
{
|
||||
EVENT_LOGGER.trace("RenderRegion start Upload @ " + renderSource.sectionPos);
|
||||
|
||||
ColumnRenderBuffer buffer = renderBufferRef.swap(null);
|
||||
if (buffer == null)
|
||||
{
|
||||
buffer = new ColumnRenderBuffer(new DhBlockPos(renderSource.sectionPos.getMinCornerLodPos().getCornerBlockPos(), clientLevel.getMinY()), renderSource.sectionPos);
|
||||
}
|
||||
|
||||
ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(renderSource.pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(renderSource.pos)));
|
||||
try
|
||||
{
|
||||
buffer.uploadBuffer(quadBuilder, GLProxy.getInstance().getGpuUploadMethod());
|
||||
LodUtil.assertTrue(buffer.buffersUploaded);
|
||||
EVENT_LOGGER.trace("RenderRegion end Upload @ " + renderSource.sectionPos);
|
||||
return buffer;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -145,49 +150,33 @@ public class ColumnRenderBufferBuilder
|
||||
}
|
||||
catch (Throwable e3)
|
||||
{
|
||||
LOGGER.error("\"LodNodeBufferBuilder\" was unable to upload buffer: ", e3);
|
||||
LOGGER.error("LodNodeBufferBuilder was unable to upload buffer: " + e3.getMessage(), e3);
|
||||
throw e3;
|
||||
}
|
||||
}, bufferUploaderExecutor)
|
||||
.handle((columnRenderBuffer, ex) ->
|
||||
{
|
||||
//LOGGER.info("RenderRegion endBuild @ {}", renderSource.sectionPos);
|
||||
if (ex != null)
|
||||
{
|
||||
LOGGER.warn("Buffer building failed: " + ex.getMessage(), ex);
|
||||
|
||||
if (!renderBufferRef.isEmpty())
|
||||
{
|
||||
ColumnRenderBuffer buffer = renderBufferRef.swap(null);
|
||||
buffer.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (columnRenderBuffer != null)
|
||||
{
|
||||
LodUtil.assertTrue(columnRenderBuffer.buffersUploaded);
|
||||
}
|
||||
|
||||
return columnRenderBuffer;
|
||||
}
|
||||
});
|
||||
}, 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
|
||||
|
||||
CompletableFuture<ColumnRenderBuffer> future = new CompletableFuture<>();
|
||||
future.cancel(true);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
private static void makeLodRenderData(LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, ColumnRenderSource[] adjRegions)
|
||||
{
|
||||
// Variable initialization
|
||||
EDebugRendering debugMode = Config.Client.Advanced.Debugging.debugRendering.get();
|
||||
EDhApiDebugRendering debugMode = Config.Client.Advanced.Debugging.debugRendering.get();
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (renderSource.sectionPos.getDetailLevel() == Config.Client.Advanced.Debugging.columnBuilderDebugDetailLevel.get()
|
||||
&& renderSource.sectionPos.getX() == Config.Client.Advanced.Debugging.columnBuilderDebugXPos.get()
|
||||
&& renderSource.sectionPos.getZ() == Config.Client.Advanced.Debugging.columnBuilderDebugZPos.get())
|
||||
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;
|
||||
}
|
||||
@@ -312,8 +301,7 @@ public class ColumnRenderBufferBuilder
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
EVENT_LOGGER.warn("Failed to get adj data for [" + detailLevel + ":" + x + "," + z + "] at [" + lodDirection + "]");
|
||||
EVENT_LOGGER.warn("Detail exception: ", e);
|
||||
EVENT_LOGGER.warn("Failed to get adj data for [" + detailLevel + ":" + x + "," + z + "] at [" + lodDirection + "], Error: "+e.getMessage(), e);
|
||||
}
|
||||
} // for adjacent directions
|
||||
|
||||
|
||||
+6
-4
@@ -25,7 +25,7 @@ import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
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.api.enums.rendering.EDebugRendering;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
@@ -42,7 +42,7 @@ public class CubicLodTemplate
|
||||
public static void addLodToBuffer(
|
||||
long data, long topData, long bottomData, ColumnArrayView[][] adjColumnViews,
|
||||
byte detailLevel, int offsetPosX, int offsetOosZ, LodQuadBuilder quadBuilder,
|
||||
EDebugRendering debugging, ColumnRenderSource.DebugSourceFlag debugSource)
|
||||
EDhApiDebugRendering debugging, ColumnRenderSource.DebugSourceFlag debugSource)
|
||||
{
|
||||
DhLodPos blockOffsetPos = new DhLodPos(detailLevel, offsetPosX, offsetOosZ).convertToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
|
||||
@@ -88,7 +88,6 @@ public class CubicLodTemplate
|
||||
break;
|
||||
}
|
||||
case SHOW_DETAIL:
|
||||
case SHOW_GENMODE:
|
||||
{
|
||||
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel];
|
||||
fullBright = true;
|
||||
@@ -104,7 +103,7 @@ public class CubicLodTemplate
|
||||
break;
|
||||
|
||||
case IBlockStateWrapper.IrisBlockMaterial.LEAVES:
|
||||
color = ColorUtil.GREEN;
|
||||
color = ColorUtil.DARK_GREEN;
|
||||
break;
|
||||
case IBlockStateWrapper.IrisBlockMaterial.STONE:
|
||||
color = ColorUtil.GRAY;
|
||||
@@ -139,6 +138,9 @@ public class CubicLodTemplate
|
||||
case IBlockStateWrapper.IrisBlockMaterial.WATER:
|
||||
color = ColorUtil.BLUE;
|
||||
break;
|
||||
case IBlockStateWrapper.IrisBlockMaterial.GRASS:
|
||||
color = ColorUtil.GREEN;
|
||||
break;
|
||||
case IBlockStateWrapper.IrisBlockMaterial.ILLUMINATED:
|
||||
color = ColorUtil.YELLOW;
|
||||
break;
|
||||
|
||||
+70
-23
@@ -23,12 +23,18 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.*;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiGrassSideRendering;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
|
||||
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.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.render.AbstractRenderBuffer;
|
||||
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.api.enums.config.EGpuUploadMethod;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -42,6 +48,7 @@ import org.apache.logging.log4j.Logger;
|
||||
public class LodQuadBuilder
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
|
||||
public final boolean skipQuadsWithZeroSkylight;
|
||||
public final short skyLightCullingBelow;
|
||||
@@ -52,7 +59,10 @@ public class LodQuadBuilder
|
||||
private final ArrayList<BufferQuad>[] transparentQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
|
||||
|
||||
private final boolean doTransparency;
|
||||
private final IClientLevelWrapper clientLevelWrapper;
|
||||
|
||||
private final EDhApiDebugRendering debugRenderingMode;
|
||||
private final EDhApiGrassSideRendering grassSideRenderingMode;
|
||||
|
||||
|
||||
public static final int[][][] DIRECTION_VERTEX_IBO_QUAD = new int[][][]
|
||||
@@ -112,7 +122,7 @@ public class LodQuadBuilder
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public LodQuadBuilder(boolean enableSkylightCulling, short skyLightCullingBelow, boolean doTransparency)
|
||||
public LodQuadBuilder(boolean enableSkylightCulling, short skyLightCullingBelow, boolean doTransparency, IClientLevelWrapper clientLevelWrapper)
|
||||
{
|
||||
this.doTransparency = doTransparency;
|
||||
for (int i = 0; i < 6; i++)
|
||||
@@ -123,6 +133,10 @@ public class LodQuadBuilder
|
||||
|
||||
this.skipQuadsWithZeroSkylight = enableSkylightCulling;
|
||||
this.skyLightCullingBelow = skyLightCullingBelow;
|
||||
this.clientLevelWrapper = clientLevelWrapper;
|
||||
|
||||
this.debugRenderingMode = Config.Client.Advanced.Debugging.debugRendering.get();
|
||||
this.grassSideRenderingMode = Config.Client.Advanced.Graphics.AdvancedGraphics.grassSideRendering.get();
|
||||
|
||||
}
|
||||
|
||||
@@ -254,8 +268,41 @@ public class LodQuadBuilder
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid Axis enum: " + axis);
|
||||
}
|
||||
putVertex(bb, (short) (quad.x + dx), (short) (quad.y + dy), (short) (quad.z + dz),
|
||||
quad.hasError ? ColorUtil.RED : quad.color, // TODO add debug config that allows toggling this
|
||||
|
||||
|
||||
int color = quad.color;
|
||||
|
||||
// use custom side color logic for grass blocks
|
||||
if (quad.irisBlockMaterialId == IBlockStateWrapper.IrisBlockMaterial.GRASS)
|
||||
{
|
||||
// only use dirt colors if debug rendering is disabled
|
||||
if (this.debugRenderingMode == EDhApiDebugRendering.OFF)
|
||||
{
|
||||
// determine if any custom coloring logic should be used
|
||||
if (this.grassSideRenderingMode != EDhApiGrassSideRendering.AS_GRASS)
|
||||
{
|
||||
// only change the vertex color if it's on the side or bottom
|
||||
if (quad.direction.getAxis().isHorizontal() || quad.direction == EDhDirection.DOWN)
|
||||
{
|
||||
if (this.grassSideRenderingMode == EDhApiGrassSideRendering.AS_DIRT
|
||||
// if we want the color to fade, only apply the dirt color to the bottom vertices
|
||||
|| (this.grassSideRenderingMode == EDhApiGrassSideRendering.FADE_TO_DIRT && quadBase[i][1] == 0)
|
||||
// always render the bottom as dirt
|
||||
|| quad.direction == EDhDirection.DOWN)
|
||||
{
|
||||
// for horizontal and bottom faces of grass blocks, use the dirt color to
|
||||
// prevent green cliff walls
|
||||
color = this.clientLevelWrapper.getDirtBlockColor();
|
||||
color = ColorUtil.applyShade(color, MC.getShade(quad.direction));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.putVertex(bb, (short) (quad.x + dx), (short) (quad.y + dy), (short) (quad.z + dz),
|
||||
quad.hasError ? ColorUtil.RED : color,
|
||||
quad.hasError ? 0 : normalIndex,
|
||||
quad.hasError ? 0 : quad.irisBlockMaterialId,
|
||||
quad.hasError ? 15 : quad.skyLight,
|
||||
@@ -386,7 +433,7 @@ public class LodQuadBuilder
|
||||
{
|
||||
return new Iterator<ByteBuffer>()
|
||||
{
|
||||
final ByteBuffer bb = ByteBuffer.allocateDirect(AbstractRenderBuffer.FULL_SIZED_BUFFER)
|
||||
final ByteBuffer bb = ByteBuffer.allocateDirect(ColumnRenderBuffer.FULL_SIZED_BUFFER)
|
||||
.order(ByteOrder.nativeOrder());
|
||||
int dir = skipEmpty(0);
|
||||
int quad = 0;
|
||||
@@ -414,7 +461,7 @@ public class LodQuadBuilder
|
||||
return null;
|
||||
}
|
||||
bb.clear();
|
||||
bb.limit(AbstractRenderBuffer.FULL_SIZED_BUFFER);
|
||||
bb.limit(ColumnRenderBuffer.FULL_SIZED_BUFFER);
|
||||
while (bb.hasRemaining() && dir < 6)
|
||||
{
|
||||
writeData();
|
||||
@@ -454,7 +501,7 @@ public class LodQuadBuilder
|
||||
{
|
||||
return new Iterator<ByteBuffer>()
|
||||
{
|
||||
final ByteBuffer bb = ByteBuffer.allocateDirect(AbstractRenderBuffer.FULL_SIZED_BUFFER)
|
||||
final ByteBuffer bb = ByteBuffer.allocateDirect(ColumnRenderBuffer.FULL_SIZED_BUFFER)
|
||||
.order(ByteOrder.nativeOrder());
|
||||
int directionIndex = this.skipEmptyDirectionIndices(0);
|
||||
int quad = 0;
|
||||
@@ -483,7 +530,7 @@ public class LodQuadBuilder
|
||||
}
|
||||
|
||||
this.bb.clear();
|
||||
this.bb.limit(AbstractRenderBuffer.FULL_SIZED_BUFFER);
|
||||
this.bb.limit(ColumnRenderBuffer.FULL_SIZED_BUFFER);
|
||||
while (this.bb.hasRemaining() && this.directionIndex < 6)
|
||||
{
|
||||
this.writeData();
|
||||
@@ -525,7 +572,7 @@ public class LodQuadBuilder
|
||||
|
||||
}
|
||||
|
||||
public BufferFiller makeOpaqueBufferFiller(EGpuUploadMethod method)
|
||||
public BufferFiller makeOpaqueBufferFiller(EDhApiGpuUploadMethod method)
|
||||
{
|
||||
return new BufferFiller()
|
||||
{
|
||||
@@ -541,19 +588,19 @@ public class LodQuadBuilder
|
||||
}
|
||||
|
||||
int numOfQuads = _countRemainingQuads();
|
||||
if (numOfQuads > AbstractRenderBuffer.MAX_QUADS_PER_BUFFER)
|
||||
numOfQuads = AbstractRenderBuffer.MAX_QUADS_PER_BUFFER;
|
||||
if (numOfQuads > ColumnRenderBuffer.MAX_QUADS_PER_BUFFER)
|
||||
numOfQuads = ColumnRenderBuffer.MAX_QUADS_PER_BUFFER;
|
||||
if (numOfQuads == 0)
|
||||
{
|
||||
vbo.setVertexCount(0);
|
||||
return false;
|
||||
}
|
||||
ByteBuffer bb = vbo.mapBuffer(numOfQuads * AbstractRenderBuffer.QUADS_BYTE_SIZE, method,
|
||||
AbstractRenderBuffer.FULL_SIZED_BUFFER);
|
||||
ByteBuffer bb = vbo.mapBuffer(numOfQuads * ColumnRenderBuffer.QUADS_BYTE_SIZE, method,
|
||||
ColumnRenderBuffer.FULL_SIZED_BUFFER);
|
||||
if (bb == null)
|
||||
throw new NullPointerException("mapBuffer returned null");
|
||||
bb.clear();
|
||||
bb.limit(numOfQuads * AbstractRenderBuffer.QUADS_BYTE_SIZE);
|
||||
bb.limit(numOfQuads * ColumnRenderBuffer.QUADS_BYTE_SIZE);
|
||||
while (bb.hasRemaining() && dir < 6)
|
||||
{
|
||||
writeData(bb);
|
||||
@@ -605,7 +652,7 @@ public class LodQuadBuilder
|
||||
};
|
||||
}
|
||||
|
||||
public BufferFiller makeTransparentBufferFiller(EGpuUploadMethod method)
|
||||
public BufferFiller makeTransparentBufferFiller(EDhApiGpuUploadMethod method)
|
||||
{
|
||||
return new BufferFiller()
|
||||
{
|
||||
@@ -621,19 +668,19 @@ public class LodQuadBuilder
|
||||
}
|
||||
|
||||
int numOfQuads = _countRemainingQuads();
|
||||
if (numOfQuads > AbstractRenderBuffer.MAX_QUADS_PER_BUFFER)
|
||||
numOfQuads = AbstractRenderBuffer.MAX_QUADS_PER_BUFFER;
|
||||
if (numOfQuads > ColumnRenderBuffer.MAX_QUADS_PER_BUFFER)
|
||||
numOfQuads = ColumnRenderBuffer.MAX_QUADS_PER_BUFFER;
|
||||
if (numOfQuads == 0)
|
||||
{
|
||||
vbo.setVertexCount(0);
|
||||
return false;
|
||||
}
|
||||
ByteBuffer bb = vbo.mapBuffer(numOfQuads * AbstractRenderBuffer.QUADS_BYTE_SIZE, method,
|
||||
AbstractRenderBuffer.FULL_SIZED_BUFFER);
|
||||
ByteBuffer bb = vbo.mapBuffer(numOfQuads * ColumnRenderBuffer.QUADS_BYTE_SIZE, method,
|
||||
ColumnRenderBuffer.FULL_SIZED_BUFFER);
|
||||
if (bb == null)
|
||||
throw new NullPointerException("mapBuffer returned null");
|
||||
bb.clear();
|
||||
bb.limit(numOfQuads * AbstractRenderBuffer.QUADS_BYTE_SIZE);
|
||||
bb.limit(numOfQuads * ColumnRenderBuffer.QUADS_BYTE_SIZE);
|
||||
while (bb.hasRemaining() && dir < 6)
|
||||
{
|
||||
writeData(bb);
|
||||
@@ -718,7 +765,7 @@ public class LodQuadBuilder
|
||||
}
|
||||
|
||||
/** Returns how many GpuBuffers will be needed to render opaque quads in this builder. */
|
||||
public int getCurrentNeededOpaqueVertexBufferCount() { return MathUtil.ceilDiv(this.getCurrentOpaqueQuadsCount(), AbstractRenderBuffer.MAX_QUADS_PER_BUFFER); }
|
||||
public int getCurrentNeededOpaqueVertexBufferCount() { return MathUtil.ceilDiv(this.getCurrentOpaqueQuadsCount(), ColumnRenderBuffer.MAX_QUADS_PER_BUFFER); }
|
||||
/** Returns how many GpuBuffers will be needed to render transparent quads in this builder. */
|
||||
public int getCurrentNeededTransparentVertexBufferCount()
|
||||
{
|
||||
@@ -727,7 +774,7 @@ public class LodQuadBuilder
|
||||
return 0;
|
||||
}
|
||||
|
||||
return MathUtil.ceilDiv(this.getCurrentTransparentQuadsCount(), AbstractRenderBuffer.MAX_QUADS_PER_BUFFER);
|
||||
return MathUtil.ceilDiv(this.getCurrentTransparentQuadsCount(), ColumnRenderBuffer.MAX_QUADS_PER_BUFFER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+23
-17
@@ -21,19 +21,21 @@ package com.seibel.distanthorizons.core.dataObjects.render.columnViews;
|
||||
|
||||
|
||||
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class ColumnArrayView implements IColumnDataView
|
||||
{
|
||||
public final long[] data;
|
||||
public final LongArrayList data;
|
||||
public final int size;
|
||||
public final int offset; // offset in longs
|
||||
/** can be 0 if this column was created for an empty data source */
|
||||
public final int vertSize; // vertical size in longs
|
||||
|
||||
|
||||
|
||||
public ColumnArrayView(long[] data, int size, int offset, int vertSize)
|
||||
public ColumnArrayView(LongArrayList data, int size, int offset, int vertSize)
|
||||
{
|
||||
this.data = data;
|
||||
this.size = size;
|
||||
@@ -44,9 +46,9 @@ public final class ColumnArrayView implements IColumnDataView
|
||||
|
||||
|
||||
@Override
|
||||
public long get(int index) { return data[index + offset]; }
|
||||
public long get(int index) { return data.getLong(index + offset); }
|
||||
|
||||
public void set(int index, long value) { data[index + offset] = value; }
|
||||
public void set(int index, long value) { data.set(index + offset, value); }
|
||||
|
||||
@Override
|
||||
public int size() { return size; }
|
||||
@@ -55,7 +57,7 @@ public final class ColumnArrayView implements IColumnDataView
|
||||
public int verticalSize() { return vertSize; }
|
||||
|
||||
@Override
|
||||
public int dataCount() { return size / vertSize; }
|
||||
public int dataCount() { return (this.vertSize != 0) ? (this.size / this.vertSize) : 0; }
|
||||
|
||||
@Override
|
||||
public ColumnArrayView subView(int dataIndexStart, int dataCount)
|
||||
@@ -63,7 +65,7 @@ public final class ColumnArrayView implements IColumnDataView
|
||||
return new ColumnArrayView(data, dataCount * vertSize, offset + dataIndexStart * vertSize, vertSize);
|
||||
}
|
||||
|
||||
public void fill(long value) { Arrays.fill(data, offset, offset + size, value); }
|
||||
public void fill(long value) { Arrays.fill(data.elements(), offset, offset + size, value); }
|
||||
|
||||
public void copyFrom(IColumnDataView source) { copyFrom(source, 0); }
|
||||
public void copyFrom(IColumnDataView source, int outputDataIndexOffset)
|
||||
@@ -81,19 +83,19 @@ public final class ColumnArrayView implements IColumnDataView
|
||||
for (int i = 0; i < source.dataCount(); i++)
|
||||
{
|
||||
int outputOffset = offset + outputDataIndexOffset * vertSize + i * vertSize;
|
||||
source.subView(i, 1).copyTo(data, outputOffset, source.verticalSize());
|
||||
Arrays.fill(data, outputOffset + source.verticalSize(),
|
||||
source.subView(i, 1).copyTo(data.elements(), outputOffset, source.verticalSize());
|
||||
Arrays.fill(data.elements(), outputOffset + source.verticalSize(),
|
||||
outputOffset + vertSize, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
source.copyTo(data, offset + outputDataIndexOffset * vertSize, source.size());
|
||||
source.copyTo(data.elements(), offset + outputDataIndexOffset * vertSize, source.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTo(long[] target, int offset, int size) { System.arraycopy(data, this.offset, target, offset, size); }
|
||||
public void copyTo(long[] target, int offset, int size) { System.arraycopy(data.elements(), this.offset, target, offset, size); }
|
||||
|
||||
public boolean mergeWith(ColumnArrayView source, boolean override)
|
||||
{
|
||||
@@ -130,17 +132,18 @@ public final class ColumnArrayView implements IColumnDataView
|
||||
|
||||
public void changeVerticalSizeFrom(IColumnDataView source)
|
||||
{
|
||||
if (dataCount() != source.dataCount())
|
||||
if (this.dataCount() != source.dataCount())
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot copy and resize to views with different dataCounts");
|
||||
}
|
||||
if (vertSize >= source.verticalSize())
|
||||
|
||||
if (this.vertSize >= source.verticalSize())
|
||||
{
|
||||
copyFrom(source);
|
||||
this.copyFrom(source);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < dataCount(); i++)
|
||||
for (int i = 0; i < this.dataCount(); i++)
|
||||
{
|
||||
RenderDataPointUtil.mergeMultiData(source.subView(i, 1), subView(i, 1));
|
||||
}
|
||||
@@ -168,7 +171,7 @@ public final class ColumnArrayView implements IColumnDataView
|
||||
sb.append(" [");
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
sb.append(RenderDataPointUtil.toString(data[offset + i]));
|
||||
sb.append(RenderDataPointUtil.toString(data.getLong(offset + i)));
|
||||
if (i < size - 1)
|
||||
{
|
||||
sb.append(",\n");
|
||||
@@ -184,15 +187,18 @@ public final class ColumnArrayView implements IColumnDataView
|
||||
return arrayHash(data, offset, size);
|
||||
}
|
||||
|
||||
private static int arrayHash(long[] a, int offset, int length)
|
||||
private static int arrayHash(LongArrayList a, int offset, int length)
|
||||
{
|
||||
if (a == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = 1;
|
||||
int end = offset + length;
|
||||
for (int i = offset; i < end; i++)
|
||||
{
|
||||
long element = a[i];
|
||||
long element = a.getLong(i);
|
||||
int elementHash = (int) (element ^ (element >>> 32));
|
||||
result = 31 * result + elementHash;
|
||||
}
|
||||
|
||||
+12
-7
@@ -19,19 +19,24 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.dataObjects.render.columnViews;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
|
||||
public class ColumnQuadView implements IColumnDataView
|
||||
{
|
||||
private final long[] data;
|
||||
private final LongArrayList data;
|
||||
private final int perColumnOffset; // per column (of columns of data) offset in longs
|
||||
private final int xSize; // x size in datapoints
|
||||
private final int zSize; // x size in datapoints
|
||||
private final int offset; // offset in longs
|
||||
private final int vertSize; // vertical size in longs
|
||||
|
||||
public ColumnQuadView(long[] data, int dataZWidth, int dataVertSize, int viewXOffset, int viewZOffset, int xSize, int zSize)
|
||||
public ColumnQuadView(LongArrayList data, int dataZWidth, int dataVertSize, int viewXOffset, int viewZOffset, int xSize, int zSize)
|
||||
{
|
||||
if (viewXOffset + xSize > (data.length / (dataZWidth * dataVertSize)) || viewZOffset + zSize > dataZWidth)
|
||||
if (viewXOffset + xSize > (data.size() / (dataZWidth * dataVertSize)) || viewZOffset + zSize > dataZWidth)
|
||||
{
|
||||
throw new IllegalArgumentException("View is out of bounds");
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this.xSize = xSize;
|
||||
this.zSize = zSize;
|
||||
@@ -39,7 +44,7 @@ public class ColumnQuadView implements IColumnDataView
|
||||
this.perColumnOffset = dataZWidth * dataVertSize;
|
||||
this.offset = viewXOffset * perColumnOffset + viewZOffset * dataVertSize;
|
||||
}
|
||||
private ColumnQuadView(long[] data, int perColumnOffset, int offset, int vertSize, int xSize, int zSize)
|
||||
private ColumnQuadView(LongArrayList data, int perColumnOffset, int offset, int vertSize, int xSize, int zSize)
|
||||
{
|
||||
this.data = data;
|
||||
this.perColumnOffset = perColumnOffset;
|
||||
@@ -60,12 +65,12 @@ public class ColumnQuadView implements IColumnDataView
|
||||
|
||||
public long get(int x, int z, int v)
|
||||
{
|
||||
return data[offset + x * perColumnOffset + z * vertSize + v];
|
||||
return data.getLong(offset + x * perColumnOffset + z * vertSize + v);
|
||||
}
|
||||
|
||||
public long set(int x, int z, int v, long value)
|
||||
{
|
||||
return data[offset + x * perColumnOffset + z * vertSize + v] = value;
|
||||
return data.set(offset + x * perColumnOffset + z * vertSize + v, value);
|
||||
}
|
||||
|
||||
public ColumnArrayView get(int x, int z)
|
||||
@@ -82,7 +87,7 @@ public class ColumnQuadView implements IColumnDataView
|
||||
{
|
||||
if (singleColumn.verticalSize() != vertSize) throw new IllegalArgumentException("Vertical size of singleColumn must be equal to vertSize");
|
||||
if (singleColumn.dataCount() != 1) throw new IllegalArgumentException("SingleColumn must contain exactly one data point");
|
||||
singleColumn.copyTo(data, offset + x * perColumnOffset + z * vertSize, singleColumn.size());
|
||||
singleColumn.copyTo(data.elements(), offset + x * perColumnOffset + z * vertSize, singleColumn.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-4
@@ -19,6 +19,8 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.dataObjects.render.columnViews;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public interface IColumnDataView
|
||||
@@ -28,18 +30,18 @@ public interface IColumnDataView
|
||||
// FIXME probably horizontal size in blocks?
|
||||
int size();
|
||||
|
||||
default Iterator<Long> iterator()
|
||||
default LongIterator iterator()
|
||||
{
|
||||
return new Iterator<Long>()
|
||||
return new LongIterator()
|
||||
{
|
||||
private int index = 0;
|
||||
private final int size = size();
|
||||
private final int size = IColumnDataView.this.size();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() { return this.index < this.size; }
|
||||
|
||||
@Override
|
||||
public Long next() { return get(this.index++); }
|
||||
public long nextLong() { return IColumnDataView.this.get(this.index++); }
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
+24
-20
@@ -23,11 +23,11 @@ import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPools;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -57,7 +57,7 @@ public class ChunkToLodBuilder implements AutoCloseable
|
||||
// data generation //
|
||||
//=================//
|
||||
|
||||
public CompletableFuture<ChunkSizedFullDataAccessor> tryGenerateData(IChunkWrapper chunkWrapper)
|
||||
public CompletableFuture<FullDataSourceV2> tryGenerateData(IChunkWrapper chunkWrapper)
|
||||
{
|
||||
if (chunkWrapper == null)
|
||||
{
|
||||
@@ -74,7 +74,7 @@ public class ChunkToLodBuilder implements AutoCloseable
|
||||
}
|
||||
|
||||
// Otherwise, it means we're the first to do so. Let's submit our task to this entry.
|
||||
CompletableFuture<ChunkSizedFullDataAccessor> future = new CompletableFuture<>();
|
||||
CompletableFuture<FullDataSourceV2> future = new CompletableFuture<>();
|
||||
this.concurrentTaskToBuildList.addLast(new Task(chunkWrapper.getChunkPos(), future));
|
||||
return future;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ public class ChunkToLodBuilder implements AutoCloseable
|
||||
// TODO why on tick?
|
||||
public void tick()
|
||||
{
|
||||
int threadCount = ThreadPools.getWorkerThreadCount();
|
||||
int threadCount = ThreadPoolUtil.getWorkerThreadCount();
|
||||
if (this.runningCount.get() >= threadCount)
|
||||
{
|
||||
return;
|
||||
@@ -102,7 +102,7 @@ public class ChunkToLodBuilder implements AutoCloseable
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadPoolExecutor lodBuilderExecutor = ThreadPools.getChunkToLodBuilderExecutor();
|
||||
ThreadPoolExecutor lodBuilderExecutor = ThreadPoolUtil.getChunkToLodBuilderExecutor();
|
||||
if (lodBuilderExecutor == null)
|
||||
{
|
||||
return;
|
||||
@@ -112,17 +112,21 @@ public class ChunkToLodBuilder implements AutoCloseable
|
||||
for (int i = 0; i < threadCount; i++)
|
||||
{
|
||||
this.runningCount.incrementAndGet();
|
||||
CompletableFuture.runAsync(() ->
|
||||
try
|
||||
{
|
||||
try
|
||||
CompletableFuture.runAsync(() ->
|
||||
{
|
||||
this.tickThreadTask();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.runningCount.decrementAndGet();
|
||||
}
|
||||
}, lodBuilderExecutor);
|
||||
try
|
||||
{
|
||||
this.tickThreadTask();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.runningCount.decrementAndGet();
|
||||
}
|
||||
}, lodBuilderExecutor);
|
||||
}
|
||||
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 */ }
|
||||
}
|
||||
}
|
||||
private void tickThreadTask()
|
||||
@@ -158,10 +162,10 @@ public class ChunkToLodBuilder implements AutoCloseable
|
||||
{
|
||||
if (LodDataBuilder.canGenerateLodFromChunk(latestChunk))
|
||||
{
|
||||
ChunkSizedFullDataAccessor data = LodDataBuilder.createChunkData(latestChunk);
|
||||
if (data != null)
|
||||
FullDataSourceV2 dataSource = LodDataBuilder.createGeneratedDataSource(latestChunk);
|
||||
if (dataSource != null)
|
||||
{
|
||||
task.future.complete(data);
|
||||
task.future.complete(dataSource);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -233,11 +237,11 @@ public class ChunkToLodBuilder implements AutoCloseable
|
||||
private static class Task
|
||||
{
|
||||
public final DhChunkPos chunkPos;
|
||||
public final CompletableFuture<ChunkSizedFullDataAccessor> future;
|
||||
public final CompletableFuture<FullDataSourceV2> future;
|
||||
/** This is tracked so impossible tasks can be removed from the queue */
|
||||
public long generationAttemptExpirationTimeMs = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10);
|
||||
|
||||
Task(DhChunkPos chunkPos, CompletableFuture<ChunkSizedFullDataAccessor> future)
|
||||
Task(DhChunkPos chunkPos, CompletableFuture<FullDataSourceV2> future)
|
||||
{
|
||||
this.chunkPos = chunkPos;
|
||||
this.future = future;
|
||||
|
||||
+73
-110
@@ -19,14 +19,10 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.dataObjects.transformers;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EBlocksToAvoid;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiBlocksToAvoid;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.SingleColumnFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
@@ -34,20 +30,21 @@ import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Handles converting {@link ChunkSizedFullDataAccessor}, {@link IIncompleteFullDataSource},
|
||||
* and {@link IFullDataSource}'s to {@link ColumnRenderSource}.
|
||||
* Handles converting {@link FullDataSourceV2}'s to {@link ColumnRenderSource}.
|
||||
*/
|
||||
public class FullDataToRenderDataTransformer
|
||||
{
|
||||
@@ -56,19 +53,21 @@ public class FullDataToRenderDataTransformer
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
|
||||
private static final LongOpenHashSet brokenPos = new LongOpenHashSet();
|
||||
|
||||
|
||||
|
||||
//==============================//
|
||||
// public transformer interface //
|
||||
//==============================//
|
||||
|
||||
public static ColumnRenderSource transformFullDataToRenderSource(IFullDataSource fullDataSource, IDhClientLevel level)
|
||||
public static ColumnRenderSource transformFullDataToRenderSource(FullDataSourceV2 fullDataSource, IDhClientLevel level)
|
||||
{
|
||||
if (fullDataSource == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (MC.getWrappedClientLevel() == null)
|
||||
else if (level == null)
|
||||
{
|
||||
// if the client is no longer loaded in the world, render sources cannot be created
|
||||
return null;
|
||||
@@ -77,17 +76,7 @@ public class FullDataToRenderDataTransformer
|
||||
|
||||
try
|
||||
{
|
||||
if (fullDataSource instanceof CompleteFullDataSource)
|
||||
{
|
||||
return transformCompleteFullDataToColumnData(level, (CompleteFullDataSource) fullDataSource);
|
||||
}
|
||||
else if (fullDataSource instanceof IIncompleteFullDataSource)
|
||||
{
|
||||
return transformIncompleteFullDataToColumnData(level, (IIncompleteFullDataSource) fullDataSource);
|
||||
}
|
||||
|
||||
LodUtil.assertNotReach("Unimplemented Full Data transformer for "+IFullDataSource.class.getSimpleName()+" of type ["+fullDataSource.getClass().getSimpleName()+"].");
|
||||
return null;
|
||||
return transformCompleteFullDataToColumnData(level, fullDataSource);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
@@ -108,13 +97,13 @@ public class FullDataToRenderDataTransformer
|
||||
* @throws InterruptedException Can be caused by interrupting the thread upstream.
|
||||
* Generally thrown if the method is running after the client leaves the current world.
|
||||
*/
|
||||
private static ColumnRenderSource transformCompleteFullDataToColumnData(IDhClientLevel level, CompleteFullDataSource fullDataSource) throws InterruptedException
|
||||
private static ColumnRenderSource transformCompleteFullDataToColumnData(IDhClientLevel level, FullDataSourceV2 fullDataSource) throws InterruptedException
|
||||
{
|
||||
final DhSectionPos pos = fullDataSource.getSectionPos();
|
||||
final long pos = fullDataSource.getPos();
|
||||
final byte dataDetail = fullDataSource.getDataDetailLevel();
|
||||
final int vertSize = Config.Client.Advanced.Graphics.Quality.verticalQuality.get().calculateMaxVerticalData(fullDataSource.getDataDetailLevel());
|
||||
final ColumnRenderSource columnSource = new ColumnRenderSource(pos, vertSize, level.getMinY());
|
||||
if (fullDataSource.isEmpty())
|
||||
final ColumnRenderSource columnSource = ColumnRenderSource.getPooledRenderSource(pos, vertSize, level.getMinY(), true);
|
||||
if (fullDataSource.isEmpty)
|
||||
{
|
||||
return columnSource;
|
||||
}
|
||||
@@ -123,18 +112,18 @@ public class FullDataToRenderDataTransformer
|
||||
|
||||
if (dataDetail == columnSource.getDataDetailLevel())
|
||||
{
|
||||
int baseX = pos.getMinCornerLodPos().getCornerBlockPos().x;
|
||||
int baseZ = pos.getMinCornerLodPos().getCornerBlockPos().z;
|
||||
int baseX = DhSectionPos.getMinCornerBlockX(pos);
|
||||
int baseZ = DhSectionPos.getMinCornerBlockZ(pos);
|
||||
|
||||
for (int x = 0; x < pos.getWidthCountForLowerDetailedSection(dataDetail); x++)
|
||||
for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++)
|
||||
{
|
||||
for (int z = 0; z < pos.getWidthCountForLowerDetailedSection(dataDetail); z++)
|
||||
for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++)
|
||||
{
|
||||
throwIfThreadInterrupted();
|
||||
|
||||
ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z);
|
||||
SingleColumnFullDataAccessor fullArrayView = fullDataSource.get(x, z);
|
||||
convertColumnData(level, baseX + x, baseZ + z, columnArrayView, fullArrayView);
|
||||
LongArrayList dataColumn = fullDataSource.get(x, z);
|
||||
convertColumnData(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,56 +138,6 @@ public class FullDataToRenderDataTransformer
|
||||
return columnSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InterruptedException Can be caused by interrupting the thread upstream.
|
||||
* Generally thrown if the method is running after the client leaves the current world.
|
||||
*/
|
||||
private static ColumnRenderSource transformIncompleteFullDataToColumnData(IDhClientLevel level, IIncompleteFullDataSource data) throws InterruptedException
|
||||
{
|
||||
final DhSectionPos pos = data.getSectionPos();
|
||||
final byte dataDetail = data.getDataDetailLevel();
|
||||
final int vertSize = Config.Client.Advanced.Graphics.Quality.verticalQuality.get().calculateMaxVerticalData(data.getDataDetailLevel());
|
||||
final ColumnRenderSource columnSource = new ColumnRenderSource(pos, vertSize, level.getMinY());
|
||||
if (data.isEmpty())
|
||||
{
|
||||
return columnSource;
|
||||
}
|
||||
|
||||
columnSource.markNotEmpty();
|
||||
|
||||
if (dataDetail == columnSource.getDataDetailLevel())
|
||||
{
|
||||
int baseX = pos.getMinCornerLodPos().getCornerBlockPos().x;
|
||||
int baseZ = pos.getMinCornerLodPos().getCornerBlockPos().z;
|
||||
|
||||
int width = pos.getWidthCountForLowerDetailedSection(dataDetail);
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
throwIfThreadInterrupted();
|
||||
|
||||
SingleColumnFullDataAccessor fullArrayView = data.tryGet(x, z);
|
||||
if (fullArrayView == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z);
|
||||
convertColumnData(level, baseX + x, baseZ + z, columnArrayView, fullArrayView);
|
||||
|
||||
columnSource.fillDebugFlag(x, z, 1, 1, ColumnRenderSource.DebugSourceFlag.SPARSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new UnsupportedOperationException("To be implemented");
|
||||
//FIXME: Implement different size creation of renderData
|
||||
}
|
||||
return columnSource;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
@@ -218,30 +157,35 @@ public class FullDataToRenderDataTransformer
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<DhSectionPos> brokenPos = new HashSet<>();
|
||||
|
||||
|
||||
// TODO what does this mean?
|
||||
private static void iterateAndConvert(IDhClientLevel level, int blockX, int blockZ, ColumnArrayView column, SingleColumnFullDataAccessor data)
|
||||
private static void iterateAndConvert(
|
||||
IDhClientLevel level, FullDataPointIdMap fullDataMapping,
|
||||
int blockX, int blockZ,
|
||||
ColumnArrayView renderColumnData, LongArrayList fullColumnData)
|
||||
{
|
||||
boolean avoidSolidBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EBlocksToAvoid.NON_COLLIDING);
|
||||
boolean avoidSolidBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
|
||||
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get();
|
||||
|
||||
FullDataPointIdMap fullDataMapping = data.getMapping();
|
||||
HashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(level.getLevelWrapper());
|
||||
|
||||
boolean isVoid = true;
|
||||
int colorToApplyToNextBlock = -1;
|
||||
int lastColor = 0;
|
||||
int lastBottom = -10000;
|
||||
int skylightToApplyToNextBlock = -1;
|
||||
int blocklightToApplyToNextBlock = -1;
|
||||
int columnOffset = 0;
|
||||
|
||||
// goes from the top down
|
||||
for (int i = 0; i < data.getSingleLength(); i++)
|
||||
for (int i = 0; i < fullColumnData.size(); i++)
|
||||
{
|
||||
long fullData = data.getSingle(i);
|
||||
long fullData = fullColumnData.getLong(i);
|
||||
int bottomY = FullDataPointUtil.getBottomY(fullData);
|
||||
int blockHeight = FullDataPointUtil.getHeight(fullData);
|
||||
int id = FullDataPointUtil.getId(fullData);
|
||||
int light = FullDataPointUtil.getLight(fullData);
|
||||
int blockLight = FullDataPointUtil.getBlockLight(fullData);
|
||||
int skyLight = FullDataPointUtil.getSkyLight(fullData);
|
||||
|
||||
// TODO how should corrupted data be handled?
|
||||
// TODO why is the full data corrupted in the first place? FullDataPointUtil hasn't been changed in a long time, could one of the full data point objects be corrupted?
|
||||
@@ -296,7 +240,16 @@ public class FullDataToRenderDataTransformer
|
||||
{
|
||||
if (colorBelowWithAvoidedBlocks)
|
||||
{
|
||||
colorToApplyToNextBlock = level.computeBaseColor(new DhBlockPos(blockX, bottomY + level.getMinY(), blockZ), biome, block);
|
||||
int tempColor = level.computeBaseColor(new DhBlockPos(blockX, bottomY + level.getMinY(), blockZ), biome, block);
|
||||
if (ColorUtil.getAlpha(tempColor) == 0)
|
||||
{
|
||||
//make sure to not transfer the color when alpha is 0
|
||||
continue;
|
||||
}
|
||||
//mare sure to not trnasfer alpha if for some reason grass is semi transparent
|
||||
colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255);
|
||||
skylightToApplyToNextBlock = skyLight;
|
||||
blocklightToApplyToNextBlock = blockLight;
|
||||
}
|
||||
|
||||
// don't add this block
|
||||
@@ -315,46 +268,56 @@ public class FullDataToRenderDataTransformer
|
||||
// use the previous block's color
|
||||
color = colorToApplyToNextBlock;
|
||||
colorToApplyToNextBlock = -1;
|
||||
skyLight = skylightToApplyToNextBlock;
|
||||
blockLight = blocklightToApplyToNextBlock;
|
||||
}
|
||||
|
||||
|
||||
// add the block
|
||||
isVoid = false;
|
||||
long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, light, block.getIrisBlockMaterialId());
|
||||
column.set(columnOffset, columnData);
|
||||
columnOffset++;
|
||||
|
||||
//check if they share a top-bottom face and if they have same collor
|
||||
if (color == lastColor && bottomY + blockHeight == lastBottom && columnOffset > 0)
|
||||
{
|
||||
//replace the previus block with new bottom
|
||||
long columnData = renderColumnData.get(columnOffset - 1);
|
||||
columnData = RenderDataPointUtil.setYMin(columnData, bottomY);
|
||||
renderColumnData.set(columnOffset - 1, columnData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add the block
|
||||
isVoid = false;
|
||||
long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, skyLight, blockLight, block.getIrisBlockMaterialId());
|
||||
renderColumnData.set(columnOffset, columnData);
|
||||
columnOffset++;
|
||||
}
|
||||
lastBottom = bottomY;
|
||||
lastColor = color;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (isVoid)
|
||||
{
|
||||
column.set(0, RenderDataPointUtil.createVoidDataPoint());
|
||||
renderColumnData.set(0, RenderDataPointUtil.createVoidDataPoint());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO what does this mean?
|
||||
public static void convertColumnData(IDhClientLevel level, int blockX, int blockZ, ColumnArrayView columnArrayView, SingleColumnFullDataAccessor fullArrayView)
|
||||
public static void convertColumnData(IDhClientLevel level, FullDataPointIdMap fullDataMapping, int blockX, int blockZ, ColumnArrayView columnArrayView, LongArrayList fullDataColumn)
|
||||
{
|
||||
if (!fullArrayView.doesColumnExist())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int dataTotalLength = fullArrayView.getSingleLength();
|
||||
if (dataTotalLength == 0)
|
||||
if (fullDataColumn == null || fullDataColumn.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int dataTotalLength = fullDataColumn.size();
|
||||
if (dataTotalLength > columnArrayView.verticalSize())
|
||||
{
|
||||
ColumnArrayView totalColumnData = new ColumnArrayView(new long[dataTotalLength], dataTotalLength, 0, dataTotalLength);
|
||||
iterateAndConvert(level, blockX, blockZ, totalColumnData, fullArrayView);
|
||||
ColumnArrayView totalColumnData = new ColumnArrayView(new LongArrayList(new long[dataTotalLength]), dataTotalLength, 0, dataTotalLength);
|
||||
iterateAndConvert(level, fullDataMapping, blockX, blockZ, totalColumnData, fullDataColumn);
|
||||
columnArrayView.changeVerticalSizeFrom(totalColumnData);
|
||||
}
|
||||
else
|
||||
{
|
||||
iterateAndConvert(level, blockX, blockZ, columnArrayView, fullArrayView); //Directly use the arrayView since it fits.
|
||||
iterateAndConvert(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn); //Directly use the arrayView since it fits.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+251
-79
@@ -21,14 +21,21 @@ package com.seibel.distanthorizons.core.dataObjects.transformers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiWorldCompressionMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.api.objects.data.DhApiChunk;
|
||||
import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
@@ -40,6 +47,8 @@ public class LodDataBuilder
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final IBlockStateWrapper AIR = SingletonInjector.INSTANCE.get(IWrapperFactory.class).getAirBlockStateWrapper();
|
||||
/** how many chunks wide the {@link FullDataSourceV2} is. */
|
||||
private static final int NUMB_OF_CHUNKS_WIDE = FullDataSourceV2.WIDTH / LodUtil.CHUNK_WIDTH;
|
||||
|
||||
private static boolean getTopErrorLogged = false;
|
||||
|
||||
@@ -49,7 +58,7 @@ public class LodDataBuilder
|
||||
// converters //
|
||||
//============//
|
||||
|
||||
public static ChunkSizedFullDataAccessor createChunkData(IChunkWrapper chunkWrapper)
|
||||
public static FullDataSourceV2 createGeneratedDataSource(IChunkWrapper chunkWrapper)
|
||||
{
|
||||
if (!canGenerateLodFromChunk(chunkWrapper))
|
||||
{
|
||||
@@ -57,84 +66,244 @@ public class LodDataBuilder
|
||||
}
|
||||
|
||||
|
||||
ChunkSizedFullDataAccessor chunkData = new ChunkSizedFullDataAccessor(chunkWrapper.getChunkPos());
|
||||
int minBuildHeight = chunkWrapper.getMinNonEmptyHeight();
|
||||
|
||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
||||
// get the section position
|
||||
int sectionPosX = chunkWrapper.getChunkPos().x;
|
||||
// negative positions start at -1 so the logic there is slightly different
|
||||
sectionPosX = (sectionPosX < 0) ? ((sectionPosX + 1) / NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPosX / NUMB_OF_CHUNKS_WIDE);
|
||||
int sectionPosZ = chunkWrapper.getChunkPos().z;
|
||||
sectionPosZ = (sectionPosZ < 0) ? ((sectionPosZ + 1) / NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPosZ / NUMB_OF_CHUNKS_WIDE);
|
||||
long pos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ);
|
||||
|
||||
FullDataSourceV2 dataSource = FullDataSourceV2.createEmpty(pos);
|
||||
dataSource.isEmpty = false;
|
||||
|
||||
|
||||
|
||||
// compute the chunk dataSource offset
|
||||
// this offset is used to determine where in the dataSource this chunk's data should go
|
||||
int chunkOffsetX = chunkWrapper.getChunkPos().x;
|
||||
if (chunkWrapper.getChunkPos().x < 0)
|
||||
{
|
||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
||||
// expected offset positions:
|
||||
// chunkPos -> offset
|
||||
// 5 -> 1
|
||||
// 4 -> 0 ---
|
||||
// 3 -> 3
|
||||
// 2 -> 2
|
||||
// 1 -> 1
|
||||
// 0 -> 0 ===
|
||||
// -1 -> 3
|
||||
// -2 -> 2
|
||||
// -3 -> 1
|
||||
// -4 -> 0 ---
|
||||
// -5 -> 3
|
||||
chunkOffsetX = ((chunkOffsetX) % NUMB_OF_CHUNKS_WIDE);
|
||||
if (chunkOffsetX != 0)
|
||||
{
|
||||
LongArrayList longs = new LongArrayList(chunkWrapper.getHeight() / 4);
|
||||
int lastY = chunkWrapper.getMaxBuildHeight();
|
||||
IBiomeWrapper biome = chunkWrapper.getBiome(x, lastY, z);
|
||||
IBlockStateWrapper blockState = AIR;
|
||||
int mappedId = chunkData.getMapping().addIfNotPresentAndGetId(biome, blockState);
|
||||
// FIXME: The +1 offset to reproduce the old behavior. Remove this when we get per-face lighting
|
||||
byte light = (byte) ((chunkWrapper.getBlockLight(x, lastY + 1, z) << 4) + chunkWrapper.getSkyLight(x, lastY + 1, z));
|
||||
|
||||
|
||||
// determine the starting Y Pos
|
||||
int y = chunkWrapper.getLightBlockingHeightMapValue(x,z);
|
||||
// go up until we reach open air or the world limit
|
||||
IBlockStateWrapper topBlockState = chunkWrapper.getBlockState(x, y, z);
|
||||
while (!topBlockState.isAir() && y < chunkWrapper.getMaxBuildHeight())
|
||||
{
|
||||
try
|
||||
{
|
||||
// This is necessary in some edge cases with snow layers and some other blocks that may not appear in the height map but do block light.
|
||||
// Interestingly this doesn't appear to be the case in the DhLightingEngine, if this same logic is added there the lighting breaks for the affected blocks.
|
||||
y++;
|
||||
topBlockState = chunkWrapper.getBlockState(x, y, z);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!getTopErrorLogged)
|
||||
{
|
||||
LOGGER.warn("Unexpected issue in LodDataBuilder, future errors won't be logged. Chunk [" + chunkWrapper.getChunkPos() + "] with max height: [" + chunkWrapper.getMaxBuildHeight() + "] had issue getting block at pos [" + x + "," + y + "," + z + "] error: " + e.getMessage(), e);
|
||||
getTopErrorLogged = true;
|
||||
}
|
||||
|
||||
y--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (; y >= minBuildHeight; y--)
|
||||
{
|
||||
IBiomeWrapper newBiome = chunkWrapper.getBiome(x, y, z);
|
||||
IBlockStateWrapper newBlockState = chunkWrapper.getBlockState(x, y, z);
|
||||
byte newLight = (byte) ((chunkWrapper.getBlockLight(x, y + 1, z) << 4) + chunkWrapper.getSkyLight(x, y + 1, z));
|
||||
|
||||
if (!newBiome.equals(biome) || !newBlockState.equals(blockState))
|
||||
{
|
||||
longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), light));
|
||||
biome = newBiome;
|
||||
blockState = newBlockState;
|
||||
mappedId = chunkData.getMapping().addIfNotPresentAndGetId(biome, blockState);
|
||||
light = newLight;
|
||||
lastY = y;
|
||||
}
|
||||
// else if (newLight != light) {
|
||||
// longs.add(FullFormat.encode(mappedId, lastY-y, y+1 - chunk.getMinBuildHeight(), light));
|
||||
// light = newLight;
|
||||
// lastY = y;
|
||||
// }
|
||||
}
|
||||
longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), light));
|
||||
|
||||
chunkData.setSingleColumn(longs.toLongArray(), x, z);
|
||||
chunkOffsetX += NUMB_OF_CHUNKS_WIDE;
|
||||
}
|
||||
}
|
||||
if (!canGenerateLodFromChunk(chunkWrapper)) return null;
|
||||
LodUtil.assertTrue(chunkData.emptyCount() == 0);
|
||||
return chunkData;
|
||||
else
|
||||
{
|
||||
chunkOffsetX %= NUMB_OF_CHUNKS_WIDE;
|
||||
}
|
||||
chunkOffsetX *= LodUtil.CHUNK_WIDTH;
|
||||
|
||||
int chunkOffsetZ = chunkWrapper.getChunkPos().z;
|
||||
if (chunkWrapper.getChunkPos().z < 0)
|
||||
{
|
||||
chunkOffsetZ = ((chunkOffsetZ) % NUMB_OF_CHUNKS_WIDE);
|
||||
if (chunkOffsetZ != 0)
|
||||
{
|
||||
chunkOffsetZ += NUMB_OF_CHUNKS_WIDE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
chunkOffsetZ %= NUMB_OF_CHUNKS_WIDE;
|
||||
}
|
||||
chunkOffsetZ *= LodUtil.CHUNK_WIDTH;
|
||||
|
||||
|
||||
|
||||
//==========================//
|
||||
// populate the data source //
|
||||
//==========================//
|
||||
|
||||
EDhApiWorldCompressionMode worldCompressionMode = Config.Client.Advanced.LodBuilding.worldCompression.get();
|
||||
boolean ignoreHiddenBlocks = (worldCompressionMode != EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS);
|
||||
|
||||
try
|
||||
{
|
||||
int minBuildHeight = chunkWrapper.getMinNonEmptyHeight();
|
||||
for (int relBlockX = 0; relBlockX < LodUtil.CHUNK_WIDTH; relBlockX++)
|
||||
{
|
||||
for (int relBlockZ = 0; relBlockZ < LodUtil.CHUNK_WIDTH; relBlockZ++)
|
||||
{
|
||||
LongArrayList longs = new LongArrayList(chunkWrapper.getHeight() / 4);
|
||||
int lastY = chunkWrapper.getMaxBuildHeight();
|
||||
IBiomeWrapper biome = chunkWrapper.getBiome(relBlockX, lastY, relBlockZ);
|
||||
IBlockStateWrapper blockState = AIR;
|
||||
int mappedId = dataSource.mapping.addIfNotPresentAndGetId(biome, blockState);
|
||||
|
||||
|
||||
byte blockLight;
|
||||
byte skyLight;
|
||||
if (lastY < chunkWrapper.getMaxBuildHeight())
|
||||
{
|
||||
// FIXME: The lastY +1 offset is to reproduce the old behavior. Remove this when we get per-face lighting
|
||||
blockLight = (byte) chunkWrapper.getBlockLight(relBlockX, lastY + 1, relBlockZ);
|
||||
skyLight = (byte) chunkWrapper.getSkyLight(relBlockX, lastY + 1, relBlockZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
//we are at the height limit. There are no torches here, and sky is not obscured.
|
||||
blockLight = 0;
|
||||
skyLight = 15;
|
||||
}
|
||||
|
||||
|
||||
// determine the starting Y Pos
|
||||
int y = chunkWrapper.getLightBlockingHeightMapValue(relBlockX, relBlockZ);
|
||||
// go up until we reach open air or the world limit
|
||||
IBlockStateWrapper topBlockState = chunkWrapper.getBlockState(relBlockX, y, relBlockZ);
|
||||
while (!topBlockState.isAir() && y < chunkWrapper.getMaxBuildHeight())
|
||||
{
|
||||
try
|
||||
{
|
||||
// This is necessary in some edge cases with snow layers and some other blocks that may not appear in the height map but do block light.
|
||||
// Interestingly this doesn't appear to be the case in the DhLightingEngine, if this same logic is added there the lighting breaks for the affected blocks.
|
||||
y++;
|
||||
topBlockState = chunkWrapper.getBlockState(relBlockX, y, relBlockZ);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!getTopErrorLogged)
|
||||
{
|
||||
LOGGER.warn("Unexpected issue in LodDataBuilder, future errors won't be logged. Chunk [" + chunkWrapper.getChunkPos() + "] with max height: [" + chunkWrapper.getMaxBuildHeight() + "] had issue getting block at pos [" + relBlockX + "," + y + "," + relBlockZ + "] error: " + e.getMessage(), e);
|
||||
getTopErrorLogged = true;
|
||||
}
|
||||
|
||||
y--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (; y >= minBuildHeight; y--)
|
||||
{
|
||||
IBiomeWrapper newBiome = chunkWrapper.getBiome(relBlockX, y, relBlockZ);
|
||||
IBlockStateWrapper newBlockState = chunkWrapper.getBlockState(relBlockX, y, relBlockZ);
|
||||
byte newBlockLight = (byte) chunkWrapper.getBlockLight(relBlockX, y + 1, relBlockZ);
|
||||
byte newSkyLight = (byte) chunkWrapper.getSkyLight(relBlockX, y + 1, relBlockZ);
|
||||
|
||||
// save the biome/block change
|
||||
if (!newBiome.equals(biome) || !newBlockState.equals(blockState))
|
||||
{
|
||||
// if we ignore hidden blocks, don't save this biome/block change
|
||||
// wait until the block is visible and then save the new datapoint
|
||||
if (!ignoreHiddenBlocks
|
||||
// if the last block is air, this block will always be visible
|
||||
|| blockState.isAir()
|
||||
// check if this block is visible from any direction
|
||||
|| blockVisible(chunkWrapper, relBlockX, y, relBlockZ))
|
||||
{
|
||||
longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), blockLight, skyLight));
|
||||
biome = newBiome;
|
||||
blockState = newBlockState;
|
||||
mappedId = dataSource.mapping.addIfNotPresentAndGetId(biome, blockState);
|
||||
blockLight = newBlockLight;
|
||||
skyLight = newSkyLight;
|
||||
lastY = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), blockLight, skyLight));
|
||||
|
||||
dataSource.setSingleColumn(longs,
|
||||
relBlockX + chunkOffsetX,
|
||||
relBlockZ + chunkOffsetZ,
|
||||
EDhApiWorldGenerationStep.LIGHT,
|
||||
worldCompressionMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (DataCorruptedException e)
|
||||
{
|
||||
LOGGER.error("Unable to convert chunk at pos ["+chunkWrapper.getChunkPos()+"] to an LOD. Error: "+e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
|
||||
LodUtil.assertTrue(!dataSource.isEmpty);
|
||||
return dataSource;
|
||||
}
|
||||
private static boolean blockVisible(IChunkWrapper chunkWrapper, int relBlockX, int blockY, int relBlockZ)
|
||||
{
|
||||
DhBlockPos originalBlockPos = new DhBlockPos(relBlockX,blockY,relBlockZ);
|
||||
DhBlockPos testBlockPos = new DhBlockPos(relBlockX,blockY,relBlockZ);
|
||||
|
||||
// up/down
|
||||
if (blockInDirectionVisible(chunkWrapper, EDhDirection.UP, originalBlockPos, testBlockPos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (blockInDirectionVisible(chunkWrapper, EDhDirection.DOWN, originalBlockPos, testBlockPos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// north/south
|
||||
if (blockInDirectionVisible(chunkWrapper, EDhDirection.NORTH, originalBlockPos, testBlockPos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (blockInDirectionVisible(chunkWrapper, EDhDirection.SOUTH, originalBlockPos, testBlockPos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// east/west
|
||||
if (blockInDirectionVisible(chunkWrapper, EDhDirection.EAST, originalBlockPos, testBlockPos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (blockInDirectionVisible(chunkWrapper, EDhDirection.WEST, originalBlockPos, testBlockPos))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
private static boolean blockInDirectionVisible(IChunkWrapper chunkWrapper, EDhDirection direction, DhBlockPos originalBlockPos, DhBlockPos testBlockPos)
|
||||
{
|
||||
originalBlockPos.mutateOffset(direction, testBlockPos);
|
||||
|
||||
// if the block is next to the border of a chunk, assume it's visible
|
||||
if (testBlockPos.x < 0 || testBlockPos.x >= LodUtil.CHUNK_WIDTH)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (testBlockPos.z < 0 || testBlockPos.z >= LodUtil.CHUNK_WIDTH)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (testBlockPos.y < chunkWrapper.getMinBuildHeight() || testBlockPos.y > chunkWrapper.getMaxBuildHeight())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// this block isn't on a chunk boundary, check if it is next to a transparent/air block
|
||||
IBlockStateWrapper blockState = chunkWrapper.getBlockState(testBlockPos);
|
||||
return blockState.isAir() || blockState.getOpacity() != IBlockStateWrapper.FULLY_OPAQUE;
|
||||
}
|
||||
|
||||
|
||||
/** @throws ClassCastException if an API user returns the wrong object type(s) */
|
||||
public static ChunkSizedFullDataAccessor createApiChunkData(DhApiChunk dataPoints) throws ClassCastException
|
||||
public static FullDataSourceV2 createFromApiChunkData(DhApiChunk dataPoints) throws ClassCastException, DataCorruptedException
|
||||
{
|
||||
ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(dataPoints.chunkPosX, dataPoints.chunkPosZ));
|
||||
FullDataSourceV2 accessor = FullDataSourceV2.createEmpty(DhSectionPos.encode(new DhChunkPos(dataPoints.chunkPosX, dataPoints.chunkPosZ)));
|
||||
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
|
||||
{
|
||||
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
|
||||
@@ -148,31 +317,34 @@ public class LodDataBuilder
|
||||
// AND the below loop won't run.
|
||||
int size = (columnDataPoints != null) ? columnDataPoints.size() : 0;
|
||||
|
||||
long[] packedDataPoints = new long[size];
|
||||
LongArrayList packedDataPoints = new LongArrayList(new long[size]);
|
||||
for (int index = 0; index < size; index++)
|
||||
{
|
||||
DhApiTerrainDataPoint dataPoint = columnDataPoints.get(index);
|
||||
|
||||
int id = accessor.getMapping().addIfNotPresentAndGetId(
|
||||
int id = accessor.mapping.addIfNotPresentAndGetId(
|
||||
(IBiomeWrapper) (dataPoint.biomeWrapper),
|
||||
(IBlockStateWrapper) (dataPoint.blockStateWrapper)
|
||||
);
|
||||
);
|
||||
|
||||
packedDataPoints[index] = FullDataPointUtil.encode(
|
||||
packedDataPoints.set(index, FullDataPointUtil.encode(
|
||||
id,
|
||||
dataPoint.topYBlockPos - dataPoint.bottomYBlockPos,
|
||||
dataPoint.bottomYBlockPos - dataPoints.topYBlockPos,
|
||||
(byte) (dataPoint.lightLevel)
|
||||
);
|
||||
(byte) (dataPoint.blockLightLevel),
|
||||
(byte) (dataPoint.skyLightLevel)
|
||||
));
|
||||
}
|
||||
|
||||
accessor.setSingleColumn(packedDataPoints, relX, relZ);
|
||||
// TODO add the ability for API users to define a different compression mode
|
||||
// or add a "unkown" compression mode
|
||||
accessor.setSingleColumn(packedDataPoints, relX, relZ, EDhApiWorldGenerationStep.LIGHT, EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS);
|
||||
}
|
||||
}
|
||||
|
||||
return accessor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user