Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into feature/2.0.4
This commit is contained in:
+5
-3
@@ -56,14 +56,15 @@ public enum EDhApiDataCompressionMode
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
Z_STD(2),
|
||||
//@Deprecated
|
||||
//Z_STD(2),
|
||||
|
||||
/**
|
||||
* Extremely slow, but very good compression. <br><br>
|
||||
@@ -82,7 +83,8 @@ public enum EDhApiDataCompressionMode
|
||||
EDhApiDataCompressionMode(int value) { this.value = (byte) value; }
|
||||
|
||||
|
||||
public static EDhApiDataCompressionMode getFromValue(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++)
|
||||
|
||||
+4
-65
@@ -40,37 +40,8 @@ dependencies { // All of these dependencies are in Vanilla Minecraft, but we nee
|
||||
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
|
||||
|
||||
|
||||
// fast util
|
||||
shade("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
|
||||
|
||||
// Compression
|
||||
// needs to be here and in core to prevent runtime/compiler errors
|
||||
shade("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
|
||||
shade("com.github.luben:zstd-jni:${rootProject.zstd_version}") // Zstd
|
||||
shade("org.tukaani:xz:${rootProject.xz_version}") // LZMA
|
||||
|
||||
// Sqlite Database
|
||||
shade("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}")
|
||||
|
||||
// Netty
|
||||
implementation("io.netty:netty-buffer:${rootProject.netty_version}")
|
||||
implementation("io.netty:netty-codec:${rootProject.netty_version}")
|
||||
implementation("io.netty:netty-transport:${rootProject.netty_version}")
|
||||
implementation("io.netty:netty-handler:${rootProject.netty_version}")
|
||||
|
||||
// NightConfig (includes Toml & Json)
|
||||
// needs to be here and in core to prevent runtime/compiler errors
|
||||
shade("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
|
||||
shade("com.electronwill.night-config:json:${rootProject.nightconfig_version}")
|
||||
|
||||
|
||||
// needed for the standalone jar
|
||||
shade("org.apache.logging.log4j:log4j-core:2.23.1")
|
||||
shade("org.apache.logging.log4j:log4j-api:2.23.1")
|
||||
|
||||
// SVG (not needed atm)
|
||||
//shade("com.formdev:svgSalamander:${rootProject.svgSalamander_version}")
|
||||
// FIXME for some reason this line doesn't actually shade in the library
|
||||
// shade "it.unimi.dsi:fastutil:${rootProject.fastutil_version}" // Add our own fastutil version
|
||||
|
||||
|
||||
// Some other dependencies
|
||||
@@ -78,47 +49,15 @@ dependencies { // All of these dependencies are in Vanilla Minecraft, but we nee
|
||||
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
||||
implementation("com.google.common:google-collect:0.5")
|
||||
implementation("com.google.guava:guava:31.1-jre")
|
||||
|
||||
|
||||
}
|
||||
|
||||
artifacts {
|
||||
shade shadowJar
|
||||
shadowedArtifact shadowJar // Setup the configuration shadowedArtifact to be the shadowJar
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
configurations = [project.configurations.shade]
|
||||
|
||||
def librariesLocation = "distanthorizons.libraries"
|
||||
|
||||
|
||||
relocate "it.unimi.dsi.fastutil", "${librariesLocation}.unimi.dsi.fastutil"
|
||||
|
||||
// LWJGL
|
||||
// Only ever shadow the dependencies we use otherwise some stuff would break when running on an external client
|
||||
relocate "org.lwjgl.system.jawt", "${librariesLocation}.lwjgl.system.jawt"
|
||||
|
||||
// Compression
|
||||
relocate "net.jpountz", "${librariesLocation}.jpountz"
|
||||
relocate "com.github.luben", "${librariesLocation}.github.luben"
|
||||
relocate "org.tukaani", "${librariesLocation}.tukaani"
|
||||
|
||||
// Sqlite Database
|
||||
//At the moment, there is a bug in this library which doesnt allow it to be relocated
|
||||
// relocate "org.sqlite", "${librariesLocation}.sqlite"
|
||||
|
||||
// JOML
|
||||
if (project.hasProperty("embed_joml") && embed_joml == "true")
|
||||
relocate "org.joml", "${librariesLocation}.joml"
|
||||
|
||||
// NightConfig (includes Toml & Json)
|
||||
relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig"
|
||||
|
||||
// Netty
|
||||
relocate "io.netty", "${librariesLocation}.netty"
|
||||
|
||||
relocate "org.apache.logging", "${librariesLocation}.apache.logging"
|
||||
|
||||
|
||||
// relocate "it.unimi.dsi.fastutil", "${librariesLocation}.unimi.dsi.fastutil"
|
||||
mergeServiceFiles()
|
||||
}
|
||||
@@ -28,10 +28,10 @@ import com.seibel.distanthorizons.core.api.external.methods.config.DhApiConfig;
|
||||
import com.seibel.distanthorizons.core.api.external.methods.data.DhApiTerrainDataRepo;
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.jpountz.lz4.LZ4FrameOutputStream;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.tukaani.xz.XZOutputStream;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
@@ -47,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 = LZ4FrameOutputStream.class;
|
||||
Class<?> networking = ByteBuf.class;
|
||||
Class<?> toml = com.electronwill.nightconfig.core.Config.class;
|
||||
Class<?> fastCompressor = LZ4FrameOutputStream.class;
|
||||
Class<?> smallCompressor = XZOutputStream.class;
|
||||
//Class<?> networking = ByteBuf.class;
|
||||
Class<?> config = com.electronwill.nightconfig.core.Config.class;
|
||||
Class<?> oldFastUtil = it.unimi.dsi.fastutil.longs.LongArrayList.class; // available in 8.2.1
|
||||
//Class<?> newFastUtil = it.unimi.dsi.fastutil.ints.IntUnaryOperator.class; // available in 8.5.13
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -78,8 +80,7 @@ public class ClientApi
|
||||
|
||||
private boolean configOverrideReminderPrinted = false;
|
||||
|
||||
private boolean migrationMessageShown = false;
|
||||
private boolean showMigrationMessageNextFrame = false;
|
||||
private final Queue<String> chatMessageQueueForNextFrame = new LinkedBlockingQueue<>();
|
||||
|
||||
public boolean rendererDisabledBecauseOfExceptions = false;
|
||||
|
||||
@@ -362,23 +363,22 @@ public class ClientApi
|
||||
this.configOverrideReminderPrinted = true;
|
||||
|
||||
// remind the user that this is a development build
|
||||
MC.sendChatMessage("Distant Horizons nightly experimental build version [" + 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("");
|
||||
}
|
||||
|
||||
// data migration
|
||||
if (this.showMigrationMessageNextFrame
|
||||
&& !this.migrationMessageShown
|
||||
&& Config.Client.Advanced.LodBuilding.showMigrationChatWarning.get())
|
||||
// generic messages
|
||||
while (!this.chatMessageQueueForNextFrame.isEmpty())
|
||||
{
|
||||
this.showMigrationMessageNextFrame = false;
|
||||
this.migrationMessageShown = true;
|
||||
|
||||
MC.sendChatMessage("Old Distant Horizons data is being migrated.");
|
||||
MC.sendChatMessage("During migration LODs may load slowly and DH world gen is disabled.");
|
||||
MC.sendChatMessage("");
|
||||
String message = this.chatMessageQueueForNextFrame.poll();
|
||||
if (message == null)
|
||||
{
|
||||
// done to prevent potential null pointers
|
||||
message = "";
|
||||
}
|
||||
MC.sendChatMessage(message);
|
||||
}
|
||||
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
@@ -535,7 +535,10 @@ public class ClientApi
|
||||
}
|
||||
}
|
||||
|
||||
// TODO there's probably a better way of handling chat messages
|
||||
public void showMigrationMessageOnNextFrame() { this.showMigrationMessageNextFrame = true; }
|
||||
/**
|
||||
* 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); }
|
||||
|
||||
}
|
||||
|
||||
@@ -114,6 +114,8 @@ public class SharedApi
|
||||
{
|
||||
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
|
||||
@@ -369,4 +371,4 @@ public class SharedApi
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -780,12 +780,6 @@ public class Config
|
||||
+ "Estimated average DTO read speed: 1.85 ms\n"
|
||||
+ "Estimated average DTO write speed: 9.46 ms\n"
|
||||
+ "\n"
|
||||
+ EDhApiDataCompressionMode.Z_STD + " \n"
|
||||
+ "A good middle ground between speed and compression.\n"
|
||||
+ "Expected Compression Ratio: 0.21\n"
|
||||
+ "Estimated average DTO read speed: 11.78 ms\n"
|
||||
+ "Estimated average DTO write speed: 16.77 ms\n"
|
||||
+ "\n"
|
||||
+ EDhApiDataCompressionMode.LZMA2 + " \n"
|
||||
+ "Slow but very good compression.\n"
|
||||
+ "Expected Compression Ratio: 0.14\n"
|
||||
|
||||
+16
-9
@@ -21,6 +21,7 @@ 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.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;
|
||||
@@ -259,9 +260,14 @@ public class FullDataPointIdMap
|
||||
}
|
||||
|
||||
/** 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, DhSectionPos 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<>();
|
||||
@@ -269,6 +275,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);
|
||||
@@ -457,18 +470,12 @@ public class FullDataPointIdMap
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
// necessary to prevent issues with deserializing objects after the level has been closed
|
||||
if (Thread.interrupted())
|
||||
{
|
||||
throw new InterruptedException(FullDataPointIdMap.class.getSimpleName() + " task interrupted.");
|
||||
throw new DataCorruptedException("Failed to deserialize BiomeBlockStateEntry");
|
||||
}
|
||||
|
||||
IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapper(stringArray[0], levelWrapper);
|
||||
|
||||
+4
-3
@@ -27,6 +27,7 @@ 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;
|
||||
@@ -152,7 +153,7 @@ public class FullDataSourceV1 implements IDataSource<IDhLevel>
|
||||
* 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
|
||||
public void repopulateFromStream(FullDataSourceV1DTO dto, DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
// clear/overwrite the old data
|
||||
this.resizeDataStructuresForRepopulation(dto.pos);
|
||||
@@ -166,7 +167,7 @@ public class FullDataSourceV1 implements IDataSource<IDhLevel>
|
||||
* 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
|
||||
public void populateFromStream(FullDataSourceV1DTO dto, DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
FullDataSourceSummaryData summaryData = this.readSourceSummaryInfo(dto, inputStream, level);
|
||||
this.setSourceSummaryData(summaryData);
|
||||
@@ -361,7 +362,7 @@ public class FullDataSourceV1 implements IDataSource<IDhLevel>
|
||||
outputStream.writeInt(DATA_GUARD_BYTE);
|
||||
this.mapping.serialize(outputStream);
|
||||
}
|
||||
public FullDataPointIdMap readIdMappings(DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException
|
||||
public FullDataPointIdMap readIdMappings(DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
int guardByte = inputStream.readInt();
|
||||
if (guardByte != DATA_GUARD_BYTE)
|
||||
|
||||
+20
-2
@@ -32,6 +32,7 @@ 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;
|
||||
@@ -618,7 +619,16 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
{
|
||||
if (height != 0)
|
||||
{
|
||||
newColumnList.add(FullDataPointUtil.encode(lastId, height, minY, lastBlockLight, lastSkyLight));
|
||||
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;
|
||||
@@ -632,7 +642,15 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
// add the last slice if present
|
||||
if (height != 0)
|
||||
{
|
||||
newColumnList.add(FullDataPointUtil.encode(lastId, height, minY, lastBlockLight, lastSkyLight));
|
||||
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+"].");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+8
-2
@@ -239,8 +239,14 @@ public class FullDataToRenderDataTransformer
|
||||
{
|
||||
if (colorBelowWithAvoidedBlocks)
|
||||
{
|
||||
//mare sure to not trnasfer alpha if for some reason grass is transparent
|
||||
colorToApplyToNextBlock = ColorUtil.setAlpha(level.computeBaseColor(new DhBlockPos(blockX, bottomY + level.getMinY(), blockZ), biome, block),255);
|
||||
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;
|
||||
}
|
||||
|
||||
+91
-82
@@ -35,6 +35,7 @@ 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;
|
||||
@@ -133,98 +134,106 @@ public class LodDataBuilder
|
||||
EDhApiWorldCompressionMode worldCompressionMode = Config.Client.Advanced.LodBuilding.worldCompression.get();
|
||||
boolean ignoreHiddenBlocks = (worldCompressionMode != EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS);
|
||||
|
||||
int minBuildHeight = chunkWrapper.getMinNonEmptyHeight();
|
||||
for (int relBlockX = 0; relBlockX < LodUtil.CHUNK_WIDTH; relBlockX++)
|
||||
try
|
||||
{
|
||||
for (int relBlockZ = 0; relBlockZ < LodUtil.CHUNK_WIDTH; relBlockZ++)
|
||||
int minBuildHeight = chunkWrapper.getMinNonEmptyHeight();
|
||||
for (int relBlockX = 0; relBlockX < LodUtil.CHUNK_WIDTH; relBlockX++)
|
||||
{
|
||||
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())
|
||||
for (int relBlockZ = 0; relBlockZ < LodUtil.CHUNK_WIDTH; relBlockZ++)
|
||||
{
|
||||
// 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);
|
||||
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);
|
||||
|
||||
// save the biome/block change
|
||||
if (!newBiome.equals(biome) || !newBlockState.equals(blockState))
|
||||
|
||||
byte blockLight;
|
||||
byte skyLight;
|
||||
if (lastY < chunkWrapper.getMaxBuildHeight())
|
||||
{
|
||||
// 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))
|
||||
// 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
|
||||
{
|
||||
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;
|
||||
// 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);
|
||||
}
|
||||
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;
|
||||
@@ -292,7 +301,7 @@ public class LodDataBuilder
|
||||
|
||||
|
||||
/** @throws ClassCastException if an API user returns the wrong object type(s) */
|
||||
public static FullDataSourceV2 createFromApiChunkData(DhApiChunk dataPoints) throws ClassCastException
|
||||
public static FullDataSourceV2 createFromApiChunkData(DhApiChunk dataPoints) throws ClassCastException, DataCorruptedException
|
||||
{
|
||||
FullDataSourceV2 accessor = FullDataSourceV2.createEmpty(new DhSectionPos(new DhChunkPos(dataPoints.chunkPosX, dataPoints.chunkPosZ)));
|
||||
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
|
||||
|
||||
+22
-3
@@ -8,6 +8,7 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
|
||||
import com.seibel.distanthorizons.core.sql.dto.IBaseDTO;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.threading.PositionalLockProvider;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -16,6 +17,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StreamCorruptedException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -31,6 +33,8 @@ public abstract class AbstractDataSourceHandler
|
||||
implements AutoCloseable
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final Set<String> CORRUPT_DATA_ERRORS_LOGGED = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||
|
||||
|
||||
/**
|
||||
* The highest numerical detail level possible.
|
||||
@@ -95,7 +99,7 @@ public abstract class AbstractDataSourceHandler
|
||||
/** When this is called the parent folders should be created */
|
||||
protected abstract TRepo createRepo();
|
||||
|
||||
protected abstract TDataSource createDataSourceFromDto(TDTO dto) throws InterruptedException, IOException;
|
||||
protected abstract TDataSource createDataSourceFromDto(TDTO dto) throws InterruptedException, IOException, DataCorruptedException;
|
||||
protected abstract TDTO createDtoFromDataSource(TDataSource dataSource);
|
||||
|
||||
protected abstract TDataSource makeEmptyDataSource(DhSectionPos pos);
|
||||
@@ -145,8 +149,23 @@ public abstract class AbstractDataSourceHandler
|
||||
TDTO dto = this.repo.getByKey(pos);
|
||||
if (dto != null)
|
||||
{
|
||||
// load from database
|
||||
dataSource = this.createDataSourceFromDto(dto);
|
||||
try
|
||||
{
|
||||
// load from database
|
||||
dataSource = this.createDataSourceFromDto(dto);
|
||||
}
|
||||
catch (DataCorruptedException e)
|
||||
{
|
||||
// Only log each message type once.
|
||||
// This is done to prevent logging "No compression mode with the value [2]" 10,000 times
|
||||
// if the user is migrating from a nightly build and used ZStd.
|
||||
if (CORRUPT_DATA_ERRORS_LOGGED.add(e.getMessage()))
|
||||
{
|
||||
LOGGER.warn("Corrupted data found at pos " + pos + ". Data at position will be deleted so it can be re-generated to prevent issues. Future errors with this same message won't be logged. Error: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
this.repo.deleteWithKey(pos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+9
-1
@@ -7,6 +7,7 @@ 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.sql.repo.FullDataSourceV1Repo;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -72,7 +73,7 @@ public class FullDataSourceProviderV1<TDhLevel extends IDhLevel>
|
||||
}
|
||||
}
|
||||
|
||||
protected FullDataSourceV1 createDataSourceFromDto(FullDataSourceV1DTO dto) throws InterruptedException, IOException
|
||||
protected FullDataSourceV1 createDataSourceFromDto(FullDataSourceV1DTO dto) throws InterruptedException, IOException, DataCorruptedException
|
||||
{
|
||||
FullDataSourceV1 dataSource = FullDataSourceV1.createEmpty(dto.pos);
|
||||
dataSource.populateFromStream(dto, dto.getInputStream(), this.level);
|
||||
@@ -128,6 +129,13 @@ public class FullDataSourceProviderV1<TDhLevel extends IDhLevel>
|
||||
}
|
||||
}
|
||||
catch (InterruptedException ignore) { }
|
||||
catch (DataCorruptedException e)
|
||||
{
|
||||
// stack trace not included since a lot of corrupt data would cause the log to get quite messy,
|
||||
// and it should be fairly easy to see what the problem was from the message
|
||||
LOGGER.warn("Corrupted data found at pos "+pos+". Data at position will be deleted so it can be re-generated and to prevent future issues. Error: "+e.getMessage());
|
||||
this.repo.deleteWithKey(pos);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn("File read Error for pos ["+pos+"], error: "+e.getMessage(), e);
|
||||
|
||||
+44
-4
@@ -35,6 +35,7 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
|
||||
import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -90,6 +91,8 @@ public class FullDataSourceProviderV2
|
||||
protected final AtomicBoolean migrationThreadRunning = new AtomicBoolean(true);
|
||||
protected final FullDataSourceProviderV1<IDhLevel> legacyFileHandler;
|
||||
|
||||
protected boolean migrationStartMessageQueued = false;
|
||||
|
||||
protected long legacyDeletionCount = -1;
|
||||
protected long migrationCount = -1;
|
||||
|
||||
@@ -168,7 +171,7 @@ public class FullDataSourceProviderV2
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FullDataSourceV2 createDataSourceFromDto(FullDataSourceV2DTO dto) throws InterruptedException, IOException
|
||||
protected FullDataSourceV2 createDataSourceFromDto(FullDataSourceV2DTO dto) throws InterruptedException, IOException, DataCorruptedException
|
||||
{ return dto.createPooledDataSource(this.level.getLevelWrapper()); }
|
||||
|
||||
@Override
|
||||
@@ -323,7 +326,7 @@ public class FullDataSourceProviderV2
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("issue in update for parent pos: " + parentUpdatePos);
|
||||
LOGGER.error("issue in update for parent pos: " + parentUpdatePos+ " Error: "+e.getMessage(), e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -394,7 +397,7 @@ public class FullDataSourceProviderV2
|
||||
{
|
||||
// this should only be shown once per session but should be shown during
|
||||
// either when the deletion or migration phases start
|
||||
ClientApi.INSTANCE.showMigrationMessageOnNextFrame();
|
||||
this.showMigrationStartMessage();
|
||||
|
||||
|
||||
LOGGER.info("deleting [" + dimensionName + "] - [" + totalDeleteCount + "] unused data sources...");
|
||||
@@ -447,7 +450,7 @@ public class FullDataSourceProviderV2
|
||||
ArrayList<FullDataSourceV1> legacyDataSourceList = this.legacyFileHandler.getDataSourcesToMigrate(MIGRATION_BATCH_COUNT);
|
||||
if (!legacyDataSourceList.isEmpty())
|
||||
{
|
||||
ClientApi.INSTANCE.showMigrationMessageOnNextFrame();
|
||||
this.showMigrationStartMessage();
|
||||
|
||||
|
||||
// keep going until every data source has been migrated
|
||||
@@ -520,11 +523,13 @@ public class FullDataSourceProviderV2
|
||||
if (this.migrationThreadRunning.get())
|
||||
{
|
||||
LOGGER.info("migration complete for: [" + dimensionName + "]-[" + this.saveDir + "].");
|
||||
this.showMigrationEndMessage(true);
|
||||
this.migrationCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.info("migration stopped for: [" + dimensionName + "]-[" + this.saveDir + "].");
|
||||
this.showMigrationEndMessage(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -539,6 +544,41 @@ public class FullDataSourceProviderV2
|
||||
public long getTotalMigrationCount() { return this.migrationCount; }
|
||||
|
||||
|
||||
private void showMigrationStartMessage()
|
||||
{
|
||||
if (this.migrationStartMessageQueued)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.migrationStartMessageQueued = true;
|
||||
|
||||
String dimName = this.level.getLevelWrapper().getDimensionType().getDimensionName();
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(
|
||||
"Old Distant Horizons data is being migrated for ["+dimName+"]. \n" +
|
||||
"While migrating LODs may load slowly \n" +
|
||||
"and DH world gen will be disabled. \n" +
|
||||
"You can see migration progress in the F3 menu."
|
||||
);
|
||||
}
|
||||
|
||||
private void showMigrationEndMessage(boolean success)
|
||||
{
|
||||
String dimName = this.level.getLevelWrapper().getDimensionType().getDimensionName();
|
||||
|
||||
if (success)
|
||||
{
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame("Distant Horizons data migration for ["+dimName+"] completed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(
|
||||
"Distant Horizons data migration for ["+dimName+"] stopped. \n" +
|
||||
"Some data may not have been migrated."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=======================//
|
||||
// retrieval (world gen) //
|
||||
|
||||
+15
-4
@@ -208,14 +208,18 @@ public class SubDimensionLevelMatcher implements AutoCloseable
|
||||
for (File testLevelFolder : this.potentialLevelFolders)
|
||||
{
|
||||
LOGGER.info("Testing level folder: [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "]");
|
||||
|
||||
FullDataSourceV2 testFullDataSource = null;
|
||||
try
|
||||
{
|
||||
// get the data source to compare against
|
||||
IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false, null);
|
||||
FullDataSourceV2 testFullDataSource = tempLevel.getFullDataProvider().getAsync(new DhSectionPos(this.playerData.playerBlockPos)).join();
|
||||
if (testFullDataSource == null)
|
||||
try (IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false, null))
|
||||
{
|
||||
continue;
|
||||
testFullDataSource = tempLevel.getFullDataProvider().getAsync(new DhSectionPos(this.playerData.playerBlockPos)).join();
|
||||
if (testFullDataSource == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -334,6 +338,13 @@ public class SubDimensionLevelMatcher implements AutoCloseable
|
||||
// for now we are just assuming it is an unrelated file
|
||||
LOGGER.warn("Error checking level: "+e.getMessage(), e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (testFullDataSource != null)
|
||||
{
|
||||
try { testFullDataSource.close(); } catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
@@ -36,6 +36,7 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil.AssertFailureException;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
@@ -469,6 +470,11 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
FullDataSourceV2 dataSource = LodDataBuilder.createFromApiChunkData(dataPoints);
|
||||
chunkDataConsumer.accept(dataSource);
|
||||
}
|
||||
catch (DataCorruptedException e)
|
||||
{
|
||||
LOGGER.error("World generator returned a corrupt chunk. Error: [" + e.getMessage() + "]. World generator disabled.", e);
|
||||
Config.Client.Advanced.WorldGenerator.enableDistantGeneration.set(false);
|
||||
}
|
||||
catch (ClassCastException e)
|
||||
{
|
||||
LOGGER.error("World generator return type incorrect. Error: [" + e.getMessage() + "]. World generator disabled.", e);
|
||||
|
||||
@@ -111,29 +111,10 @@ public class ConfigBasedLogger
|
||||
}
|
||||
}
|
||||
|
||||
public void error(String str, Object... param)
|
||||
{
|
||||
log(Level.ERROR, str, param);
|
||||
}
|
||||
|
||||
public void warn(String str, Object... param)
|
||||
{
|
||||
log(Level.WARN, str, param);
|
||||
}
|
||||
|
||||
public void info(String str, Object... param)
|
||||
{
|
||||
log(Level.INFO, str, param);
|
||||
}
|
||||
|
||||
public void debug(String str, Object... param)
|
||||
{
|
||||
log(Level.DEBUG, str, param);
|
||||
}
|
||||
|
||||
public void trace(String str, Object... param)
|
||||
{
|
||||
log(Level.TRACE, str, param);
|
||||
}
|
||||
public void error(String str, Object... param) { this.log(Level.ERROR, str, param); }
|
||||
public void warn(String str, Object... param) { this.log(Level.WARN, str, param); }
|
||||
public void info(String str, Object... param) { this.log(Level.INFO, str, param); }
|
||||
public void debug(String str, Object... param) { this.log(Level.DEBUG, str, param); }
|
||||
public void trace(String str, Object... param) { this.log(Level.TRACE, str, param); }
|
||||
|
||||
}
|
||||
|
||||
+14
-11
@@ -57,6 +57,9 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
@@ -402,17 +405,10 @@ public class LodRenderer
|
||||
this.renderTransparentBuffers(profiler, renderEventParam, renderEventParam.partialTicks);
|
||||
}
|
||||
|
||||
|
||||
if (this.usingMcFrameBuffer)
|
||||
{
|
||||
// If MC's framebuffer is being used the depth needs to be cleared to prevent rendering on top of MC.
|
||||
// This should only happen when Optifine shaders are being used.
|
||||
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
drawLagSpikeCatcher.end("LodDraw");
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// debug rendering //
|
||||
//=================//
|
||||
@@ -425,12 +421,21 @@ public class LodRenderer
|
||||
combinedMatrix.multiply(renderEventParam.dhModelViewMatrix);
|
||||
|
||||
// Note: this can be very slow if a lot of boxes are being rendered
|
||||
DebugRenderer.INSTANCE.render(combinedMatrix);
|
||||
DebugRenderer.INSTANCE.render(combinedMatrix);
|
||||
profiler.popPush("LOD cleanup");
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (this.usingMcFrameBuffer)
|
||||
{
|
||||
// If MC's framebuffer is being used the depth needs to be cleared to prevent rendering on top of MC.
|
||||
// This should only happen when Optifine shaders are being used.
|
||||
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============================//
|
||||
// Apply to the MC FrameBuffer //
|
||||
//=============================//
|
||||
@@ -591,8 +596,6 @@ public class LodRenderer
|
||||
{
|
||||
if (this.usingMcFrameBuffer && framebufferOverride == null)
|
||||
{
|
||||
// recreating the GL State at this point is necessary in order to get the correct depth texture
|
||||
minecraftGlState.saveState();
|
||||
if (ENABLE_DUMP_GL_STATE)
|
||||
{
|
||||
tickLogger.debug("Re-saving GL state due to Optifine presence: " + minecraftGlState);
|
||||
|
||||
+64
-24
@@ -26,6 +26,8 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.network.protocol.INetworkObject;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
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.world.ILevelWrapper;
|
||||
@@ -33,15 +35,16 @@ import io.netty.buffer.ByteBuf;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.util.zip.Adler32;
|
||||
import java.util.zip.CheckedOutputStream;
|
||||
|
||||
/** handles storing {@link FullDataSourceV2}'s in the database. */
|
||||
public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObject
|
||||
{
|
||||
public static final boolean VALIDATE_INPUT_DATAPOINTS = true;
|
||||
|
||||
|
||||
public DhSectionPos pos;
|
||||
|
||||
public int levelMinY;
|
||||
@@ -59,7 +62,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
public byte[] compressedMappingByteArray;
|
||||
|
||||
public byte dataFormatVersion;
|
||||
public EDhApiDataCompressionMode compressionModeEnum;
|
||||
public byte compressionModeValue;
|
||||
|
||||
public boolean applyToParent;
|
||||
|
||||
@@ -81,7 +84,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
|
||||
return new FullDataSourceV2DTO(
|
||||
dataSource.getPos(),
|
||||
checkedDataPointArray.checksum, compressedWorldGenStepByteArray, compressedWorldCompressionModeByteArray, FullDataSourceV2.DATA_FORMAT_VERSION, compressionModeEnum, checkedDataPointArray.byteArray,
|
||||
checkedDataPointArray.checksum, compressedWorldGenStepByteArray, compressedWorldCompressionModeByteArray, FullDataSourceV2.DATA_FORMAT_VERSION, compressionModeEnum.value, checkedDataPointArray.byteArray,
|
||||
dataSource.lastModifiedUnixDateTime, dataSource.createdUnixDateTime,
|
||||
mappingByteArray, dataSource.applyToParent,
|
||||
dataSource.levelMinY
|
||||
@@ -93,7 +96,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
|
||||
public FullDataSourceV2DTO(
|
||||
DhSectionPos pos,
|
||||
int dataChecksum, byte[] compressedColumnGenStepByteArray, byte[] compressedWorldCompressionModeByteArray, byte dataFormatVersion, EDhApiDataCompressionMode compressionModeEnum, byte[] compressedDataByteArray,
|
||||
int dataChecksum, byte[] compressedColumnGenStepByteArray, byte[] compressedWorldCompressionModeByteArray, byte dataFormatVersion, byte compressionModeValue, byte[] compressedDataByteArray,
|
||||
long lastModifiedUnixDateTime, long createdUnixDateTime,
|
||||
byte[] compressedMappingByteArray, boolean applyToParent,
|
||||
int levelMinY)
|
||||
@@ -104,7 +107,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
this.compressedWorldCompressionModeByteArray = compressedWorldCompressionModeByteArray;
|
||||
|
||||
this.dataFormatVersion = dataFormatVersion;
|
||||
this.compressionModeEnum = compressionModeEnum;
|
||||
this.compressionModeValue = compressionModeValue;
|
||||
|
||||
this.compressedDataByteArray = compressedDataByteArray;
|
||||
this.compressedMappingByteArray = compressedMappingByteArray;
|
||||
@@ -123,32 +126,46 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
// data source population //
|
||||
//========================//
|
||||
|
||||
public FullDataSourceV2 createPooledDataSource(@NotNull ILevelWrapper levelWrapper) throws IOException, InterruptedException
|
||||
public FullDataSourceV2 createPooledDataSource(@NotNull ILevelWrapper levelWrapper) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
FullDataSourceV2 dataSource = FullDataSourceV2.DATA_SOURCE_POOL.getPooledSource(this.pos, false);
|
||||
return this.populateDataSource(dataSource, levelWrapper);
|
||||
}
|
||||
|
||||
public FullDataSourceV2 populateDataSource(FullDataSourceV2 dataSource, @NotNull ILevelWrapper levelWrapper) throws IOException, InterruptedException
|
||||
public FullDataSourceV2 populateDataSource(FullDataSourceV2 dataSource, @NotNull ILevelWrapper levelWrapper) throws IOException, InterruptedException, DataCorruptedException
|
||||
{ return this.internalPopulateDataSource(dataSource, levelWrapper, false); }
|
||||
|
||||
/**
|
||||
* May be missing one or more data fields. <br>
|
||||
* Designed to be used without access to Minecraft or any supporting objects.
|
||||
*/
|
||||
public FullDataSourceV2 createUnitTestDataSource() throws IOException, InterruptedException
|
||||
public FullDataSourceV2 createUnitTestDataSource() throws IOException, InterruptedException, DataCorruptedException
|
||||
{ return this.internalPopulateDataSource(FullDataSourceV2.createEmpty(this.pos), null, true); }
|
||||
|
||||
private FullDataSourceV2 internalPopulateDataSource(FullDataSourceV2 dataSource, ILevelWrapper levelWrapper, boolean unitTest) throws IOException, InterruptedException
|
||||
private FullDataSourceV2 internalPopulateDataSource(FullDataSourceV2 dataSource, ILevelWrapper levelWrapper, boolean unitTest) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
if (FullDataSourceV2.DATA_FORMAT_VERSION != this.dataFormatVersion)
|
||||
{
|
||||
throw new IllegalStateException("There should only be one data format [" + FullDataSourceV2.DATA_FORMAT_VERSION + "].");
|
||||
}
|
||||
|
||||
dataSource.columnGenerationSteps = readBlobToGenerationSteps(this.compressedColumnGenStepByteArray, this.compressionModeEnum);
|
||||
dataSource.columnWorldCompressionMode = readBlobToGenerationSteps(this.compressedWorldCompressionModeByteArray, this.compressionModeEnum);
|
||||
dataSource.dataPoints = readBlobToDataSourceDataArray(this.compressedDataByteArray, this.compressionModeEnum);
|
||||
|
||||
EDhApiDataCompressionMode compressionModeEnum;
|
||||
try
|
||||
{
|
||||
compressionModeEnum = this.getCompressionMode();
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
// may happen if ZStd was used (which was added and removed during the nightly builds)
|
||||
// or if the compressor value is changed to an invalid option
|
||||
throw new DataCorruptedException(e);
|
||||
}
|
||||
|
||||
|
||||
dataSource.columnGenerationSteps = readBlobToGenerationSteps(this.compressedColumnGenStepByteArray, compressionModeEnum);
|
||||
dataSource.columnWorldCompressionMode = readBlobToGenerationSteps(this.compressedWorldCompressionModeByteArray, compressionModeEnum);
|
||||
dataSource.dataPoints = readBlobToDataSourceDataArray(this.compressedDataByteArray, compressionModeEnum);
|
||||
|
||||
dataSource.mapping.clear(dataSource.getPos());
|
||||
// should only be null when used in a unit test
|
||||
@@ -159,7 +176,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
throw new NullPointerException("No level wrapper present, unable to deserialize data map. This should only be used for unit tests.");
|
||||
}
|
||||
|
||||
dataSource.mapping.mergeAndReturnRemappedEntityIds(readBlobToDataMapping(this.compressedMappingByteArray, dataSource.getPos(), levelWrapper, this.compressionModeEnum));
|
||||
dataSource.mapping.mergeAndReturnRemappedEntityIds(readBlobToDataMapping(this.compressedMappingByteArray, dataSource.getPos(), levelWrapper, compressionModeEnum));
|
||||
}
|
||||
|
||||
dataSource.lastModifiedUnixDateTime = this.lastModifiedUnixDateTime;
|
||||
@@ -217,7 +234,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
|
||||
return new CheckedByteArray(checksum, byteArrayOutputStream.toByteArray());
|
||||
}
|
||||
private static LongArrayList[] readBlobToDataSourceDataArray(byte[] compressedDataByteArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException
|
||||
private static LongArrayList[] readBlobToDataSourceDataArray(byte[] compressedDataByteArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException, DataCorruptedException
|
||||
{
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedDataByteArray);
|
||||
DhDataInputStream compressedIn = new DhDataInputStream(byteArrayInputStream, compressionModeEnum);
|
||||
@@ -230,12 +247,21 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
{
|
||||
// read the column length
|
||||
short dataColumnLength = compressedIn.readShort(); // separate variables are used for debugging and in case validation wants to be added later
|
||||
if (dataColumnLength < 0)
|
||||
{
|
||||
throw new DataCorruptedException("Read DataSource Blob data at index ["+xz+"], column length ["+dataColumnLength+"] should be greater than zero.");
|
||||
}
|
||||
|
||||
LongArrayList dataColumn = new LongArrayList(new long[dataColumnLength]);
|
||||
|
||||
// read column data (will be skipped if no data was present)
|
||||
for (int y = 0; y < dataColumnLength; y++)
|
||||
{
|
||||
long dataPoint = compressedIn.readLong();
|
||||
if (VALIDATE_INPUT_DATAPOINTS)
|
||||
{
|
||||
FullDataPointUtil.validateDatapoint(dataPoint);
|
||||
}
|
||||
dataColumn.set(y, dataPoint);
|
||||
}
|
||||
|
||||
@@ -259,15 +285,22 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
private static byte[] readBlobToGenerationSteps(byte[] dataByteArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException, InterruptedException
|
||||
private static byte[] readBlobToGenerationSteps(byte[] dataByteArray, EDhApiDataCompressionMode compressionModeEnum) throws IOException, DataCorruptedException
|
||||
{
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(dataByteArray);
|
||||
DhDataInputStream compressedIn = new DhDataInputStream(byteArrayInputStream, compressionModeEnum);
|
||||
|
||||
byte[] columnGenStepByteArray = new byte[FullDataSourceV2.WIDTH * FullDataSourceV2.WIDTH];
|
||||
compressedIn.readFully(columnGenStepByteArray);
|
||||
|
||||
return columnGenStepByteArray;
|
||||
try
|
||||
{
|
||||
byte[] columnGenStepByteArray = new byte[FullDataSourceV2.WIDTH * FullDataSourceV2.WIDTH];
|
||||
compressedIn.readFully(columnGenStepByteArray);
|
||||
|
||||
return columnGenStepByteArray;
|
||||
}
|
||||
catch (EOFException e)
|
||||
{
|
||||
throw new DataCorruptedException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -307,7 +340,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
private static FullDataPointIdMap readBlobToDataMapping(byte[] compressedMappingByteArray, DhSectionPos pos, @NotNull ILevelWrapper levelWrapper, EDhApiDataCompressionMode compressionModeEnum) throws IOException, InterruptedException
|
||||
private static FullDataPointIdMap readBlobToDataMapping(byte[] compressedMappingByteArray, DhSectionPos pos, @NotNull ILevelWrapper levelWrapper, EDhApiDataCompressionMode compressionModeEnum) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedMappingByteArray);
|
||||
DhDataInputStream compressedIn = new DhDataInputStream(byteArrayInputStream, compressionModeEnum);
|
||||
@@ -334,7 +367,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
out.writeBytes(this.compressedMappingByteArray);
|
||||
|
||||
out.writeByte(this.dataFormatVersion);
|
||||
out.writeByte(this.compressionModeEnum.ordinal());
|
||||
out.writeByte(this.compressionModeValue);
|
||||
|
||||
out.writeBoolean(this.applyToParent);
|
||||
|
||||
@@ -360,7 +393,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
in.readBytes(this.compressedMappingByteArray);
|
||||
|
||||
this.dataFormatVersion = in.readByte();
|
||||
this.compressionModeEnum = EDhApiDataCompressionMode.values()[in.readByte()];
|
||||
this.compressionModeValue = in.readByte();
|
||||
|
||||
this.applyToParent = in.readBoolean();
|
||||
|
||||
@@ -379,6 +412,13 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>, INetworkObje
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
public EDhApiDataCompressionMode getCompressionMode() throws IllegalArgumentException { return EDhApiDataCompressionMode.getFromValue(this.compressionModeValue); }
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
+5
-5
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -91,8 +92,7 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
|
||||
|
||||
byte dataFormatVersion = (Byte) objectMap.get("DataFormatVersion");
|
||||
byte compressionMode = (Byte) objectMap.get("CompressionMode");
|
||||
EDhApiDataCompressionMode compressionModeEnum = EDhApiDataCompressionMode.getFromValue(compressionMode);
|
||||
byte compressionModeValue = (Byte) objectMap.get("CompressionMode");
|
||||
|
||||
boolean applyToParent = ((int) objectMap.get("ApplyToParent")) == 1;
|
||||
|
||||
@@ -101,7 +101,7 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
|
||||
FullDataSourceV2DTO dto = new FullDataSourceV2DTO(
|
||||
pos,
|
||||
dataChecksum, columnGenStepByteArray, columnWorldCompressionByteArray, dataFormatVersion, compressionModeEnum, dataByteArray,
|
||||
dataChecksum, columnGenStepByteArray, columnWorldCompressionByteArray, dataFormatVersion, compressionModeValue, dataByteArray,
|
||||
lastModifiedUnixDateTime, createdUnixDateTime,
|
||||
mappingByteArray, applyToParent,
|
||||
minY);
|
||||
@@ -141,7 +141,7 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
statement.setObject(i++, dto.compressedMappingByteArray);
|
||||
|
||||
statement.setObject(i++, dto.dataFormatVersion);
|
||||
statement.setObject(i++, dto.compressionModeEnum.value);
|
||||
statement.setObject(i++, dto.compressionModeValue);
|
||||
statement.setObject(i++, dto.applyToParent);
|
||||
|
||||
statement.setObject(i++, System.currentTimeMillis()); // last modified unix time
|
||||
@@ -184,7 +184,7 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
statement.setObject(i++, dto.compressedMappingByteArray);
|
||||
|
||||
statement.setObject(i++, dto.dataFormatVersion);
|
||||
statement.setObject(i++, dto.compressionModeEnum.value);
|
||||
statement.setObject(i++, dto.compressionModeValue);
|
||||
statement.setObject(i++, dto.applyToParent);
|
||||
|
||||
statement.setObject(i++, System.currentTimeMillis()); // last modified unix time
|
||||
|
||||
@@ -1,141 +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.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.booleans.BooleanObjectImmutablePair;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* While java 8 does have built in atomic operations, there doesn't seem to be any Compare And Exchange operation... <br>
|
||||
* So here we implement our own.
|
||||
*/
|
||||
public class AtomicsUtil
|
||||
{
|
||||
|
||||
public static <T> T conditionalAndExchange(AtomicReference<T> atomic, Predicate<T> requirement, T newValue)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
T oldValue = atomic.get();
|
||||
if (!requirement.test(oldValue)) return oldValue;
|
||||
if (atomic.weakCompareAndSet(oldValue, newValue)) return oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> BooleanObjectImmutablePair<T> conditionalAndExchangeWeak(AtomicReference<T> atomic, Predicate<T> requirement, T newValue)
|
||||
{
|
||||
T oldValue = atomic.get();
|
||||
if (requirement.test(oldValue) && atomic.weakCompareAndSet(oldValue, newValue))
|
||||
{
|
||||
return new BooleanObjectImmutablePair<>(true, oldValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new BooleanObjectImmutablePair<>(false, oldValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the {@link AtomicReference}'s current value matches the expected value, the newValue will be swapped in and the expected value returned. <br>
|
||||
* If the {@link AtomicReference}'s current value DOESN'T match the expected value, the {@link AtomicReference}'s current value will be returned without modification.
|
||||
*/
|
||||
public static <T> T compareAndExchange(AtomicReference<T> atomic, T expected, T newValue)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
T oldValue = atomic.get();
|
||||
if (oldValue != expected)
|
||||
{
|
||||
return oldValue;
|
||||
}
|
||||
else if (atomic.weakCompareAndSet(expected, newValue))
|
||||
{
|
||||
return expected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> BooleanObjectImmutablePair<T> compareAndExchangeWeak(AtomicReference<T> atomic, T expected, T newValue)
|
||||
{
|
||||
T oldValue = atomic.get();
|
||||
if (oldValue == expected && atomic.weakCompareAndSet(expected, newValue))
|
||||
{
|
||||
return new BooleanObjectImmutablePair<>(true, expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new BooleanObjectImmutablePair<>(false, oldValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Additionally, we implement some helper methods for frequently used atomic operations. //
|
||||
|
||||
// Compare with expected value and set new value if equal. Then return whatever value the atomic now contains.
|
||||
public static <T> T compareAndSetThenGet(AtomicReference<T> atomic, T expected, T newValue)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
T oldValue = atomic.get();
|
||||
if (oldValue != expected) return oldValue;
|
||||
if (atomic.weakCompareAndSet(expected, newValue)) return newValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Below is the array version of the above. //
|
||||
|
||||
public static <T> T compareAndExchange(AtomicReferenceArray<T> array, int index, T expected, T newValue)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
T oldValue = array.get(index);
|
||||
if (oldValue != expected) return oldValue;
|
||||
if (array.weakCompareAndSet(index, expected, newValue)) return expected;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> BooleanObjectImmutablePair<T> compareAndExchangeWeak(AtomicReferenceArray<T> array, int index, T expected, T newValue)
|
||||
{
|
||||
T oldValue = array.get(index);
|
||||
if (oldValue == expected && array.weakCompareAndSet(index, expected, newValue))
|
||||
{
|
||||
return new BooleanObjectImmutablePair<>(true, expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new BooleanObjectImmutablePair<>(false, oldValue);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T compareAndSetThenGet(AtomicReferenceArray<T> array, int index, T expected, T newValue)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
T oldValue = array.get(index);
|
||||
if (oldValue != expected) return oldValue;
|
||||
if (array.weakCompareAndSet(index, expected, newValue)) return newValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.util;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV1;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
@@ -72,23 +73,11 @@ public class FullDataPointUtil
|
||||
* creates a new datapoint with the given values
|
||||
* @param relMinY relative to the minimum level Y value
|
||||
*/
|
||||
public static long encode(int id, int height, int relMinY, byte blockLight, byte skyLight)
|
||||
public static long encode(int id, int height, int relMinY, byte blockLight, byte skyLight) throws DataCorruptedException
|
||||
{
|
||||
if (RUN_VALIDATION)
|
||||
{
|
||||
// assertions are inside if-blocks to prevent unnecessary string concatenations
|
||||
if (relMinY < 0 || relMinY >= RenderDataPointUtil.MAX_WORLD_Y_SIZE)
|
||||
{
|
||||
LodUtil.assertNotReach("Trying to create datapoint with y[" + relMinY + "] out of range!");
|
||||
}
|
||||
if (height <= 0 || height >= RenderDataPointUtil.MAX_WORLD_Y_SIZE)
|
||||
{
|
||||
LodUtil.assertNotReach("Trying to create datapoint with height[" + height + "] out of range!");
|
||||
}
|
||||
if (relMinY + height > RenderDataPointUtil.MAX_WORLD_Y_SIZE)
|
||||
{
|
||||
LodUtil.assertNotReach("Trying to create datapoint with y+depth[" + (relMinY + height) + "] out of range!");
|
||||
}
|
||||
validateData(id, height, relMinY, blockLight, skyLight);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +105,44 @@ public class FullDataPointUtil
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void validateDatapoint(long datapoint) throws DataCorruptedException { validateData(getId(datapoint), getHeight(datapoint), getBottomY(datapoint), (byte)getBlockLight(datapoint), (byte)getSkyLight(datapoint)); }
|
||||
/**
|
||||
* Throws {@link DataCorruptedException} if any of the given values are outside
|
||||
* their expected range.
|
||||
*/
|
||||
public static void validateData(int id, int height, int relMinY, byte blockLight, byte skyLight) throws DataCorruptedException
|
||||
{
|
||||
// ID
|
||||
if (id < 0)
|
||||
{
|
||||
throw new DataCorruptedException("Full datapoint ID [" + relMinY + "] must be greater than zero.");
|
||||
}
|
||||
|
||||
// height
|
||||
if (relMinY < 0 || relMinY >= RenderDataPointUtil.MAX_WORLD_Y_SIZE)
|
||||
{
|
||||
throw new DataCorruptedException("Full datapoint relative min y [" + relMinY + "] must be in the range [0 - "+RenderDataPointUtil.MAX_WORLD_Y_SIZE+"] (inclusive).");
|
||||
}
|
||||
if (height <= 0 || height >= RenderDataPointUtil.MAX_WORLD_Y_SIZE)
|
||||
{
|
||||
throw new DataCorruptedException("Full datapoint height [" + height + "] must be in the range [1 - "+RenderDataPointUtil.MAX_WORLD_Y_SIZE+"] (inclusive).");
|
||||
}
|
||||
if (relMinY + height > RenderDataPointUtil.MAX_WORLD_Y_SIZE)
|
||||
{
|
||||
throw new DataCorruptedException("Full datapoint y+depth [" + (relMinY + height) + "] is higher than the maximum world Y height ["+RenderDataPointUtil.MAX_WORLD_Y_SIZE+"].");
|
||||
}
|
||||
|
||||
// lighting
|
||||
if (blockLight < LodUtil.MIN_MC_LIGHT || blockLight > LodUtil.MAX_MC_LIGHT)
|
||||
{
|
||||
throw new DataCorruptedException("Full datapoint block light [" + blockLight + "] must be in the range ["+LodUtil.MIN_MC_LIGHT+" - "+LodUtil.MAX_MC_LIGHT+"] (inclusive).");
|
||||
}
|
||||
if (skyLight < LodUtil.MIN_MC_LIGHT || skyLight > LodUtil.MAX_MC_LIGHT)
|
||||
{
|
||||
throw new DataCorruptedException("Full datapoint sky light [" + skyLight + "] must be in the range ["+LodUtil.MIN_MC_LIGHT+" - "+LodUtil.MAX_MC_LIGHT+"] (inclusive).");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Returns the BlockState/Biome pair ID used to identify this LOD's color */
|
||||
public static int getId(long data) { return (int) (data & ID_MASK); }
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.seibel.distanthorizons.core.util.objects;
|
||||
|
||||
/**
|
||||
* Thrown when a DH handled resource or datasource isn't in the
|
||||
* correct format. <Br><Br>
|
||||
*
|
||||
* IE: a blocklight with the value -4 when it should be between 0 and 15.
|
||||
*/
|
||||
public class DataCorruptedException extends Exception
|
||||
{
|
||||
/** replaces this exception's stack trace with the incoming one */
|
||||
public DataCorruptedException(Exception e)
|
||||
{
|
||||
super(e.getMessage());
|
||||
this.setStackTrace(e.getStackTrace());
|
||||
this.addSuppressed(e);
|
||||
}
|
||||
|
||||
public DataCorruptedException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-4
@@ -19,9 +19,8 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.util.objects.dataStreams;
|
||||
|
||||
import com.github.luben.zstd.RecyclingBufferPool;
|
||||
import com.github.luben.zstd.ZstdInputStream;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import net.jpountz.lz4.LZ4FrameInputStream;
|
||||
import org.tukaani.xz.XZInputStream;
|
||||
|
||||
@@ -51,8 +50,6 @@ public class DhDataInputStream extends DataInputStream
|
||||
return stream;
|
||||
case LZ4:
|
||||
return new LZ4FrameInputStream(stream);
|
||||
case Z_STD:
|
||||
return new ZstdInputStream(stream, RecyclingBufferPool.INSTANCE);
|
||||
case LZMA2:
|
||||
// Note: all LZMA/XZ compressors can be decompressed using this same InputStream
|
||||
return new XZInputStream(stream);
|
||||
|
||||
-6
@@ -19,14 +19,10 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.util.objects.dataStreams;
|
||||
|
||||
import com.github.luben.zstd.RecyclingBufferPool;
|
||||
import com.github.luben.zstd.ZstdOutputStream;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import net.jpountz.lz4.LZ4Factory;
|
||||
import net.jpountz.lz4.LZ4FrameOutputStream;
|
||||
import net.jpountz.xxhash.XXHashFactory;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.tukaani.xz.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -59,8 +55,6 @@ public class DhDataOutputStream extends DataOutputStream
|
||||
LZ4Factory.nativeInstance().fastCompressor(),
|
||||
XXHashFactory.nativeInstance().hash32(),
|
||||
LZ4FrameOutputStream.FLG.Bits.BLOCK_INDEPENDENCE);
|
||||
case Z_STD:
|
||||
return new ZstdOutputStream(stream, RecyclingBufferPool.INSTANCE);
|
||||
case LZMA2:
|
||||
// using an array cache significantly reduces GC pressure
|
||||
ResettableArrayCache arrayCache = LZMA_RESETTABLE_ARRAY_CACHE_GETTER.get();
|
||||
|
||||
+1
-1
@@ -2,12 +2,12 @@ package com.seibel.distanthorizons.core.util.objects.dataStreams;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntUnaryOperator;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.tukaani.xz.ArrayCache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab
|
||||
public interface ILightMapWrapper extends IBindable
|
||||
{
|
||||
|
||||
// Returns the binded texture position
|
||||
/** Returns the bound texture position */
|
||||
void bind();
|
||||
void unbind();
|
||||
|
||||
|
||||
+5
@@ -28,6 +28,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
*/
|
||||
public interface IClientLevelWrapper extends ILevelWrapper
|
||||
{
|
||||
|
||||
@Nullable
|
||||
IServerLevelWrapper tryGetServerSideWrapper();
|
||||
|
||||
@@ -36,4 +37,8 @@ public interface IClientLevelWrapper extends ILevelWrapper
|
||||
/** @return -1 if there was a problem getting the color */
|
||||
int getDirtBlockColor();
|
||||
|
||||
/** Will return null if there was an issue finding the biome. */
|
||||
@Nullable
|
||||
IBiomeWrapper getPlainsBiomeWrapper();
|
||||
|
||||
}
|
||||
@@ -321,7 +321,7 @@
|
||||
"distanthorizons.config.client.advanced.worldGenerator.distantGeneratorMode":
|
||||
"Distance Generator Mode",
|
||||
"distanthorizons.config.client.advanced.worldGenerator.distantGeneratorMode.@tooltip":
|
||||
"How complicated the generation should be when generating LODs outside the vanilla render distance.\n\n§6§6Fastest:§r Biome only\n§6Best Quality:§r Features (suggested)",
|
||||
"How complicated the generation should be when generating LODs outside the vanilla render distance.",
|
||||
"distanthorizons.config.client.advanced.worldGenerator.worldGenLightingEngine":
|
||||
"Lighting Engine",
|
||||
"distanthorizons.config.client.advanced.worldGenerator.worldGenLightingEngine.@tooltip":
|
||||
@@ -797,13 +797,11 @@
|
||||
"Full",
|
||||
|
||||
"distanthorizons.config.enum.EDhApiDataCompressionMode.UNCOMPRESSED":
|
||||
"1. Uncompressed",
|
||||
"Uncompressed",
|
||||
"distanthorizons.config.enum.EDhApiDataCompressionMode.LZ4":
|
||||
"2. LZ4",
|
||||
"distanthorizons.config.enum.EDhApiDataCompressionMode.Z_STD":
|
||||
"3. Zstd",
|
||||
"Fast/Big - LZ4",
|
||||
"distanthorizons.config.enum.EDhApiDataCompressionMode.LZMA2":
|
||||
"4. LZMA2",
|
||||
"Slow/Small - LZMA2",
|
||||
|
||||
"distanthorizons.config.enum.EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS":
|
||||
"1. Merge Same Blocks",
|
||||
|
||||
@@ -213,11 +213,11 @@ public class CompressionTest
|
||||
}
|
||||
|
||||
//@Test
|
||||
public void Zstd() // middle of the road
|
||||
{
|
||||
String compressorName = "Zstd";
|
||||
this.testCompressor(compressorName, EDhApiDataCompressionMode.Z_STD);
|
||||
}
|
||||
//public void Zstd() // middle of the road
|
||||
//{
|
||||
// String compressorName = "Zstd";
|
||||
// this.testCompressor(compressorName, EDhApiDataCompressionMode.Z_STD);
|
||||
//}
|
||||
|
||||
//@Test
|
||||
public void LZMA2() // very slow, very good compression though
|
||||
@@ -293,7 +293,7 @@ public class CompressionTest
|
||||
// uncompressed input //
|
||||
|
||||
FullDataSourceV2DTO uncompressedDto = uncompressedRepo.getByKey(pos);
|
||||
Assert.assertEquals(uncompressedDto.compressionModeEnum, EDhApiDataCompressionMode.UNCOMPRESSED);
|
||||
Assert.assertEquals(uncompressedDto.compressionModeValue, EDhApiDataCompressionMode.UNCOMPRESSED.value);
|
||||
FullDataSourceV2 uncompressedDataSource = uncompressedDto.createUnitTestDataSource();
|
||||
|
||||
long uncompressedDtoSize = uncompressedRepo.getDataSizeInBytes(pos);
|
||||
|
||||
Reference in New Issue
Block a user