Add a optional memory cache to the IDhApiTerrainDataRepo
This commit is contained in:
+2
-1
@@ -39,7 +39,8 @@ public interface IDhApiBlockStateWrapper extends IDhApiUnsafeWrapper
|
||||
boolean isLiquid();
|
||||
|
||||
/**
|
||||
* Returns the full serialized form of the given block.
|
||||
* Returns the full serialized form of the given block
|
||||
* as defined by DH's serialization methods.
|
||||
* @since API 3.0.0
|
||||
*/
|
||||
String getSerialString();
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.seibel.distanthorizons.api.interfaces.data;
|
||||
|
||||
/**
|
||||
* Can be used to drastically speed up repeat read operations in {@link IDhApiTerrainDataRepo}.
|
||||
*
|
||||
* @see IDhApiTerrainDataRepo
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2024-7-14
|
||||
* @since API 3.0.0
|
||||
*/
|
||||
public interface IDhApiTerrainDataCache
|
||||
{
|
||||
/**
|
||||
* Removes any data that's currently stored in this cache.
|
||||
* This cane be done to free up memory or invalidate
|
||||
* the cache so fresh data can be pulled in.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
}
|
||||
+69
-12
@@ -29,6 +29,8 @@ import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
|
||||
/**
|
||||
* Used to interface with Distant Horizons' terrain data.
|
||||
*
|
||||
* @see IDhApiTerrainDataCache
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2023-6-22
|
||||
* @since API 1.0.0
|
||||
@@ -40,29 +42,50 @@ public interface IDhApiTerrainDataRepo
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
/** Returns the terrain datapoint at the given block position, at/or containing the given Y position. */
|
||||
DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ);
|
||||
/** Returns every datapoint in the column located at the given block X and Z position top to bottom. */
|
||||
DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ);
|
||||
/** @see IDhApiTerrainDataRepo#getSingleDataPointAtBlockPos(IDhApiLevelWrapper, int, int, int, IDhApiTerrainDataCache) */
|
||||
default DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ) { return this.getSingleDataPointAtBlockPos(levelWrapper, blockPosX, blockPosY, blockPosZ, null); }
|
||||
/**
|
||||
* Returns the terrain datapoint at the given block position, at/or containing the given Y position.
|
||||
* @since API 3.0.0
|
||||
*/
|
||||
DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, IDhApiTerrainDataCache dataCache);
|
||||
|
||||
/** @see IDhApiTerrainDataRepo#getColumnDataAtBlockPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */
|
||||
default DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ) { return this.getColumnDataAtBlockPos(levelWrapper, blockPosX, blockPosZ, null); }
|
||||
/**
|
||||
* Returns every datapoint in the column located at the given block X and Z position top to bottom.
|
||||
* @since API 3.0.0
|
||||
*/
|
||||
DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, IDhApiTerrainDataCache dataCache);
|
||||
|
||||
/** @see IDhApiTerrainDataRepo#getAllTerrainDataAtChunkPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */
|
||||
default DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ) { return this.getAllTerrainDataAtChunkPos(levelWrapper, chunkPosX, chunkPosZ, null); }
|
||||
/**
|
||||
* Returns every datapoint in the given chunk's X and Z position. <br><br>
|
||||
*
|
||||
* The returned array is ordered: [relativeBlockX][relativeBlockZ][columnIndex] <br>
|
||||
* RelativeBlockX/Z are relative to the block position closest to negative infinity in the chunk's position. <br>
|
||||
* The column data is ordered from top to bottom. Note: each column may have a different number of values. <br>
|
||||
*
|
||||
* @since API 3.0.0
|
||||
*/
|
||||
DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ);
|
||||
DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, IDhApiTerrainDataCache dataCache);
|
||||
|
||||
/** @see IDhApiTerrainDataRepo#getAllTerrainDataAtRegionPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */
|
||||
default DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ) { return this.getAllTerrainDataAtRegionPos(levelWrapper, regionPosX, regionPosZ, null); }
|
||||
/**
|
||||
* Returns every datapoint in the given region's X and Z position. <br><br>
|
||||
*
|
||||
* The returned array is ordered: [relativeBlockX][relativeBlockZ][columnIndex] <br>
|
||||
* RelativeBlockX/Z are relative to the block position closest to negative infinity in the region's position. <br>
|
||||
* The column data is ordered from top to bottom. Note: each column may have a different number of values. <br>
|
||||
*
|
||||
* @since API 3.0.0
|
||||
*/
|
||||
DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ);
|
||||
DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, IDhApiTerrainDataCache dataCache);
|
||||
|
||||
/** @see IDhApiTerrainDataRepo#getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper, byte, int, int, IDhApiTerrainDataCache) */
|
||||
default DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ) { return this.getAllTerrainDataAtDetailLevelAndPos(levelWrapper, detailLevel, posX, posZ, null); }
|
||||
/**
|
||||
* Returns every datapoint in the column located at the given detail level and X/Z position. <br>
|
||||
* This can be used to return terrain data for non-standard sizes (IE 2x2 blocks or 2x2 chunks).
|
||||
@@ -71,20 +94,42 @@ public interface IDhApiTerrainDataRepo
|
||||
* Every increase doubles the width of the returned area. <br>
|
||||
* Example values: 0 = block, 1 = 2x2 blocks, 2 = 4x4 blocks, ... 4 = chunk (16x16 blocks), ... 9 = region (512x512 blocks) <br>
|
||||
* See {@link EDhApiDetailLevel} for more information.
|
||||
*
|
||||
* @since API 3.0.0
|
||||
*/
|
||||
DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ);
|
||||
DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, IDhApiTerrainDataCache dataCache);
|
||||
|
||||
|
||||
|
||||
/** @see IDhApiTerrainDataRepo#raycast(IDhApiLevelWrapper, double, double, double, float, float, float, int, IDhApiTerrainDataCache) */
|
||||
default DhApiResult<DhApiRaycastResult> raycast(
|
||||
IDhApiLevelWrapper levelWrapper,
|
||||
double rayOriginX, double rayOriginY, double rayOriginZ,
|
||||
float rayDirectionX, float rayDirectionY, float rayDirectionZ,
|
||||
int maxRayBlockLength)
|
||||
{
|
||||
return this.raycast(
|
||||
levelWrapper,
|
||||
rayOriginX, rayOriginY, rayOriginZ,
|
||||
rayDirectionX, rayDirectionY, rayDirectionZ,
|
||||
maxRayBlockLength,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the datapoint and position of the LOD
|
||||
* at the end of the given ray. <br><br>
|
||||
*
|
||||
* Will return "success" with a null datapoint if the ray reaches the max length without finding any data.
|
||||
*
|
||||
* @since API 3.0.0
|
||||
*/
|
||||
DhApiResult<DhApiRaycastResult> raycast(
|
||||
IDhApiLevelWrapper levelWrapper,
|
||||
double rayOriginX, double rayOriginY, double rayOriginZ,
|
||||
float rayDirectionX, float rayDirectionY, float rayDirectionZ,
|
||||
int maxRayBlockLength);
|
||||
int maxRayBlockLength,
|
||||
IDhApiTerrainDataCache dataCache);
|
||||
|
||||
|
||||
|
||||
@@ -98,15 +143,27 @@ public interface IDhApiTerrainDataRepo
|
||||
* Notes: <br>
|
||||
* - Only works if the given {@link IDhApiLevelWrapper} points to a loaded level. <br>
|
||||
* - If the player travels to this chunk, or the chunk is updated in some other way; your data will be replaced
|
||||
* by whatever the current chunk is. <br>
|
||||
* - This method may not update the LOD data immediately. Any other chunks have
|
||||
* been queued to update, they will be handled first.
|
||||
* by whatever the current chunk is. <br>
|
||||
* - This method may not update the LOD data immediately. Any other chunks that have
|
||||
* been queued to update will be handled first.
|
||||
*
|
||||
* @param levelWrapper the level wrapper that the chunk should be saved to.
|
||||
* @param chunkObjectArray see {@link IDhApiWorldGenerator#generateChunks} for what objects are expected.
|
||||
* @throws ClassCastException if chunkObjectArray doesn't contain the right objects.
|
||||
* The exception will contain the expected object(s).
|
||||
*/
|
||||
public DhApiResult<Void> overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException;
|
||||
DhApiResult<Void> overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException;
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// helpers //
|
||||
//=========//
|
||||
|
||||
/**
|
||||
* @return a {@link IDhApiTerrainDataCache} backed by {@link java.lang.ref.SoftReference}'s.
|
||||
* @since API 3.0.0
|
||||
*/
|
||||
IDhApiTerrainDataCache getSoftCache();
|
||||
|
||||
}
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package com.seibel.distanthorizons.core.api.external.methods.data;
|
||||
|
||||
import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataCache;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
|
||||
public class DhApiTerrainDataCache implements IDhApiTerrainDataCache
|
||||
{
|
||||
private final Object modificationLock = new Object();
|
||||
private Long2ReferenceOpenHashMap<SoftReference<FullDataSourceV2>> posToFullDataRef = new Long2ReferenceOpenHashMap<>();
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(DhApiTerrainDataCache.class.getSimpleName());
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// internal methods //
|
||||
//==================//
|
||||
|
||||
public void add(long pos, FullDataSourceV2 dataSource)
|
||||
{
|
||||
synchronized (this.modificationLock)
|
||||
{
|
||||
this.posToFullDataRef.put(pos, new SoftReference<>(dataSource));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FullDataSourceV2 get(long pos)
|
||||
{
|
||||
synchronized (this.modificationLock)
|
||||
{
|
||||
SoftReference<FullDataSourceV2> ref = this.posToFullDataRef.get(pos);
|
||||
if (ref != null)
|
||||
{
|
||||
return ref.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// API methods //
|
||||
//=============//
|
||||
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
synchronized (this.modificationLock)
|
||||
{
|
||||
LongSet keySet = this.posToFullDataRef.keySet();
|
||||
for (long pos : keySet)
|
||||
{
|
||||
SoftReference<FullDataSourceV2> dataRef = this.posToFullDataRef.remove(pos);
|
||||
if (dataRef != null)
|
||||
{
|
||||
FullDataSourceV2 dataSource = dataRef.get();
|
||||
if (dataSource != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
dataSource.close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.warn("Unable to close data source, error: [" + e.getMessage() + "].", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+138
-85
@@ -19,6 +19,7 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.api.external.methods.data;
|
||||
|
||||
import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataCache;
|
||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
|
||||
import com.seibel.distanthorizons.api.objects.DhApiResult;
|
||||
import com.seibel.distanthorizons.api.objects.data.DhApiRaycastResult;
|
||||
@@ -49,6 +50,7 @@ import com.seibel.distanthorizons.core.util.math.Vec3i;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -56,9 +58,6 @@ import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Allows interfacing with the terrain data Distant Horizons has stored.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-11-19
|
||||
*/
|
||||
public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
{
|
||||
@@ -68,12 +67,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
|
||||
// debugging values
|
||||
private static volatile boolean debugThreadRunning = false;
|
||||
private static String currentDebugBiomeName = "";
|
||||
private static int currentDebugBlockColorInt = -1;
|
||||
private static DhApiTerrainDataCache debugDataCache = new DhApiTerrainDataCache();
|
||||
private static DhApiVec3i currentDebugVec3i = new Vec3i();
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
private DhApiTerrainDataRepo()
|
||||
{
|
||||
|
||||
@@ -86,41 +88,32 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ)
|
||||
{
|
||||
return getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY);
|
||||
}
|
||||
public DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, @Nullable IDhApiTerrainDataCache dataCache)
|
||||
{ return getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, dataCache); }
|
||||
@Override
|
||||
public DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ)
|
||||
{
|
||||
return getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null);
|
||||
}
|
||||
public DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, @Nullable IDhApiTerrainDataCache dataCache)
|
||||
{ return getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, dataCache); }
|
||||
|
||||
@Override
|
||||
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ)
|
||||
{
|
||||
return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ));
|
||||
}
|
||||
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, @Nullable IDhApiTerrainDataCache dataCache)
|
||||
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ), dataCache); }
|
||||
|
||||
@Override
|
||||
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ)
|
||||
{
|
||||
return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ));
|
||||
}
|
||||
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, @Nullable IDhApiTerrainDataCache dataCache)
|
||||
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ), dataCache); }
|
||||
|
||||
@Override
|
||||
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(detailLevel, posX, posZ));
|
||||
}
|
||||
public DhApiResult<DhApiTerrainDataPoint[][][]> getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, @Nullable IDhApiTerrainDataCache dataCache)
|
||||
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(detailLevel, posX, posZ), dataCache); }
|
||||
|
||||
|
||||
|
||||
// private getters //
|
||||
|
||||
/** Returns a single API terrain datapoint that contains the given Y block position */
|
||||
private static DhApiResult<DhApiTerrainDataPoint> getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer blockYPos)
|
||||
private static DhApiResult<DhApiTerrainDataPoint> getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer blockYPos, @Nullable IDhApiTerrainDataCache dataCache)
|
||||
{
|
||||
DhApiResult<DhApiTerrainDataPoint[]> result = getTerrainDataColumnArray(levelWrapper, requestedColumnPos, blockYPos);
|
||||
DhApiResult<DhApiTerrainDataPoint[]> result = getTerrainDataColumnArray(levelWrapper, requestedColumnPos, blockYPos, dataCache);
|
||||
if (result.success && result.payload.length > 0)
|
||||
{
|
||||
return DhApiResult.createSuccess(result.message, result.payload[0]);
|
||||
@@ -140,7 +133,9 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
*
|
||||
* will stop and return the in progress data if any errors are encountered.
|
||||
*/
|
||||
private static DhApiResult<DhApiTerrainDataPoint[][][]> getTerrainDataOverAreaForPositionDetailLevel(IDhApiLevelWrapper levelWrapper, DhLodPos requestedAreaPos)
|
||||
private static DhApiResult<DhApiTerrainDataPoint[][][]> getTerrainDataOverAreaForPositionDetailLevel(
|
||||
IDhApiLevelWrapper levelWrapper, DhLodPos requestedAreaPos,
|
||||
@Nullable IDhApiTerrainDataCache dataCache)
|
||||
{
|
||||
DhLodPos startingBlockPos = requestedAreaPos.getCornerLodPos(LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
int widthOfAreaInBlocks = BitShiftUtil.powerOfTwo(requestedAreaPos.detailLevel);
|
||||
@@ -154,7 +149,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
for (int z = 0; z < widthOfAreaInBlocks; z++)
|
||||
{
|
||||
DhLodPos blockColumnPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, startingBlockPos.x + x, startingBlockPos.z + z);
|
||||
DhApiResult<DhApiTerrainDataPoint[]> result = getTerrainDataColumnArray(levelWrapper, blockColumnPos, null);
|
||||
DhApiResult<DhApiTerrainDataPoint[]> result = getTerrainDataColumnArray(levelWrapper, blockColumnPos, null, dataCache);
|
||||
if (result.success)
|
||||
{
|
||||
returnArray[x][z] = result.payload;
|
||||
@@ -177,8 +172,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
* If the ApiResult is successful there will be an array of data. <br>
|
||||
* The returned array will be empty if no data could be retrieved.
|
||||
*/
|
||||
private static DhApiResult<DhApiTerrainDataPoint[]> getTerrainDataColumnArray(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer nullableBlockYPos)
|
||||
private static DhApiResult<DhApiTerrainDataPoint[]> getTerrainDataColumnArray(
|
||||
IDhApiLevelWrapper levelWrapper,
|
||||
DhLodPos requestedColumnPos, Integer nullableBlockYPos,
|
||||
@Nullable IDhApiTerrainDataCache apiDataCache)
|
||||
{
|
||||
//============//
|
||||
// validation //
|
||||
//============//
|
||||
|
||||
AbstractDhWorld currentWorld = SharedApi.getAbstractDhWorld();
|
||||
if (currentWorld == null)
|
||||
{
|
||||
@@ -194,6 +196,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
|
||||
|
||||
|
||||
if (!(apiDataCache instanceof DhApiTerrainDataCache))
|
||||
{
|
||||
// custom level wrappers aren't supported,
|
||||
// the API user must get a level wrapper from our code somewhere
|
||||
return DhApiResult.createFail("Unsupported [" + IDhApiTerrainDataCache.class.getSimpleName() + "] implementation, only the core class [" + DhApiTerrainDataCache.class.getSimpleName() + "] is a valid parameter.");
|
||||
}
|
||||
DhApiTerrainDataCache dataCache = (DhApiTerrainDataCache) apiDataCache;
|
||||
|
||||
|
||||
IDhLevel level = currentWorld.getLevel(coreLevelWrapper);
|
||||
if (level == null)
|
||||
{
|
||||
@@ -209,70 +220,96 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
DhLodPos relativePos = requestedColumnPos.getDhSectionRelativePositionForDetailLevel();
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// get the data source //
|
||||
//=====================//
|
||||
|
||||
try
|
||||
{
|
||||
// attempt to get/generate the data source for this section
|
||||
FullDataSourceV2 dataSource = level.getFullDataProvider().getAsync(sectionPos).get();
|
||||
FullDataSourceV2 dataSource = null;
|
||||
|
||||
// try using the cached data if possible
|
||||
if (dataCache != null)
|
||||
{
|
||||
dataSource = dataCache.get(sectionPos);
|
||||
}
|
||||
|
||||
if (dataSource == null)
|
||||
{
|
||||
return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "].");
|
||||
}
|
||||
else
|
||||
{
|
||||
// attempt to get the LOD data from the data source
|
||||
FullDataPointIdMap mapping = dataSource.mapping;
|
||||
LongArrayList dataColumn = dataSource.get(relativePos.x, relativePos.z);
|
||||
if (dataColumn != null)
|
||||
// attempt to get/generate the data source for this section
|
||||
dataSource = level.getFullDataProvider().getAsync(sectionPos).get();
|
||||
if (dataSource == null)
|
||||
{
|
||||
int dataColumnIndexCount = dataColumn.size();
|
||||
DhApiTerrainDataPoint[] returnArray = new DhApiTerrainDataPoint[dataColumnIndexCount];
|
||||
long dataPoint;
|
||||
return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "].");
|
||||
}
|
||||
dataCache.add(sectionPos, dataSource);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===============================//
|
||||
// get LOD data from data source //
|
||||
//===============================//
|
||||
|
||||
FullDataPointIdMap mapping = dataSource.mapping;
|
||||
LongArrayList dataColumn = dataSource.get(relativePos.x, relativePos.z);
|
||||
if (dataColumn != null)
|
||||
{
|
||||
int dataColumnIndexCount = dataColumn.size();
|
||||
DhApiTerrainDataPoint[] returnArray = new DhApiTerrainDataPoint[dataColumnIndexCount];
|
||||
long dataPoint;
|
||||
|
||||
boolean getSpecificYCoordinate = nullableBlockYPos != null;
|
||||
int levelMinimumHeight = levelWrapper.getMinHeight();
|
||||
|
||||
|
||||
// search for a datapoint that contains the block y position
|
||||
for (int i = 0; i < dataColumnIndexCount; i++)
|
||||
{
|
||||
dataPoint = dataColumn.getLong(i);
|
||||
|
||||
boolean getSpecificYCoordinate = nullableBlockYPos != null;
|
||||
int levelMinimumHeight = levelWrapper.getMinHeight();
|
||||
|
||||
|
||||
// search for a datapoint that contains the block y position
|
||||
for (int i = 0; i < dataColumnIndexCount; i++)
|
||||
if (!getSpecificYCoordinate)
|
||||
{
|
||||
dataPoint = dataColumn.getLong(i);
|
||||
|
||||
if (!getSpecificYCoordinate)
|
||||
// if we aren't look for a specific datapoint, add each datapoint to the return array
|
||||
returnArray[i] = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are looking for a specific datapoint,
|
||||
// don't look at null ones
|
||||
if (dataPoint != 0)
|
||||
{
|
||||
// if we aren't look for a specific datapoint, add each datapoint to the return array
|
||||
returnArray[i] = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are looking for a specific datapoint,
|
||||
// don't look at null ones
|
||||
if (dataPoint != 0)
|
||||
int requestedY = nullableBlockYPos;
|
||||
int bottomY = FullDataPointUtil.getBottomY(dataPoint) + levelMinimumHeight;
|
||||
int height = FullDataPointUtil.getHeight(dataPoint);
|
||||
int topY = bottomY + height;
|
||||
|
||||
// does this datapoint contain the requested Y position?
|
||||
if (bottomY <= requestedY && requestedY < topY) // blockPositions start from the bottom of the block, thus "<=" for bottomY, just "<" for topY
|
||||
{
|
||||
int requestedY = nullableBlockYPos;
|
||||
int bottomY = FullDataPointUtil.getBottomY(dataPoint) + levelMinimumHeight;
|
||||
int height = FullDataPointUtil.getHeight(dataPoint);
|
||||
int topY = bottomY + height;
|
||||
|
||||
// does this datapoint contain the requested Y position?
|
||||
if (bottomY <= requestedY && requestedY < topY) // blockPositions start from the bottom of the block, thus "<=" for bottomY, just "<" for topY
|
||||
{
|
||||
// this datapoint contains the requested block position, return it
|
||||
DhApiTerrainDataPoint apiTerrainData = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint);
|
||||
return DhApiResult.createSuccess(new DhApiTerrainDataPoint[]{apiTerrainData});
|
||||
}
|
||||
// this datapoint contains the requested block position, return it
|
||||
DhApiTerrainDataPoint apiTerrainData = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint);
|
||||
return DhApiResult.createSuccess(new DhApiTerrainDataPoint[]{apiTerrainData});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return all collected data
|
||||
return DhApiResult.createSuccess(returnArray);
|
||||
}
|
||||
|
||||
// the requested data wasn't present in this column (and/or the column wasn't able to be accessed/generated)
|
||||
return DhApiResult.createSuccess(new DhApiTerrainDataPoint[0]);
|
||||
// return all collected data
|
||||
return DhApiResult.createSuccess(returnArray);
|
||||
}
|
||||
|
||||
// the requested data wasn't present in this column (and/or the column wasn't able to be accessed/generated)
|
||||
return DhApiResult.createSuccess(new DhApiTerrainDataPoint[0]);
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e)
|
||||
{
|
||||
// shouldn't normally happen, but just in case
|
||||
LOGGER.error("getTerrainDataColumnArray operation canceled. Error: [" + e.getMessage() + "]", e);
|
||||
return DhApiResult.createFail("Operation cancled before it could complete: [" + e.getMessage() + "].");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// shouldn't normally happen, but just in case
|
||||
LOGGER.error("Unexpected exception in getTerrainDataColumnArray. Error: [" + e.getMessage() + "]", e);
|
||||
@@ -306,9 +343,11 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
IDhApiLevelWrapper levelWrapper,
|
||||
double rayOriginX, double rayOriginY, double rayOriginZ,
|
||||
float rayDirectionX, float rayDirectionY, float rayDirectionZ,
|
||||
int maxRayBlockLength)
|
||||
int maxRayBlockLength,
|
||||
@Nullable
|
||||
IDhApiTerrainDataCache dataCache)
|
||||
{
|
||||
return this.raycastLodData(levelWrapper, new Vec3d(rayOriginX, rayOriginY, rayOriginZ), new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ), maxRayBlockLength);
|
||||
return this.raycastLodData(levelWrapper, new Vec3d(rayOriginX, rayOriginY, rayOriginZ), new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ), maxRayBlockLength, dataCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,7 +356,12 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
* Works by walking through the world and attempting to get the LOD <br>
|
||||
* data present at each step.
|
||||
*/
|
||||
private DhApiResult<DhApiRaycastResult> raycastLodData(IDhApiLevelWrapper levelWrapper, Vec3d rayOrigin, Vec3f rayDirection, int maxRayBlockLength)
|
||||
private DhApiResult<DhApiRaycastResult> raycastLodData(
|
||||
IDhApiLevelWrapper levelWrapper,
|
||||
Vec3d rayOrigin, Vec3f rayDirection,
|
||||
int maxRayBlockLength,
|
||||
@Nullable
|
||||
IDhApiTerrainDataCache dataCache)
|
||||
{
|
||||
rayDirection.normalize();
|
||||
|
||||
@@ -346,7 +390,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
for (Vec3i columnPos : columnPositions)
|
||||
{
|
||||
// check each column
|
||||
DhApiResult<DhApiTerrainDataPoint[]> result = this.getColumnDataAtBlockPos(levelWrapper, columnPos.x, columnPos.z);
|
||||
DhApiResult<DhApiTerrainDataPoint[]> result = this.getColumnDataAtBlockPos(levelWrapper, columnPos.x, columnPos.z, dataCache);
|
||||
if (!result.success)
|
||||
{
|
||||
// if there was an error, stop and return it
|
||||
@@ -469,6 +513,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// API helpers //
|
||||
//=============//
|
||||
|
||||
@Override
|
||||
public IDhApiTerrainDataCache getSoftCache() { return new DhApiTerrainDataCache(); }
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// debug methods //
|
||||
//===============//
|
||||
@@ -485,15 +538,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
Thread thread = new Thread(() -> {
|
||||
try
|
||||
{
|
||||
DhApiResult<DhApiTerrainDataPoint> single = getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY);
|
||||
DhApiResult<DhApiTerrainDataPoint[]> column = getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null);
|
||||
DhApiResult<DhApiTerrainDataPoint> single = getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, debugDataCache);
|
||||
DhApiResult<DhApiTerrainDataPoint[]> column = getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, debugDataCache);
|
||||
|
||||
DhLodPos chunkPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ).convertToDetailLevel(LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
DhApiResult<DhApiTerrainDataPoint[][][]> area = getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, chunkPos);
|
||||
DhApiResult<DhApiTerrainDataPoint[][][]> area = getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, chunkPos, debugDataCache);
|
||||
|
||||
|
||||
IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||
DhApiResult<DhApiRaycastResult> rayCast = INSTANCE.raycastLodData(levelWrapper, MC_RENDER.getCameraExactPosition(), MC_RENDER.getLookAtVector(), 1000);
|
||||
DhApiResult<DhApiRaycastResult> rayCast = INSTANCE.raycastLodData(levelWrapper, MC_RENDER.getCameraExactPosition(), MC_RENDER.getLookAtVector(), 1000, debugDataCache);
|
||||
if (rayCast.payload != null && !rayCast.payload.pos.equals(currentDebugVec3i))
|
||||
{
|
||||
currentDebugVec3i = rayCast.payload.pos;
|
||||
|
||||
Reference in New Issue
Block a user