Compare commits
39 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 | |||
| cb0c294df6 | |||
| 7714569251 | |||
| 8b9e48d4d3 |
+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++)
|
||||
|
||||
@@ -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.3-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");
|
||||
|
||||
|
||||
+4
-63
@@ -39,36 +39,9 @@ dependencies { // All of these dependencies are in Vanilla Minecraft, but we nee
|
||||
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
|
||||
|
||||
|
||||
// fast util
|
||||
shade("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
|
||||
|
||||
// Compression
|
||||
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
|
||||
shade("io.netty:netty-all:${rootProject.netty_version}")
|
||||
|
||||
// NightConfig (includes Toml & Json)
|
||||
// needed in both common and core
|
||||
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
|
||||
@@ -80,43 +53,11 @@ dependencies { // All of these dependencies are in Vanilla Minecraft, but we nee
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -205,7 +205,7 @@ 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();
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
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
|
||||
{
|
||||
|
||||
@@ -55,6 +55,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;
|
||||
|
||||
/**
|
||||
@@ -79,8 +81,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;
|
||||
|
||||
@@ -480,23 +481,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();
|
||||
@@ -648,7 +648,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); }
|
||||
|
||||
}
|
||||
|
||||
@@ -110,6 +110,8 @@ public class SharedApi
|
||||
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();
|
||||
|
||||
@@ -771,12 +771,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"
|
||||
@@ -1258,6 +1252,11 @@ public class Config
|
||||
.comment("Render LOD section status?")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> showQuadTreeRenderStatus = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment("Render Quad Tree Rendering status?")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> showFullDataUpdateStatus = new ConfigEntry.Builder<Boolean>()
|
||||
.set(false)
|
||||
.comment("Render full data update/lock status?")
|
||||
|
||||
+89
-17
@@ -21,6 +21,8 @@ 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;
|
||||
@@ -65,7 +67,7 @@ public class FullDataPointIdMap
|
||||
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<>();
|
||||
@@ -77,7 +79,7 @@ public class FullDataPointIdMap
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public FullDataPointIdMap(DhSectionPos pos) { this.pos = pos; }
|
||||
public FullDataPointIdMap(long pos) { this.pos = pos; }
|
||||
|
||||
|
||||
|
||||
@@ -117,9 +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 boolean isEmpty() { return this.entryList.isEmpty(); }
|
||||
|
||||
public DhSectionPos getPos() { return this.pos; }
|
||||
public long getPos() { return this.pos; }
|
||||
|
||||
|
||||
|
||||
@@ -168,9 +172,67 @@ public class FullDataPointIdMap
|
||||
}
|
||||
}
|
||||
|
||||
/** allows for adding duplicate {@link Entry} */
|
||||
private void add(Entry biomeBlockStateEntry, boolean useWriteLocks)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (useWriteLocks)
|
||||
{
|
||||
this.readWriteLock.writeLock().lock();
|
||||
}
|
||||
|
||||
|
||||
int id = this.entryList.size();
|
||||
this.entryList.add(biomeBlockStateEntry);
|
||||
this.idMap.put(biomeBlockStateEntry, id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (useWriteLocks)
|
||||
{
|
||||
this.readWriteLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds each entry from the given map to this map.
|
||||
* 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 = 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.
|
||||
@@ -208,7 +270,7 @@ public class FullDataPointIdMap
|
||||
}
|
||||
|
||||
/** 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();
|
||||
@@ -259,9 +321,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, 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<>();
|
||||
@@ -269,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);
|
||||
@@ -287,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;
|
||||
}
|
||||
@@ -457,22 +535,16 @@ 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");
|
||||
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);
|
||||
IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapperOrGetDefault(stringArray[0], levelWrapper);
|
||||
IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapperOrGetDefault(stringArray[1], levelWrapper);
|
||||
return Entry.getEntry(biome, blockState);
|
||||
}
|
||||
|
||||
|
||||
+11
-10
@@ -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;
|
||||
@@ -74,7 +75,7 @@ public class FullDataSourceV1 implements IDataSource<IDhLevel>
|
||||
/** A flattened 2D array (for the X and Z directions) containing an array for the Y direction. */
|
||||
private final long[][] dataArrays;
|
||||
|
||||
private DhSectionPos sectionPos;
|
||||
private long sectionPos;
|
||||
|
||||
private boolean isEmpty = true;
|
||||
|
||||
@@ -84,8 +85,8 @@ public class FullDataSourceV1 implements IDataSource<IDhLevel>
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static FullDataSourceV1 createEmpty(DhSectionPos pos) { return new FullDataSourceV1(pos); }
|
||||
private FullDataSourceV1(DhSectionPos sectionPos)
|
||||
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);
|
||||
@@ -110,19 +111,19 @@ public class FullDataSourceV1 implements IDataSource<IDhLevel>
|
||||
//=====================//
|
||||
|
||||
@Override
|
||||
public DhSectionPos getKey() { return this.sectionPos; }
|
||||
public Long getKey() { return this.sectionPos; }
|
||||
|
||||
@Override
|
||||
public DhSectionPos getPos() { return this.sectionPos; }
|
||||
public Long getPos() { return this.sectionPos; }
|
||||
|
||||
public void resizeDataStructuresForRepopulation(DhSectionPos pos)
|
||||
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) (this.sectionPos.getDetailLevel() - SECTION_SIZE_OFFSET); }
|
||||
public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.sectionPos) - SECTION_SIZE_OFFSET); }
|
||||
|
||||
public boolean isEmpty() { return this.isEmpty; }
|
||||
|
||||
@@ -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)
|
||||
|
||||
+45
-27
@@ -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;
|
||||
@@ -70,9 +71,9 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
|
||||
private int cachedHashCode = 0;
|
||||
|
||||
private DhSectionPos pos;
|
||||
private long pos;
|
||||
@Override
|
||||
public DhSectionPos getKey() { return this.pos; }
|
||||
public Long getKey() { return this.pos; }
|
||||
|
||||
|
||||
public final FullDataPointIdMap mapping;
|
||||
@@ -111,8 +112,8 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static FullDataSourceV2 createEmpty(DhSectionPos pos) { return new FullDataSourceV2(pos); }
|
||||
private FullDataSourceV2(DhSectionPos pos)
|
||||
public static FullDataSourceV2 createEmpty(long pos) { return new FullDataSourceV2(pos); }
|
||||
private FullDataSourceV2(long pos)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.dataPoints = new LongArrayList[WIDTH * WIDTH];
|
||||
@@ -125,8 +126,8 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
this.columnWorldCompressionMode = new byte[WIDTH * WIDTH];
|
||||
}
|
||||
|
||||
public static FullDataSourceV2 createWithData(DhSectionPos pos, FullDataPointIdMap mapping, LongArrayList[] data, byte[] columnGenerationStep, byte[] columnWorldCompressionMode) { return new FullDataSourceV2(pos, mapping, data, columnGenerationStep, columnWorldCompressionMode); }
|
||||
private FullDataSourceV2(DhSectionPos pos, FullDataPointIdMap mapping, LongArrayList[] data, byte[] columnGenerationSteps, byte[] columnWorldCompressionMode)
|
||||
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);
|
||||
|
||||
@@ -229,8 +230,8 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
}
|
||||
|
||||
|
||||
byte thisDetailLevel = this.pos.getDetailLevel();
|
||||
byte inputDetailLevel = inputDataSource.pos.getDetailLevel();
|
||||
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
|
||||
@@ -255,7 +256,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
}
|
||||
|
||||
// determine if this data source should be applied to its parent
|
||||
this.applyToParent = (dataChanged && this.pos.getDetailLevel() < AbstractDataSourceHandler.TOP_SECTION_DETAIL_LEVEL);
|
||||
this.applyToParent = (dataChanged && DhSectionPos.getDetailLevel(this.pos) < AbstractDataSourceHandler.TOP_SECTION_DETAIL_LEVEL);
|
||||
|
||||
if (dataChanged)
|
||||
{
|
||||
@@ -268,9 +269,9 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
public boolean updateFromSameDetailLevel(FullDataSourceV2 inputDataSource, int[] remappedIds)
|
||||
{
|
||||
// both data sources should have the same detail level
|
||||
if (inputDataSource.pos.getDetailLevel() != this.pos.getDetailLevel())
|
||||
if (DhSectionPos.getDetailLevel(inputDataSource.pos) != DhSectionPos.getDetailLevel(this.pos))
|
||||
{
|
||||
throw new IllegalArgumentException("Both data sources must have the same detail level. Expected ["+this.pos.getDetailLevel()+"], received ["+inputDataSource.pos.getDetailLevel()+"].");
|
||||
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
|
||||
@@ -350,9 +351,9 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
}
|
||||
public boolean updateFromOneBelowDetailLevel(FullDataSourceV2 inputDataSource, int[] remappedIds)
|
||||
{
|
||||
if (inputDataSource.pos.getDetailLevel() + 1 != this.pos.getDetailLevel())
|
||||
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 [" + (this.pos.getDetailLevel() - 1) + "], received [" + inputDataSource.pos.getDetailLevel() + "].");
|
||||
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)
|
||||
@@ -361,10 +362,10 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
|
||||
// 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 = this.pos.getChildByIndex(0).getX();
|
||||
int recipientOffsetX = (inputDataSource.pos.getX() == minChildXPos) ? 0 : (WIDTH / 2);
|
||||
int minChildZPos = this.pos.getChildByIndex(0).getZ();
|
||||
int recipientOffsetZ = (inputDataSource.pos.getZ() == minChildZPos) ? 0 : (WIDTH / 2);
|
||||
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);
|
||||
|
||||
|
||||
|
||||
@@ -616,7 +617,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;
|
||||
@@ -630,7 +640,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+"].");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -765,7 +783,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
*
|
||||
* @see FullDataSourceV2#dataPoints
|
||||
*/
|
||||
public static void throwIfDataColumnInWrongOrder(DhSectionPos pos, LongArrayList dataArray) throws IllegalStateException
|
||||
public static void throwIfDataColumnInWrongOrder(long pos, LongArrayList dataArray) throws IllegalStateException
|
||||
{
|
||||
long firstDataPoint = dataArray.getLong(0);
|
||||
int firstBottomY = FullDataPointUtil.getBottomY(firstDataPoint);
|
||||
@@ -775,7 +793,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
|
||||
if (firstBottomY < lastBottomY)
|
||||
{
|
||||
throw new IllegalStateException("Incorrect data point order at pos: "+pos+", first datapoint bottom Y ["+firstBottomY+"], last datapoint bottom Y ["+lastBottomY+"].");
|
||||
throw new IllegalStateException("Incorrect data point order at pos: ["+ DhSectionPos.toString(pos)+"], first datapoint bottom Y ["+firstBottomY+"], last datapoint bottom Y ["+lastBottomY+"].");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -811,7 +829,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
// pooling //
|
||||
//=========//
|
||||
|
||||
private static void prepPooledDataSource(DhSectionPos pos, boolean clearData, FullDataSourceV2 dataSource)
|
||||
private static void prepPooledDataSource(long pos, boolean clearData, FullDataSourceV2 dataSource)
|
||||
{
|
||||
dataSource.pos = pos;
|
||||
|
||||
@@ -839,10 +857,10 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
//=====================//
|
||||
|
||||
@Override
|
||||
public DhSectionPos getPos() { return this.pos; }
|
||||
public Long getPos() { return this.pos; }
|
||||
|
||||
@Override
|
||||
public byte getDataDetailLevel() { return (byte) (this.pos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); }
|
||||
public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); }
|
||||
|
||||
public EDhApiWorldGenerationStep getWorldGenStepAtRelativePos(int relX, int relZ)
|
||||
{
|
||||
@@ -881,7 +899,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public String toString() { return this.pos.toString(); }
|
||||
public String toString() { return DhSectionPos.toString(this.pos); }
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
@@ -894,7 +912,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
}
|
||||
private void generateHashCode()
|
||||
{
|
||||
int result = this.pos.hashCode();
|
||||
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);
|
||||
@@ -911,7 +929,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
||||
}
|
||||
FullDataSourceV2 other = (FullDataSourceV2) obj;
|
||||
|
||||
if (!other.pos.equals(this.pos))
|
||||
if (other.pos != this.pos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
+11
-11
@@ -59,7 +59,7 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
|
||||
/** will be zero if an empty data source was created */
|
||||
public int verticalDataCount;
|
||||
public DhSectionPos pos;
|
||||
public long pos;
|
||||
public int yOffset;
|
||||
|
||||
public LongArrayList renderDataContainer;
|
||||
@@ -77,11 +77,11 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
//==============//
|
||||
|
||||
/**
|
||||
* This is separate from {@link DataSourcePool#getPooledSource(DhSectionPos, boolean)}
|
||||
* 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(DhSectionPos pos, int maxVerticalSize, int yOffset, boolean clearData)
|
||||
public static ColumnRenderSource getPooledRenderSource(long pos, int maxVerticalSize, int yOffset, boolean clearData)
|
||||
{
|
||||
ColumnRenderSource renderSource = DATA_SOURCE_POOL.getPooledSource(pos);
|
||||
|
||||
@@ -109,14 +109,14 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
}
|
||||
|
||||
|
||||
private static ColumnRenderSource createEmptyRenderSource(DhSectionPos sectionPos) { return new ColumnRenderSource(sectionPos, 0, 0); }
|
||||
private static ColumnRenderSource createEmptyRenderSource(long sectionPos) { return new ColumnRenderSource(sectionPos, 0, 0); }
|
||||
/**
|
||||
* Creates an empty ColumnRenderSource.
|
||||
*
|
||||
* @param pos the relative position of the container
|
||||
* @param maxVerticalSize the maximum vertical size of the container
|
||||
*/
|
||||
private ColumnRenderSource(DhSectionPos pos, int maxVerticalSize, int yOffset)
|
||||
private ColumnRenderSource(long pos, int maxVerticalSize, int yOffset)
|
||||
{
|
||||
this.verticalDataCount = maxVerticalSize;
|
||||
this.renderDataContainer = new LongArrayList(new long[SECTION_SIZE * SECTION_SIZE * this.verticalDataCount]);
|
||||
@@ -155,7 +155,7 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
final String errorMessagePrefix = "Unable to complete update for RenderSource pos: [" + this.pos + "] and pos: [" + inputFullDataSource.getPos() + "]. Error:";
|
||||
|
||||
boolean dataChanged = false;
|
||||
if (inputFullDataSource.getPos().getDetailLevel() == this.pos.getDetailLevel())
|
||||
if (DhSectionPos.getDetailLevel(inputFullDataSource.getPos()) == DhSectionPos.getDetailLevel(this.pos))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -167,8 +167,8 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
|
||||
|
||||
|
||||
DhBlockPos2D centerBlockPos = inputFullDataSource.getPos().getCenterBlockPos();
|
||||
int halfBlockWidth = inputFullDataSource.getPos().getBlockWidth() / 2;
|
||||
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++)
|
||||
@@ -215,11 +215,11 @@ public class ColumnRenderSource implements IDataSource<IDhClientLevel>
|
||||
// data helper methods //
|
||||
//=====================//
|
||||
|
||||
public DhSectionPos getPos() { return this.pos; }
|
||||
public Long getPos() { return this.pos; }
|
||||
@Override
|
||||
public DhSectionPos getKey() { return this.pos; }
|
||||
public Long getKey() { return this.pos; }
|
||||
|
||||
public byte getDataDetailLevel() { return (byte) (this.pos.getDetailLevel() - SECTION_SIZE_OFFSET); }
|
||||
public byte getDataDetailLevel() { return (byte) (DhSectionPos.getDetailLevel(this.pos) - SECTION_SIZE_OFFSET); }
|
||||
|
||||
public boolean isEmpty() { return this.isEmpty; }
|
||||
public void markNotEmpty() { this.isEmpty = false; }
|
||||
|
||||
+14
-7
@@ -343,17 +343,24 @@ public class ColumnRenderBuffer implements AutoCloseable
|
||||
//==============//
|
||||
|
||||
/** 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;
|
||||
}
|
||||
|
||||
public void debugDumpStats(StatsMap statsMap)
|
||||
|
||||
+5
-5
@@ -98,7 +98,7 @@ public class ColumnRenderBufferBuilder
|
||||
&& !clientLevel.getLevelWrapper().getDimensionType().isTheEnd()
|
||||
// FIXME temporary fix
|
||||
// Cave culling is currently broken for any detail level above 0
|
||||
&& renderSource.pos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL
|
||||
&& DhSectionPos.getDetailLevel(renderSource.pos) == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL
|
||||
);
|
||||
|
||||
int skyLightCullingBelow = Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight.get();
|
||||
@@ -131,7 +131,7 @@ public class ColumnRenderBufferBuilder
|
||||
{
|
||||
try
|
||||
{
|
||||
ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(renderSource.pos.getMinCornerLodPos().getCornerBlockPos(), clientLevel.getMinY()));
|
||||
ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(renderSource.pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(renderSource.pos)));
|
||||
try
|
||||
{
|
||||
buffer.uploadBuffer(quadBuilder, GLProxy.getInstance().getGpuUploadMethod());
|
||||
@@ -174,9 +174,9 @@ public class ColumnRenderBufferBuilder
|
||||
boolean enableColumnBufferLimit = Config.Client.Advanced.Debugging.columnBuilderDebugEnable.get();
|
||||
if (enableColumnBufferLimit)
|
||||
{
|
||||
if (renderSource.pos.getDetailLevel() == Config.Client.Advanced.Debugging.columnBuilderDebugDetailLevel.get()
|
||||
&& renderSource.pos.getX() == Config.Client.Advanced.Debugging.columnBuilderDebugXPos.get()
|
||||
&& renderSource.pos.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;
|
||||
}
|
||||
|
||||
+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++); }
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
+16
-9
@@ -38,6 +38,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
|
||||
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;
|
||||
@@ -52,6 +53,8 @@ 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();
|
||||
|
||||
|
||||
|
||||
//==============================//
|
||||
@@ -96,7 +99,7 @@ public class FullDataToRenderDataTransformer
|
||||
*/
|
||||
private static ColumnRenderSource transformCompleteFullDataToColumnData(IDhClientLevel level, FullDataSourceV2 fullDataSource) throws InterruptedException
|
||||
{
|
||||
final DhSectionPos pos = fullDataSource.getPos();
|
||||
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 = ColumnRenderSource.getPooledRenderSource(pos, vertSize, level.getMinY(), true);
|
||||
@@ -109,12 +112,12 @@ 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();
|
||||
|
||||
@@ -154,8 +157,6 @@ public class FullDataToRenderDataTransformer
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<DhSectionPos> brokenPos = new HashSet<>();
|
||||
|
||||
|
||||
// TODO what does this mean?
|
||||
private static void iterateAndConvert(
|
||||
@@ -239,8 +240,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;
|
||||
}
|
||||
|
||||
+93
-84
@@ -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;
|
||||
@@ -72,7 +73,7 @@ public class LodDataBuilder
|
||||
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);
|
||||
DhSectionPos pos = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ);
|
||||
long pos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ);
|
||||
|
||||
FullDataSourceV2 dataSource = FullDataSourceV2.createEmpty(pos);
|
||||
dataSource.isEmpty = false;
|
||||
@@ -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,9 +301,9 @@ 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)));
|
||||
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++)
|
||||
|
||||
@@ -43,11 +43,17 @@ import com.seibel.distanthorizons.coreapi.util.math.Vec3i;
|
||||
*/
|
||||
public enum EDhDirection
|
||||
{
|
||||
/** negative Y */
|
||||
DOWN(0, 1, -1, "down", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.Y, new Vec3i(0, -1, 0)),
|
||||
/** positive Y */
|
||||
UP(1, 0, -1, "up", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.Y, new Vec3i(0, 1, 0)),
|
||||
/** negative Z */
|
||||
NORTH(2, 3, 2, "north", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.Z, new Vec3i(0, 0, -1)),
|
||||
/** positive Z */
|
||||
SOUTH(3, 2, 0, "south", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.Z, new Vec3i(0, 0, 1)),
|
||||
/** negative X */
|
||||
WEST(4, 5, 1, "west", EDhDirection.AxisDirection.NEGATIVE, EDhDirection.Axis.X, new Vec3i(-1, 0, 0)),
|
||||
/** positive X */
|
||||
EAST(5, 4, 3, "east", EDhDirection.AxisDirection.POSITIVE, EDhDirection.Axis.X, new Vec3i(1, 0, 0));
|
||||
|
||||
/**
|
||||
|
||||
+33
-15
@@ -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;
|
||||
@@ -25,12 +26,14 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
// We shouldn't need multiple data source handlers
|
||||
public abstract class AbstractDataSourceHandler
|
||||
<TDataSource extends IDataSource<TDhLevel>,
|
||||
TDTO extends IBaseDTO<DhSectionPos>,
|
||||
TRepo extends AbstractDhRepo<DhSectionPos, TDTO>,
|
||||
TDTO extends IBaseDTO<Long>,
|
||||
TRepo extends AbstractDhRepo<Long, TDTO>,
|
||||
TDhLevel extends IDhLevel>
|
||||
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.
|
||||
@@ -52,8 +55,8 @@ public abstract class AbstractDataSourceHandler
|
||||
* generally just used for debugging,
|
||||
* keeps track of which positions are currently locked.
|
||||
*/
|
||||
public final Set<DhSectionPos> lockedPosSet = ConcurrentHashMap.newKeySet();
|
||||
public final ConcurrentHashMap<DhSectionPos, AtomicInteger> queuedUpdateCountsByPos = new ConcurrentHashMap<>();
|
||||
public final Set<Long> lockedPosSet = ConcurrentHashMap.newKeySet();
|
||||
public final ConcurrentHashMap<Long, AtomicInteger> queuedUpdateCountsByPos = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
protected final ReentrantLock closeLock = new ReentrantLock();
|
||||
@@ -95,10 +98,10 @@ 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);
|
||||
protected abstract TDataSource makeEmptyDataSource(long pos);
|
||||
|
||||
|
||||
|
||||
@@ -112,7 +115,7 @@ public abstract class AbstractDataSourceHandler
|
||||
*
|
||||
* This call is concurrent. I.e. it supports being called by multiple threads at the same time.
|
||||
*/
|
||||
public CompletableFuture<TDataSource> getAsync(DhSectionPos pos)
|
||||
public CompletableFuture<TDataSource> getAsync(long pos)
|
||||
{
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (executor == null || executor.isTerminated())
|
||||
@@ -134,10 +137,10 @@ public abstract class AbstractDataSourceHandler
|
||||
/**
|
||||
* Should only be used in internal file handler methods where we are already running on a file handler thread.
|
||||
* Can return null if the repo is in the process of being shut down
|
||||
* @see AbstractDataSourceHandler#getAsync(DhSectionPos)
|
||||
* @see AbstractDataSourceHandler#getAsync(long)
|
||||
*/
|
||||
@Nullable
|
||||
public TDataSource get(DhSectionPos pos)
|
||||
public TDataSource get(long pos)
|
||||
{
|
||||
TDataSource dataSource = null;
|
||||
try
|
||||
@@ -145,8 +148,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 [" + DhSectionPos.toString(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
|
||||
{
|
||||
@@ -159,7 +177,7 @@ public abstract class AbstractDataSourceHandler
|
||||
catch (InterruptedException ignore) { }
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn("File read Error for pos ["+pos+"], error: "+e.getMessage(), e);
|
||||
LOGGER.warn("File read Error for pos ["+ DhSectionPos.toString(pos)+"], error: "+e.getMessage(), e);
|
||||
}
|
||||
|
||||
return dataSource;
|
||||
@@ -212,7 +230,7 @@ public abstract class AbstractDataSourceHandler
|
||||
* After this method returns the inputData will be written to file.
|
||||
* @param updatePos the position to update
|
||||
*/
|
||||
protected void updateDataSourceAtPos(DhSectionPos updatePos, @NotNull FullDataSourceV2 inputData, boolean lockOnUpdatePos)
|
||||
protected void updateDataSourceAtPos(long updatePos, @NotNull FullDataSourceV2 inputData, boolean lockOnUpdatePos)
|
||||
{
|
||||
boolean methodLocked = false;
|
||||
// a lock is necessary to prevent two threads from writing to the same position at once,
|
||||
@@ -274,7 +292,7 @@ public abstract class AbstractDataSourceHandler
|
||||
//================//
|
||||
|
||||
/** used for debugging to track which positions are queued for updating */
|
||||
private void markUpdateStart(DhSectionPos dataSourcePos)
|
||||
private void markUpdateStart(long dataSourcePos)
|
||||
{
|
||||
this.queuedUpdateCountsByPos.compute(dataSourcePos, (pos, atomicCount) ->
|
||||
{
|
||||
@@ -287,7 +305,7 @@ public abstract class AbstractDataSourceHandler
|
||||
});
|
||||
}
|
||||
/** used for debugging to track which positions are queued for updating */
|
||||
private void markUpdateEnd(DhSectionPos dataSourcePos)
|
||||
private void markUpdateEnd(long dataSourcePos)
|
||||
{
|
||||
this.queuedUpdateCountsByPos.compute(dataSourcePos, (pos, atomicCount) ->
|
||||
{
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.seibel.distanthorizons.core.file;
|
||||
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -27,7 +25,7 @@ public class DataSourcePool<TDataSource extends IDataSource<TDhLevel>, TDhLevel
|
||||
private final ArrayList<TDataSource> pooledDataSources = new ArrayList<>();
|
||||
private final ReentrantLock poolLock = new ReentrantLock();
|
||||
|
||||
private final Function<DhSectionPos, TDataSource> createEmptyDatasourceFunc;
|
||||
private final Function<Long, TDataSource> createEmptyDatasourceFunc;
|
||||
@Nullable
|
||||
private final IPrepPooledDataSourceFunc<TDataSource, TDhLevel> prepDatasourceFunc;
|
||||
|
||||
@@ -37,7 +35,7 @@ public class DataSourcePool<TDataSource extends IDataSource<TDhLevel>, TDhLevel
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public DataSourcePool(Function<DhSectionPos, TDataSource> createEmptyDatasourceFunc, @Nullable IPrepPooledDataSourceFunc<TDataSource, TDhLevel> prepDatasourceFunc)
|
||||
public DataSourcePool(Function<Long, TDataSource> createEmptyDatasourceFunc, @Nullable IPrepPooledDataSourceFunc<TDataSource, TDhLevel> prepDatasourceFunc)
|
||||
{
|
||||
this.createEmptyDatasourceFunc = createEmptyDatasourceFunc;
|
||||
this.prepDatasourceFunc = prepDatasourceFunc;
|
||||
@@ -51,12 +49,12 @@ public class DataSourcePool<TDataSource extends IDataSource<TDhLevel>, TDhLevel
|
||||
|
||||
/**
|
||||
* Returns a cleared data source.
|
||||
* @see DataSourcePool#getPooledSource(DhSectionPos, boolean)
|
||||
* @see DataSourcePool#getPooledSource(long, boolean)
|
||||
*/
|
||||
public TDataSource getPooledSource(DhSectionPos pos) { return this.getPooledSource(pos, true);}
|
||||
public TDataSource getPooledSource(long pos) { return this.getPooledSource(pos, true);}
|
||||
|
||||
/** @return an empty data source if non are cached */
|
||||
public TDataSource getPooledSource(DhSectionPos pos, boolean clearData)
|
||||
public TDataSource getPooledSource(long pos, boolean clearData)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -133,7 +131,7 @@ public class DataSourcePool<TDataSource extends IDataSource<TDhLevel>, TDhLevel
|
||||
public interface IPrepPooledDataSourceFunc<TDataSource extends IDataSource<TDhLevel>, TDhLevel extends IDhLevel>
|
||||
{
|
||||
/** @param clearData will be false if the data will be immediately overwritten anyway */
|
||||
void prepDataSource(DhSectionPos pos, boolean clearData, TDataSource dataSource);
|
||||
void prepDataSource(long pos, boolean clearData, TDataSource dataSource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.seibel.distanthorizons.core.file;
|
||||
import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.dto.IBaseDTO;
|
||||
|
||||
/**
|
||||
@@ -13,9 +12,9 @@ import com.seibel.distanthorizons.core.sql.dto.IBaseDTO;
|
||||
*
|
||||
* @param <TDhLevel> there are times when we need specifically a client level vs a more generic level
|
||||
*/
|
||||
public interface IDataSource<TDhLevel extends IDhLevel> extends IBaseDTO<DhSectionPos>, AutoCloseable
|
||||
public interface IDataSource<TDhLevel extends IDhLevel> extends IBaseDTO<Long>, AutoCloseable
|
||||
{
|
||||
DhSectionPos getPos();
|
||||
Long getPos();
|
||||
|
||||
/** @return true if the data was changed */
|
||||
boolean update(FullDataSourceV2 chunkData, TDhLevel level);
|
||||
|
||||
+3
-4
@@ -2,7 +2,6 @@ package com.seibel.distanthorizons.core.file.fullDatafile;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -22,8 +21,8 @@ public class DelayedFullDataSourceSaveCache
|
||||
private static final Timer DELAY_UPDATE_TIMER = TimerUtil.CreateTimer("Delayed Full Datasource Save Timer");
|
||||
|
||||
|
||||
public final ConcurrentHashMap<DhSectionPos, FullDataSourceV2> dataSourceByPosition = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<DhSectionPos, TimerTask> saveTimerTasksBySectionPos = new ConcurrentHashMap<>();
|
||||
public final ConcurrentHashMap<Long, FullDataSourceV2> dataSourceByPosition = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Long, TimerTask> saveTimerTasksBySectionPos = new ConcurrentHashMap<>();
|
||||
|
||||
private final ISaveDataSourceFunc onSaveTimeoutFunc;
|
||||
private final int saveDelayInMs;
|
||||
@@ -48,7 +47,7 @@ public class DelayedFullDataSourceSaveCache
|
||||
|
||||
public void queueDataSourceForUpdateAndSave(FullDataSourceV2 inputDataSource)
|
||||
{
|
||||
DhSectionPos dataSourcePos = inputDataSource.getPos();
|
||||
long dataSourcePos = inputDataSource.getPos();
|
||||
this.dataSourceByPosition.compute(dataSourcePos, (inputPos, temporaryDataSource) ->
|
||||
{
|
||||
if (temporaryDataSource == null)
|
||||
|
||||
+17
-8
@@ -7,7 +7,9 @@ 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 it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -72,7 +74,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);
|
||||
@@ -91,7 +93,7 @@ public class FullDataSourceProviderV1<TDhLevel extends IDhLevel>
|
||||
*
|
||||
* This call is concurrent. I.e. it supports being called by multiple threads at the same time.
|
||||
*/
|
||||
public CompletableFuture<FullDataSourceV1> getAsync(DhSectionPos pos)
|
||||
public CompletableFuture<FullDataSourceV1> getAsync(long pos)
|
||||
{
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (executor == null || executor.isTerminated())
|
||||
@@ -112,10 +114,10 @@ public class FullDataSourceProviderV1<TDhLevel extends IDhLevel>
|
||||
/**
|
||||
* Should only be used in internal file handler methods where we are already running on a file handler thread.
|
||||
* Can return null.
|
||||
* @see FullDataSourceProviderV1#getAsync(DhSectionPos)
|
||||
* @see FullDataSourceProviderV1#getAsync(long)
|
||||
*/
|
||||
@Nullable
|
||||
public FullDataSourceV1 get(DhSectionPos pos)
|
||||
public FullDataSourceV1 get(Long pos)
|
||||
{
|
||||
FullDataSourceV1 dataSource = null;
|
||||
try
|
||||
@@ -128,9 +130,16 @@ 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 ["+ DhSectionPos.toString(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);
|
||||
LOGGER.warn("File read Error for pos ["+ DhSectionPos.toString(pos)+"], error: "+e.getMessage(), e);
|
||||
}
|
||||
|
||||
return dataSource;
|
||||
@@ -148,10 +157,10 @@ public class FullDataSourceProviderV1<TDhLevel extends IDhLevel>
|
||||
{
|
||||
ArrayList<FullDataSourceV1> dataSourceList = new ArrayList<>();
|
||||
|
||||
ArrayList<DhSectionPos> migrationPosList = this.repo.getPositionsToMigrate(limit);
|
||||
LongArrayList migrationPosList = this.repo.getPositionsToMigrate(limit);
|
||||
for (int i = 0; i < migrationPosList.size(); i++)
|
||||
{
|
||||
DhSectionPos pos = migrationPosList.get(i);
|
||||
Long pos = migrationPosList.getLong(i);
|
||||
FullDataSourceV1 dataSource = this.get(pos);
|
||||
if (dataSource != null)
|
||||
{
|
||||
@@ -162,7 +171,7 @@ public class FullDataSourceProviderV1<TDhLevel extends IDhLevel>
|
||||
return dataSourceList;
|
||||
}
|
||||
|
||||
public void markMigrationFailed(DhSectionPos pos) { ((FullDataSourceV1Repo) this.repo).markMigrationFailed(pos); }
|
||||
public void markMigrationFailed(long pos) { ((FullDataSourceV1Repo) this.repo).markMigrationFailed(pos); }
|
||||
|
||||
|
||||
|
||||
|
||||
+58
-18
@@ -35,8 +35,10 @@ 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 it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -48,7 +50,6 @@ import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Handles reading/writing {@link FullDataSourceV2}
|
||||
@@ -87,6 +88,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;
|
||||
|
||||
@@ -94,7 +97,7 @@ public class FullDataSourceProviderV2
|
||||
* Tracks which positions are currently being updated
|
||||
* to prevent duplicate concurrent updates.
|
||||
*/
|
||||
public final Set<DhSectionPos> parentUpdatingPosSet = ConcurrentHashMap.newKeySet();
|
||||
public final Set<Long> parentUpdatingPosSet = ConcurrentHashMap.newKeySet();
|
||||
|
||||
// TODO only run thread if modifications happened recently
|
||||
/**
|
||||
@@ -165,11 +168,11 @@ 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
|
||||
protected FullDataSourceV2 makeEmptyDataSource(DhSectionPos pos) { return FullDataSourceV2.DATA_SOURCE_POOL.getPooledSource(pos, true); }
|
||||
protected FullDataSourceV2 makeEmptyDataSource(long pos) { return FullDataSourceV2.DATA_SOURCE_POOL.getPooledSource(pos, true); }
|
||||
|
||||
|
||||
|
||||
@@ -198,13 +201,13 @@ public class FullDataSourceProviderV2
|
||||
&& this.parentUpdatingPosSet.size() < MAX_UPDATE_TASK_COUNT)
|
||||
{
|
||||
// get the positions that need to be applied to their parents
|
||||
ArrayList<DhSectionPos> parentUpdatePosList = this.repo.getPositionsToUpdate(MAX_UPDATE_TASK_COUNT);
|
||||
LongArrayList parentUpdatePosList = this.repo.getPositionsToUpdate(MAX_UPDATE_TASK_COUNT);
|
||||
|
||||
// combine updates together based on their parent
|
||||
HashMap<DhSectionPos, HashSet<DhSectionPos>> updatePosByParentPos = new HashMap<>();
|
||||
for (DhSectionPos pos : parentUpdatePosList)
|
||||
HashMap<Long, HashSet<Long>> updatePosByParentPos = new HashMap<>();
|
||||
for (Long pos : parentUpdatePosList)
|
||||
{
|
||||
updatePosByParentPos.compute(pos.getParentPos(), (parentPos, updatePosSet) ->
|
||||
updatePosByParentPos.compute(DhSectionPos.getParentPos(pos), (parentPos, updatePosSet) ->
|
||||
{
|
||||
if (updatePosSet == null)
|
||||
{
|
||||
@@ -216,7 +219,7 @@ public class FullDataSourceProviderV2
|
||||
}
|
||||
|
||||
// queue the updates
|
||||
for (DhSectionPos parentUpdatePos : updatePosByParentPos.keySet())
|
||||
for (Long parentUpdatePos : updatePosByParentPos.keySet())
|
||||
{
|
||||
// stop if there are already a bunch of updates queued
|
||||
if (this.parentUpdatingPosSet.size() > MAX_UPDATE_TASK_COUNT
|
||||
@@ -243,7 +246,7 @@ public class FullDataSourceProviderV2
|
||||
this.lockedPosSet.add(parentUpdatePos);
|
||||
|
||||
// apply each child pos to the parent
|
||||
for (DhSectionPos childPos : updatePosByParentPos.get(parentUpdatePos))
|
||||
for (Long childPos : updatePosByParentPos.get(parentUpdatePos))
|
||||
{
|
||||
ReentrantLock childReadLock = this.updateLockProvider.getLock(childPos);
|
||||
try
|
||||
@@ -263,7 +266,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
|
||||
{
|
||||
@@ -330,7 +333,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...");
|
||||
@@ -381,7 +384,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
|
||||
@@ -420,7 +423,7 @@ public class FullDataSourceProviderV2
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DhSectionPos migrationPos = legacyDataSource.getPos();
|
||||
Long migrationPos = legacyDataSource.getPos();
|
||||
LOGGER.warn("Unexpected issue migrating data source at pos " + migrationPos + ". Error: " + e.getMessage(), e);
|
||||
this.legacyFileHandler.markMigrationFailed(migrationPos);
|
||||
}
|
||||
@@ -452,11 +455,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
|
||||
@@ -471,6 +476,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) //
|
||||
@@ -515,18 +555,18 @@ public class FullDataSourceProviderV2
|
||||
* an empty array if all positions were generated
|
||||
*/
|
||||
@Nullable
|
||||
public ArrayList<DhSectionPos> getPositionsToRetrieve(DhSectionPos pos) { return null; }
|
||||
public LongArrayList getPositionsToRetrieve(Long pos) { return null; }
|
||||
/**
|
||||
* Returns how many positions could potentially be generated for this position assuming the position is empty.
|
||||
* Used when estimating the total number of retrieval requests.
|
||||
*/
|
||||
public int getMaxPossibleRetrievalPositionCountForPos(DhSectionPos pos) { return -1; }
|
||||
public int getMaxPossibleRetrievalPositionCountForPos(Long pos) { return -1; }
|
||||
|
||||
/** @return true if the position was queued, false if not */
|
||||
public boolean queuePositionForRetrieval(DhSectionPos genPos) { return false; }
|
||||
public boolean queuePositionForRetrieval(Long genPos) { return false; }
|
||||
|
||||
/** does nothing if the given position isn't present in the queue */
|
||||
public void removeRetrievalRequestIf(Function<DhSectionPos, Boolean> removeIf) { }
|
||||
public void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf) { }
|
||||
|
||||
public void clearRetrievalQueue() { }
|
||||
|
||||
|
||||
+31
-17
@@ -34,6 +34,7 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.*;
|
||||
@@ -41,12 +42,19 @@ import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 implements IDebugRenderable
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
/**
|
||||
* Having this number too high causes the system to become overwhelmed by
|
||||
* world gen requests and other jobs won't be done. <br>
|
||||
* IE: LODs won't update or render because world gen is hogging the CPU.
|
||||
* <br><br>
|
||||
* TODO this should be dynamically allocated based on CPU load
|
||||
* and abilities.
|
||||
*/
|
||||
public static final int MAX_WORLD_GEN_REQUESTS_PER_THREAD = 20;
|
||||
|
||||
|
||||
@@ -108,7 +116,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
}
|
||||
|
||||
// TODO only fire after the section has finished generated or once every X seconds
|
||||
private void fireOnGenPosSuccessListeners(DhSectionPos pos)
|
||||
private void fireOnGenPosSuccessListeners(long pos)
|
||||
{
|
||||
// fire the event listeners
|
||||
for (IOnWorldGenCompleteListener listener : this.onWorldGenTaskCompleteListeners)
|
||||
@@ -172,6 +180,15 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
}
|
||||
|
||||
|
||||
ThreadPoolExecutor fileExecutor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||
if (fileExecutor == null || fileExecutor.getQueue().size() >= MAX_UPDATE_TASK_COUNT / 2)
|
||||
{
|
||||
// don't queue additional world gen requests if the file handler is overwhelmed,
|
||||
// otherwise LODs may not load in properly
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int maxQueueCount = MAX_WORLD_GEN_REQUESTS_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads.get();
|
||||
|
||||
if (this.delayedFullDataSourceSaveCache.getUnsavedCount() >= maxQueueCount)
|
||||
@@ -188,7 +205,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean queuePositionForRetrieval(DhSectionPos genPos)
|
||||
public boolean queuePositionForRetrieval(Long genPos)
|
||||
{
|
||||
IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||
if (worldGenQueue == null)
|
||||
@@ -197,14 +214,14 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
}
|
||||
|
||||
GenTask genTask = new GenTask(genPos);
|
||||
CompletableFuture<WorldGenResult> worldGenFuture = worldGenQueue.submitGenTask(genPos, (byte) (genPos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), genTask);
|
||||
CompletableFuture<WorldGenResult> worldGenFuture = worldGenQueue.submitGenTask(genPos, (byte) (DhSectionPos.getDetailLevel(genPos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), genTask);
|
||||
worldGenFuture.whenComplete((genTaskResult, ex) -> this.onWorldGenTaskComplete(genTaskResult, ex));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRetrievalRequestIf(Function<DhSectionPos, Boolean> removeIf)
|
||||
public void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf)
|
||||
{
|
||||
IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||
if (worldGenQueue != null)
|
||||
@@ -221,7 +238,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
|
||||
|
||||
@Override
|
||||
public ArrayList<DhSectionPos> getPositionsToRetrieve(DhSectionPos pos)
|
||||
public LongArrayList getPositionsToRetrieve(Long pos)
|
||||
{
|
||||
IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||
if (worldGenQueue == null)
|
||||
@@ -251,7 +268,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
|
||||
if (positionFullyGenerated)
|
||||
{
|
||||
return new ArrayList<>();
|
||||
return new LongArrayList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,9 +277,9 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
|
||||
// this section is missing one or more columns, queue the missing ones for generation.
|
||||
// TODO speed up this logic by only checking ungenerated columns
|
||||
ArrayList<DhSectionPos> generationList = new ArrayList<>();
|
||||
LongArrayList generationList = new LongArrayList();
|
||||
byte minGeneratorSectionDetailLevel = (byte) (worldGenQueue.highestDataDetail() + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||
pos.forEachChildAtDetailLevel(minGeneratorSectionDetailLevel, (genPos) ->
|
||||
DhSectionPos.forEachChildAtDetailLevel(pos, minGeneratorSectionDetailLevel, (genPos) ->
|
||||
{
|
||||
if (!this.repo.existsWithKey(genPos))
|
||||
{
|
||||
@@ -319,7 +336,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPossibleRetrievalPositionCountForPos(DhSectionPos pos)
|
||||
public int getMaxPossibleRetrievalPositionCountForPos(Long pos)
|
||||
{
|
||||
IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||
if (worldGenQueue == null)
|
||||
@@ -328,7 +345,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
}
|
||||
|
||||
int minGeneratorSectionDetailLevel = worldGenQueue.highestDataDetail() + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
int detailLevelDiff = pos.getDetailLevel() - minGeneratorSectionDetailLevel;
|
||||
int detailLevelDiff = DhSectionPos.getDetailLevel(pos) - minGeneratorSectionDetailLevel;
|
||||
|
||||
return BitShiftUtil.powerOfTwo(detailLevelDiff);
|
||||
}
|
||||
@@ -358,12 +375,9 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
// TODO may not be needed
|
||||
private class GenTask implements IWorldGenTaskTracker
|
||||
{
|
||||
private final DhSectionPos pos;
|
||||
private final long pos;
|
||||
|
||||
public GenTask(DhSectionPos pos)
|
||||
{
|
||||
this.pos = pos;
|
||||
}
|
||||
public GenTask(long pos) { this.pos = pos; }
|
||||
|
||||
|
||||
|
||||
@@ -389,7 +403,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
public interface IOnWorldGenCompleteListener
|
||||
{
|
||||
/** Fired whenever a section has completed generating */
|
||||
void onWorldGenTaskComplete(DhSectionPos pos);
|
||||
void onWorldGenTaskComplete(long pos);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+12
-1
@@ -57,7 +57,18 @@ public class SubDimCompare implements Comparable<SubDimCompare>
|
||||
}
|
||||
|
||||
/** returns a number between 0 (no equal datapoint) and 1 (totally equal) */
|
||||
public double getPercentEqual() { return (double) this.equalDataPoints / (double) this.totalDataPoints; }
|
||||
public double getPercentEqual()
|
||||
{
|
||||
// its possible the comparison didn't find any data points
|
||||
if (this.totalDataPoints != 0)
|
||||
{
|
||||
return (double) this.equalDataPoints / (double) this.totalDataPoints;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
+19
-8
@@ -190,7 +190,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable
|
||||
}
|
||||
FullDataSourceV2 newChunkSizedFullDataView = FullDataSourceV2.createFromChunk(newlyLoadedChunk);
|
||||
// convert to a data source for easier comparing
|
||||
FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(new DhSectionPos(this.playerData.playerBlockPos));
|
||||
FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encode(this.playerData.playerBlockPos));
|
||||
newDataSource.update(newChunkSizedFullDataView);
|
||||
|
||||
|
||||
@@ -208,21 +208,25 @@ 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);
|
||||
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))
|
||||
{
|
||||
continue;
|
||||
testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encode(this.playerData.playerBlockPos)).join();
|
||||
if (testFullDataSource == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// confirm both data sources have the same section pos
|
||||
DhSectionPos newSectionChunkPos = newDataSource.getPos().convertNewToDetailLevel(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL);
|
||||
DhSectionPos testSectionChunkPos = testFullDataSource.getPos().convertNewToDetailLevel(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL);
|
||||
LodUtil.assertTrue(newSectionChunkPos.equals(testSectionChunkPos), "data source positions don't match");
|
||||
long newSectionChunkPos = DhSectionPos.convertToDetailLevel(newDataSource.getPos(), DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL);
|
||||
long testSectionChunkPos = DhSectionPos.convertToDetailLevel(testFullDataSource.getPos(), DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL);
|
||||
LodUtil.assertTrue(newSectionChunkPos == testSectionChunkPos, "data source positions don't match");
|
||||
|
||||
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-3
@@ -27,7 +27,6 @@ import com.seibel.distanthorizons.core.render.LodQuadTree;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Used to track what full data sources the system currently
|
||||
@@ -80,9 +79,9 @@ public interface IFullDataSourceRetrievalQueue extends Closeable
|
||||
* Generally the retrieval queue should be fairly small, so its faster to iterate over the existing list
|
||||
* and check if each one is valid vs dumbly attempting to remove every position that just went out of range.
|
||||
*/
|
||||
void removeRetrievalRequestIf(Function<DhSectionPos, Boolean> removeIf);
|
||||
void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf);
|
||||
|
||||
CompletableFuture<WorldGenResult> submitGenTask(DhSectionPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker);
|
||||
CompletableFuture<WorldGenResult> submitGenTask(long pos, byte requiredDataDetail, IWorldGenTaskTracker tracker);
|
||||
|
||||
|
||||
|
||||
|
||||
+28
-23
@@ -36,18 +36,19 @@ 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;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDebugRenderable
|
||||
{
|
||||
@@ -57,9 +58,9 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
private final IDhApiWorldGenerator generator;
|
||||
|
||||
/** contains the positions that need to be generated */
|
||||
private final ConcurrentHashMap<DhSectionPos, WorldGenTask> waitingTasks = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Long, WorldGenTask> waitingTasks = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentHashMap<DhSectionPos, InProgressWorldGenTaskGroup> inProgressGenTasksByLodPos = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Long, InProgressWorldGenTaskGroup> inProgressGenTasksByLodPos = new ConcurrentHashMap<>();
|
||||
|
||||
// granularity is the detail level for batching world generator requests together
|
||||
public final byte maxGranularity;
|
||||
@@ -88,8 +89,8 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
// debug variables to test for duplicate world generator requests //
|
||||
/** limits how many of the previous world gen requests we should track */
|
||||
private static final int MAX_ALREADY_GENERATED_COUNT = 100;
|
||||
private final HashMap<DhSectionPos, StackTraceElement[]> alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT);
|
||||
private final Queue<DhSectionPos> alreadyGeneratedPosQueue = new LinkedList<>();
|
||||
private final HashMap<Long, StackTraceElement[]> alreadyGeneratedPosHashSet = new HashMap<>(MAX_ALREADY_GENERATED_COUNT);
|
||||
private final LongArrayFIFOQueue alreadyGeneratedPosQueue = new LongArrayFIFOQueue();
|
||||
|
||||
/** just used for rendering to the F3 menu */
|
||||
private int estimatedTotalTaskCount = 0;
|
||||
@@ -130,7 +131,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
//=================//
|
||||
|
||||
@Override
|
||||
public CompletableFuture<WorldGenResult> submitGenTask(DhSectionPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker)
|
||||
public CompletableFuture<WorldGenResult> submitGenTask(long pos, byte requiredDataDetail, IWorldGenTaskTracker tracker)
|
||||
{
|
||||
// the generator is shutting down, don't add new tasks
|
||||
if (this.generatorClosingFuture != null)
|
||||
@@ -150,7 +151,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
}
|
||||
|
||||
// Assert that the data at least can fill in 1 single ChunkSizedFullDataAccessor
|
||||
LodUtil.assertTrue(pos.getDetailLevel() > requiredDataDetail + LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
LodUtil.assertTrue(DhSectionPos.getDetailLevel(pos) > requiredDataDetail + LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
|
||||
|
||||
CompletableFuture<WorldGenResult> future = new CompletableFuture<>();
|
||||
@@ -159,11 +160,11 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRetrievalRequestIf(Function<DhSectionPos, Boolean> removeIf)
|
||||
public void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf)
|
||||
{
|
||||
this.waitingTasks.forEachKey(100, (genPos) ->
|
||||
{
|
||||
if (removeIf.apply(genPos))
|
||||
if (removeIf.accept(genPos))
|
||||
{
|
||||
this.waitingTasks.remove(genPos);
|
||||
}
|
||||
@@ -253,7 +254,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
|
||||
|
||||
Mapper closestTaskMap = this.waitingTasks.reduceEntries(1024,
|
||||
entry -> new Mapper(entry.getValue(), entry.getValue().pos.getSectionBBoxPos().getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D())),
|
||||
entry -> new Mapper(entry.getValue(), DhSectionPos.getSectionBBoxPos(entry.getValue().pos).getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D())),
|
||||
(aMapper, bMapper) -> aMapper.dist < bMapper.dist ? aMapper : bMapper);
|
||||
|
||||
if (closestTaskMap == null)
|
||||
@@ -306,14 +307,14 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
|
||||
// split up the task and add each one to the tree
|
||||
LinkedList<CompletableFuture<WorldGenResult>> childFutures = new LinkedList<>();
|
||||
DhSectionPos sectionPos = new DhSectionPos(closestTask.pos.getDetailLevel(), closestTask.pos.getX(), closestTask.pos.getZ());
|
||||
long sectionPos = closestTask.pos;
|
||||
WorldGenTask finalClosestTask = closestTask;
|
||||
sectionPos.forEachChild((childDhSectionPos) ->
|
||||
DhSectionPos.forEachChild(sectionPos, (childDhSectionPos) ->
|
||||
{
|
||||
CompletableFuture<WorldGenResult> newFuture = new CompletableFuture<>();
|
||||
childFutures.add(newFuture);
|
||||
|
||||
WorldGenTask newGenTask = new WorldGenTask(childDhSectionPos, childDhSectionPos.getDetailLevel(), finalClosestTask.taskTracker, newFuture);
|
||||
WorldGenTask newGenTask = new WorldGenTask(childDhSectionPos, DhSectionPos.getDetailLevel(childDhSectionPos), finalClosestTask.taskTracker, newFuture);
|
||||
this.waitingTasks.put(newGenTask.pos, newGenTask);
|
||||
});
|
||||
|
||||
@@ -328,12 +329,12 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
private boolean tryStartingWorldGenTaskGroup(InProgressWorldGenTaskGroup newTaskGroup)
|
||||
{
|
||||
byte taskDetailLevel = newTaskGroup.group.dataDetail;
|
||||
DhSectionPos taskPos = newTaskGroup.group.pos;
|
||||
byte granularity = (byte) (taskPos.getDetailLevel() - taskDetailLevel);
|
||||
long taskPos = newTaskGroup.group.pos;
|
||||
byte granularity = (byte) (DhSectionPos.getDetailLevel(taskPos) - taskDetailLevel);
|
||||
LodUtil.assertTrue(granularity >= this.minGranularity && granularity <= this.maxGranularity);
|
||||
LodUtil.assertTrue(taskDetailLevel >= this.highestDataDetail && taskDetailLevel <= this.lowestDataDetail);
|
||||
|
||||
DhChunkPos chunkPosMin = new DhChunkPos(taskPos.getSectionBBoxPos().getCornerBlockPos());
|
||||
DhChunkPos chunkPosMin = new DhChunkPos(DhSectionPos.getSectionBBoxPos(taskPos).getCornerBlockPos());
|
||||
|
||||
// check if this is a duplicate generation task
|
||||
if (this.alreadyGeneratedPosHashSet.containsKey(newTaskGroup.group.pos))
|
||||
@@ -342,16 +343,16 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
//LOGGER.trace("Duplicate generation section " + taskPos + " with granularity [" + granularity + "] at " + chunkPosMin + ". Skipping...");
|
||||
|
||||
// sending a success result is necessary to make sure the render sections are reloaded correctly
|
||||
newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.getX(), taskPos.getZ()))));
|
||||
newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(DhSectionPos.encode(granularity, DhSectionPos.getX(taskPos), DhSectionPos.getZ(taskPos)))));
|
||||
return false;
|
||||
}
|
||||
this.alreadyGeneratedPosHashSet.put(newTaskGroup.group.pos, Thread.currentThread().getStackTrace());
|
||||
this.alreadyGeneratedPosQueue.add(newTaskGroup.group.pos);
|
||||
this.alreadyGeneratedPosQueue.enqueue(newTaskGroup.group.pos);
|
||||
|
||||
// remove extra tracked duplicate positions
|
||||
while (this.alreadyGeneratedPosQueue.size() > MAX_ALREADY_GENERATED_COUNT)
|
||||
{
|
||||
DhSectionPos posToRemove = this.alreadyGeneratedPosQueue.poll();
|
||||
long posToRemove = this.alreadyGeneratedPosQueue.dequeueLong();
|
||||
this.alreadyGeneratedPosHashSet.remove(posToRemove);
|
||||
}
|
||||
|
||||
@@ -379,8 +380,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
}
|
||||
else
|
||||
{
|
||||
//LOGGER.info("Section generation at "+pos+" completed");
|
||||
newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.getX(), taskPos.getZ()))));
|
||||
newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(DhSectionPos.encode(granularity, DhSectionPos.getX(taskPos), DhSectionPos.getZ(taskPos)))));
|
||||
}
|
||||
boolean worked = this.inProgressGenTasksByLodPos.remove(taskPos, newTaskGroup);
|
||||
LodUtil.assertTrue(worked);
|
||||
@@ -469,6 +469,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);
|
||||
@@ -618,9 +623,9 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
private boolean canGeneratePos(byte worldGenTaskGroupDetailLevel /*when in doubt use 0*/ , DhSectionPos taskPos)
|
||||
private boolean canGeneratePos(byte worldGenTaskGroupDetailLevel /*when in doubt use 0*/ , long taskPos)
|
||||
{
|
||||
byte granularity = (byte) (taskPos.getDetailLevel() - worldGenTaskGroupDetailLevel);
|
||||
byte granularity = (byte) (DhSectionPos.getDetailLevel(taskPos) - worldGenTaskGroupDetailLevel);
|
||||
return (granularity >= this.minGranularity && granularity <= this.maxGranularity);
|
||||
}
|
||||
|
||||
|
||||
+5
-7
@@ -19,8 +19,6 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.generation.tasks;
|
||||
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -30,15 +28,15 @@ public class WorldGenResult
|
||||
/** true if terrain was generated */
|
||||
public final boolean success;
|
||||
/** the position that was generated, will be null if nothing was generated */
|
||||
public final DhSectionPos pos;
|
||||
public final long pos;
|
||||
/** if a position is too high detail for world generator to handle it, these futures are for its 4 children positions after being split up. */
|
||||
public final LinkedList<CompletableFuture<WorldGenResult>> childFutures = new LinkedList<>();
|
||||
|
||||
|
||||
public static WorldGenResult CreateSplit(Collection<CompletableFuture<WorldGenResult>> siblingFutures) { return new WorldGenResult(false, null, siblingFutures); }
|
||||
public static WorldGenResult CreateFail() { return new WorldGenResult(false, null, null); }
|
||||
public static WorldGenResult CreateSuccess(DhSectionPos pos) { return new WorldGenResult(true, pos, null); }
|
||||
private WorldGenResult(boolean success, DhSectionPos pos, Collection<CompletableFuture<WorldGenResult>> childFutures)
|
||||
public static WorldGenResult CreateSplit(Collection<CompletableFuture<WorldGenResult>> siblingFutures) { return new WorldGenResult(false, 0, siblingFutures); }
|
||||
public static WorldGenResult CreateFail() { return new WorldGenResult(false, 0, null); }
|
||||
public static WorldGenResult CreateSuccess(long pos) { return new WorldGenResult(true, pos, null); }
|
||||
private WorldGenResult(boolean success, long pos, Collection<CompletableFuture<WorldGenResult>> childFutures)
|
||||
{
|
||||
this.success = success;
|
||||
this.pos = pos;
|
||||
|
||||
+2
-5
@@ -19,9 +19,6 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.generation.tasks;
|
||||
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
@@ -30,14 +27,14 @@ import java.util.concurrent.CompletableFuture;
|
||||
*/
|
||||
public final class WorldGenTask
|
||||
{
|
||||
public final DhSectionPos pos;
|
||||
public final long pos;
|
||||
public final byte dataDetailLevel;
|
||||
public final IWorldGenTaskTracker taskTracker;
|
||||
public final CompletableFuture<WorldGenResult> future;
|
||||
|
||||
|
||||
|
||||
public WorldGenTask(DhSectionPos pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture<WorldGenResult> future)
|
||||
public WorldGenTask(long pos, byte dataDetail, IWorldGenTaskTracker taskTracker, CompletableFuture<WorldGenResult> future)
|
||||
{
|
||||
this.dataDetailLevel = dataDetail;
|
||||
this.pos = pos;
|
||||
|
||||
+2
-3
@@ -20,7 +20,6 @@
|
||||
package com.seibel.distanthorizons.core.generation.tasks;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
@@ -33,14 +32,14 @@ import java.util.function.Consumer;
|
||||
@Deprecated // TODO look into how these are used and if they should continue to be used
|
||||
public final class WorldGenTaskGroup
|
||||
{
|
||||
public final DhSectionPos pos;
|
||||
public final long pos;
|
||||
public byte dataDetail;
|
||||
/** Only accessed by the generator polling thread */
|
||||
public final LinkedList<WorldGenTask> worldGenTasks = new LinkedList<>();
|
||||
|
||||
|
||||
|
||||
public WorldGenTaskGroup(DhSectionPos pos, byte dataDetail)
|
||||
public WorldGenTaskGroup(long pos, byte dataDetail)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.dataDetail = dataDetail;
|
||||
|
||||
@@ -37,7 +37,7 @@ public abstract class AbstractDhLevel implements IDhLevel
|
||||
|
||||
protected final DelayedFullDataSourceSaveCache delayedFullDataSourceSaveCache = new DelayedFullDataSourceSaveCache(this::onDataSourceSave, 2_000);
|
||||
/** contains the {@link DhChunkPos} for each {@link DhSectionPos} that are queued to save via {@link AbstractDhLevel#delayedFullDataSourceSaveCache} */
|
||||
protected final ConcurrentHashMap<DhSectionPos, HashSet<DhChunkPos>> updatedChunkPosSetBySectionPos = new ConcurrentHashMap<>();
|
||||
protected final ConcurrentHashMap<Long, HashSet<DhChunkPos>> updatedChunkPosSetBySectionPos = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.render.LodQuadTree;
|
||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
|
||||
@@ -313,7 +312,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
|
||||
}
|
||||
}
|
||||
|
||||
public void reloadPos(DhSectionPos pos)
|
||||
public void reloadPos(long pos)
|
||||
{
|
||||
ClientRenderState clientRenderState = this.ClientRenderStateRef.get();
|
||||
if (clientRenderState != null && clientRenderState.quadtree != null)
|
||||
|
||||
@@ -28,7 +28,6 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
@@ -199,7 +198,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWorldGenTaskComplete(DhSectionPos pos)
|
||||
public void onWorldGenTaskComplete(long pos)
|
||||
{
|
||||
DebugRenderer.makeParticle(
|
||||
new DebugRenderer.BoxParticle(
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
|
||||
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
@@ -105,7 +104,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel
|
||||
public boolean hasSkyLight() { return this.serverLevelWrapper.hasSkyLight(); }
|
||||
|
||||
@Override
|
||||
public void onWorldGenTaskComplete(DhSectionPos pos)
|
||||
public void onWorldGenTaskComplete(long pos)
|
||||
{
|
||||
//TODO: Send packet to client
|
||||
}
|
||||
|
||||
@@ -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); }
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public class DhLodPos implements Comparable<DhLodPos>
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
}
|
||||
public DhLodPos(DhSectionPos sectionPos) { this(sectionPos.getDetailLevel(), sectionPos.getX(), sectionPos.getZ()); }
|
||||
public DhLodPos(long sectionPos) { this(DhSectionPos.getDetailLevel(sectionPos), DhSectionPos.getX(sectionPos), DhSectionPos.getZ(sectionPos)); }
|
||||
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ public class DhLodPos implements Comparable<DhLodPos>
|
||||
* @param sectionDetailLevel This is different from the normal LOD Detail level, see {@link DhSectionPos} for more information
|
||||
* @throws IllegalArgumentException if this position's detail level is lower than the output detail level
|
||||
*/
|
||||
public DhSectionPos getSectionPosWithSectionDetailLevel(byte sectionDetailLevel) throws IllegalArgumentException
|
||||
public long getSectionPosWithSectionDetailLevel(byte sectionDetailLevel) throws IllegalArgumentException
|
||||
{
|
||||
if (sectionDetailLevel < this.detailLevel)
|
||||
{
|
||||
@@ -174,7 +174,7 @@ public class DhLodPos implements Comparable<DhLodPos>
|
||||
|
||||
DhLodPos lodPos = new DhLodPos(this.detailLevel, this.x, this.z);
|
||||
lodPos = lodPos.convertToDetailLevel(sectionDetailLevel);
|
||||
return new DhSectionPos(lodPos.detailLevel, lodPos.x, lodPos.z);
|
||||
return DhSectionPos.encode(lodPos.detailLevel, lodPos.x, lodPos.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,12 +20,10 @@
|
||||
package com.seibel.distanthorizons.core.pos;
|
||||
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
/**
|
||||
* The position object used to define LOD objects in the quad trees. <br><br>
|
||||
@@ -49,19 +47,29 @@ public class DhSectionPos
|
||||
* The lowest detail level a Section position can hold.
|
||||
* This section DetailLevel holds 64 x 64 Block level (detail level 0) LODs.
|
||||
*/
|
||||
public final static byte SECTION_MINIMUM_DETAIL_LEVEL = 6;
|
||||
public static final byte SECTION_MINIMUM_DETAIL_LEVEL = 6;
|
||||
|
||||
public final static byte SECTION_BLOCK_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.BLOCK_DETAIL_LEVEL;
|
||||
public final static byte SECTION_CHUNK_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.CHUNK_DETAIL_LEVEL;
|
||||
public final static byte SECTION_REGION_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.REGION_DETAIL_LEVEL;
|
||||
public static final byte SECTION_BLOCK_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.BLOCK_DETAIL_LEVEL;
|
||||
public static final byte SECTION_CHUNK_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.CHUNK_DETAIL_LEVEL;
|
||||
public static final byte SECTION_REGION_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.REGION_DETAIL_LEVEL;
|
||||
|
||||
|
||||
protected byte detailLevel;
|
||||
|
||||
/** in a sectionDetailLevel grid */
|
||||
protected int x;
|
||||
/** in a sectionDetailLevel grid */
|
||||
protected int z;
|
||||
public static final int DETAIL_LEVEL_WIDTH = 8;
|
||||
public static final int X_POS_WIDTH = 28;
|
||||
public static final int Z_POS_WIDTH = 28;
|
||||
public static final int X_POS_MISSING_WIDTH = 32 - 28;
|
||||
public static final int Z_POS_MISSING_WIDTH = 32 - 28;
|
||||
|
||||
|
||||
public static final int DETAIL_LEVEL_OFFSET = 0;
|
||||
public static final int POS_X_OFFSET = DETAIL_LEVEL_OFFSET + DETAIL_LEVEL_WIDTH;
|
||||
/** indicates the Y position where the LOD starts relative to the level's minimum height */
|
||||
public static final int POS_Z_OFFSET = POS_X_OFFSET + X_POS_WIDTH;
|
||||
|
||||
public static final long DETAIL_LEVEL_MASK = Byte.MAX_VALUE;
|
||||
public static final int POS_X_MASK = (int) Math.pow(2, X_POS_WIDTH) - 1;
|
||||
public static final int POS_Z_MASK = (int) Math.pow(2, Z_POS_WIDTH) - 1;
|
||||
|
||||
|
||||
|
||||
@@ -69,35 +77,45 @@ public class DhSectionPos
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public DhSectionPos(byte detailLevel, int x, int z)
|
||||
/**
|
||||
* This class just holds utility methods for handling a packed
|
||||
* {@link DhSectionPos} and shouldn't be constructed. <Br><br>
|
||||
*
|
||||
* Use one of the {@link DhSectionPos#encode(byte, int, int)} methods instead
|
||||
*/
|
||||
private DhSectionPos() { }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Note:
|
||||
* no validation is done for whether the detail level is positive
|
||||
* or if the X/Z positions can be represented by available bits.
|
||||
*/
|
||||
public static long encode(byte detailLevel, int x, int z)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
long data = 0;
|
||||
data |= detailLevel & DETAIL_LEVEL_MASK;
|
||||
data |= (long) (x & POS_X_MASK) << POS_X_OFFSET;
|
||||
data |= (long) (z & POS_Z_MASK) << POS_Z_OFFSET;
|
||||
return data;
|
||||
}
|
||||
|
||||
public DhSectionPos(DhBlockPos blockPos)
|
||||
public static long encode(DhBlockPos pos) { return encodeBlockPos(pos.x, pos.z); }
|
||||
public static long encode(DhBlockPos2D pos) { return encodeBlockPos(pos.x, pos.z); }
|
||||
public static long encodeBlockPos(int blockX, int blockZ)
|
||||
{
|
||||
this(LodUtil.BLOCK_DETAIL_LEVEL, blockPos.x, blockPos.z);
|
||||
this.convertSelfToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
}
|
||||
public DhSectionPos(DhBlockPos2D blockPos)
|
||||
{
|
||||
this(LodUtil.BLOCK_DETAIL_LEVEL, blockPos.x, blockPos.z);
|
||||
this.convertSelfToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
long pos = encode(LodUtil.BLOCK_DETAIL_LEVEL, blockX, blockZ);
|
||||
pos = convertToDetailLevel(pos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
return pos;
|
||||
}
|
||||
|
||||
public DhSectionPos(DhChunkPos chunkPos)
|
||||
public static long encode(DhChunkPos pos) { return encodeChunkPos(pos.x, pos.z); }
|
||||
public static long encodeChunkPos(int chunkX, int chunkZ)
|
||||
{
|
||||
this(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z);
|
||||
this.convertSelfToDetailLevel(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL);
|
||||
}
|
||||
|
||||
public DhSectionPos(byte detailLevel, DhLodPos dhLodPos)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
this.x = dhLodPos.x;
|
||||
this.z = dhLodPos.z;
|
||||
long pos = encode(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, chunkZ);
|
||||
pos = convertToDetailLevel(pos, DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL);
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,36 +123,27 @@ public class DhSectionPos
|
||||
//============//
|
||||
// converters //
|
||||
//============//
|
||||
|
||||
/**
|
||||
* uses the absolute detail level aka detail levels like {@link LodUtil#CHUNK_DETAIL_LEVEL} instead of the dhSectionPos detailLevels.
|
||||
*
|
||||
* @return the new position closest to negative infinity with the new detail level
|
||||
*/
|
||||
public DhSectionPos convertNewToDetailLevel(byte newSectionDetailLevel)
|
||||
{
|
||||
DhSectionPos newPos = new DhSectionPos(this.detailLevel, this.x, this.z);
|
||||
newPos.convertSelfToDetailLevel(newSectionDetailLevel);
|
||||
|
||||
return newPos;
|
||||
}
|
||||
|
||||
|
||||
/** uses the absolute detail level aka detail levels like {@link LodUtil#CHUNK_DETAIL_LEVEL} instead of the dhSectionPos detailLevels. */
|
||||
protected void convertSelfToDetailLevel(byte newDetailLevel)
|
||||
public static long convertToDetailLevel(long pos, byte newDetailLevel)
|
||||
{
|
||||
byte detailLevel = getDetailLevel(pos);
|
||||
int x = getX(pos);
|
||||
int z = getZ(pos);
|
||||
|
||||
// logic originally taken from DhLodPos
|
||||
if (newDetailLevel >= this.detailLevel)
|
||||
if (newDetailLevel >= detailLevel)
|
||||
{
|
||||
this.x = Math.floorDiv(this.x, BitShiftUtil.powerOfTwo(newDetailLevel - this.detailLevel));
|
||||
this.z = Math.floorDiv(this.z, BitShiftUtil.powerOfTwo(newDetailLevel - this.detailLevel));
|
||||
x = Math.floorDiv(x, BitShiftUtil.powerOfTwo(newDetailLevel - detailLevel));
|
||||
z = Math.floorDiv(z, BitShiftUtil.powerOfTwo(newDetailLevel - detailLevel));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.x = this.x * BitShiftUtil.powerOfTwo(this.detailLevel - newDetailLevel);
|
||||
this.z = this.z * BitShiftUtil.powerOfTwo(this.detailLevel - newDetailLevel);
|
||||
x = x * BitShiftUtil.powerOfTwo(detailLevel - newDetailLevel);
|
||||
z = z * BitShiftUtil.powerOfTwo(detailLevel - newDetailLevel);
|
||||
}
|
||||
|
||||
this.detailLevel = newDetailLevel;
|
||||
|
||||
return encode(newDetailLevel, x, z);
|
||||
}
|
||||
|
||||
|
||||
@@ -143,46 +152,41 @@ public class DhSectionPos
|
||||
// property getters //
|
||||
//==================//
|
||||
|
||||
public byte getDetailLevel() { return this.detailLevel; }
|
||||
|
||||
public int getX() { return this.x; }
|
||||
public int getZ() { return this.z; }
|
||||
public static byte getDetailLevel(long pos) { return (byte) ((pos >> DETAIL_LEVEL_OFFSET) & DETAIL_LEVEL_MASK); }
|
||||
public static int getX(long pos)
|
||||
{
|
||||
// unpack the position
|
||||
int x = (int) ((pos >> POS_X_OFFSET) & POS_X_MASK);
|
||||
// add the missing 2's compliment most-significant bits (if not done negative numbers will parse incorrectly)
|
||||
x = (x << X_POS_MISSING_WIDTH) >> X_POS_MISSING_WIDTH;
|
||||
return x;
|
||||
}
|
||||
public static int getZ(long pos)
|
||||
{
|
||||
int Z = (int) ((pos >> POS_Z_OFFSET) & POS_Z_MASK);
|
||||
Z = (Z << Z_POS_MISSING_WIDTH) >> Z_POS_MISSING_WIDTH;
|
||||
return Z;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
/**
|
||||
* @deprecated use DhSectionPos instead
|
||||
* @return the corner with the smallest X and Z coordinate
|
||||
*/
|
||||
@Deprecated
|
||||
public DhLodPos getMinCornerLodPos() { return this.getMinCornerLodPos((byte) (this.detailLevel - 1)); }
|
||||
/**
|
||||
* @deprecated use DhSectionPos instead
|
||||
* @return the corner with the smallest X and Z coordinate
|
||||
*/
|
||||
@Deprecated
|
||||
public DhLodPos getMinCornerLodPos(byte returnDetailLevel)
|
||||
|
||||
/** @return the block X pos that represents the smallest X coordinate of this section */
|
||||
public static int getMinCornerBlockX(long pos)
|
||||
{
|
||||
LodUtil.assertTrue(returnDetailLevel <= this.detailLevel, "returnDetailLevel must be less than sectionDetail");
|
||||
|
||||
byte offset = (byte) (this.detailLevel - returnDetailLevel);
|
||||
return new DhLodPos(returnDetailLevel,
|
||||
this.x * BitShiftUtil.powerOfTwo(offset),
|
||||
this.z * BitShiftUtil.powerOfTwo(offset));
|
||||
// detail level 1 (2x2 blocks) is a special case,
|
||||
// if this isn't done it will return (1,1) instead of (0,0)
|
||||
int halfBlockWidth = (getDetailLevel(pos) != 1) ? (DhSectionPos.getBlockWidth(pos) / 2) : 0;
|
||||
return DhSectionPos.getCenterBlockPosX(pos) - halfBlockWidth;
|
||||
}
|
||||
|
||||
public DhSectionPos getMinCornerPos(byte returnDetailLevel)
|
||||
/** @return the block Z pos that represents the smallest Z coordinate of this section */
|
||||
public static int getMinCornerBlockZ(long pos)
|
||||
{
|
||||
LodUtil.assertTrue(returnDetailLevel <= this.detailLevel, "returnDetailLevel must be less than sectionDetail");
|
||||
|
||||
byte offset = (byte) (this.detailLevel - returnDetailLevel);
|
||||
return new DhSectionPos(returnDetailLevel,
|
||||
this.x * BitShiftUtil.powerOfTwo(offset),
|
||||
this.z * BitShiftUtil.powerOfTwo(offset));
|
||||
int halfBlockWidth = (getDetailLevel(pos) != 1) ? (DhSectionPos.getBlockWidth(pos) / 2) : 0;
|
||||
return DhSectionPos.getCenterBlockPosZ(pos) - halfBlockWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,26 +199,33 @@ public class DhSectionPos
|
||||
*
|
||||
* @return how many {@link DhSectionPos}'s at the given detail level it would take to span the width of this section.
|
||||
*/
|
||||
public int getWidthCountForLowerDetailedSection(byte returnDetailLevel)
|
||||
public static int getWidthCountForLowerDetailedSection(long pos, byte returnDetailLevel)
|
||||
{
|
||||
LodUtil.assertTrue(returnDetailLevel <= this.detailLevel, "returnDetailLevel must be less than sectionDetail");
|
||||
byte offset = (byte) (this.detailLevel - returnDetailLevel);
|
||||
byte detailLevel = getDetailLevel(pos);
|
||||
|
||||
LodUtil.assertTrue(returnDetailLevel <= detailLevel, "returnDetailLevel must be less than sectionDetail");
|
||||
byte offset = (byte) (detailLevel - returnDetailLevel);
|
||||
return BitShiftUtil.powerOfTwo(offset);
|
||||
}
|
||||
|
||||
|
||||
/** @return how wide this section is in blocks */
|
||||
public int getBlockWidth() { return BitShiftUtil.powerOfTwo(this.detailLevel); }
|
||||
|
||||
|
||||
public DhBlockPos2D getCenterBlockPos() { return new DhBlockPos2D(this.getCenterBlockPosX(), this.getCenterBlockPosZ()); }
|
||||
|
||||
public int getCenterBlockPosX() { return this.getCenterBlockPos(true); }
|
||||
public int getCenterBlockPosZ() { return this.getCenterBlockPos(false); }
|
||||
private int getCenterBlockPos(boolean returnX)
|
||||
public static int getBlockWidth(long pos) { return BitShiftUtil.powerOfTwo(getDetailLevel(pos)); }
|
||||
|
||||
|
||||
public static DhBlockPos2D getCenterBlockPos(long pos) { return new DhBlockPos2D(getCenterBlockPosX(pos), getCenterBlockPosZ(pos)); }
|
||||
|
||||
public static int getCenterBlockPosX(long pos) { return getCenterBlockPosXOrZ(pos, true); }
|
||||
public static int getCenterBlockPosZ(long pos) { return getCenterBlockPosXOrZ(pos, false); }
|
||||
private static int getCenterBlockPosXOrZ(long pos, boolean returnX)
|
||||
{
|
||||
int centerBlockPos = returnX ? this.x : this.z;
|
||||
byte detailLevel = getDetailLevel(pos);
|
||||
int x = getX(pos);
|
||||
int z = getZ(pos);
|
||||
|
||||
if (this.detailLevel == 0)
|
||||
|
||||
int centerBlockPos = returnX ? x : z;
|
||||
|
||||
if (detailLevel == 0)
|
||||
{
|
||||
// already at block detail level, no conversion necessary
|
||||
return centerBlockPos;
|
||||
@@ -222,18 +233,18 @@ public class DhSectionPos
|
||||
|
||||
// we can't get the center of the position at block level, only attempt to get the position offset for detail levels above 0
|
||||
int positionOffset = 0;
|
||||
if (this.detailLevel != 1)
|
||||
if (detailLevel != 1)
|
||||
{
|
||||
positionOffset = BitShiftUtil.powerOfTwo(this.detailLevel - 1);
|
||||
positionOffset = BitShiftUtil.powerOfTwo(detailLevel - 1);
|
||||
}
|
||||
|
||||
return (centerBlockPos * BitShiftUtil.powerOfTwo(this.detailLevel)) + positionOffset;
|
||||
return (centerBlockPos * BitShiftUtil.powerOfTwo(detailLevel)) + positionOffset;
|
||||
}
|
||||
|
||||
public int getManhattanBlockDistance(DhBlockPos2D blockPos)
|
||||
public static int getManhattanBlockDistance(long pos, DhBlockPos2D blockPos)
|
||||
{
|
||||
return Math.abs(this.getCenterBlockPosX() - blockPos.x)
|
||||
+ Math.abs(this.getCenterBlockPosZ() - blockPos.z);
|
||||
return Math.abs(getCenterBlockPosX(pos) - blockPos.x)
|
||||
+ Math.abs(getCenterBlockPosZ(pos) - blockPos.z);
|
||||
}
|
||||
|
||||
|
||||
@@ -241,9 +252,9 @@ public class DhSectionPos
|
||||
//==================//
|
||||
// parent child pos //
|
||||
//==================//
|
||||
|
||||
|
||||
/**
|
||||
* Returns the DhLodPos 1 detail level lower <br><br>
|
||||
* Returns a position 1 detail level lower. <br><br>
|
||||
*
|
||||
* Relative child positions returned for each index: <br>
|
||||
* 0 = (0,0) - North West <br>
|
||||
@@ -253,248 +264,147 @@ public class DhSectionPos
|
||||
*
|
||||
* @param child0to3 must be an int between 0 and 3
|
||||
*/
|
||||
public DhSectionPos getChildByIndex(int child0to3) throws IllegalArgumentException, IllegalStateException
|
||||
public static long getChildByIndex(long pos, int child0to3) throws IllegalArgumentException, IllegalStateException
|
||||
{
|
||||
byte detailLevel = getDetailLevel(pos);
|
||||
int x = getX(pos);
|
||||
int z = getZ(pos);
|
||||
|
||||
if (child0to3 < 0 || child0to3 > 3)
|
||||
{
|
||||
throw new IllegalArgumentException("child0to3 must be between 0 and 3");
|
||||
}
|
||||
if (this.detailLevel <= 0)
|
||||
if (detailLevel <= 0)
|
||||
{
|
||||
throw new IllegalStateException("section detail must be greater than 0");
|
||||
}
|
||||
|
||||
return new DhSectionPos((byte) (this.detailLevel - 1),
|
||||
this.x * 2 + (child0to3 & 1),
|
||||
this.z * 2 + BitShiftUtil.half(child0to3 & 2));
|
||||
|
||||
return DhSectionPos.encode((byte) (detailLevel - 1),
|
||||
x * 2 + (child0to3 & 1),
|
||||
z * 2 + BitShiftUtil.half(child0to3 & 2));
|
||||
}
|
||||
/** Returns this position's child index in its parent */
|
||||
public int getChildIndexOfParent() { return (this.x & 1) + BitShiftUtil.square(this.z & 1); }
|
||||
public static int getChildIndexOfParent(long pos) { return (getX(pos) & 1) + BitShiftUtil.square(getZ(pos) & 1); }
|
||||
|
||||
public DhSectionPos getParentPos() { return new DhSectionPos((byte) (this.detailLevel + 1), BitShiftUtil.half(this.x), BitShiftUtil.half(this.z)); }
|
||||
public static long getParentPos(long pos) { return DhSectionPos.encode((byte) (getDetailLevel(pos) + 1), BitShiftUtil.half(getX(pos)), BitShiftUtil.half(getZ(pos))); }
|
||||
|
||||
|
||||
|
||||
|
||||
public DhSectionPos getAdjacentPos(EDhDirection dir)
|
||||
|
||||
|
||||
public static long getAdjacentPos(long pos, EDhDirection dir) throws IllegalArgumentException
|
||||
{
|
||||
return new DhSectionPos(this.detailLevel,
|
||||
this.x + dir.getNormal().x,
|
||||
this.z + dir.getNormal().z);
|
||||
if (dir == EDhDirection.UP || dir == EDhDirection.DOWN)
|
||||
{
|
||||
throw new IllegalArgumentException("getAdjacentPos can't be UP or DOWN, direction given: ["+dir.name()+"].");
|
||||
}
|
||||
|
||||
return DhSectionPos.encode(getDetailLevel(pos),
|
||||
getX(pos) + dir.getNormal().x,
|
||||
getZ(pos) + dir.getNormal().z);
|
||||
}
|
||||
|
||||
public DhLodPos getSectionBBoxPos() { return new DhLodPos(this.detailLevel, this.x, this.z); }
|
||||
|
||||
|
||||
|
||||
@Deprecated
|
||||
public static DhLodPos getSectionBBoxPos(long pos) { return new DhLodPos(getDetailLevel(pos), getX(pos), getZ(pos)); }
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// comparisons //
|
||||
//=============//
|
||||
|
||||
public boolean overlapsExactly(DhSectionPos other)
|
||||
public static boolean contains(long aPos, long bPos)
|
||||
{
|
||||
// original logic from DhLodPos
|
||||
if (this.equals(other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (this.detailLevel == other.detailLevel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.detailLevel > other.detailLevel)
|
||||
{
|
||||
return this.equals(other.convertNewToDetailLevel(this.detailLevel));
|
||||
}
|
||||
else
|
||||
{
|
||||
return other.equals(this.convertNewToDetailLevel(other.detailLevel));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean contains(DhSectionPos otherPos)
|
||||
{
|
||||
DhBlockPos2D thisMinBlockPos = this.getMinCornerLodPos(LodUtil.BLOCK_DETAIL_LEVEL).getCornerBlockPos();
|
||||
DhBlockPos2D otherCornerBlockPos = otherPos.getMinCornerLodPos(LodUtil.BLOCK_DETAIL_LEVEL).getCornerBlockPos();
|
||||
int aMinX = getMinCornerBlockX(aPos);
|
||||
int aMinZ = getMinCornerBlockZ(aPos);
|
||||
|
||||
int thisBlockWidth = this.getBlockWidth() - 1; // minus 1 to account for zero based positional indexing
|
||||
DhBlockPos2D thisMaxBlockPos = new DhBlockPos2D(thisMinBlockPos.x + thisBlockWidth, thisMinBlockPos.z + thisBlockWidth);
|
||||
|
||||
return thisMinBlockPos.x <= otherCornerBlockPos.x && otherCornerBlockPos.x <= thisMaxBlockPos.x &&
|
||||
thisMinBlockPos.z <= otherCornerBlockPos.z && otherCornerBlockPos.z <= thisMaxBlockPos.z;
|
||||
int bMinX = getMinCornerBlockX(bPos);
|
||||
int bMinZ = getMinCornerBlockZ(bPos);
|
||||
|
||||
int aBlockWidth = getBlockWidth(aPos) - 1; // minus 1 to account for zero based positional indexing
|
||||
int aMaxX = aMinX + aBlockWidth;
|
||||
int aMaxZ = aMinZ + aBlockWidth;
|
||||
|
||||
return aMinX <= bMinX && bMinX <= aMaxX &&
|
||||
aMinZ <= bMinZ && bMinZ <= aMaxZ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// iterators //
|
||||
//===========//
|
||||
|
||||
|
||||
/** Applies the given consumer to all 4 of this position's children. */
|
||||
public void forEachChild(Consumer<DhSectionPos> callback)
|
||||
public static void forEachChild(long pos, LongConsumer callback) throws IllegalArgumentException, IllegalStateException
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
callback.accept(this.getChildByIndex(i));
|
||||
callback.accept(getChildByIndex(pos, i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Applies the given consumer to all children of the position at the given section detail level. */
|
||||
public void forEachChildDownToDetailLevel(byte minSectionDetailLevel, Function<DhSectionPos, Boolean> callback)
|
||||
public static void forEachChildDownToDetailLevel(long pos, byte minSectionDetailLevel, ICancelablePrimitiveLongConsumer callback) throws IllegalArgumentException, IllegalStateException
|
||||
{
|
||||
boolean stop = callback.apply(this);
|
||||
if (stop || minSectionDetailLevel == this.detailLevel)
|
||||
boolean stop = callback.accept(pos);
|
||||
if (stop || minSectionDetailLevel == getDetailLevel(pos))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
this.getChildByIndex(i).forEachChildDownToDetailLevel(minSectionDetailLevel, callback);
|
||||
forEachChildDownToDetailLevel(getChildByIndex(pos, i), minSectionDetailLevel, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Applies the given consumer to all children of the position at the given section detail level. */
|
||||
public void forEachChildAtDetailLevel(byte sectionDetailLevel, Consumer<DhSectionPos> callback)
|
||||
public static void forEachChildAtDetailLevel(long pos, byte sectionDetailLevel, LongConsumer callback) throws IllegalArgumentException, IllegalStateException
|
||||
{
|
||||
if (sectionDetailLevel == this.detailLevel)
|
||||
if (sectionDetailLevel == getDetailLevel(pos))
|
||||
{
|
||||
callback.accept(this);
|
||||
callback.accept(pos);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
this.getChildByIndex(i).forEachChildAtDetailLevel(sectionDetailLevel, callback);
|
||||
forEachChildAtDetailLevel(getChildByIndex(pos, i), sectionDetailLevel, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Applies the given consumer to all children of the position at the given section detail level. */
|
||||
public void forEachPosUpToDetailLevel(byte maxSectionDetailLevel, Consumer<DhSectionPos> callback)
|
||||
public static void forEachPosUpToDetailLevel(long pos, byte maxSectionDetailLevel, LongConsumer callback)
|
||||
{
|
||||
callback.accept(this);
|
||||
if (maxSectionDetailLevel == this.detailLevel)
|
||||
callback.accept(pos);
|
||||
if (maxSectionDetailLevel == getDetailLevel(pos))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.getParentPos().forEachPosUpToDetailLevel(maxSectionDetailLevel, callback);
|
||||
forEachPosUpToDetailLevel(getParentPos(pos), maxSectionDetailLevel, callback);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// Base methods //
|
||||
//==============//
|
||||
|
||||
public static String toString(long pos) { return getDetailLevel(pos) + "*" + getX(pos) + "," + getZ(pos); }
|
||||
public static int hashCode(long pos) { return Long.hashCode(pos); }
|
||||
|
||||
|
||||
//===============//
|
||||
// serialization //
|
||||
//===============//
|
||||
|
||||
/** Serialize() is different from toString() as it must NEVER be changed, and should be in a short format */
|
||||
public String serialize() { return "[" + this.detailLevel + ',' + this.x + ',' + this.z + ']'; }
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
@Nullable
|
||||
public static DhSectionPos deserialize(String value)
|
||||
/** Used instead of {@link java.util.function.Function} to prevent unnecessary (un)wrapping. */
|
||||
@FunctionalInterface
|
||||
public interface ICancelablePrimitiveLongConsumer
|
||||
{
|
||||
if (value.charAt(0) != '[' || value.charAt(value.length() - 1) != ']') return null;
|
||||
String[] split = value.substring(1, value.length() - 1).split(",");
|
||||
if (split.length != 3) return null;
|
||||
return new DhSectionPos(Byte.parseByte(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// overrides //
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public String toString() { return "{" + this.detailLevel + "*" + this.x + "," + this.z + "}"; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj == null || obj.getClass() != DhSectionPos.class)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DhSectionPos that = (DhSectionPos) obj;
|
||||
return this.detailLevel == that.detailLevel &&
|
||||
this.x == that.x &&
|
||||
this.z == that.z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Integer.hashCode(this.detailLevel) ^ // XOR
|
||||
Integer.hashCode(this.x) ^ // XOR
|
||||
Integer.hashCode(this.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// sub classes //
|
||||
//=============//
|
||||
|
||||
/**
|
||||
* Identical to {@link DhSectionPos} except it is mutable.
|
||||
* See {@link DhSectionPos} for full documentation.
|
||||
*
|
||||
* @see DhSectionPos
|
||||
*/
|
||||
public static class DhMutableSectionPos extends DhSectionPos
|
||||
{
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public DhMutableSectionPos(byte sectionDetailLevel, int sectionX, int sectionZ) { super(sectionDetailLevel, sectionX, sectionZ); }
|
||||
public DhMutableSectionPos(DhBlockPos blockPos) { super(blockPos); }
|
||||
public DhMutableSectionPos(DhBlockPos2D blockPos) { super(blockPos); }
|
||||
public DhMutableSectionPos(DhChunkPos chunkPos) { super(chunkPos); }
|
||||
public DhMutableSectionPos(byte detailLevel, DhLodPos dhLodPos) { super(detailLevel, dhLodPos); }
|
||||
|
||||
|
||||
|
||||
//============//
|
||||
// converters //
|
||||
//============//
|
||||
|
||||
/**
|
||||
* Overwrites this section pos with the given input. <br>
|
||||
* Can be useful to prevent duplicate allocations in high traffic loops but should
|
||||
* be used sparingly as it could accidentally cause bugs due to unexpected modifications.
|
||||
*/
|
||||
public void mutate(byte sectionDetailLevel, int sectionX, int sectionZ)
|
||||
{
|
||||
this.detailLevel = sectionDetailLevel;
|
||||
this.x = sectionX;
|
||||
this.z = sectionZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertSelfToDetailLevel(byte newDetailLevel) { super.convertSelfToDetailLevel(newDetailLevel); }
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// property getters //
|
||||
//==================//
|
||||
|
||||
public void setDetailLevel(byte sectionDetailLevel) { this.detailLevel = sectionDetailLevel; }
|
||||
|
||||
public void setX(int sectionX) { this.x = sectionX; }
|
||||
|
||||
public void setZ(int sectionZ) { this.z = sectionZ; }
|
||||
|
||||
/** @return true if this method should cancel further consumers. */
|
||||
boolean accept(long value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,18 +30,20 @@ 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.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadTree;
|
||||
import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.WillNotClose;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -51,7 +53,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
* This quadTree structure is our core data structure and holds
|
||||
* all rendering data.
|
||||
*/
|
||||
public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoCloseable
|
||||
public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRenderable, AutoCloseable
|
||||
{
|
||||
public static final byte TREE_LOWEST_DETAIL_LEVEL = ColumnRenderSource.SECTION_SIZE_OFFSET;
|
||||
|
||||
@@ -69,12 +71,16 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
* This holds every {@link DhSectionPos} that should be reloaded next tick. <br>
|
||||
* This is a {@link ConcurrentLinkedQueue} because new sections can be added to this list via the world generator threads.
|
||||
*/
|
||||
private final ConcurrentLinkedQueue<DhSectionPos> sectionsToReload = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<Long> sectionsToReload = new ConcurrentLinkedQueue<>();
|
||||
private final IDhClientLevel level; //FIXME: Proper hierarchy to remove this reference!
|
||||
private final ConfigChangeListener<EDhApiHorizontalQuality> horizontalScaleChangeListener;
|
||||
private final ReentrantLock treeReadWriteLock = new ReentrantLock();
|
||||
private final AtomicBoolean fullDataRetrievalQueueRunning = new AtomicBoolean(false);
|
||||
|
||||
private ArrayList<LodRenderSection> debugRenderSections = new ArrayList<>();
|
||||
private ArrayList<LodRenderSection> altDebugRenderSections = new ArrayList<>();
|
||||
private final ReentrantLock debugRenderSectionLock = new ReentrantLock();
|
||||
|
||||
/** the smallest numerical detail level number that can be rendered */
|
||||
private byte maxRenderDetailLevel;
|
||||
/** the largest numerical detail level number that can be rendered */
|
||||
@@ -98,6 +104,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
{
|
||||
super(viewDiameterInBlocks, new DhBlockPos2D(initialPlayerBlockX, initialPlayerBlockZ), TREE_LOWEST_DETAIL_LEVEL);
|
||||
|
||||
DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
|
||||
|
||||
this.level = level;
|
||||
this.fullDataSourceProvider = fullDataSourceProvider;
|
||||
this.blockRenderDistanceDiameter = viewDiameterInBlocks;
|
||||
@@ -152,13 +160,33 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
}
|
||||
private void updateAllRenderSections(DhBlockPos2D playerPos)
|
||||
{
|
||||
if (Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus.get())
|
||||
{
|
||||
try
|
||||
{
|
||||
// lock to prevent accidentally rendering an array that's being populated/cleared
|
||||
this.debugRenderSectionLock.lock();
|
||||
|
||||
// swap the debug arrays
|
||||
this.debugRenderSections.clear();
|
||||
ArrayList<LodRenderSection> temp = this.debugRenderSections;
|
||||
this.debugRenderSections = this.altDebugRenderSections;
|
||||
this.altDebugRenderSections = temp;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.debugRenderSectionLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// reload any sections that need it
|
||||
DhSectionPos pos;
|
||||
Long pos;
|
||||
while ((pos = this.sectionsToReload.poll()) != null)
|
||||
{
|
||||
// walk up the tree until we hit the root node
|
||||
// this is done so any high detail changes flow up to the lower detail render sections as well
|
||||
while (pos.getDetailLevel() <= this.treeMinDetailLevel)
|
||||
while (DhSectionPos.getDetailLevel(pos) <= this.treeMinDetailLevel)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -171,7 +199,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
catch (IndexOutOfBoundsException e)
|
||||
{ /* the section is now out of bounds, it doesn't need to be reloaded */ }
|
||||
|
||||
pos = pos.getParentPos();
|
||||
pos = DhSectionPos.getParentPos(pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,11 +207,11 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
// walk through each root node
|
||||
ArrayList<LodRenderSection> nodesNeedingRetrieval = new ArrayList<>();
|
||||
ArrayList<LodRenderSection> nodesNeedingLoading = new ArrayList<>();
|
||||
Iterator<DhSectionPos> rootPosIterator = this.rootNodePosIterator();
|
||||
LongIterator rootPosIterator = this.rootNodePosIterator();
|
||||
while (rootPosIterator.hasNext())
|
||||
{
|
||||
// make sure all root nodes have been created
|
||||
DhSectionPos rootPos = rootPosIterator.next();
|
||||
long rootPos = rootPosIterator.nextLong();
|
||||
if (this.getNode(rootPos) == null)
|
||||
{
|
||||
this.setValue(rootPos, new LodRenderSection(rootPos, this, this.level, this.fullDataSourceProvider));
|
||||
@@ -204,8 +232,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
|
||||
nodesNeedingLoading.sort((a, b) ->
|
||||
{
|
||||
int aDist = a.pos.getManhattanBlockDistance(playerPos);
|
||||
int bDist = b.pos.getManhattanBlockDistance(playerPos);
|
||||
int aDist = DhSectionPos.getManhattanBlockDistance(a.pos, playerPos);
|
||||
int bDist = DhSectionPos.getManhattanBlockDistance(b.pos, playerPos);
|
||||
return Integer.compare(aDist, bDist);
|
||||
});
|
||||
|
||||
@@ -222,7 +250,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
/** @return whether the current position is able to render (note: not if it IS rendering, just if it is ABLE to.) */
|
||||
private boolean recursivelyUpdateRenderSectionNode(
|
||||
DhBlockPos2D playerPos,
|
||||
QuadNode<LodRenderSection> rootNode, QuadNode<LodRenderSection> quadNode, DhSectionPos sectionPos,
|
||||
QuadNode<LodRenderSection> rootNode, QuadNode<LodRenderSection> quadNode, long sectionPos,
|
||||
boolean parentSectionIsRendering,
|
||||
ArrayList<LodRenderSection> nodesNeedingRetrieval,
|
||||
ArrayList<LodRenderSection> nodesNeedingLoading)
|
||||
@@ -267,17 +295,17 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
expectedDetailLevel += DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL;
|
||||
|
||||
|
||||
if (sectionPos.getDetailLevel() > expectedDetailLevel)
|
||||
if (DhSectionPos.getDetailLevel(sectionPos) > expectedDetailLevel)
|
||||
{
|
||||
// section detail level too high //
|
||||
boolean thisPosIsRendering = renderSection.renderingEnabled;
|
||||
boolean allChildrenSectionsAreLoaded = true;
|
||||
|
||||
// recursively update all child render sections
|
||||
Iterator<DhSectionPos> childPosIterator = quadNode.getChildPosIterator();
|
||||
LongIterator childPosIterator = quadNode.getChildPosIterator();
|
||||
while (childPosIterator.hasNext())
|
||||
{
|
||||
DhSectionPos childPos = childPosIterator.next();
|
||||
long childPos = childPosIterator.nextLong();
|
||||
QuadNode<LodRenderSection> childNode = rootNode.getNode(childPos);
|
||||
|
||||
boolean childSectionLoaded = this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, childNode, childPos, thisPosIsRendering || parentSectionIsRendering, nodesNeedingRetrieval, nodesNeedingLoading);
|
||||
@@ -310,7 +338,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
childPosIterator = quadNode.getChildPosIterator();
|
||||
while (childPosIterator.hasNext())
|
||||
{
|
||||
DhSectionPos childPos = childPosIterator.next();
|
||||
long childPos = childPosIterator.nextLong();
|
||||
QuadNode<LodRenderSection> childNode = rootNode.getNode(childPos);
|
||||
|
||||
boolean childSectionLoaded = this.recursivelyUpdateRenderSectionNode(playerPos, rootNode, childNode, childPos, parentSectionIsRendering, nodesNeedingRetrieval, nodesNeedingLoading);
|
||||
@@ -320,7 +348,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
{
|
||||
// FIXME having world generation enabled in a pre-generated world that doesn't have any DH data can cause this to happen
|
||||
// surprisingly reloadPos() doesn't appear to be the culprit, maybe there is an issue with reloading/changing the full data source?
|
||||
//LOGGER.warn("Potential QuadTree concurrency issue. All child sections should be enabled and ready to render for pos: "+sectionPos);
|
||||
//LOGGER.warn("Potential QuadTree concurrency issue. All child sections should be enabled and ready to render for pos: "+DhSectionPos.toString(sectionPos));
|
||||
}
|
||||
|
||||
// this section is now being rendered via its children
|
||||
@@ -328,7 +356,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
}
|
||||
}
|
||||
// TODO this should only equal the expected detail level, the (expectedDetailLevel-1) is a temporary fix to prevent corners from being cut out
|
||||
else if (sectionPos.getDetailLevel() == expectedDetailLevel || sectionPos.getDetailLevel() == expectedDetailLevel - 1)
|
||||
else if (DhSectionPos.getDetailLevel(sectionPos) == expectedDetailLevel || DhSectionPos.getDetailLevel(sectionPos) == expectedDetailLevel - 1)
|
||||
{
|
||||
// this is the detail level we want to render //
|
||||
|
||||
@@ -349,6 +377,11 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
nodesNeedingLoading.add(renderSection);
|
||||
}
|
||||
|
||||
if (Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus.get())
|
||||
{
|
||||
this.debugRenderSections.add(renderSection);
|
||||
}
|
||||
|
||||
// wait for the parent to disable before enabling this section, so we don't overdraw/overlap render sections
|
||||
if (!parentSectionIsRendering && renderSection.canRender())
|
||||
{
|
||||
@@ -412,7 +445,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
* @param sectionPos section position
|
||||
* @return detail level of this section pos
|
||||
*/
|
||||
public byte calculateExpectedDetailLevel(DhBlockPos2D playerPos, DhSectionPos sectionPos) { return this.getDetailLevelFromDistance(playerPos.dist(sectionPos.getCenterBlockPosX(), sectionPos.getCenterBlockPosZ())); }
|
||||
public byte calculateExpectedDetailLevel(DhBlockPos2D playerPos, long sectionPos) { return this.getDetailLevelFromDistance(playerPos.dist(DhSectionPos.getCenterBlockPosX(sectionPos), DhSectionPos.getCenterBlockPosZ(sectionPos))); }
|
||||
private byte getDetailLevelFromDistance(double distance)
|
||||
{
|
||||
double maxDetailDistance = this.getDrawDistanceFromDetail(Byte.MAX_VALUE - 1);
|
||||
@@ -504,16 +537,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
* Can be called whenever a render section's data needs to be refreshed. <br>
|
||||
* This should be called whenever a world generation task is completed or if the connected server has new data to show.
|
||||
*/
|
||||
public void reloadPos(@NotNull DhSectionPos pos)
|
||||
public void reloadPos(long pos)
|
||||
{
|
||||
if (pos == null)
|
||||
{
|
||||
// shouldn't happen, but James saw it happen once, so this is here just in case
|
||||
LOGGER.warn("reloadPos given a null pos.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.sectionsToReload.add(pos);
|
||||
|
||||
// the adjacent locations also need to be updated to make sure lighting
|
||||
@@ -521,7 +546,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
// and lights may not show up over LOD borders
|
||||
for (EDhDirection direction : EDhDirection.ADJ_DIRECTIONS)
|
||||
{
|
||||
this.sectionsToReload.add(pos.getAdjacentPos(direction));
|
||||
this.sectionsToReload.add(DhSectionPos.getAdjacentPos(pos, direction));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,8 +566,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
// sort the nodes from nearest to farthest
|
||||
nodesNeedingRetrieval.sort((a, b) ->
|
||||
{
|
||||
int aDist = a.pos.getManhattanBlockDistance(playerPos);
|
||||
int bDist = b.pos.getManhattanBlockDistance(playerPos);
|
||||
int aDist = DhSectionPos.getManhattanBlockDistance(a.pos, playerPos);
|
||||
int bDist = DhSectionPos.getManhattanBlockDistance(b.pos, playerPos);
|
||||
return Integer.compare(aDist, bDist);
|
||||
});
|
||||
|
||||
@@ -593,6 +618,56 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
private void onHorizontalQualityChange() { this.clearRenderDataCache(); }
|
||||
|
||||
|
||||
//===========//
|
||||
// debugging //
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public void debugRender(DebugRenderer debugRenderer)
|
||||
{
|
||||
try
|
||||
{
|
||||
// lock to prevent accidentally rendering the array that's being cleared
|
||||
this.debugRenderSectionLock.lock();
|
||||
|
||||
|
||||
for (int i = 0; i < this.debugRenderSections.size(); i++)
|
||||
{
|
||||
LodRenderSection renderSection = this.debugRenderSections.get(i);
|
||||
|
||||
Color color = Color.BLACK;
|
||||
if (renderSection.gpuUploadInProgress())
|
||||
{
|
||||
color = Color.ORANGE;
|
||||
}
|
||||
else if (renderSection.renderBuffer == null)
|
||||
{
|
||||
// uploaded but the buffer is missing
|
||||
color = Color.PINK;
|
||||
}
|
||||
else if (renderSection.renderBuffer.hasNonNullVbos())
|
||||
{
|
||||
if (renderSection.renderBuffer.vboBufferCount() != 0)
|
||||
{
|
||||
color = Color.GREEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This section is probably rendering an empty chunk
|
||||
color = Color.RED;
|
||||
}
|
||||
}
|
||||
|
||||
debugRenderer.renderBox(new DebugRenderer.Box(renderSection.pos, 400, 400f, Objects.hashCode(this), 0.05f, color));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.debugRenderSectionLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// base methods //
|
||||
@@ -605,6 +680,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements AutoClose
|
||||
|
||||
this.horizontalScaleChangeListener.close();
|
||||
|
||||
DebugRenderer.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
|
||||
|
||||
Iterator<QuadNode<LodRenderSection>> nodeIterator = this.nodeIterator();
|
||||
while (nodeIterator.hasNext())
|
||||
{
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBuffer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -56,7 +57,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
|
||||
|
||||
|
||||
public final DhSectionPos pos;
|
||||
public final long pos;
|
||||
|
||||
private final IDhClientLevel level;
|
||||
@WillNotClose
|
||||
@@ -85,7 +86,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
|
||||
private boolean missingPositionsCalculated = false;
|
||||
/** should be an empty array if no positions need to be generated */
|
||||
private ArrayList<DhSectionPos> missingGenerationPos = null;
|
||||
private LongArrayList missingGenerationPos = null;
|
||||
|
||||
|
||||
|
||||
@@ -93,7 +94,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public LodRenderSection(DhSectionPos pos, LodQuadTree quadTree, IDhClientLevel level, FullDataSourceProviderV2 fullDataSourceProvider)
|
||||
public LodRenderSection(long pos, LodQuadTree quadTree, IDhClientLevel level, FullDataSourceProviderV2 fullDataSourceProvider)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.quadTree = quadTree;
|
||||
@@ -168,6 +169,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
|
||||
// nothing needs to be rendered
|
||||
this.canRender = false;
|
||||
this.uploadRenderDataToGpuFuture = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -235,7 +237,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
EDhDirection direction = EDhDirection.ADJ_DIRECTIONS[i];
|
||||
int arrayIndex = direction.ordinal() - 2;
|
||||
|
||||
DhSectionPos adjPos = this.pos.getAdjacentPos(direction);
|
||||
long adjPos = DhSectionPos.getAdjacentPos(this.pos, direction);
|
||||
try
|
||||
{
|
||||
LodRenderSection adjRenderSection = this.quadTree.getValue(adjPos);
|
||||
@@ -349,7 +351,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
break;
|
||||
}
|
||||
|
||||
DhSectionPos pos = this.missingGenerationPos.remove(i);
|
||||
long pos = this.missingGenerationPos.removeLong(i);
|
||||
boolean positionQueued = this.fullDataSourceProvider.queuePositionForRetrieval(pos);
|
||||
if (!positionQueued)
|
||||
{
|
||||
@@ -442,7 +444,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
|
||||
try
|
||||
{
|
||||
executor.execute(() -> this.fullDataSourceProvider.removeRetrievalRequestIf((genPos) -> this.pos.contains(genPos)));
|
||||
executor.execute(() -> this.fullDataSourceProvider.removeRetrievalRequestIf((genPos) -> DhSectionPos.contains(this.pos, genPos)));
|
||||
}
|
||||
catch (RejectedExecutionException ignore)
|
||||
{ /* If this happens that means everything is already shut down and no additional cleanup will be necessary */ }
|
||||
|
||||
@@ -205,8 +205,8 @@ public class RenderBufferHandler implements AutoCloseable
|
||||
// Now that we have the axis directions, we can sort the render list
|
||||
Comparator<LoadedRenderBuffer> farToNearComparator = (loadedBufferA, loadedBufferB) ->
|
||||
{
|
||||
Pos2D aPos = loadedBufferA.pos.getCenterBlockPos().toPos2D();
|
||||
Pos2D bPos = loadedBufferB.pos.getCenterBlockPos().toPos2D();
|
||||
Pos2D aPos = DhSectionPos.getCenterBlockPos(loadedBufferA.pos).toPos2D();
|
||||
Pos2D bPos = DhSectionPos.getCenterBlockPos(loadedBufferB.pos).toPos2D();
|
||||
if (true)
|
||||
{
|
||||
int aManhattanDistance = aPos.manhattanDist(cPos);
|
||||
@@ -243,7 +243,7 @@ public class RenderBufferHandler implements AutoCloseable
|
||||
return abPosDifference;
|
||||
}
|
||||
|
||||
return loadedBufferA.pos.getDetailLevel() - loadedBufferB.pos.getDetailLevel(); // If all else fails, sort by detail
|
||||
return DhSectionPos.getDetailLevel(loadedBufferA.pos) - DhSectionPos.getDetailLevel(loadedBufferB.pos); // If all else fails, sort by detail
|
||||
};
|
||||
this.loadedNearToFarBuffers = new SortedArraySet<>((a, b) -> -farToNearComparator.compare(a, b)); // TODO is the comparator named wrong?
|
||||
|
||||
@@ -309,7 +309,7 @@ public class RenderBufferHandler implements AutoCloseable
|
||||
{
|
||||
QuadNode<LodRenderSection> node = nodeIterator.next();
|
||||
|
||||
DhSectionPos sectionPos = node.sectionPos;
|
||||
long sectionPos = node.sectionPos;
|
||||
LodRenderSection renderSection = node.value;
|
||||
if (renderSection == null)
|
||||
{
|
||||
@@ -320,7 +320,7 @@ public class RenderBufferHandler implements AutoCloseable
|
||||
{
|
||||
if (enableFrustumCulling)
|
||||
{
|
||||
DhLodPos lodBounds = renderSection.pos.getSectionBBoxPos();
|
||||
DhLodPos lodBounds = DhSectionPos.getSectionBBoxPos(renderSection.pos);
|
||||
int blockMinX = lodBounds.getMinX().toBlockWidth();
|
||||
int blockMinZ = lodBounds.getMinZ().toBlockWidth();
|
||||
int lodBlockWidth = lodBounds.getBlockWidth();
|
||||
@@ -344,7 +344,7 @@ public class RenderBufferHandler implements AutoCloseable
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.loadedNearToFarBuffers.add(new LoadedRenderBuffer(buffer, sectionPos));
|
||||
}
|
||||
@@ -425,9 +425,9 @@ public class RenderBufferHandler implements AutoCloseable
|
||||
private static class LoadedRenderBuffer
|
||||
{
|
||||
public final ColumnRenderBuffer buffer;
|
||||
public final DhSectionPos pos;
|
||||
public final long pos;
|
||||
|
||||
LoadedRenderBuffer(ColumnRenderBuffer buffer, DhSectionPos pos)
|
||||
LoadedRenderBuffer(ColumnRenderBuffer buffer, long pos)
|
||||
{
|
||||
this.buffer = buffer;
|
||||
this.pos = pos;
|
||||
|
||||
+6
-4
@@ -247,6 +247,8 @@ public class DebugRenderer
|
||||
public Vec3f b;
|
||||
public Color color;
|
||||
|
||||
|
||||
|
||||
public Box(Vec3f a, Vec3f b, Color color)
|
||||
{
|
||||
this.a = a;
|
||||
@@ -288,14 +290,14 @@ public class DebugRenderer
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public Box(DhSectionPos pos, float minY, float maxY, float marginPercent, Color color)
|
||||
public Box(long pos, float minY, float maxY, float marginPercent, Color color)
|
||||
{
|
||||
this(pos.getSectionBBoxPos(), minY, maxY, marginPercent, color);
|
||||
this(DhSectionPos.getSectionBBoxPos(pos), minY, maxY, marginPercent, color);
|
||||
}
|
||||
|
||||
public Box(DhSectionPos pos, float y, float yDiff, Object hash, float marginPercent, Color color)
|
||||
public Box(long pos, float y, float yDiff, Object hash, float marginPercent, Color color)
|
||||
{
|
||||
this(pos.getSectionBBoxPos(), y, yDiff, hash, marginPercent, color);
|
||||
this(DhSectionPos.getSectionBBoxPos(pos), y, yDiff, hash, marginPercent, color);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+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);
|
||||
|
||||
@@ -22,21 +22,18 @@ package com.seibel.distanthorizons.core.sql.dto;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV1;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Handles storing{@link FullDataSourceV1}'s in the database.
|
||||
*/
|
||||
public class FullDataSourceV1DTO implements IBaseDTO<DhSectionPos>
|
||||
public class FullDataSourceV1DTO implements IBaseDTO<Long>
|
||||
{
|
||||
public DhSectionPos pos;
|
||||
public long pos;
|
||||
public int checksum;
|
||||
public byte dataDetailLevel;
|
||||
public EDhApiWorldGenerationStep worldGenStep;
|
||||
@@ -54,7 +51,7 @@ public class FullDataSourceV1DTO implements IBaseDTO<DhSectionPos>
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public FullDataSourceV1DTO(DhSectionPos pos, int checksum, byte dataDetailLevel, EDhApiWorldGenerationStep worldGenStep, String dataType, byte binaryDataFormatVersion, byte[] dataArray)
|
||||
public FullDataSourceV1DTO(long pos, int checksum, byte dataDetailLevel, EDhApiWorldGenerationStep worldGenStep, String dataType, byte binaryDataFormatVersion, byte[] dataArray)
|
||||
{
|
||||
this.pos = pos;
|
||||
this.checksum = checksum;
|
||||
@@ -83,7 +80,7 @@ public class FullDataSourceV1DTO implements IBaseDTO<DhSectionPos>
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public DhSectionPos getKey() { return this.pos; }
|
||||
public Long getKey() { return this.pos; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
+73
-27
@@ -24,23 +24,26 @@ 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.fullData.sources.FullDataSourceV2;
|
||||
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.util.objects.dataStreams.DhDataInputStream;
|
||||
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
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>
|
||||
public class FullDataSourceV2DTO implements IBaseDTO<Long>
|
||||
{
|
||||
public DhSectionPos pos;
|
||||
public static final boolean VALIDATE_INPUT_DATAPOINTS = true;
|
||||
|
||||
|
||||
public long pos;
|
||||
|
||||
public int levelMinY;
|
||||
|
||||
@@ -57,7 +60,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
public byte[] compressedMappingByteArray;
|
||||
|
||||
public byte dataFormatVersion;
|
||||
public EDhApiDataCompressionMode compressionModeEnum;
|
||||
public byte compressionModeValue;
|
||||
|
||||
public boolean applyToParent;
|
||||
|
||||
@@ -79,7 +82,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
|
||||
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
|
||||
@@ -87,8 +90,8 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
}
|
||||
|
||||
public FullDataSourceV2DTO(
|
||||
DhSectionPos pos,
|
||||
int dataChecksum, byte[] compressedColumnGenStepByteArray, byte[] compressedWorldCompressionModeByteArray, byte dataFormatVersion, EDhApiDataCompressionMode compressionModeEnum, byte[] compressedDataByteArray,
|
||||
long pos,
|
||||
int dataChecksum, byte[] compressedColumnGenStepByteArray, byte[] compressedWorldCompressionModeByteArray, byte dataFormatVersion, byte compressionModeValue, byte[] compressedDataByteArray,
|
||||
long lastModifiedUnixDateTime, long createdUnixDateTime,
|
||||
byte[] compressedMappingByteArray, boolean applyToParent,
|
||||
int levelMinY)
|
||||
@@ -99,7 +102,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
this.compressedWorldCompressionModeByteArray = compressedWorldCompressionModeByteArray;
|
||||
|
||||
this.dataFormatVersion = dataFormatVersion;
|
||||
this.compressionModeEnum = compressionModeEnum;
|
||||
this.compressionModeValue = compressionModeValue;
|
||||
|
||||
this.compressedDataByteArray = compressedDataByteArray;
|
||||
this.compressedMappingByteArray = compressedMappingByteArray;
|
||||
@@ -118,32 +121,46 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
// 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
|
||||
@@ -154,7 +171,13 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
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));
|
||||
FullDataPointIdMap newMap = readBlobToDataMapping(this.compressedMappingByteArray, dataSource.getPos(), levelWrapper, compressionModeEnum);
|
||||
dataSource.mapping.addAll(newMap);
|
||||
if (dataSource.mapping.size() != newMap.size())
|
||||
{
|
||||
// if the mappings are out of sync then the LODs will render incorrectly due to IDs being wrong
|
||||
LodUtil.assertNotReach("ID maps out of sync for pos: "+this.pos);
|
||||
}
|
||||
}
|
||||
|
||||
dataSource.lastModifiedUnixDateTime = this.lastModifiedUnixDateTime;
|
||||
@@ -212,7 +235,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
|
||||
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);
|
||||
@@ -225,12 +248,21 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -254,15 +286,22 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -302,7 +341,7 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
|
||||
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, long pos, @NotNull ILevelWrapper levelWrapper, EDhApiDataCompressionMode compressionModeEnum) throws IOException, InterruptedException, DataCorruptedException
|
||||
{
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedMappingByteArray);
|
||||
DhDataInputStream compressedIn = new DhDataInputStream(byteArrayInputStream, compressionModeEnum);
|
||||
@@ -318,10 +357,17 @@ public class FullDataSourceV2DTO implements IBaseDTO<DhSectionPos>
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public DhSectionPos getKey() { return this.pos; }
|
||||
public Long getKey() { return this.pos; }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
public EDhApiDataCompressionMode getCompressionMode() throws IllegalArgumentException { return EDhApiDataCompressionMode.getFromValue(this.compressionModeValue); }
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package com.seibel.distanthorizons.core.sql.repo;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.DatabaseUpdater;
|
||||
import com.seibel.distanthorizons.core.sql.DbConnectionClosedException;
|
||||
import com.seibel.distanthorizons.core.sql.dto.IBaseDTO;
|
||||
@@ -39,7 +38,8 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
*/
|
||||
public abstract class AbstractDhRepo<TKey, TDTO extends IBaseDTO<TKey>> implements AutoCloseable
|
||||
{
|
||||
public static final int TIMEOUT_SECONDS = 30;
|
||||
/** a value of 0 means there's no timeout */
|
||||
public static final int TIMEOUT_SECONDS = 0;
|
||||
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final ConcurrentHashMap<String, Connection> CONNECTIONS_BY_CONNECTION_STRING = new ConcurrentHashMap<>();
|
||||
|
||||
+38
-10
@@ -23,6 +23,8 @@ import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGeneratio
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV1DTO;
|
||||
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
@@ -30,7 +32,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FullDataSourceV1Repo extends AbstractDhRepo<DhSectionPos, FullDataSourceV1DTO>
|
||||
public class FullDataSourceV1Repo extends AbstractDhRepo<Long, FullDataSourceV1DTO>
|
||||
{
|
||||
public static final String TABLE_NAME = "Legacy_FullData_V1";
|
||||
|
||||
@@ -55,7 +57,7 @@ public class FullDataSourceV1Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
public String getTableName() { return TABLE_NAME; }
|
||||
|
||||
@Override
|
||||
public String createWhereStatement(DhSectionPos pos) { return "DhSectionPos = '"+pos.serialize()+"'"; }
|
||||
public String createWhereStatement(Long pos) { return "DhSectionPos = '"+serializeSectionPos(pos)+"'"; }
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +69,7 @@ public class FullDataSourceV1Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
public FullDataSourceV1DTO convertDictionaryToDto(Map<String, Object> objectMap) throws ClassCastException
|
||||
{
|
||||
String posString = (String) objectMap.get("DhSectionPos");
|
||||
DhSectionPos pos = DhSectionPos.deserialize(posString);
|
||||
Long pos = deserializeSectionPos(posString);
|
||||
|
||||
// meta data
|
||||
int checksum = (Integer) objectMap.get("Checksum");
|
||||
@@ -106,7 +108,7 @@ public class FullDataSourceV1Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
PreparedStatement statement = this.createPreparedStatement(sql);
|
||||
|
||||
int i = 1;
|
||||
statement.setObject(i++, dto.pos.serialize());
|
||||
statement.setObject(i++, serializeSectionPos(dto.pos));
|
||||
|
||||
statement.setObject(i++, dto.checksum);
|
||||
statement.setObject(i++, 0 /*dto.dataVersion*/);
|
||||
@@ -149,7 +151,7 @@ public class FullDataSourceV1Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
|
||||
statement.setObject(i++, dto.dataArray);
|
||||
|
||||
statement.setObject(i++, dto.pos.serialize());
|
||||
statement.setObject(i++, serializeSectionPos(dto.pos));
|
||||
|
||||
return statement;
|
||||
}
|
||||
@@ -205,9 +207,9 @@ public class FullDataSourceV1Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
}
|
||||
|
||||
/** Returns the new "returnCount" positions that need to be migrated */
|
||||
public ArrayList<DhSectionPos> getPositionsToMigrate(int returnCount)
|
||||
public LongArrayList getPositionsToMigrate(int returnCount)
|
||||
{
|
||||
ArrayList<DhSectionPos> list = new ArrayList<>();
|
||||
LongArrayList list = new LongArrayList();
|
||||
|
||||
List<Map<String, Object>> resultMapList = this.queryDictionary(
|
||||
"select DhSectionPos " +
|
||||
@@ -218,19 +220,19 @@ public class FullDataSourceV1Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
for (Map<String, Object> resultMap : resultMapList)
|
||||
{
|
||||
// returned in the format [sectionDetailLevel,x,z] IE [6,0,0]
|
||||
DhSectionPos sectionPos = DhSectionPos.deserialize((String) resultMap.get("DhSectionPos"));
|
||||
long sectionPos = deserializeSectionPos((String) resultMap.get("DhSectionPos"));
|
||||
list.add(sectionPos);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public void markMigrationFailed(DhSectionPos pos)
|
||||
public void markMigrationFailed(long pos)
|
||||
{
|
||||
String sql =
|
||||
"UPDATE "+this.getTableName()+" \n" +
|
||||
"SET MigrationFailed = 1 \n" +
|
||||
"WHERE DhSectionPos = '"+pos.serialize()+"'";
|
||||
"WHERE DhSectionPos = '"+serializeSectionPos(pos)+"'";
|
||||
|
||||
this.queryDictionaryFirst(sql);
|
||||
}
|
||||
@@ -283,4 +285,30 @@ public class FullDataSourceV1Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
this.queryDictionaryFirst("delete from " + this.getTableName() + " where DhSectionPos in (" + sectionPosCsv + ")");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// section pos helpers //
|
||||
//=====================//
|
||||
|
||||
private static String serializeSectionPos(long pos) { return "[" + DhSectionPos.getDetailLevel(pos) + ',' + DhSectionPos.getX(pos) + ',' + DhSectionPos.getZ(pos) + ']'; }
|
||||
|
||||
|
||||
@Nullable
|
||||
private static Long deserializeSectionPos(String value)
|
||||
{
|
||||
if (value.charAt(0) != '[' || value.charAt(value.length() - 1) != ']')
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] split = value.substring(1, value.length() - 1).split(",");
|
||||
if (split.length != 3)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return DhSectionPos.encode(Byte.parseByte(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+32
-33
@@ -25,17 +25,17 @@ 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.dataStreams.DhDataInputStream;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataSourceV2DTO>
|
||||
public class FullDataSourceV2Repo extends AbstractDhRepo<Long, FullDataSourceV2DTO>
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
@@ -60,10 +60,10 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
public String getTableName() { return "FullData"; }
|
||||
|
||||
@Override
|
||||
public String createWhereStatement(DhSectionPos pos)
|
||||
public String createWhereStatement(Long pos)
|
||||
{
|
||||
int detailLevel = pos.getDetailLevel() - DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL;
|
||||
return "DetailLevel = '"+detailLevel+"' AND PosX = '"+pos.getX()+"' AND PosZ = '"+pos.getZ()+"'";
|
||||
int detailLevel = DhSectionPos.getDetailLevel(pos) - DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL;
|
||||
return "DetailLevel = '"+detailLevel+"' AND PosX = '"+ DhSectionPos.getX(pos)+"' AND PosZ = '"+ DhSectionPos.getZ(pos)+"'";
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
byte sectionDetailLevel = (byte) (detailLevel + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||
int posX = (Integer) objectMap.get("PosX");
|
||||
int posZ = (Integer) objectMap.get("PosZ");
|
||||
DhSectionPos pos = new DhSectionPos(sectionDetailLevel, posX, posZ);
|
||||
long pos = DhSectionPos.encode(sectionDetailLevel, posX, posZ);
|
||||
|
||||
int minY = (Integer) objectMap.get("MinY");
|
||||
int dataChecksum = (Integer) objectMap.get("DataChecksum");
|
||||
@@ -91,8 +91,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 +100,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);
|
||||
@@ -128,9 +127,9 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
PreparedStatement statement = this.createPreparedStatement(sql);
|
||||
|
||||
int i = 1;
|
||||
statement.setObject(i++, dto.pos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||
statement.setObject(i++, dto.pos.getX());
|
||||
statement.setObject(i++, dto.pos.getZ());
|
||||
statement.setObject(i++, DhSectionPos.getDetailLevel(dto.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||
statement.setObject(i++, DhSectionPos.getX(dto.pos));
|
||||
statement.setObject(i++, DhSectionPos.getZ(dto.pos));
|
||||
|
||||
statement.setObject(i++, dto.levelMinY);
|
||||
statement.setObject(i++, dto.dataChecksum);
|
||||
@@ -141,7 +140,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,15 +183,15 @@ 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
|
||||
statement.setObject(i++, dto.createdUnixDateTime);
|
||||
|
||||
statement.setObject(i++, dto.pos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||
statement.setObject(i++, dto.pos.getX());
|
||||
statement.setObject(i++, dto.pos.getZ());
|
||||
statement.setObject(i++, DhSectionPos.getDetailLevel(dto.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||
statement.setObject(i++, DhSectionPos.getX(dto.pos));
|
||||
statement.setObject(i++, DhSectionPos.getZ(dto.pos));
|
||||
|
||||
return statement;
|
||||
}
|
||||
@@ -201,21 +200,21 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
|
||||
// updates //
|
||||
|
||||
public void setApplyToParent(DhSectionPos pos, boolean applyToParent) throws SQLException
|
||||
public void setApplyToParent(long pos, boolean applyToParent) throws SQLException
|
||||
{
|
||||
int detailLevel = pos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
int detailLevel = DhSectionPos.getDetailLevel(pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
|
||||
String sql =
|
||||
"UPDATE "+this.getTableName()+" \n" +
|
||||
"SET ApplyToParent = "+applyToParent+" \n" +
|
||||
"WHERE DetailLevel = "+detailLevel+" AND PosX = "+pos.getX()+" AND PosZ = "+pos.getZ();
|
||||
"WHERE DetailLevel = "+detailLevel+" AND PosX = "+ DhSectionPos.getX(pos)+" AND PosZ = "+ DhSectionPos.getZ(pos);
|
||||
|
||||
this.queryDictionaryFirst(sql);
|
||||
}
|
||||
|
||||
public ArrayList<DhSectionPos> getPositionsToUpdate(int returnCount)
|
||||
public LongArrayList getPositionsToUpdate(int returnCount)
|
||||
{
|
||||
ArrayList<DhSectionPos> list = new ArrayList<>();
|
||||
LongArrayList list = new LongArrayList();
|
||||
|
||||
List<Map<String, Object>> resultMapList = this.queryDictionary(
|
||||
"select DetailLevel, PosX, PosZ " +
|
||||
@@ -230,7 +229,7 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
int posX = (Integer) resultMap.get("PosX");
|
||||
int posZ = (Integer) resultMap.get("PosZ");
|
||||
|
||||
DhSectionPos pos = new DhSectionPos(sectionDetailLevel, posX, posZ);
|
||||
long pos = DhSectionPos.encode(sectionDetailLevel, posX, posZ);
|
||||
list.add(pos);
|
||||
}
|
||||
|
||||
@@ -238,14 +237,14 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
}
|
||||
|
||||
/** @return null if nothing exists for this position */
|
||||
public byte[] getColumnGenerationStepForPos(DhSectionPos pos)
|
||||
public byte[] getColumnGenerationStepForPos(long pos)
|
||||
{
|
||||
int detailLevel = pos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
int detailLevel = DhSectionPos.getDetailLevel(pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
|
||||
Map<String, Object> resultMap = this.queryDictionaryFirst(
|
||||
"select ColumnGenerationStep, CompressionMode " +
|
||||
"from "+this.getTableName()+" " +
|
||||
"WHERE DetailLevel = "+detailLevel+" AND PosX = "+pos.getX()+" AND PosZ = "+pos.getZ());
|
||||
"WHERE DetailLevel = "+detailLevel+" AND PosX = "+ DhSectionPos.getX(pos)+" AND PosZ = "+ DhSectionPos.getZ(pos));
|
||||
|
||||
if (resultMap != null)
|
||||
{
|
||||
@@ -267,7 +266,7 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warn("Decompression issue when getting column gen steps for pos: "+pos, e);
|
||||
LOGGER.warn("Decompression issue when getting column gen steps for pos: [" + DhSectionPos.toString(pos) + "]", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -284,9 +283,9 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
//===================//
|
||||
|
||||
/** @return every position in this database */
|
||||
public ArrayList<DhSectionPos> getAllPositions()
|
||||
public LongArrayList getAllPositions()
|
||||
{
|
||||
ArrayList<DhSectionPos> list = new ArrayList<>();
|
||||
LongArrayList list = new LongArrayList();
|
||||
|
||||
List<Map<String, Object>> resultMapList = this.queryDictionary(
|
||||
"select DetailLevel, PosX, PosZ " +
|
||||
@@ -299,7 +298,7 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
int posX = (Integer) resultMap.get("PosX");
|
||||
int posZ = (Integer) resultMap.get("PosZ");
|
||||
|
||||
DhSectionPos pos = new DhSectionPos(sectionDetailLevel, posX, posZ);
|
||||
long pos = DhSectionPos.encode(sectionDetailLevel, posX, posZ);
|
||||
list.add(pos);
|
||||
}
|
||||
|
||||
@@ -310,14 +309,14 @@ public class FullDataSourceV2Repo extends AbstractDhRepo<DhSectionPos, FullDataS
|
||||
* @return the size of the full data at the given position
|
||||
* (doesn't include the size of the mapping or any other column)
|
||||
*/
|
||||
public long getDataSizeInBytes(DhSectionPos pos)
|
||||
public long getDataSizeInBytes(long pos)
|
||||
{
|
||||
int detailLevel = pos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
int detailLevel = DhSectionPos.getDetailLevel(pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
|
||||
Map<String, Object> resultMap = this.queryDictionaryFirst(
|
||||
"select LENGTH(Data) as dataSize " +
|
||||
"from "+this.getTableName()+" " +
|
||||
"WHERE DetailLevel = "+detailLevel+" AND PosX = "+pos.getX()+" AND PosZ = "+pos.getZ());
|
||||
"WHERE DetailLevel = "+detailLevel+" AND PosX = "+ DhSectionPos.getX(pos)+" AND PosZ = "+ DhSectionPos.getZ(pos));
|
||||
|
||||
if (resultMap != null && resultMap.get("dataSize") != null)
|
||||
{
|
||||
|
||||
@@ -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); }
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
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); }
|
||||
|
||||
public DataCorruptedException(String message, Exception e)
|
||||
{
|
||||
super(message);
|
||||
this.setStackTrace(e.getStackTrace());
|
||||
this.addSuppressed(e);
|
||||
}
|
||||
|
||||
}
|
||||
+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;
|
||||
|
||||
/**
|
||||
|
||||
+23
-22
@@ -25,6 +25,7 @@ import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.iterators.QuadNodeDirectChildIterator;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.iterators.QuadNodeDirectChildPosIterator;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.iterators.QuadTreeNodeIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -35,7 +36,7 @@ public class QuadNode<T>
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
|
||||
public final DhSectionPos sectionPos;
|
||||
public final long sectionPos;
|
||||
public final byte minimumDetailLevel;
|
||||
public T value;
|
||||
|
||||
@@ -67,7 +68,7 @@ public class QuadNode<T>
|
||||
|
||||
|
||||
|
||||
public QuadNode(DhSectionPos sectionPos, byte minimumDetailLevel)
|
||||
public QuadNode(long sectionPos, byte minimumDetailLevel)
|
||||
{
|
||||
this.sectionPos = sectionPos;
|
||||
this.minimumDetailLevel = minimumDetailLevel;
|
||||
@@ -145,14 +146,14 @@ public class QuadNode<T>
|
||||
* @return the node at the given position
|
||||
* @throws IllegalArgumentException if childSectionPos has the wrong detail level or is outside the bounds of this node
|
||||
*/
|
||||
public QuadNode<T> getNode(DhSectionPos sectionPos) throws IllegalArgumentException { return this.getOrSetValue(sectionPos, false, null); }
|
||||
public QuadNode<T> getNode(long sectionPos) throws IllegalArgumentException { return this.getOrSetValue(sectionPos, false, null); }
|
||||
|
||||
/**
|
||||
* @param sectionPos must be 1 detail level lower than this node's detail level
|
||||
* @return the value at the given position before the new value was set
|
||||
* @throws IllegalArgumentException if childSectionPos has the wrong detail level or is outside the bounds of this node
|
||||
*/
|
||||
public T setValue(DhSectionPos sectionPos, T newValue) throws IllegalArgumentException
|
||||
public T setValue(long sectionPos, T newValue) throws IllegalArgumentException
|
||||
{
|
||||
QuadNode<T> previousNode = this.getNode(sectionPos);
|
||||
if (previousNode != null)
|
||||
@@ -173,27 +174,27 @@ public class QuadNode<T>
|
||||
* @return the node at the given position before the new node was set (if the new node should be set)
|
||||
* @throws IllegalArgumentException if childSectionPos has the wrong detail level or is outside the bounds of this
|
||||
*/
|
||||
private QuadNode<T> getOrSetValue(DhSectionPos inputSectionPos, boolean replaceValue, T newValue) throws IllegalArgumentException
|
||||
private QuadNode<T> getOrSetValue(long inputSectionPos, boolean replaceValue, T newValue) throws IllegalArgumentException
|
||||
{
|
||||
// debug validation
|
||||
|
||||
if (!this.sectionPos.contains(inputSectionPos))
|
||||
if (!DhSectionPos.contains(this.sectionPos, inputSectionPos))
|
||||
{
|
||||
LOGGER.error((replaceValue ? "set " : "get ") + inputSectionPos + " center block: " + inputSectionPos.getCenterBlockPos() + ", this pos: " + this.sectionPos + " this center block: " + this.sectionPos.getCenterBlockPos());
|
||||
throw new IllegalArgumentException("Input section pos " + inputSectionPos + " outside of this quadNode's pos: " + this.sectionPos + ", this node's blockPos: " + this.sectionPos.convertNewToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL) + " block width: " + this.sectionPos.getBlockWidth() + " input detail level: " + inputSectionPos.convertNewToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL) + " width: " + inputSectionPos.getBlockWidth());
|
||||
LOGGER.error((replaceValue ? "set " : "get ") + inputSectionPos + " center block: " + DhSectionPos.getCenterBlockPos(inputSectionPos) + ", this pos: " + this.sectionPos + " this center block: " + DhSectionPos.getCenterBlockPos(this.sectionPos));
|
||||
throw new IllegalArgumentException("Input section pos " + inputSectionPos + " outside of this quadNode's pos: " + this.sectionPos + ", this node's blockPos: " + DhSectionPos.convertToDetailLevel(this.sectionPos, LodUtil.BLOCK_DETAIL_LEVEL) + " block width: " + DhSectionPos.getBlockWidth(this.sectionPos) + " input detail level: " + DhSectionPos.convertToDetailLevel(inputSectionPos, LodUtil.BLOCK_DETAIL_LEVEL) + " width: " + DhSectionPos.getBlockWidth(inputSectionPos));
|
||||
}
|
||||
|
||||
if (inputSectionPos.getDetailLevel() > this.sectionPos.getDetailLevel())
|
||||
if (DhSectionPos.getDetailLevel(inputSectionPos) > DhSectionPos.getDetailLevel(this.sectionPos))
|
||||
{
|
||||
throw new IllegalArgumentException("detail level higher than this node. Node Detail level: " + this.sectionPos.getDetailLevel() + " input detail level: " + inputSectionPos.getDetailLevel());
|
||||
throw new IllegalArgumentException("detail level higher than this node. Node Detail level: " + DhSectionPos.getDetailLevel(this.sectionPos) + " input detail level: " + DhSectionPos.getDetailLevel(inputSectionPos));
|
||||
}
|
||||
|
||||
if (inputSectionPos.getDetailLevel() == this.sectionPos.getDetailLevel() && !inputSectionPos.equals(this.sectionPos))
|
||||
if (DhSectionPos.getDetailLevel(inputSectionPos) == DhSectionPos.getDetailLevel(this.sectionPos) && inputSectionPos != this.sectionPos)
|
||||
{
|
||||
throw new IllegalArgumentException("Node and input detail level are equal, however positions are not; this tree doesn't contain the requested position. Node pos: " + this.sectionPos + ", input pos: " + inputSectionPos);
|
||||
}
|
||||
|
||||
if (inputSectionPos.getDetailLevel() < this.minimumDetailLevel)
|
||||
if (DhSectionPos.getDetailLevel(inputSectionPos) < this.minimumDetailLevel)
|
||||
{
|
||||
throw new IllegalArgumentException("Input position is requesting a detail level lower than what this node can provide. Node minimum detail level: " + this.minimumDetailLevel + ", input pos: " + inputSectionPos);
|
||||
}
|
||||
@@ -201,7 +202,7 @@ public class QuadNode<T>
|
||||
|
||||
|
||||
// get/set logic
|
||||
if (inputSectionPos.getDetailLevel() == this.sectionPos.getDetailLevel())
|
||||
if (DhSectionPos.getDetailLevel(inputSectionPos) == DhSectionPos.getDetailLevel(this.sectionPos))
|
||||
{
|
||||
// this node is the requested position
|
||||
if (replaceValue)
|
||||
@@ -217,14 +218,14 @@ public class QuadNode<T>
|
||||
|
||||
// LOGGER.info((replaceValue ? "set " : "get ")+inputSectionPos+" center block: "+inputSectionPos.getCenter().getCornerBlockPos()+", this pos: "+this.sectionPos+" this center block: "+this.sectionPos.getCenter().getCornerBlockPos());
|
||||
|
||||
DhSectionPos nwPos = this.sectionPos.getChildByIndex(0);
|
||||
DhSectionPos swPos = this.sectionPos.getChildByIndex(1);
|
||||
DhSectionPos nePos = this.sectionPos.getChildByIndex(2);
|
||||
DhSectionPos sePos = this.sectionPos.getChildByIndex(3);
|
||||
long nwPos = DhSectionPos.getChildByIndex(this.sectionPos, 0);
|
||||
long swPos = DhSectionPos.getChildByIndex(this.sectionPos, 1);
|
||||
long nePos = DhSectionPos.getChildByIndex(this.sectionPos, 2);
|
||||
long sePos = DhSectionPos.getChildByIndex(this.sectionPos, 3);
|
||||
|
||||
// look for the child that contains the input position (there may be a faster way to do this, but this works for now)
|
||||
QuadNode<T> childNode;
|
||||
if (nwPos.contains(inputSectionPos))
|
||||
if (DhSectionPos.contains(nwPos, inputSectionPos))
|
||||
{
|
||||
// TODO merge duplicate code
|
||||
if (replaceValue && this.nwChild == null)
|
||||
@@ -237,7 +238,7 @@ public class QuadNode<T>
|
||||
// childNode should only be null when replaceValue = false and the end of a node chain has been reached
|
||||
return (childNode != null) ? childNode.getOrSetValue(inputSectionPos, replaceValue, newValue) : null;
|
||||
}
|
||||
else if (swPos.contains(inputSectionPos))
|
||||
else if (DhSectionPos.contains(swPos, inputSectionPos))
|
||||
{
|
||||
// TODO merge duplicate code
|
||||
if (replaceValue && this.swChild == null)
|
||||
@@ -250,7 +251,7 @@ public class QuadNode<T>
|
||||
// childNode should only be null when replaceValue = false and the end of a node chain has been reached
|
||||
return (childNode != null) ? childNode.getOrSetValue(inputSectionPos, replaceValue, newValue) : null;
|
||||
}
|
||||
else if (nePos.contains(inputSectionPos))
|
||||
else if (DhSectionPos.contains(nePos, inputSectionPos))
|
||||
{
|
||||
// TODO merge duplicate code
|
||||
if (replaceValue && this.neChild == null)
|
||||
@@ -263,7 +264,7 @@ public class QuadNode<T>
|
||||
// childNode should only be null when replaceValue = false and the end of a node chain has been reached
|
||||
return (childNode != null) ? childNode.getOrSetValue(inputSectionPos, replaceValue, newValue) : null;
|
||||
}
|
||||
else if (sePos.contains(inputSectionPos))
|
||||
else if (DhSectionPos.contains(sePos, inputSectionPos))
|
||||
{
|
||||
// TODO merge duplicate code
|
||||
if (replaceValue && this.seChild == null)
|
||||
@@ -293,7 +294,7 @@ public class QuadNode<T>
|
||||
public Iterator<QuadNode<T>> getLeafNodeIterator() { return new QuadTreeNodeIterator<>(this, true); }
|
||||
|
||||
/** positions can point to null children */
|
||||
public Iterator<DhSectionPos> getChildPosIterator() { return new QuadNodeDirectChildPosIterator<>(this); }
|
||||
public LongIterator getChildPosIterator() { return new QuadNodeDirectChildPosIterator<>(this); }
|
||||
public Iterator<QuadNode<T>> getChildNodeIterator() { return new QuadNodeDirectChildIterator<>(this); }
|
||||
|
||||
|
||||
|
||||
+30
-31
@@ -28,13 +28,13 @@ import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
||||
import com.seibel.distanthorizons.core.util.gridList.MovableGridRingList;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Queue;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
/**
|
||||
* This class represents a quadTree of T type values.
|
||||
@@ -96,9 +96,9 @@ public class QuadTree<T>
|
||||
//=====================//
|
||||
|
||||
/** @return the node at the given section position */
|
||||
public final QuadNode<T> getNode(DhSectionPos pos) throws IndexOutOfBoundsException { return this.getOrSetNode(pos, false, null, true); }
|
||||
public final QuadNode<T> getNode(long pos) throws IndexOutOfBoundsException { return this.getOrSetNode(pos, false, null, true); }
|
||||
/** @return the value at the given section position */
|
||||
public final T getValue(DhSectionPos pos) throws IndexOutOfBoundsException
|
||||
public final T getValue(long pos) throws IndexOutOfBoundsException
|
||||
{
|
||||
QuadNode<T> node = this.getNode(pos);
|
||||
if (node != null)
|
||||
@@ -109,7 +109,7 @@ public class QuadTree<T>
|
||||
}
|
||||
|
||||
/** @return the value that was previously in the given position, null if nothing */
|
||||
public final T setValue(DhSectionPos pos, T value) throws IndexOutOfBoundsException
|
||||
public final T setValue(long pos, T value) throws IndexOutOfBoundsException
|
||||
{
|
||||
T previousValue = this.getValue(pos);
|
||||
this.getOrSetNode(pos, true, value, true);
|
||||
@@ -117,21 +117,21 @@ public class QuadTree<T>
|
||||
}
|
||||
|
||||
/** @param runBoundaryChecks should only ever be set to true internally for removing out of bound nodes */
|
||||
protected final QuadNode<T> getOrSetNode(DhSectionPos pos, boolean setNewValue, T newValue, boolean runBoundaryChecks) throws IndexOutOfBoundsException
|
||||
protected final QuadNode<T> getOrSetNode(long pos, boolean setNewValue, T newValue, boolean runBoundaryChecks) throws IndexOutOfBoundsException
|
||||
{
|
||||
if (runBoundaryChecks && !this.isSectionPosInBounds(pos))
|
||||
{
|
||||
int radius = this.diameterInBlocks() / 2;
|
||||
DhBlockPos2D minPos = this.getCenterBlockPos().add(new DhBlockPos2D(-radius, -radius));
|
||||
DhBlockPos2D maxPos = this.getCenterBlockPos().add(new DhBlockPos2D(radius, radius));
|
||||
throw new IndexOutOfBoundsException("QuadTree GetOrSet failed. Position out of bounds, min pos: " + minPos + ", max pos: " + maxPos + ", min detail level: " + this.treeMaxDetailLevel + ", max detail level: " + this.treeMinDetailLevel + ". Given Position: " + pos + " = block pos: " + pos.convertNewToDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL));
|
||||
throw new IndexOutOfBoundsException("QuadTree GetOrSet failed. Position out of bounds, min pos: " + minPos + ", max pos: " + maxPos + ", min detail level: " + this.treeMaxDetailLevel + ", max detail level: " + this.treeMinDetailLevel + ". Given Position: [" + DhSectionPos.toString(pos) + "] = block pos: " + DhSectionPos.convertToDetailLevel(pos, LodUtil.BLOCK_DETAIL_LEVEL));
|
||||
}
|
||||
|
||||
|
||||
|
||||
DhSectionPos rootPos = pos.convertNewToDetailLevel(this.treeMinDetailLevel);
|
||||
int ringListPosX = rootPos.getX();
|
||||
int ringListPosZ = rootPos.getZ();
|
||||
long rootPos = DhSectionPos.convertToDetailLevel(pos, this.treeMinDetailLevel);
|
||||
int ringListPosX = DhSectionPos.getX(rootPos);
|
||||
int ringListPosZ = DhSectionPos.getZ(rootPos);
|
||||
|
||||
QuadNode<T> topQuadNode = this.topRingList.get(ringListPosX, ringListPosZ);
|
||||
if (topQuadNode == null)
|
||||
@@ -149,7 +149,7 @@ public class QuadTree<T>
|
||||
}
|
||||
}
|
||||
|
||||
if (!topQuadNode.sectionPos.contains(pos))
|
||||
if (!DhSectionPos.contains(topQuadNode.sectionPos, pos))
|
||||
{
|
||||
LodUtil.assertNotReach("failed to get a root node that contains the input position: " + pos + " root node pos: " + topQuadNode.sectionPos);
|
||||
}
|
||||
@@ -163,10 +163,10 @@ public class QuadTree<T>
|
||||
return returnNode;
|
||||
}
|
||||
|
||||
public boolean isSectionPosInBounds(DhSectionPos testPos)
|
||||
public boolean isSectionPosInBounds(long testPos)
|
||||
{
|
||||
// check if the testPos is within the detail level limits of the tree
|
||||
boolean detailLevelWithinBounds = this.treeMaxDetailLevel <= testPos.getDetailLevel() && testPos.getDetailLevel() <= this.treeMinDetailLevel;
|
||||
boolean detailLevelWithinBounds = this.treeMaxDetailLevel <= DhSectionPos.getDetailLevel(testPos) && DhSectionPos.getDetailLevel(testPos) <= this.treeMinDetailLevel;
|
||||
if (!detailLevelWithinBounds)
|
||||
{
|
||||
return false;
|
||||
@@ -177,9 +177,9 @@ public class QuadTree<T>
|
||||
DhBlockPos2D treeBlockCorner = this.centerBlockPos.add(new DhBlockPos2D(-this.diameterInBlocks / 2, -this.diameterInBlocks / 2));
|
||||
DhLodPos treeCornerPos = new DhLodPos((byte) 0, treeBlockCorner.x, treeBlockCorner.z);
|
||||
|
||||
DhSectionPos inputSectionCorner = testPos.convertNewToDetailLevel((byte) 0);
|
||||
DhLodPos inputCornerPos = new DhLodPos((byte) 0, inputSectionCorner.getX(), inputSectionCorner.getZ());
|
||||
int inputBlockWidth = BitShiftUtil.powerOfTwo(testPos.getDetailLevel());
|
||||
long inputSectionCorner = DhSectionPos.convertToDetailLevel(testPos, (byte) 0);
|
||||
DhLodPos inputCornerPos = new DhLodPos((byte) 0, DhSectionPos.getX(inputSectionCorner), DhSectionPos.getZ(inputSectionCorner));
|
||||
int inputBlockWidth = BitShiftUtil.powerOfTwo(DhSectionPos.getDetailLevel(testPos));
|
||||
|
||||
return DoSquaresOverlap(treeCornerPos, this.diameterInBlocks, inputCornerPos, inputBlockWidth);
|
||||
}
|
||||
@@ -205,13 +205,13 @@ public class QuadTree<T>
|
||||
}
|
||||
|
||||
|
||||
public int getNonNullChildCountAtPos(DhSectionPos pos) { return this.getChildCountAtPos(pos, false); }
|
||||
public int getChildCountAtPos(DhSectionPos pos, boolean includeNullValues)
|
||||
public int getNonNullChildCountAtPos(long pos) { return this.getChildCountAtPos(pos, false); }
|
||||
public int getChildCountAtPos(long pos, boolean includeNullValues)
|
||||
{
|
||||
int childCount = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
DhSectionPos childPos = pos.getChildByIndex(i);
|
||||
long childPos = DhSectionPos.getChildByIndex(pos, i);
|
||||
if (this.isSectionPosInBounds(childPos))
|
||||
{
|
||||
T value = this.getValue(childPos);
|
||||
@@ -232,7 +232,7 @@ public class QuadTree<T>
|
||||
//===========//
|
||||
|
||||
/** can include null nodes */
|
||||
public Iterator<DhSectionPos> rootNodePosIterator() { return new QuadTreeRootPosIterator(true); }
|
||||
public LongIterator rootNodePosIterator() { return new QuadTreeRootPosIterator(true); }
|
||||
|
||||
public Iterator<QuadNode<T>> nodeIterator() { return new QuadTreeNodeIterator(false); }
|
||||
public Iterator<QuadNode<T>> leafNodeIterator() { return new QuadTreeNodeIterator(true); }
|
||||
@@ -397,9 +397,9 @@ public class QuadTree<T>
|
||||
// iterator classes //
|
||||
//==================//
|
||||
|
||||
private class QuadTreeRootPosIterator implements Iterator<DhSectionPos>
|
||||
private class QuadTreeRootPosIterator implements LongIterator
|
||||
{
|
||||
private final Queue<DhSectionPos> iteratorPosQueue = new LinkedList<>();
|
||||
private final LongArrayFIFOQueue iteratorPosQueue = new LongArrayFIFOQueue();
|
||||
|
||||
|
||||
|
||||
@@ -409,11 +409,10 @@ public class QuadTree<T>
|
||||
{
|
||||
if (node != null || includeNullNodes)
|
||||
{
|
||||
// TODO can these DhSectionPos be pooled?
|
||||
DhSectionPos rootPos = new DhSectionPos(QuadTree.this.treeMinDetailLevel, pos2D.x, pos2D.y);
|
||||
long rootPos = DhSectionPos.encode(QuadTree.this.treeMinDetailLevel, pos2D.x, pos2D.y);
|
||||
if (QuadTree.this.isSectionPosInBounds(rootPos))
|
||||
{
|
||||
this.iteratorPosQueue.add(rootPos);
|
||||
this.iteratorPosQueue.enqueue(rootPos);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -425,7 +424,7 @@ public class QuadTree<T>
|
||||
public boolean hasNext() { return this.iteratorPosQueue.size() != 0; }
|
||||
|
||||
@Override
|
||||
public DhSectionPos next()
|
||||
public long nextLong()
|
||||
{
|
||||
if (this.iteratorPosQueue.size() == 0)
|
||||
{
|
||||
@@ -433,7 +432,7 @@ public class QuadTree<T>
|
||||
}
|
||||
|
||||
|
||||
DhSectionPos sectionPos = this.iteratorPosQueue.poll();
|
||||
long sectionPos = this.iteratorPosQueue.dequeueLong();
|
||||
return sectionPos;
|
||||
}
|
||||
|
||||
@@ -443,7 +442,7 @@ public class QuadTree<T>
|
||||
public void remove() { throw new UnsupportedOperationException("remove"); }
|
||||
|
||||
@Override
|
||||
public void forEachRemaining(Consumer<? super DhSectionPos> action) { Iterator.super.forEachRemaining(action); }
|
||||
public void forEachRemaining(LongConsumer action) { LongIterator.super.forEachRemaining(action); }
|
||||
|
||||
}
|
||||
|
||||
@@ -501,7 +500,7 @@ public class QuadTree<T>
|
||||
Iterator<QuadNode<T>> nodeIterator = null;
|
||||
while ((nodeIterator == null || !nodeIterator.hasNext()) && this.rootNodeIterator.hasNext())
|
||||
{
|
||||
DhSectionPos sectionPos = this.rootNodeIterator.next();
|
||||
long sectionPos = this.rootNodeIterator.nextLong();
|
||||
QuadNode<T> rootNode = QuadTree.this.getNode(sectionPos);
|
||||
if (rootNode != null)
|
||||
{
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.util.objects.quadTree.iterators;
|
||||
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -36,7 +37,7 @@ public class QuadNodeChildIndexIterator<T> implements Iterator<Integer>
|
||||
public QuadNodeChildIndexIterator(QuadNode<T> parentNode, boolean returnNullChildPos)
|
||||
{
|
||||
// only get the children if this section isn't at the bottom of the tree
|
||||
if (parentNode.sectionPos.getDetailLevel() > parentNode.minimumDetailLevel)
|
||||
if (DhSectionPos.getDetailLevel(parentNode.sectionPos) > parentNode.minimumDetailLevel)
|
||||
{
|
||||
// go over each child pos
|
||||
for (int i = 0; i < 4; i++)
|
||||
|
||||
+6
-6
@@ -21,12 +21,12 @@ package com.seibel.distanthorizons.core.util.objects.quadTree.iterators;
|
||||
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
public class QuadNodeDirectChildPosIterator<T> implements Iterator<DhSectionPos>
|
||||
public class QuadNodeDirectChildPosIterator<T> implements LongIterator
|
||||
{
|
||||
private final QuadNodeChildIndexIterator<T> childIndexIterator;
|
||||
private final QuadNode<T> parentNode;
|
||||
@@ -44,7 +44,7 @@ public class QuadNodeDirectChildPosIterator<T> implements Iterator<DhSectionPos>
|
||||
public boolean hasNext() { return this.childIndexIterator.hasNext(); }
|
||||
|
||||
@Override
|
||||
public DhSectionPos next()
|
||||
public long nextLong()
|
||||
{
|
||||
if (!this.hasNext())
|
||||
{
|
||||
@@ -53,7 +53,7 @@ public class QuadNodeDirectChildPosIterator<T> implements Iterator<DhSectionPos>
|
||||
|
||||
|
||||
int childIndex = this.childIndexIterator.next();
|
||||
DhSectionPos sectionPos = this.parentNode.sectionPos.getChildByIndex(childIndex);
|
||||
long sectionPos = DhSectionPos.getChildByIndex(this.parentNode.sectionPos, childIndex);
|
||||
return sectionPos;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,6 @@ public class QuadNodeDirectChildPosIterator<T> implements Iterator<DhSectionPos>
|
||||
public void remove() { throw new UnsupportedOperationException("remove"); }
|
||||
|
||||
@Override
|
||||
public void forEachRemaining(Consumer<? super DhSectionPos> action) { Iterator.super.forEachRemaining(action); }
|
||||
public void forEachRemaining(LongConsumer action) { LongIterator.super.forEachRemaining(action); }
|
||||
|
||||
}
|
||||
+2
-1
@@ -19,6 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.util.objects.quadTree.iterators;
|
||||
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
|
||||
|
||||
import java.util.Iterator;
|
||||
@@ -46,7 +47,7 @@ public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>>
|
||||
this.onlyReturnLeafValues = onlyReturnLeafValues;
|
||||
// TODO the naming conversion for these are flipped in a lot of places
|
||||
this.highestDetailLevel = rootNode.minimumDetailLevel;
|
||||
this.iteratorDetailLevel = rootNode.sectionPos.getDetailLevel();
|
||||
this.iteratorDetailLevel = DhSectionPos.getDetailLevel(rootNode.sectionPos);
|
||||
|
||||
|
||||
if (!this.onlyReturnLeafValues)
|
||||
|
||||
+5
-5
@@ -32,7 +32,7 @@ public class PositionalLockProvider
|
||||
private static final int MAX_NUMBER_OF_LOCKS = 100;
|
||||
|
||||
|
||||
private final ConcurrentHashMap<DhSectionPos, ExpiringLock> lockByPos = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Long, ExpiringLock> lockByPos = new ConcurrentHashMap<>();
|
||||
|
||||
private final AtomicBoolean lockRemovalThreadRunning = new AtomicBoolean(false);
|
||||
|
||||
@@ -50,7 +50,7 @@ public class PositionalLockProvider
|
||||
// getter //
|
||||
//========//
|
||||
|
||||
public ReentrantLock getLock(DhSectionPos pos)
|
||||
public ReentrantLock getLock(long pos)
|
||||
{
|
||||
return this.lockByPos.compute(pos, (ignorePos, lock) ->
|
||||
{
|
||||
@@ -76,14 +76,14 @@ public class PositionalLockProvider
|
||||
Thread.sleep(CLEANUP_THREAD_MAX_FREQUENCY_IN_MS);
|
||||
|
||||
// walk over every lock and check which ones need to be removed
|
||||
Iterator<DhSectionPos> keySet = this.lockByPos.keySet().iterator();
|
||||
Iterator<Long> keySet = this.lockByPos.keySet().iterator();
|
||||
while (keySet.hasNext())
|
||||
{
|
||||
try
|
||||
{
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
DhSectionPos pos = keySet.next();
|
||||
long pos = keySet.next();
|
||||
ExpiringLock lock = this.lockByPos.get(pos);
|
||||
|
||||
// don't try removing a lock that's currently in use
|
||||
@@ -92,7 +92,7 @@ public class PositionalLockProvider
|
||||
if (currentTime > lock.expirationTimeInMs)
|
||||
{
|
||||
this.lockByPos.remove(pos);
|
||||
//LOGGER.info("removed lock: "+pos);
|
||||
//LOGGER.info("removed lock: "+DhSectionPos.toString(pos));
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
+34
@@ -40,9 +40,43 @@ import java.util.HashSet;
|
||||
public interface IWrapperFactory extends IDhApiWrapperFactory, IBindable
|
||||
{
|
||||
AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel);
|
||||
|
||||
IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException;
|
||||
IBiomeWrapper getPlainsBiomeWrapper(ILevelWrapper levelWrapper); // TODO it would be nice to remove the level wrapper if possible to put this in line with getAirBlockStateWrapper() but it isn't necessary
|
||||
default IBiomeWrapper deserializeBiomeWrapperOrGetDefault(String str, ILevelWrapper levelWrapper)
|
||||
{
|
||||
IBiomeWrapper biome;
|
||||
try
|
||||
{
|
||||
biome = this.deserializeBiomeWrapper(str, levelWrapper);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
biome = this.getPlainsBiomeWrapper(levelWrapper);
|
||||
}
|
||||
|
||||
return biome;
|
||||
}
|
||||
|
||||
|
||||
IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException;
|
||||
IBlockStateWrapper getAirBlockStateWrapper();
|
||||
default IBlockStateWrapper deserializeBlockStateWrapperOrGetDefault(String str, ILevelWrapper levelWrapper)
|
||||
{
|
||||
IBlockStateWrapper blockState;
|
||||
try
|
||||
{
|
||||
blockState = this.deserializeBlockStateWrapper(str, levelWrapper);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
blockState = this.getAirBlockStateWrapper();
|
||||
}
|
||||
|
||||
return blockState;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the set of {@link IBlockStateWrapper}'s that shouldn't be rendered. <br>
|
||||
* Generally this contains blocks like: air, barriers, light blocks, etc.
|
||||
|
||||
+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();
|
||||
|
||||
|
||||
+4
@@ -37,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":
|
||||
@@ -573,6 +573,8 @@
|
||||
"Show World Gen Queue",
|
||||
"distanthorizons.config.client.advanced.debugging.debugWireframe.showRenderSectionStatus":
|
||||
"Show Render Section Status",
|
||||
"distanthorizons.config.client.advanced.debugging.debugWireframe.showQuadTreeRenderStatus":
|
||||
"Show Quad Tree Render Status",
|
||||
"distanthorizons.config.client.advanced.debugging.debugWireframe.showFullDataUpdateStatus":
|
||||
"Show Full Data Update Status",
|
||||
|
||||
@@ -776,13 +778,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",
|
||||
|
||||
@@ -21,15 +21,14 @@ package tests;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
|
||||
import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.CharacterIterator;
|
||||
import java.text.StringCharacterIterator;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* <strong>Note:</strong>
|
||||
@@ -213,11 +212,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
|
||||
@@ -272,7 +271,7 @@ public class CompressionTest
|
||||
|
||||
|
||||
|
||||
ArrayList<DhSectionPos> positionList = uncompressedRepo.getAllPositions();
|
||||
LongArrayList positionList = uncompressedRepo.getAllPositions();
|
||||
totalUncompressedFileSizeInBytes = uncompressedRepo.getTotalDataSizeInBytes();
|
||||
System.out.println("Found [" + positionList.size() + "] DTOs.");
|
||||
|
||||
@@ -282,7 +281,7 @@ public class CompressionTest
|
||||
{
|
||||
try
|
||||
{
|
||||
DhSectionPos pos = positionList.get(i);
|
||||
long pos = positionList.getLong(i);
|
||||
if (i % 20 == 0)
|
||||
{
|
||||
System.out.println(i + "/" + maxTestPosition);
|
||||
@@ -293,7 +292,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);
|
||||
|
||||
@@ -19,99 +19,143 @@
|
||||
|
||||
package tests;
|
||||
|
||||
import com.seibel.distanthorizons.core.pos.*;
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class DhSectionPosTest
|
||||
{
|
||||
|
||||
@Test
|
||||
public void ContainsPosTest()
|
||||
public void basicEncodeDecodeTest()
|
||||
{
|
||||
DhSectionPos root = new DhSectionPos((byte) 10, 0, 0);
|
||||
DhSectionPos child = new DhSectionPos((byte) 9, 1, 1);
|
||||
long pos;
|
||||
|
||||
Assert.assertTrue("section pos contains fail", root.contains(child));
|
||||
Assert.assertFalse("section pos contains fail", child.contains(root));
|
||||
// zero pos
|
||||
pos = DhSectionPos.encode((byte) 0, 0, 0);
|
||||
assertSectionPosEqual(0, DhSectionPos.getDetailLevel(pos));
|
||||
assertSectionPosEqual(0, DhSectionPos.getX(pos));
|
||||
assertSectionPosEqual(0, DhSectionPos.getZ(pos));
|
||||
|
||||
// positive values
|
||||
pos = DhSectionPos.encode((byte) 10, 4, 1);
|
||||
assertSectionPosEqual(10, DhSectionPos.getDetailLevel(pos));
|
||||
assertSectionPosEqual(4, DhSectionPos.getX(pos));
|
||||
assertSectionPosEqual(1, DhSectionPos.getZ(pos));
|
||||
|
||||
// negative position, positive detail level
|
||||
pos = DhSectionPos.encode((byte) 2, -1, -4);
|
||||
assertSectionPosEqual(2, DhSectionPos.getDetailLevel(pos));
|
||||
assertSectionPosEqual(-1, DhSectionPos.getX(pos));
|
||||
assertSectionPosEqual(-4, DhSectionPos.getZ(pos));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void containsPosTest()
|
||||
{
|
||||
long root = DhSectionPos.encode((byte) 10, 0, 0);
|
||||
long child = DhSectionPos.encode((byte) 9, 1, 1);
|
||||
|
||||
Assert.assertTrue("section pos contains fail", DhSectionPos.contains(root, child));
|
||||
Assert.assertFalse("section pos contains fail", DhSectionPos.contains(child, root));
|
||||
|
||||
|
||||
root = new DhSectionPos((byte) 10, 1, 0);
|
||||
root = DhSectionPos.encode((byte) 10, 1, 0);
|
||||
|
||||
// out of bounds
|
||||
child = new DhSectionPos((byte) 9, 0, 0);
|
||||
Assert.assertFalse("position should be out of bounds", root.contains(child));
|
||||
child = new DhSectionPos((byte) 9, 1, 1);
|
||||
Assert.assertFalse("position should be out of bounds", root.contains(child));
|
||||
child = DhSectionPos.encode((byte) 9, 0, 0);
|
||||
Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child));
|
||||
child = DhSectionPos.encode((byte) 9, 1, 1);
|
||||
Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child));
|
||||
|
||||
// in bounds
|
||||
child = new DhSectionPos((byte) 9, 2, 0);
|
||||
Assert.assertTrue("position should be in bounds", root.contains(child));
|
||||
child = new DhSectionPos((byte) 9, 3, 1);
|
||||
Assert.assertTrue("position should be in bounds", root.contains(child));
|
||||
child = DhSectionPos.encode((byte) 9, 2, 0);
|
||||
Assert.assertTrue("position should be in bounds", DhSectionPos.contains(root, child));
|
||||
child = DhSectionPos.encode((byte) 9, 3, 1);
|
||||
Assert.assertTrue("position should be in bounds", DhSectionPos.contains(root, child));
|
||||
|
||||
// out of bounds
|
||||
child = new DhSectionPos((byte) 9, 2, 2);
|
||||
Assert.assertFalse("position should be out of bounds", root.contains(child));
|
||||
child = new DhSectionPos((byte) 9, 3, 3);
|
||||
Assert.assertFalse("position should be out of bounds", root.contains(child));
|
||||
child = DhSectionPos.encode((byte) 9, 2, 2);
|
||||
Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child));
|
||||
child = DhSectionPos.encode((byte) 9, 3, 3);
|
||||
Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child));
|
||||
|
||||
child = new DhSectionPos((byte) 9, 4, 4);
|
||||
Assert.assertFalse("position should be out of bounds", root.contains(child));
|
||||
child = new DhSectionPos((byte) 9, 5, 5);
|
||||
Assert.assertFalse("position should be out of bounds", root.contains(child));
|
||||
child = DhSectionPos.encode((byte) 9, 4, 4);
|
||||
Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child));
|
||||
child = DhSectionPos.encode((byte) 9, 5, 5);
|
||||
Assert.assertFalse("position should be out of bounds", DhSectionPos.contains(root, child));
|
||||
|
||||
|
||||
Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 0, 0, 0)));
|
||||
Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 1, 0, 0)));
|
||||
Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 2, 0, 0)));
|
||||
Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 3, 0, 0)));
|
||||
Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 4, 0, 0)));
|
||||
Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 5, 0, 0)));
|
||||
Assert.assertTrue(DhSectionPos.contains(DhSectionPos.encode((byte) 6, 0, 0), DhSectionPos.encode((byte) 6, 0, 0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ContainsAdjacentPosTest()
|
||||
public void containsAdjacentPosTest()
|
||||
{
|
||||
// neither should contain the other, they are single blocks that are next to each other
|
||||
DhSectionPos left = new DhSectionPos((byte) 0, 4606, 0);
|
||||
DhSectionPos right = new DhSectionPos((byte) 0, 4607, 0);
|
||||
Assert.assertFalse(left.contains(right));
|
||||
Assert.assertFalse(right.contains(left));
|
||||
|
||||
|
||||
long left = DhSectionPos.encode((byte) 0, 4606, 0);
|
||||
long right = DhSectionPos.encode((byte) 0, 4607, 0);
|
||||
Assert.assertFalse(DhSectionPos.contains(left, right));
|
||||
Assert.assertFalse(DhSectionPos.contains(right, left));
|
||||
|
||||
|
||||
// 512 block wide sections that are adjacent, but not overlapping
|
||||
left = new DhSectionPos((byte) 9, 0, 0);
|
||||
right = new DhSectionPos((byte) 9, 1, 0);
|
||||
Assert.assertFalse(left.contains(right));
|
||||
Assert.assertFalse(right.contains(left));
|
||||
|
||||
left = DhSectionPos.encode((byte) 9, 0, 0);
|
||||
right = DhSectionPos.encode((byte) 9, 1, 0);
|
||||
Assert.assertFalse(DhSectionPos.contains(left, right));
|
||||
Assert.assertFalse(DhSectionPos.contains(right, left));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ParentPosTest()
|
||||
public void parentPosTest()
|
||||
{
|
||||
DhSectionPos leaf = new DhSectionPos((byte) 0, 0, 0);
|
||||
DhSectionPos convert = leaf.convertNewToDetailLevel((byte) 1);
|
||||
DhSectionPos parent = leaf.getParentPos();
|
||||
Assert.assertEquals("get parent at 0,0 fail", convert, parent);
|
||||
|
||||
|
||||
leaf = new DhSectionPos((byte) 0, 1, 1);
|
||||
convert = leaf.convertNewToDetailLevel((byte) 1);
|
||||
parent = leaf.getParentPos();
|
||||
Assert.assertEquals("get parent at 1,1 fail", convert, parent);
|
||||
|
||||
|
||||
leaf = new DhSectionPos((byte) 1, 2, 2);
|
||||
convert = leaf.convertNewToDetailLevel((byte) 2);
|
||||
parent = leaf.getParentPos();
|
||||
Assert.assertEquals("parent upscale fail", convert, parent);
|
||||
convert = leaf.convertNewToDetailLevel((byte) 0);
|
||||
DhSectionPos childIndex = leaf.getChildByIndex(0);
|
||||
Assert.assertEquals("child detail fail", convert, childIndex);
|
||||
|
||||
long leaf = DhSectionPos.encode((byte) 0, 0, 0);
|
||||
long convert = DhSectionPos.convertToDetailLevel(leaf, (byte) 1);
|
||||
long parent = DhSectionPos.getParentPos(leaf);
|
||||
assertSectionPosEqual("get parent at 0,0 fail", convert, parent);
|
||||
|
||||
|
||||
leaf = DhSectionPos.encode((byte) 0, 1, 1);
|
||||
convert = DhSectionPos.convertToDetailLevel(leaf, (byte) 1);
|
||||
parent = DhSectionPos.getParentPos(leaf);
|
||||
assertSectionPosEqual("get parent at 1,1 fail", convert, parent);
|
||||
|
||||
|
||||
leaf = DhSectionPos.encode((byte) 1, 2, 2);
|
||||
convert = DhSectionPos.convertToDetailLevel(leaf, (byte) 2);
|
||||
parent = DhSectionPos.getParentPos(leaf);
|
||||
assertSectionPosEqual("parent upscale fail", convert, parent);
|
||||
convert = DhSectionPos.convertToDetailLevel(leaf, (byte) 0);
|
||||
long childIndex = DhSectionPos.getChildByIndex(leaf, 0);
|
||||
assertSectionPosEqual("child detail fail", convert, childIndex);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ChildPosTest()
|
||||
public void childPosTest()
|
||||
{
|
||||
DhSectionPos node = new DhSectionPos((byte) 1, 2302, 0);
|
||||
DhSectionPos nw = node.getChildByIndex(0);
|
||||
DhSectionPos sw = node.getChildByIndex(1);
|
||||
DhSectionPos ne = node.getChildByIndex(2);
|
||||
DhSectionPos se = node.getChildByIndex(3);
|
||||
long node = DhSectionPos.encode((byte) 1, 2302, 0);
|
||||
long nw = DhSectionPos.getChildByIndex(node, 0);
|
||||
long sw = DhSectionPos.getChildByIndex(node, 1);
|
||||
long ne = DhSectionPos.getChildByIndex(node, 2);
|
||||
long se = DhSectionPos.getChildByIndex(node, 3);
|
||||
|
||||
// confirm no children have the same values
|
||||
Assert.assertNotEquals(nw, sw);
|
||||
@@ -119,244 +163,337 @@ public class DhSectionPosTest
|
||||
Assert.assertNotEquals(ne, se);
|
||||
|
||||
// confirm each child has the correct value
|
||||
Assert.assertEquals(nw, new DhSectionPos((byte) 0, 4604, 0));
|
||||
Assert.assertEquals(sw, new DhSectionPos((byte) 0, 4605, 0));
|
||||
Assert.assertEquals(ne, new DhSectionPos((byte) 0, 4604, 1));
|
||||
Assert.assertEquals(se, new DhSectionPos((byte) 0, 4605, 1));
|
||||
assertSectionPosEqual(nw, DhSectionPos.encode((byte) 0, 4604, 0));
|
||||
assertSectionPosEqual(sw, DhSectionPos.encode((byte) 0, 4605, 0));
|
||||
assertSectionPosEqual(ne, DhSectionPos.encode((byte) 0, 4604, 1));
|
||||
assertSectionPosEqual(se, DhSectionPos.encode((byte) 0, 4605, 1));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void GetCenterTest()
|
||||
public void getCenterBlock2DTest()
|
||||
{
|
||||
DhSectionPos node = new DhSectionPos((byte) 1, 2303, 0);
|
||||
DhBlockPos2D centerBlockPos = node.getCenterBlockPos();
|
||||
DhBlockPos2D expectedCenterNode = new DhBlockPos2D(4606, 0);
|
||||
Assert.assertEquals("", expectedCenterNode, centerBlockPos);
|
||||
long parentNode = DhSectionPos.encode((byte) 2, 1151, 0); // width 4 blocks
|
||||
long inputPos = DhSectionPos.encode((byte) 0, 4606, 0); // width 1 block
|
||||
Assert.assertTrue(DhSectionPos.contains(parentNode, inputPos));
|
||||
|
||||
DhBlockPos2D parentCenter = DhSectionPos.getCenterBlockPos(parentNode);
|
||||
DhBlockPos2D inputCenter = DhSectionPos.getCenterBlockPos(inputPos);
|
||||
|
||||
|
||||
|
||||
node = new DhSectionPos((byte) 10, 0, 0); // 1024 blocks wide
|
||||
centerBlockPos = node.getCenterBlockPos();
|
||||
expectedCenterNode = new DhBlockPos2D(1024 / 2, 1024 / 2);
|
||||
Assert.assertEquals("", expectedCenterNode, centerBlockPos);
|
||||
Assert.assertEquals(new DhBlockPos2D(4606, 2), parentCenter);
|
||||
Assert.assertEquals(new DhBlockPos2D(4606, 0), inputCenter);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void GetCenter2Test()
|
||||
{
|
||||
DhSectionPos parentNode = new DhSectionPos((byte) 2, 1151, 0); // width 4 blocks
|
||||
DhSectionPos inputPos = new DhSectionPos((byte) 0, 4606, 0); // width 1 block
|
||||
Assert.assertTrue(parentNode.contains(inputPos));
|
||||
|
||||
DhBlockPos2D parentCenter = parentNode.getCenterBlockPos();
|
||||
DhBlockPos2D inputCenter = inputPos.getCenterBlockPos();
|
||||
|
||||
Assert.assertEquals(new DhBlockPos2D(4606, 2), parentCenter);
|
||||
Assert.assertEquals(new DhBlockPos2D(4606, 0), inputCenter);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void CreateFromBlockPos()
|
||||
public void createFromBlockPos()
|
||||
{
|
||||
// origin pos //
|
||||
|
||||
DhBlockPos originBlockPos = new DhBlockPos(0, 0, 0);
|
||||
DhSectionPos originSectionPos = new DhSectionPos(originBlockPos);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos);
|
||||
long originsectionPos = DhSectionPos.encode(originBlockPos);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originsectionPos);
|
||||
|
||||
|
||||
// offset pos //
|
||||
long offsetSectionPos;
|
||||
|
||||
DhBlockPos offsetBlockPos = new DhBlockPos(1000, 0, 42000);
|
||||
DhSectionPos offsetSectionPos = new DhSectionPos(offsetBlockPos);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos);
|
||||
offsetSectionPos = DhSectionPos.encode(offsetBlockPos);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos);
|
||||
|
||||
offsetBlockPos = new DhBlockPos(-987654, 0, 46);
|
||||
offsetSectionPos = new DhSectionPos(offsetBlockPos);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos);
|
||||
offsetSectionPos = DhSectionPos.encode(offsetBlockPos);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void CreateFromBlockPos2D()
|
||||
public void createFromBlockPos2D()
|
||||
{
|
||||
// origin pos //
|
||||
|
||||
DhBlockPos2D originBlockPos = new DhBlockPos2D(0, 0);
|
||||
DhSectionPos originSectionPos = new DhSectionPos(originBlockPos);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos);
|
||||
long originSectionPos = DhSectionPos.encode(originBlockPos);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos);
|
||||
|
||||
|
||||
// offset pos //
|
||||
|
||||
DhBlockPos2D offsetBlockPos = new DhBlockPos2D(1000, 42000);
|
||||
DhSectionPos offsetSectionPos = new DhSectionPos(offsetBlockPos);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos);
|
||||
long offsetSectionPos = DhSectionPos.encode(offsetBlockPos);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos);
|
||||
|
||||
offsetBlockPos = new DhBlockPos2D(-987654, 46);
|
||||
offsetSectionPos = new DhSectionPos(offsetBlockPos);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos);
|
||||
offsetSectionPos = DhSectionPos.encode(offsetBlockPos);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void CreateFromChunkPos()
|
||||
public void createFromChunkPos()
|
||||
{
|
||||
// origin pos //
|
||||
|
||||
DhChunkPos originChunkPos = new DhChunkPos(0,0);
|
||||
DhSectionPos originSectionPos = new DhSectionPos(originChunkPos);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, 0, 0), originSectionPos);
|
||||
long originSectionPos = DhSectionPos.encode(originChunkPos);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, 0, 0), originSectionPos);
|
||||
|
||||
|
||||
// offset pos //
|
||||
|
||||
DhChunkPos offsetChunkPos = new DhChunkPos(1000, 42000);
|
||||
DhSectionPos offsetSectionPos = new DhSectionPos(offsetChunkPos);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, 15, 656), offsetSectionPos);
|
||||
long offsetSectionPos = DhSectionPos.encode(offsetChunkPos);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, 15, 656), offsetSectionPos);
|
||||
|
||||
offsetChunkPos = new DhChunkPos(-987654, 46);
|
||||
offsetSectionPos = new DhSectionPos(offsetChunkPos);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, -15433, 0), offsetSectionPos);
|
||||
offsetSectionPos = DhSectionPos.encode(offsetChunkPos);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, -15433, 0), offsetSectionPos);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ConvertToDetailLevel()
|
||||
public void convertToDetailLevel()
|
||||
{
|
||||
// origin pos //
|
||||
|
||||
DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0);
|
||||
long originSectionPos = DhSectionPos.encode((byte) 0,0,0);
|
||||
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1);
|
||||
Assert.assertEquals(new DhSectionPos((byte) 1, 0, 0), originSectionPos);
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 1);
|
||||
assertSectionPosEqual(DhSectionPos.encode((byte) 1, 0, 0), originSectionPos);
|
||||
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos);
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos);
|
||||
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_REGION_DETAIL_LEVEL, 0, 0), originSectionPos);
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_REGION_DETAIL_LEVEL, 0, 0), originSectionPos);
|
||||
|
||||
|
||||
// offset pos //
|
||||
|
||||
DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000);
|
||||
long offsetSectionPos = DhSectionPos.encode((byte) 0,-10000,5000);
|
||||
|
||||
sectionPos = sectionPos.convertNewToDetailLevel((byte) 1);
|
||||
Assert.assertEquals(new DhSectionPos((byte) 1, -5000, 2500), sectionPos);
|
||||
offsetSectionPos = DhSectionPos.convertToDetailLevel(offsetSectionPos, (byte) 1);
|
||||
assertSectionPosEqual(DhSectionPos.encode((byte) 1, -5000, 2500), offsetSectionPos);
|
||||
|
||||
sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -157, 78), sectionPos);
|
||||
offsetSectionPos = DhSectionPos.convertToDetailLevel(offsetSectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -157, 78), offsetSectionPos);
|
||||
|
||||
sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhSectionPos(DhSectionPos.SECTION_REGION_DETAIL_LEVEL, -1, 0), sectionPos);
|
||||
offsetSectionPos = DhSectionPos.convertToDetailLevel(offsetSectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_REGION_DETAIL_LEVEL, -1, 0), offsetSectionPos);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void GetOffsetWidth()
|
||||
public void getOffsetWidth()
|
||||
{
|
||||
DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0);
|
||||
DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000);
|
||||
long originSectionPos = DhSectionPos.encode((byte) 0,0,0);
|
||||
long sectionPos = DhSectionPos.encode((byte) 0,-10000,5000);
|
||||
|
||||
|
||||
|
||||
// 1 -> 0
|
||||
byte returnDetailLevel = 0;
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1);
|
||||
Assert.assertEquals(2, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel));
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 1);
|
||||
assertSectionPosEqual(2, DhSectionPos.getWidthCountForLowerDetailedSection(originSectionPos, returnDetailLevel));
|
||||
|
||||
sectionPos = sectionPos.convertNewToDetailLevel((byte) 1);
|
||||
Assert.assertEquals(2, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, (byte) 1);
|
||||
assertSectionPosEqual(2, DhSectionPos.getWidthCountForLowerDetailedSection(sectionPos, returnDetailLevel));
|
||||
|
||||
|
||||
// 2 -> 1
|
||||
returnDetailLevel = 1;
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 2);
|
||||
Assert.assertEquals(2, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel));
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 2);
|
||||
assertSectionPosEqual(2, DhSectionPos.getWidthCountForLowerDetailedSection(originSectionPos, returnDetailLevel));
|
||||
|
||||
sectionPos = sectionPos.convertNewToDetailLevel((byte) 2);
|
||||
Assert.assertEquals(2, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, (byte) 2);
|
||||
assertSectionPosEqual(2, DhSectionPos.getWidthCountForLowerDetailedSection(sectionPos, returnDetailLevel));
|
||||
|
||||
|
||||
// Block -> 0
|
||||
returnDetailLevel = 0;
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals(64, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel));
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(64, DhSectionPos.getWidthCountForLowerDetailedSection(originSectionPos, returnDetailLevel));
|
||||
|
||||
sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals(64, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(64, DhSectionPos.getWidthCountForLowerDetailedSection(sectionPos, returnDetailLevel));
|
||||
|
||||
|
||||
// Region -> 3
|
||||
returnDetailLevel = 3;
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
Assert.assertEquals(4096, originSectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel));
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(4096, DhSectionPos.getWidthCountForLowerDetailedSection(originSectionPos, returnDetailLevel));
|
||||
|
||||
sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
Assert.assertEquals(4096, sectionPos.getWidthCountForLowerDetailedSection(returnDetailLevel));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(4096, DhSectionPos.getWidthCountForLowerDetailedSection(sectionPos, returnDetailLevel));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void GetBlockWidth()
|
||||
public void getBlockWidth()
|
||||
{
|
||||
DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0);
|
||||
DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000);
|
||||
long originSectionPos = DhSectionPos.encode((byte) 0,0,0);
|
||||
long sectionPos = DhSectionPos.encode((byte) 0,-10000,5000);
|
||||
|
||||
|
||||
Assert.assertEquals(1, originSectionPos.getBlockWidth());
|
||||
Assert.assertEquals(1, sectionPos.getBlockWidth());
|
||||
assertSectionPosEqual(1, DhSectionPos.getBlockWidth(originSectionPos));
|
||||
assertSectionPosEqual(1, DhSectionPos.getBlockWidth(sectionPos));
|
||||
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1);
|
||||
Assert.assertEquals(2, originSectionPos.getBlockWidth());
|
||||
sectionPos = sectionPos.convertNewToDetailLevel((byte) 1);
|
||||
Assert.assertEquals(2, sectionPos.getBlockWidth());
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 1);
|
||||
assertSectionPosEqual(2, DhSectionPos.getBlockWidth(originSectionPos));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, (byte) 1);
|
||||
assertSectionPosEqual(2, DhSectionPos.getBlockWidth(sectionPos));
|
||||
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals(64, originSectionPos.getBlockWidth());
|
||||
sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals(64, sectionPos.getBlockWidth());
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(64, DhSectionPos.getBlockWidth(originSectionPos));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(64, DhSectionPos.getBlockWidth(sectionPos));
|
||||
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
Assert.assertEquals(32768, originSectionPos.getBlockWidth());
|
||||
sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
Assert.assertEquals(32768, sectionPos.getBlockWidth());
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(32768, DhSectionPos.getBlockWidth(originSectionPos));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
assertSectionPosEqual(32768, DhSectionPos.getBlockWidth(sectionPos));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void GetCenterBlockPos()
|
||||
public void getCenterBlockPosOrigin()
|
||||
{
|
||||
DhSectionPos originSectionPos = new DhSectionPos((byte) 0,0,0);
|
||||
DhSectionPos sectionPos = new DhSectionPos((byte) 0,-10000,5000);
|
||||
long originSectionPos = DhSectionPos.encode((byte) 0,0,0);
|
||||
long sectionPos = DhSectionPos.encode((byte) 0,-10000,5000);
|
||||
|
||||
|
||||
Assert.assertEquals(new DhBlockPos2D(0, 0), originSectionPos.getCenterBlockPos());
|
||||
Assert.assertEquals(new DhBlockPos2D(-10000, 5000), sectionPos.getCenterBlockPos());
|
||||
|
||||
// 1x1 blocks
|
||||
Assert.assertEquals(new DhBlockPos2D(0, 0), DhSectionPos.getCenterBlockPos(originSectionPos));
|
||||
Assert.assertEquals(new DhBlockPos2D(-10000, 5000), DhSectionPos.getCenterBlockPos(sectionPos));
|
||||
|
||||
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel((byte) 1);
|
||||
Assert.assertEquals(new DhBlockPos2D(0, 0), originSectionPos.getCenterBlockPos());
|
||||
sectionPos = sectionPos.convertNewToDetailLevel((byte) 1);
|
||||
Assert.assertEquals(new DhBlockPos2D(-10000, 5000), sectionPos.getCenterBlockPos());
|
||||
// 2x2 blocks
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 1);
|
||||
Assert.assertEquals(new DhBlockPos2D(0, 0), DhSectionPos.getCenterBlockPos(originSectionPos));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, (byte) 1);
|
||||
Assert.assertEquals(new DhBlockPos2D(-10000, 5000), DhSectionPos.getCenterBlockPos(sectionPos));
|
||||
//sectionPos = DhSectionPos.encode((byte) 1, 2303, 0);
|
||||
//Assert.assertEquals(new DhBlockPos2D(4606, 0), DhSectionPos.getCenterBlockPos(sectionPos));
|
||||
|
||||
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhBlockPos2D(32, 32), originSectionPos.getCenterBlockPos());
|
||||
sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhBlockPos2D(-10016, 5024), sectionPos.getCenterBlockPos());
|
||||
// 4x4 blocks
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, (byte) 2);
|
||||
Assert.assertEquals(new DhBlockPos2D(2, 2), DhSectionPos.getCenterBlockPos(originSectionPos));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, (byte) 2);
|
||||
Assert.assertEquals(new DhBlockPos2D(-9998, 5002), DhSectionPos.getCenterBlockPos(sectionPos));
|
||||
|
||||
|
||||
originSectionPos = originSectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhBlockPos2D(16384, 16384), originSectionPos.getCenterBlockPos());
|
||||
sectionPos = sectionPos.convertNewToDetailLevel(DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhBlockPos2D(-16384, 16384), sectionPos.getCenterBlockPos());
|
||||
// 64x64 blocks
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhBlockPos2D(32, 32), DhSectionPos.getCenterBlockPos(originSectionPos));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhBlockPos2D(-10016, 5024), DhSectionPos.getCenterBlockPos(sectionPos));
|
||||
|
||||
|
||||
// 32,768 x 32,768 blocks
|
||||
originSectionPos = DhSectionPos.convertToDetailLevel(originSectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhBlockPos2D(16384, 16384), DhSectionPos.getCenterBlockPos(originSectionPos));
|
||||
sectionPos = DhSectionPos.convertToDetailLevel(sectionPos, DhSectionPos.SECTION_REGION_DETAIL_LEVEL);
|
||||
Assert.assertEquals(new DhBlockPos2D(-16384, 16384), DhSectionPos.getCenterBlockPos(sectionPos));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMinCornerBlockPos()
|
||||
{
|
||||
long pos;
|
||||
|
||||
// origin block detail
|
||||
pos = DhSectionPos.encode((byte) 0,0,0);
|
||||
Assert.assertEquals(0, DhSectionPos.getMinCornerBlockX(pos));
|
||||
Assert.assertEquals(0, DhSectionPos.getMinCornerBlockZ(pos));
|
||||
|
||||
// offset block detail
|
||||
pos = DhSectionPos.encode((byte) 0,2,3);
|
||||
Assert.assertEquals(2 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockX(pos));
|
||||
Assert.assertEquals(3 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockZ(pos));
|
||||
|
||||
// negative offset block detail
|
||||
pos = DhSectionPos.encode((byte) 0,-1,-2);
|
||||
Assert.assertEquals(-1 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockX(pos));
|
||||
Assert.assertEquals(-2 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockZ(pos));
|
||||
|
||||
|
||||
// origin chunk detail
|
||||
pos = DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL,0,0);
|
||||
Assert.assertEquals(0, DhSectionPos.getMinCornerBlockX(pos));
|
||||
Assert.assertEquals(0, DhSectionPos.getMinCornerBlockZ(pos));
|
||||
|
||||
// offset chunk detail
|
||||
pos = DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL,2,3);
|
||||
Assert.assertEquals(2 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockX(pos));
|
||||
Assert.assertEquals(3 * DhSectionPos.getBlockWidth(pos), DhSectionPos.getMinCornerBlockZ(pos));
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAdjacentPos()
|
||||
{
|
||||
long pos = DhSectionPos.encode((byte) 0, 0, 0);
|
||||
|
||||
assertSectionPosEqual(DhSectionPos.encode((byte) 0, 0, -1), DhSectionPos.getAdjacentPos(pos, EDhDirection.NORTH));
|
||||
assertSectionPosEqual(DhSectionPos.encode((byte) 0, 0, 1), DhSectionPos.getAdjacentPos(pos, EDhDirection.SOUTH));
|
||||
|
||||
assertSectionPosEqual(DhSectionPos.encode((byte) 0, 1, 0), DhSectionPos.getAdjacentPos(pos, EDhDirection.EAST));
|
||||
assertSectionPosEqual(DhSectionPos.encode((byte) 0, -1, 0), DhSectionPos.getAdjacentPos(pos, EDhDirection.WEST));
|
||||
|
||||
// getting the adjacent position in the up and down position don't make sense
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> { DhSectionPos.getAdjacentPos(pos, EDhDirection.UP); });
|
||||
Assert.assertThrows(IllegalArgumentException.class, () -> { DhSectionPos.getAdjacentPos(pos, EDhDirection.DOWN); });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forEachChildIterator()
|
||||
{
|
||||
long pos = DhSectionPos.encode((byte) 1, 0, 0);
|
||||
|
||||
ArrayList<Long> childPosList = new ArrayList<>();
|
||||
AtomicInteger childCount = new AtomicInteger(0);
|
||||
DhSectionPos.forEachChild(pos, (childPos) ->
|
||||
{
|
||||
childCount.incrementAndGet();
|
||||
childPosList.add(childPos);
|
||||
});
|
||||
|
||||
Assert.assertTrue(childPosList.contains(DhSectionPos.encode((byte) 0, 0, 0)));
|
||||
Assert.assertTrue(childPosList.contains(DhSectionPos.encode((byte) 0, 1, 0)));
|
||||
Assert.assertTrue(childPosList.contains(DhSectionPos.encode((byte) 0, 0, 1)));
|
||||
Assert.assertTrue(childPosList.contains(DhSectionPos.encode((byte) 0, 1, 1)));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
public static void assertSectionPosEqual(long expected, long actual) { assertSectionPosEqual("", expected, actual); }
|
||||
public static void assertSectionPosEqual(String messagePrefix, long expected, long actual)
|
||||
{
|
||||
if (!messagePrefix.endsWith(" "))
|
||||
{
|
||||
messagePrefix += " ";
|
||||
}
|
||||
|
||||
String expectedString = DhSectionPos.toString(expected);
|
||||
String actualString = DhSectionPos.toString(actual);
|
||||
String mismatchSuffix = "expected: ["+expectedString+"] actual: ["+actualString+"].";
|
||||
|
||||
Assert.assertEquals(messagePrefix+"Detail level mismatch, "+mismatchSuffix, DhSectionPos.getDetailLevel(expected), DhSectionPos.getDetailLevel(actual));
|
||||
Assert.assertEquals(messagePrefix+"X Pos mismatch, "+mismatchSuffix, DhSectionPos.getX(expected), DhSectionPos.getX(actual));
|
||||
Assert.assertEquals(messagePrefix+"Z Pos mismatch, "+mismatchSuffix, DhSectionPos.getZ(expected), DhSectionPos.getZ(actual));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadTree;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
@@ -59,30 +60,30 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// (pseudo) root node //
|
||||
testSet(tree, new DhSectionPos((byte) 10, 0, 0), 0);
|
||||
testSet(tree, DhSectionPos.encode((byte) 10, 0, 0), 0);
|
||||
|
||||
// first child (0,0) //
|
||||
testSet(tree, new DhSectionPos((byte) 9, 0, 0), 1);
|
||||
testSet(tree, new DhSectionPos((byte) 9, 1, 0), 2);
|
||||
testSet(tree, new DhSectionPos((byte) 9, 0, 1), 3);
|
||||
testSet(tree, new DhSectionPos((byte) 9, 1, 1), 4);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, 0, 0), 1);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, 1, 0), 2);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, 0, 1), 3);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, 1, 1), 4);
|
||||
|
||||
// second child (0,0) (0,0) //
|
||||
testSet(tree, new DhSectionPos((byte) 8, 0, 0), 5);
|
||||
testSet(tree, new DhSectionPos((byte) 8, 1, 0), 6);
|
||||
testSet(tree, new DhSectionPos((byte) 8, 0, 1), 7);
|
||||
testSet(tree, new DhSectionPos((byte) 8, 1, 1), 8);
|
||||
testSet(tree, DhSectionPos.encode((byte) 8, 0, 0), 5);
|
||||
testSet(tree, DhSectionPos.encode((byte) 8, 1, 0), 6);
|
||||
testSet(tree, DhSectionPos.encode((byte) 8, 0, 1), 7);
|
||||
testSet(tree, DhSectionPos.encode((byte) 8, 1, 1), 8);
|
||||
// second child (0,0) (1,1) //
|
||||
testSet(tree, new DhSectionPos((byte) 8, 2, 2), 9);
|
||||
testSet(tree, new DhSectionPos((byte) 8, 3, 2), 10);
|
||||
testSet(tree, new DhSectionPos((byte) 8, 2, 3), 11);
|
||||
testSet(tree, new DhSectionPos((byte) 8, 3, 3), 12);
|
||||
testSet(tree, DhSectionPos.encode((byte) 8, 2, 2), 9);
|
||||
testSet(tree, DhSectionPos.encode((byte) 8, 3, 2), 10);
|
||||
testSet(tree, DhSectionPos.encode((byte) 8, 2, 3), 11);
|
||||
testSet(tree, DhSectionPos.encode((byte) 8, 3, 3), 12);
|
||||
|
||||
// third child (0,0) (1,0) (0,0) //
|
||||
testSet(tree, new DhSectionPos((byte) 7, 5, 0), 9);
|
||||
testSet(tree, new DhSectionPos((byte) 7, 6, 0), 10);
|
||||
testSet(tree, new DhSectionPos((byte) 7, 5, 1), 11);
|
||||
testSet(tree, new DhSectionPos((byte) 7, 6, 1), 12);
|
||||
testSet(tree, DhSectionPos.encode((byte) 7, 5, 0), 9);
|
||||
testSet(tree, DhSectionPos.encode((byte) 7, 6, 0), 10);
|
||||
testSet(tree, DhSectionPos.encode((byte) 7, 5, 1), 11);
|
||||
testSet(tree, DhSectionPos.encode((byte) 7, 6, 1), 12);
|
||||
|
||||
}
|
||||
|
||||
@@ -94,13 +95,13 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// root node //
|
||||
testSet(tree, new DhSectionPos((byte) 10, -1, -1), 0);
|
||||
testSet(tree, DhSectionPos.encode((byte) 10, -1, -1), 0);
|
||||
|
||||
// first child (-1,-1) //
|
||||
testSet(tree, new DhSectionPos((byte) 9, -2, -1), 1);
|
||||
testSet(tree, new DhSectionPos((byte) 9, -1, -1), 2);
|
||||
testSet(tree, new DhSectionPos((byte) 9, -2, -2), 3);
|
||||
testSet(tree, new DhSectionPos((byte) 9, -1, -2), 4);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, -2, -1), 1);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, -1, -1), 2);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, -2, -2), 3);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, -1, -2), 4);
|
||||
|
||||
// TODO
|
||||
// // second child (-1,-1) (0,0) //
|
||||
@@ -131,36 +132,36 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// wrong detail level on purpose, if the detail level was 0 (block) this should work
|
||||
DhSectionPos outOfBoundsPos = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2), 0);
|
||||
long outOfBoundsPos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2), 0);
|
||||
testSet(tree, outOfBoundsPos, -1, IndexOutOfBoundsException.class);
|
||||
Assert.assertEquals("incorrect leaf node count", 0, tree.leafNodeCount());
|
||||
|
||||
|
||||
// out of bounds //
|
||||
outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) + 1, 0);
|
||||
outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) + 1, 0);
|
||||
testSet(tree, outOfBoundsPos, -1, IndexOutOfBoundsException.class);
|
||||
Assert.assertEquals("incorrect leaf node count", 0, tree.leafNodeCount());
|
||||
|
||||
outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2), 0);
|
||||
outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2), 0);
|
||||
testSet(tree, outOfBoundsPos, -1, IndexOutOfBoundsException.class);
|
||||
Assert.assertEquals("incorrect leaf node count", 0, tree.leafNodeCount());
|
||||
|
||||
|
||||
// in bounds //
|
||||
outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 1, 0);
|
||||
outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 1, 0);
|
||||
testSet(tree, outOfBoundsPos, 0);
|
||||
Assert.assertEquals("incorrect leaf node count", 1, tree.leafNodeCount());
|
||||
|
||||
outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 3, 0);
|
||||
outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 3, 0);
|
||||
testSet(tree, outOfBoundsPos, 0);
|
||||
Assert.assertEquals("incorrect leaf node count", 2, tree.leafNodeCount());
|
||||
|
||||
// TODO this position probably has trouble with getting the center.
|
||||
outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 2, 0);
|
||||
outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 2, 0);
|
||||
testSet(tree, outOfBoundsPos, 0);
|
||||
Assert.assertEquals("incorrect leaf node count", 3, tree.leafNodeCount());
|
||||
|
||||
outOfBoundsPos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 4, 0);
|
||||
outOfBoundsPos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (treeParams.getWidthInBlocks() / 2) - 4, 0);
|
||||
testSet(tree, outOfBoundsPos, 0);
|
||||
Assert.assertEquals("incorrect leaf node count", 4, tree.leafNodeCount());
|
||||
|
||||
@@ -173,15 +174,15 @@ public class QuadTreeTest
|
||||
Assert.assertEquals("Test may need to be re-calculated for different max detail level.", 9, tree.treeMinDetailLevel);
|
||||
|
||||
|
||||
DhSectionPos rootPos = new DhSectionPos((byte) 9, 0, -1);
|
||||
long rootPos = DhSectionPos.encode((byte) 9, 0, -1);
|
||||
testSet(tree, rootPos, 1);
|
||||
|
||||
// pos is in tree, but out of range
|
||||
DhSectionPos midPos = new DhSectionPos((byte) 8, 0, -1);
|
||||
long midPos = DhSectionPos.encode((byte) 8, 0, -1);
|
||||
testSet(tree, midPos, 2, IndexOutOfBoundsException.class);
|
||||
|
||||
// pos is in tree, but out of range
|
||||
DhSectionPos leafPos = new DhSectionPos((byte) 7, 0, -2);
|
||||
long leafPos = DhSectionPos.encode((byte) 7, 0, -2);
|
||||
testSet(tree, leafPos, 3, IndexOutOfBoundsException.class);
|
||||
|
||||
}
|
||||
@@ -197,13 +198,13 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// (pseudo) root nodes //
|
||||
testSet(tree, new DhSectionPos((byte) 10, 0, 0), 1);
|
||||
testSet(tree, DhSectionPos.encode((byte) 10, 0, 0), 1);
|
||||
|
||||
// first child (0,0) //
|
||||
DhSectionPos nw = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0);
|
||||
DhSectionPos ne = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 1, 0);
|
||||
DhSectionPos sw = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 1);
|
||||
DhSectionPos se = new DhSectionPos(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 1, 1);
|
||||
long nw = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0);
|
||||
long ne = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 1, 0);
|
||||
long sw = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 1);
|
||||
long se = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 1, 1);
|
||||
|
||||
testSet(tree, nw, 2);
|
||||
testSet(tree, ne, 3);
|
||||
@@ -259,12 +260,12 @@ public class QuadTreeTest
|
||||
Assert.assertEquals("Tree center incorrect", DhBlockPos2D.ZERO, tree.getCenterBlockPos());
|
||||
|
||||
// on the negative X edge
|
||||
DhSectionPos edgePos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, -treeParams.getWidthInBlocks() / 2, 0);
|
||||
long edgePos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, -treeParams.getWidthInBlocks() / 2, 0);
|
||||
testSet(tree, edgePos, 1);
|
||||
Assert.assertEquals("incorrect leaf node count", 1, tree.leafNodeCount());
|
||||
|
||||
// +1 root node from the negative X edge
|
||||
DhSectionPos adjacentEdgePos = new DhSectionPos(LodUtil.BLOCK_DETAIL_LEVEL, (-treeParams.getWidthInBlocks() / 2) + pseudoRootNodeWidthInBlocks, 0);
|
||||
long adjacentEdgePos = DhSectionPos.encode(LodUtil.BLOCK_DETAIL_LEVEL, (-treeParams.getWidthInBlocks() / 2) + pseudoRootNodeWidthInBlocks, 0);
|
||||
testSet(tree, adjacentEdgePos, 2);
|
||||
Assert.assertEquals("incorrect leaf node count", 2, tree.leafNodeCount());
|
||||
|
||||
@@ -284,24 +285,24 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// (pseudo) root nodes //
|
||||
testSet(tree, new DhSectionPos((byte) 10, 0, 0), 1);
|
||||
testSet(tree, new DhSectionPos((byte) 10, 1, 0), 2);
|
||||
testSet(tree, DhSectionPos.encode((byte) 10, 0, 0), 1);
|
||||
testSet(tree, DhSectionPos.encode((byte) 10, 1, 0), 2);
|
||||
|
||||
// first child (0,0) //
|
||||
testSet(tree, new DhSectionPos((byte) 9, 0, 0), 3);
|
||||
testSet(tree, new DhSectionPos((byte) 9, 1, 0), 4);
|
||||
testSet(tree, new DhSectionPos((byte) 9, 0, 1), 5);
|
||||
testSet(tree, new DhSectionPos((byte) 9, 1, 1), 6);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, 0, 0), 3);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, 1, 0), 4);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, 0, 1), 5);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, 1, 1), 6);
|
||||
|
||||
|
||||
|
||||
// root nodes
|
||||
int rootNodeCount = 0;
|
||||
|
||||
Iterator<DhSectionPos> rootNodePosIterator = tree.rootNodePosIterator();
|
||||
LongIterator rootNodePosIterator = tree.rootNodePosIterator();
|
||||
while (rootNodePosIterator.hasNext())
|
||||
{
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodePosIterator.next());
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodePosIterator.nextLong());
|
||||
if (rootNode != null)
|
||||
{
|
||||
rootNodeCount++;
|
||||
@@ -331,20 +332,19 @@ public class QuadTreeTest
|
||||
@Test
|
||||
public void NewQuadTreeIterationTest()
|
||||
{
|
||||
AbstractTestTreeParams treeParams = new LargeTestTree();
|
||||
QuadNode<Integer> rootNode = new QuadNode<>(new DhSectionPos((byte) 10, 0, 0), LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
QuadNode<Integer> rootNode = new QuadNode<>(DhSectionPos.encode((byte) 10, 0, 0), LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
|
||||
rootNode.setValue(new DhSectionPos((byte) 10, 0, 0), 0);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 10, 0, 0), 0);
|
||||
|
||||
rootNode.setValue(new DhSectionPos((byte) 9, 0, 0), 1);
|
||||
rootNode.setValue(new DhSectionPos((byte) 9, 1, 0), 1);
|
||||
rootNode.setValue(new DhSectionPos((byte) 9, 0, 1), 1);
|
||||
rootNode.setValue(new DhSectionPos((byte) 9, 1, 1), null);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 9, 0, 0), 1);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 9, 1, 0), 1);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 9, 0, 1), 1);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 9, 1, 1), null);
|
||||
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 0, 0), 2);
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 1, 0), 2);
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 0, 1), 2);
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 1, 1), null);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 0), 2);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 0), 2);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 1), 2);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 1), null);
|
||||
|
||||
|
||||
|
||||
@@ -411,14 +411,14 @@ public class QuadTreeTest
|
||||
{
|
||||
AbstractTestTreeParams treeParams = new TinyTestTree();
|
||||
final QuadTree<Integer> tree = new QuadTree<>(treeParams.getWidthInBlocks(), treeParams.getPositiveEdgeCenterPos(), LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 0, 0), 0);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 0, 0), 0);
|
||||
|
||||
// confirm the root node were added
|
||||
int rootNodeCount = 0;
|
||||
Iterator<DhSectionPos> rootNodePosIterator = tree.rootNodePosIterator();
|
||||
LongIterator rootNodePosIterator = tree.rootNodePosIterator();
|
||||
while (rootNodePosIterator.hasNext())
|
||||
{
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodePosIterator.next());
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodePosIterator.nextLong());
|
||||
if (rootNode != null)
|
||||
{
|
||||
rootNodeCount++;
|
||||
@@ -431,7 +431,7 @@ public class QuadTreeTest
|
||||
rootNodePosIterator = tree.rootNodePosIterator();
|
||||
while (rootNodePosIterator.hasNext())
|
||||
{
|
||||
DhSectionPos rootNodePos = rootNodePosIterator.next();
|
||||
long rootNodePos = rootNodePosIterator.nextLong();
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodePos);
|
||||
if (rootNode != null)
|
||||
{
|
||||
@@ -468,10 +468,10 @@ public class QuadTreeTest
|
||||
tree.setCenterBlockPos(treeMovePos);
|
||||
Assert.assertEquals("tree move failed, incorrect position after move", treeMovePos, tree.getCenterBlockPos());
|
||||
|
||||
Iterator<DhSectionPos> rootNodePosIterator = tree.rootNodePosIterator();
|
||||
LongIterator rootNodePosIterator = tree.rootNodePosIterator();
|
||||
while (rootNodePosIterator.hasNext())
|
||||
{
|
||||
testSet(tree, rootNodePosIterator.next(), 0);
|
||||
testSet(tree, rootNodePosIterator.nextLong(), 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -480,7 +480,7 @@ public class QuadTreeTest
|
||||
rootNodePosIterator = tree.rootNodePosIterator();
|
||||
while (rootNodePosIterator.hasNext())
|
||||
{
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodePosIterator.next());
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodePosIterator.nextLong());
|
||||
if (rootNode != null)
|
||||
{
|
||||
rootNodeCount++;
|
||||
@@ -499,16 +499,16 @@ public class QuadTreeTest
|
||||
Assert.assertEquals("incorrect tree width", treeParams.getWidthInBlocks(), tree.diameterInBlocks());
|
||||
|
||||
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 0, 0), 0);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 0, 0), 0);
|
||||
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, -1, -1), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 1, 1), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, -1, -1), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 1, 1), -1, IndexOutOfBoundsException.class);
|
||||
|
||||
int rootNodeCount = 0;
|
||||
Iterator<DhSectionPos> rootNodeIterator = tree.rootNodePosIterator();
|
||||
LongIterator rootNodeIterator = tree.rootNodePosIterator();
|
||||
while (rootNodeIterator.hasNext())
|
||||
{
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodeIterator.next());
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodeIterator.nextLong());
|
||||
if (rootNode != null)
|
||||
{
|
||||
rootNodeCount++;
|
||||
@@ -529,25 +529,25 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// 2x2 valid positions (overlap the tree's width)
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 0, 0), 0);
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, -1, 0), 0);
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 0, -1), 0);
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, -1, -1), 0);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 0, 0), 0);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, -1, 0), 0);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 0, -1), 0);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, -1, -1), 0);
|
||||
|
||||
// invalid positions
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, -1, 1), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 0, 1), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, -1, 1), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 0, 1), -1, IndexOutOfBoundsException.class);
|
||||
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 1, 0), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 1, 1), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, new DhSectionPos(tree.treeMinDetailLevel, 1, -1), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 1, 0), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 1, 1), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode(tree.treeMinDetailLevel, 1, -1), -1, IndexOutOfBoundsException.class);
|
||||
|
||||
|
||||
int rootNodeCount = 0;
|
||||
Iterator<DhSectionPos> rootNodeIterator = tree.rootNodePosIterator();
|
||||
LongIterator rootNodeIterator = tree.rootNodePosIterator();
|
||||
while (rootNodeIterator.hasNext())
|
||||
{
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodeIterator.next());
|
||||
QuadNode<Integer> rootNode = tree.getNode(rootNodeIterator.nextLong());
|
||||
if (rootNode != null)
|
||||
{
|
||||
rootNodeCount++;
|
||||
@@ -565,17 +565,17 @@ public class QuadTreeTest
|
||||
Assert.assertEquals("Test detail level's need to be adjusted. This isn't necessarily a failed test.", 10, tree.treeMinDetailLevel);
|
||||
|
||||
// valid detail levels
|
||||
testSet(tree, new DhSectionPos((byte) 10, 0, 0), 1);
|
||||
testSet(tree, new DhSectionPos((byte) 9, 0, 0), 2);
|
||||
testSet(tree, new DhSectionPos((byte) 8, 0, 0), 3);
|
||||
testSet(tree, DhSectionPos.encode((byte) 10, 0, 0), 1);
|
||||
testSet(tree, DhSectionPos.encode((byte) 9, 0, 0), 2);
|
||||
testSet(tree, DhSectionPos.encode((byte) 8, 0, 0), 3);
|
||||
|
||||
// detail level too low
|
||||
testSet(tree, new DhSectionPos((byte) 7, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, new DhSectionPos((byte) 6, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode((byte) 7, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode((byte) 6, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
|
||||
// detail level too high
|
||||
testSet(tree, new DhSectionPos((byte) 11, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, new DhSectionPos((byte) 12, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode((byte) 11, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
testSet(tree, DhSectionPos.encode((byte) 12, 0, 0), -1, IndexOutOfBoundsException.class);
|
||||
|
||||
}
|
||||
|
||||
@@ -587,25 +587,25 @@ public class QuadTreeTest
|
||||
Assert.assertEquals("Test detail level's need to be adjusted. This isn't necessarily a failed test.", 10, tree.treeMinDetailLevel);
|
||||
|
||||
// create the root node
|
||||
testSet(tree, new DhSectionPos((byte) 10, 0, 0), 1);
|
||||
testSet(tree, DhSectionPos.encode((byte) 10, 0, 0), 1);
|
||||
|
||||
|
||||
|
||||
AtomicInteger minimumDetailLevelReachedRef = new AtomicInteger(tree.treeMinDetailLevel);
|
||||
|
||||
// recurse down the tree
|
||||
Iterator<DhSectionPos> rootNodePosIterator = tree.rootNodePosIterator();
|
||||
LongIterator rootNodePosIterator = tree.rootNodePosIterator();
|
||||
while (rootNodePosIterator.hasNext())
|
||||
{
|
||||
DhSectionPos sectionPos = rootNodePosIterator.next();
|
||||
long sectionPos = rootNodePosIterator.nextLong();
|
||||
QuadNode<Integer> rootNode = tree.getNode(sectionPos);
|
||||
if (rootNode != null)
|
||||
{
|
||||
// fill in the root node's direct children
|
||||
Iterator<DhSectionPos> childPosIterator = rootNode.getChildPosIterator();
|
||||
LongIterator childPosIterator = rootNode.getChildPosIterator();
|
||||
while (childPosIterator.hasNext())
|
||||
{
|
||||
DhSectionPos rootChildPos = childPosIterator.next();
|
||||
long rootChildPos = childPosIterator.nextLong();
|
||||
rootNode.setValue(rootChildPos, 0);
|
||||
}
|
||||
|
||||
@@ -618,7 +618,7 @@ public class QuadTreeTest
|
||||
QuadNode<Integer> childNode = ChildIterator.next();
|
||||
Assert.assertNotNull(childNode); // TODO is this correct?
|
||||
|
||||
recursivelyCreateNodeChildren(childNode, tree.treeMaxDetailLevel, minimumDetailLevelReachedRef);
|
||||
this.recursivelyCreateNodeChildren(childNode, tree.treeMaxDetailLevel, minimumDetailLevelReachedRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,10 +634,10 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// fill in the null children
|
||||
Iterator<DhSectionPos> directChildIterator = node.getChildPosIterator();
|
||||
LongIterator directChildIterator = node.getChildPosIterator();
|
||||
while (directChildIterator.hasNext())
|
||||
{
|
||||
node.setValue(directChildIterator.next(), 0);
|
||||
node.setValue(directChildIterator.nextLong(), 0);
|
||||
childNodesCreated = true;
|
||||
}
|
||||
|
||||
@@ -646,10 +646,10 @@ public class QuadTreeTest
|
||||
directChildIterator = node.getChildPosIterator();
|
||||
while (directChildIterator.hasNext())
|
||||
{
|
||||
DhSectionPos sectionPos = directChildIterator.next();
|
||||
long sectionPos = directChildIterator.nextLong();
|
||||
QuadNode<Integer> childNode = node.getNode(sectionPos);
|
||||
|
||||
Assert.assertTrue("Child node recurred too low. Min detail level: " + minDetailLevel + ", node detail level: " + childNode.sectionPos.getDetailLevel(), childNode.sectionPos.getDetailLevel() >= minDetailLevel);
|
||||
Assert.assertTrue("Child node recurred too low. Min detail level: " + minDetailLevel + ", node detail level: " + DhSectionPos.getDetailLevel(childNode.sectionPos), DhSectionPos.getDetailLevel(childNode.sectionPos) >= minDetailLevel);
|
||||
recursivelyCreateNodeChildren(childNode, minDetailLevel, minimumDetailLevelReachedRef);
|
||||
|
||||
childNodesIterated = true;
|
||||
@@ -657,9 +657,9 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// keep track of how far down the tree we have gone
|
||||
if (node.sectionPos.getDetailLevel() < minimumDetailLevelReachedRef.get())
|
||||
if ( DhSectionPos.getDetailLevel(node.sectionPos) < minimumDetailLevelReachedRef.get())
|
||||
{
|
||||
minimumDetailLevelReachedRef.set(node.sectionPos.getDetailLevel());
|
||||
minimumDetailLevelReachedRef.set( DhSectionPos.getDetailLevel(node.sectionPos));
|
||||
}
|
||||
|
||||
|
||||
@@ -667,22 +667,22 @@ public class QuadTreeTest
|
||||
// assertions
|
||||
if (childNodesCreated)
|
||||
{
|
||||
Assert.assertTrue("node children created below minimum detail level", node.sectionPos.getDetailLevel() >= minDetailLevel);
|
||||
Assert.assertTrue("node children created below minimum detail level", DhSectionPos.getDetailLevel( node.sectionPos) >= minDetailLevel);
|
||||
}
|
||||
if (childNodesIterated)
|
||||
{
|
||||
Assert.assertTrue("node children iterated below minimum detail level", node.sectionPos.getDetailLevel() - 1 >= minDetailLevel);
|
||||
Assert.assertTrue("node children iterated below minimum detail level", DhSectionPos.getDetailLevel(node.sectionPos) - 1 >= minDetailLevel);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void quadNodeChildPositionIndexTest()
|
||||
{
|
||||
QuadNode<Integer> rootNode = new QuadNode<>(new DhSectionPos((byte) 10, 0, 0), (byte) 0);
|
||||
Iterator<DhSectionPos> directChildPosIterator = rootNode.getChildPosIterator();
|
||||
QuadNode<Integer> rootNode = new QuadNode<>(DhSectionPos.encode((byte) 10, 0, 0), (byte) 0);
|
||||
LongIterator directChildPosIterator = rootNode.getChildPosIterator();
|
||||
while (directChildPosIterator.hasNext())
|
||||
{
|
||||
DhSectionPos sectionPos = directChildPosIterator.next();
|
||||
long sectionPos = directChildPosIterator.nextLong();
|
||||
Assert.assertNotEquals("Root node pos shouldn't be included in direct child pos iteration", sectionPos, rootNode.sectionPos);
|
||||
|
||||
rootNode.setValue(sectionPos, 1);
|
||||
@@ -692,9 +692,9 @@ public class QuadTreeTest
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
DhSectionPos childPos = rootNode.sectionPos.getChildByIndex(i);
|
||||
long childPos = DhSectionPos.getChildByIndex(rootNode.sectionPos, i);
|
||||
QuadNode<Integer> childNode = rootNode.getChildByIndex(i);
|
||||
Assert.assertEquals("child position not the same as " + DhSectionPos.class.getSimpleName() + "'s getChildByIndex()", childPos, childNode.sectionPos);
|
||||
Assert.assertEquals("child position not the same as " + long.class.getSimpleName() + "'s getChildByIndex()", childPos, childNode.sectionPos);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -708,7 +708,7 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// center root node
|
||||
DhSectionPos centerNodePos = new DhSectionPos((byte) 1, 0, 0);
|
||||
long centerNodePos = DhSectionPos.encode((byte) 1, 0, 0);
|
||||
|
||||
// create node
|
||||
tree.setValue(centerNodePos, 0);
|
||||
@@ -716,10 +716,10 @@ public class QuadTreeTest
|
||||
Assert.assertNotNull(centerRootNode);
|
||||
|
||||
// child pos in bounds of the tree
|
||||
Iterator<DhSectionPos> childPosIterator = centerRootNode.getChildPosIterator();
|
||||
LongIterator childPosIterator = centerRootNode.getChildPosIterator();
|
||||
while (childPosIterator.hasNext())
|
||||
{
|
||||
DhSectionPos childPos = childPosIterator.next();
|
||||
long childPos = childPosIterator.nextLong();
|
||||
centerRootNode.setValue(childPos, 1);
|
||||
}
|
||||
Assert.assertEquals("center node not filled", 4, centerRootNode.getNonNullChildCount());
|
||||
@@ -727,7 +727,7 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
// edge root node
|
||||
DhSectionPos offsetNodePos = new DhSectionPos((byte) 1, -17, -16);
|
||||
long offsetNodePos = DhSectionPos.encode((byte) 1, -17, -16);
|
||||
|
||||
// create node
|
||||
tree.setValue(offsetNodePos, 0);
|
||||
@@ -738,7 +738,7 @@ public class QuadTreeTest
|
||||
childPosIterator = offsetRootNode.getChildPosIterator();
|
||||
while (childPosIterator.hasNext())
|
||||
{
|
||||
DhSectionPos childPos = childPosIterator.next();
|
||||
long childPos = childPosIterator.nextLong();
|
||||
offsetRootNode.setValue(childPos, 1);
|
||||
}
|
||||
// TODO James thought this shouldn't work for all 4 nodes, but he must've thought wrong.
|
||||
@@ -772,7 +772,7 @@ public class QuadTreeTest
|
||||
|
||||
|
||||
//
|
||||
testSet(tree, new DhSectionPos((byte) 0, 0, 0), 1);
|
||||
testSet(tree, DhSectionPos.encode((byte) 0, 0, 0), 1);
|
||||
Assert.assertEquals(1, tree.count());
|
||||
tree.setCenterBlockPos(new DhBlockPos2D(treeWidth + (treeWidth / 2), 0));
|
||||
Assert.assertEquals(0, tree.count());
|
||||
@@ -783,21 +783,21 @@ public class QuadTreeTest
|
||||
//@Test
|
||||
public void autoDeleteNullQuadNodeChildTest()
|
||||
{
|
||||
QuadNode<Integer> rootNode = new QuadNode<>(new DhSectionPos((byte) 10, 0, 0), LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
QuadNode<Integer> rootNode = new QuadNode<>(DhSectionPos.encode((byte) 10, 0, 0), LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
|
||||
|
||||
rootNode.setValue(new DhSectionPos((byte) 10, 0, 0), 0);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 10, 0, 0), 0);
|
||||
|
||||
DhSectionPos midNodePos = new DhSectionPos((byte) 9, 0, 0);
|
||||
long midNodePos = DhSectionPos.encode((byte) 9, 0, 0);
|
||||
//rootNode.setValue(midNodePos, null); // holds detail 8
|
||||
rootNode.setValue(new DhSectionPos((byte) 9, 1, 0), 1);
|
||||
rootNode.setValue(new DhSectionPos((byte) 9, 0, 1), 1);
|
||||
rootNode.setValue(new DhSectionPos((byte) 9, 1, 1), 1);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 9, 1, 0), 1);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 9, 0, 1), 1);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 9, 1, 1), 1);
|
||||
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 0, 0), 2);
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 1, 0), 2);
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 0, 1), 2);
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 1, 1), 2);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 0), 2);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 0), 2);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 1), 2);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 1), 2);
|
||||
|
||||
|
||||
|
||||
@@ -810,12 +810,12 @@ public class QuadTreeTest
|
||||
// test removing nodes //
|
||||
|
||||
// remove two leaf nodes from the root
|
||||
DhSectionPos leafPos = new DhSectionPos((byte) 9, 1, 1);
|
||||
long leafPos = DhSectionPos.encode((byte) 9, 1, 1);
|
||||
rootNode.setValue(leafPos, null);
|
||||
Assert.assertEquals(3, rootNode.getNonNullChildCount());
|
||||
Assert.assertNull("Node wasn't deleted", rootNode.getNode(leafPos));
|
||||
|
||||
leafPos = new DhSectionPos((byte) 9, 0, 1);
|
||||
leafPos = DhSectionPos.encode((byte) 9, 0, 1);
|
||||
rootNode.setValue(leafPos, null);
|
||||
Assert.assertEquals(2, rootNode.getNonNullChildCount());
|
||||
Assert.assertNull("Node wasn't deleted", rootNode.getNode(leafPos));
|
||||
@@ -827,13 +827,13 @@ public class QuadTreeTest
|
||||
Assert.assertEquals(4, rootNode.getNode(midNodePos).getNonNullChildCount());
|
||||
|
||||
// remove all but one, mid-node should still be present
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 0, 0), null);
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 0, 1), null);
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 1, 0), null);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 0), null);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 0, 1), null);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 0), null);
|
||||
Assert.assertEquals(1, rootNode.getNode(midNodePos).getNonNullChildCount());
|
||||
|
||||
// remove last mid-node child, mid-node should now be removed
|
||||
rootNode.setValue(new DhSectionPos((byte) 8, 1, 1), null);
|
||||
rootNode.setValue(DhSectionPos.encode((byte) 8, 1, 1), null);
|
||||
Assert.assertNull("Mid node not deleted.", rootNode.getNode(midNodePos));
|
||||
Assert.assertEquals(3, rootNode.getNonNullChildCount());
|
||||
|
||||
@@ -847,8 +847,8 @@ public class QuadTreeTest
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
private static void testSet(QuadTree<Integer> tree, DhSectionPos pos, Integer setValue) { testSet(tree, pos, setValue, null); }
|
||||
private static <TE extends Throwable> void testSet(QuadTree<Integer> tree, DhSectionPos pos, Integer setValue, Class<TE> expectedExceptionClass)
|
||||
private static void testSet(QuadTree<Integer> tree, long pos, Integer setValue) { testSet(tree, pos, setValue, null); }
|
||||
private static <TE extends Throwable> void testSet(QuadTree<Integer> tree, long pos, Integer setValue, Class<TE> expectedExceptionClass)
|
||||
{
|
||||
// set
|
||||
try
|
||||
@@ -860,7 +860,7 @@ public class QuadTreeTest
|
||||
if (expectedExceptionClass == null || e.getClass() != expectedExceptionClass)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Assert.fail("set failed " + pos + " with exception " + e.getClass() + ", expected exception: " + expectedExceptionClass + ". error: " + e.getMessage());
|
||||
Assert.fail("set failed [" + DhSectionPos.toString(pos) + "] with exception [" + e.getClass() + "], expected exception: [" + expectedExceptionClass + "]. error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,20 +869,20 @@ public class QuadTreeTest
|
||||
testGet(tree, pos, setValue, expectedExceptionClass);
|
||||
}
|
||||
|
||||
private static void testGet(QuadTree<Integer> tree, DhSectionPos pos, Integer getValue) { testSet(tree, pos, getValue, null); }
|
||||
private static <TE extends Throwable> void testGet(QuadTree<Integer> tree, DhSectionPos pos, Integer getValue, Class<TE> expectedExceptionClass)
|
||||
private static void testGet(QuadTree<Integer> tree, long pos, Integer getValue) { testSet(tree, pos, getValue, null); }
|
||||
private static <TE extends Throwable> void testGet(QuadTree<Integer> tree, long pos, Integer getValue, Class<TE> expectedExceptionClass)
|
||||
{
|
||||
try
|
||||
{
|
||||
Integer getResult = tree.getValue(pos);
|
||||
Assert.assertEquals("get failed " + pos, getValue, getResult);
|
||||
Assert.assertEquals("get failed [" + DhSectionPos.toString(pos) + "]", getValue, getResult);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (expectedExceptionClass == null || e.getClass() != expectedExceptionClass)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Assert.fail("get failed " + pos + " with exception " + e.getClass() + ", expected exception: " + expectedExceptionClass + ". error: " + e.getMessage());
|
||||
Assert.fail("get failed [" + DhSectionPos.toString(pos) + "] with exception " + e.getClass() + ", expected exception: " + expectedExceptionClass + ". error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user