Implement DhApiTerrainDataPointRepo get methods
This commit is contained in:
@@ -1,48 +1,46 @@
|
||||
package com.seibel.lod.api.interfaces.data;
|
||||
|
||||
import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper;
|
||||
import com.seibel.lod.api.objects.DhApiResult;
|
||||
import com.seibel.lod.api.objects.data.DhApiTerrainDataPoint;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 2022-9-16
|
||||
* @version 2022-11-13
|
||||
*/
|
||||
public interface IDhApiTerrainDataRepo
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns the terrain data at the given block position.
|
||||
* Null if the position hasn't been generated.
|
||||
*/
|
||||
DhApiTerrainDataPoint getDataAtBlockPos(int blockPosX, int blockPosY, int blockPosZ);
|
||||
/** Sets the terrain data at the given block position. */
|
||||
DhApiResult setDataAtBlockPos(int blockPosX, int blockPosY, int blockPosZ, DhApiTerrainDataPoint newData);
|
||||
/** 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. */
|
||||
DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ);
|
||||
// /** Sets the terrain data at the given block position. */
|
||||
// DhApiResult setDataAtBlockPos(int blockPosX, int blockPosY, int blockPosZ, DhApiTerrainDataPoint newData);
|
||||
|
||||
/**
|
||||
* Returns the average color for the chunk at the given chunk position.
|
||||
* Returns null if the position hasn't been generated.
|
||||
*/
|
||||
DhApiTerrainDataPoint getDataAtChunkPos(int chunkPosX, int chunkPosZ);
|
||||
/** Sets the terrain data at the given chunk position. */
|
||||
DhApiResult setDataAtChunkPos(int chunkPosX, int chunkPosZ, DhApiTerrainDataPoint newData);
|
||||
|
||||
/**
|
||||
* Returns the average color for the chunk at the given chunk position.
|
||||
* May return inaccurate data if the whole region hasn't been generated yet.
|
||||
* Returns null if the position hasn't been generated.
|
||||
*/
|
||||
DhApiTerrainDataPoint getDataAtRegionPos(int regionPosX, int regionPosZ);
|
||||
/** Sets the terrain data at the given chunk position. */
|
||||
DhApiResult setDataAtRegionPos(int regionPosX, int regionPosZ, DhApiTerrainDataPoint newData);
|
||||
/** Returns every datapoint in the column located at the given chunk X and Z position. */
|
||||
DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ);
|
||||
// /** Sets the terrain data at the given chunk position. */
|
||||
// DhApiResult setDataAtChunkPos(int chunkPosX, int chunkPosZ, DhApiTerrainDataPoint newData);
|
||||
|
||||
|
||||
/** Returns every datapoint in the column located at the given region X and Z position. */
|
||||
DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ);
|
||||
// /** Sets the terrain data at the given chunk position. */
|
||||
// DhApiResult setDataAtRegionPos(int regionPosX, int regionPosZ, DhApiTerrainDataPoint newData);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the average color for the chunk at the given chunk position.
|
||||
* May return inaccurate data if the whole region hasn't been generated yet.
|
||||
* Returns null if the position hasn't been generated.
|
||||
* 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).
|
||||
*
|
||||
* @param detailLevel a positive byte defining the detail level of the returned data. <br>
|
||||
* 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)
|
||||
*/
|
||||
DhApiTerrainDataPoint getDataAtDetailLevelAndPos(short detailLevel, int relativePosX, int relativePosY, int relativePosZ);
|
||||
/** Sets the terrain data at the given chunk position. */
|
||||
DhApiResult setDataAtRegionPos(short detailLevel, int relativePosX, int relativePosY, int relativePosZ, DhApiTerrainDataPoint newData);
|
||||
DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ);
|
||||
// /** Sets the terrain data at the given chunk position. */
|
||||
// DhApiResult setDataAtRegionPos(short detailLevel, int relativePosX, int relativePosY, int relativePosZ, DhApiTerrainDataPoint newData);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,29 +1,44 @@
|
||||
package com.seibel.lod.api.objects.data;
|
||||
|
||||
import java.awt.Color;
|
||||
import com.seibel.lod.api.interfaces.block.IDhApiBiomeWrapper;
|
||||
import com.seibel.lod.api.interfaces.block.IDhApiBlockStateWrapper;
|
||||
|
||||
/**
|
||||
* Holds a single datapoint of terrain data.
|
||||
*
|
||||
* // TODO what additional data should this hold?
|
||||
*
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-7-12
|
||||
* @version 2022-11-13
|
||||
*/
|
||||
public class DhApiTerrainDataPoint
|
||||
{
|
||||
/**
|
||||
* The average color for the given data point.
|
||||
* Invisible if the position is air.
|
||||
* Null if the position is invalid.
|
||||
* 0 = block <br>
|
||||
* 1 = 2x2 blocks <br>
|
||||
* 2 = 4x4 blocks <br>
|
||||
* 4 = chunk (16x16 blocks) <br>
|
||||
* 9 = region (512x512 blocks) <br>
|
||||
*/
|
||||
public Color color;
|
||||
public final byte detailLevel;
|
||||
|
||||
/**
|
||||
* TODO is this data type correct?
|
||||
* TODO create an API enum that contains useful values (block, chunk, region, etc.)
|
||||
* 0 = block
|
||||
*/
|
||||
public short detailLevel;
|
||||
public final int lightLevel;
|
||||
public final int topYBlockPos;
|
||||
public final int bottomYBlockPos;
|
||||
|
||||
public final IDhApiBlockStateWrapper blockStateWrapper;
|
||||
public final IDhApiBiomeWrapper biomeWrapper;
|
||||
|
||||
|
||||
|
||||
public DhApiTerrainDataPoint(byte detailLevel, int lightLevel, int topYBlockPos, int bottomYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
|
||||
this.lightLevel = lightLevel;
|
||||
this.topYBlockPos = topYBlockPos;
|
||||
this.bottomYBlockPos = bottomYBlockPos;
|
||||
|
||||
this.blockStateWrapper = blockStateWrapper;
|
||||
this.biomeWrapper = biomeWrapper;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+198
-14
@@ -1,5 +1,6 @@
|
||||
package com.seibel.lod.core.api.external.methods.data;
|
||||
|
||||
import com.seibel.lod.api.interfaces.world.IDhApiLevelWrapper;
|
||||
import com.seibel.lod.api.objects.DhApiResult;
|
||||
import com.seibel.lod.api.objects.data.DhApiTerrainDataPoint;
|
||||
import com.seibel.lod.api.interfaces.data.IDhApiTerrainDataRepo;
|
||||
@@ -26,15 +27,13 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
|
||||
/**
|
||||
* Allows getting and setting any terrain data Distant Horizons has stored.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-11-12
|
||||
* @version 2022-11-13
|
||||
*/
|
||||
public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
{
|
||||
@@ -57,25 +56,183 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
|
||||
|
||||
@Override
|
||||
public DhApiTerrainDataPoint getDataAtBlockPos(int blockPosX, int blockPosY, int blockPosZ) { throw new UnsupportedOperationException(); }
|
||||
public DhApiResult<DhApiTerrainDataPoint> getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ)
|
||||
{
|
||||
return getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY);
|
||||
}
|
||||
@Override
|
||||
public DhApiResult setDataAtBlockPos(int blockPosX, int blockPosY, int blockPosZ, DhApiTerrainDataPoint newData) { throw new UnsupportedOperationException(); }
|
||||
public DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ)
|
||||
{
|
||||
return getTerrainDataArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null);
|
||||
}
|
||||
// @Override
|
||||
// public DhApiResult setDataAtBlockPos(int blockPosX, int blockPosY, int blockPosZ, DhApiTerrainDataPoint newData) { throw new UnsupportedOperationException(); }
|
||||
|
||||
@Override
|
||||
public DhApiTerrainDataPoint getDataAtChunkPos(int chunkPosX, int chunkPosZ) { throw new UnsupportedOperationException(); }
|
||||
@Override
|
||||
public DhApiResult setDataAtChunkPos(int chunkPosX, int chunkPosZ, DhApiTerrainDataPoint newData) { throw new UnsupportedOperationException(); }
|
||||
public DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ)
|
||||
{
|
||||
return getTerrainDataArray(levelWrapper, new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ), null);
|
||||
}
|
||||
// @Override
|
||||
// public DhApiResult setDataAtChunkPos(int chunkPosX, int chunkPosZ, DhApiTerrainDataPoint newData) { throw new UnsupportedOperationException(); }
|
||||
|
||||
@Override
|
||||
public DhApiTerrainDataPoint getDataAtRegionPos(int regionPosX, int regionPosZ) { throw new UnsupportedOperationException(); }
|
||||
@Override
|
||||
public DhApiResult setDataAtRegionPos(int regionPosX, int regionPosZ, DhApiTerrainDataPoint newData) { throw new UnsupportedOperationException(); }
|
||||
public DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ)
|
||||
{
|
||||
return getTerrainDataArray(levelWrapper, new DhLodPos(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ), null);
|
||||
}
|
||||
// @Override
|
||||
// public DhApiResult setDataAtRegionPos(int regionPosX, int regionPosZ, DhApiTerrainDataPoint newData) { throw new UnsupportedOperationException(); }
|
||||
|
||||
|
||||
@Override
|
||||
public DhApiTerrainDataPoint getDataAtDetailLevelAndPos(short detailLevel, int relativePosX, int relativePosY, int relativePosZ) { throw new UnsupportedOperationException(); }
|
||||
@Override
|
||||
public DhApiResult setDataAtRegionPos(short detailLevel, int relativePosX, int relativePosY, int relativePosZ, DhApiTerrainDataPoint newData) { throw new UnsupportedOperationException(); }
|
||||
public DhApiResult<DhApiTerrainDataPoint[]> getColumnDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
return getTerrainDataArray(levelWrapper, new DhLodPos(detailLevel, posX, posZ), null);
|
||||
}
|
||||
// @Override
|
||||
// public DhApiResult setDataAtRegionPos(short detailLevel, int relativePosX, int relativePosY, int relativePosZ, DhApiTerrainDataPoint newData) { throw new UnsupportedOperationException(); }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// Getter Methods //
|
||||
//================//
|
||||
|
||||
/** Returns a single API terrain datapoint that contains the given Y block position */
|
||||
private static DhApiResult<DhApiTerrainDataPoint> getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer blockYPos)
|
||||
{
|
||||
DhApiResult<DhApiTerrainDataPoint[]> result = getTerrainDataArray(levelWrapper, requestedColumnPos, blockYPos);
|
||||
if (result.success && result.payload.length > 0)
|
||||
{
|
||||
return DhApiResult.createSuccess(result.errorMessage, result.payload[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DhApiResult.createFail(result.errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If nullableBlockYPos is null: returns every datapoint in the column defined by the DhLodPos. <br>
|
||||
* If nullableBlockYPos is NOT null: returns a single datapoint in the column defined by the DhLodPos which contains the block Y position. <br><br>
|
||||
*
|
||||
* Returns an empty array if no data could be returned.
|
||||
*/
|
||||
private static DhApiResult<DhApiTerrainDataPoint[]> getTerrainDataArray(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer nullableBlockYPos)
|
||||
{
|
||||
if (SharedApi.currentWorld == null)
|
||||
{
|
||||
return DhApiResult.createFail("Unable to get terrain data before the world has loaded.");
|
||||
}
|
||||
if (!(levelWrapper instanceof ILevelWrapper coreLevelWrapper))
|
||||
{
|
||||
// custom level wrappers aren't supported,
|
||||
// the API user must get a level wrapper from our code somewhere
|
||||
return DhApiResult.createFail("Unsupported [" + IDhApiLevelWrapper.class.getSimpleName() + "] implementation, only the core class [" + IDhLevel.class.getSimpleName() + "] is a valid parameter.");
|
||||
}
|
||||
|
||||
IDhLevel level = SharedApi.currentWorld.getLevel(coreLevelWrapper);
|
||||
if (level == null)
|
||||
{
|
||||
return DhApiResult.createFail("Unable to get terrain data before the world has loaded.");
|
||||
}
|
||||
// DhClientServerLevel serverLevel = (DhClientServerLevel) level;
|
||||
// IDhLevel serverLevel = (IDhLevel) level;
|
||||
|
||||
// get the detail levels for this request
|
||||
byte requestedDetailLevel = requestedColumnPos.detailLevel;
|
||||
byte sectionDetailLevel = (byte) (requestedDetailLevel + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||
|
||||
// get the positions for this request
|
||||
DhSectionPos sectionPos = requestedColumnPos.getSectionPosWithSectionDetailLevel(sectionDetailLevel);
|
||||
DhLodPos relativePos = requestedColumnPos.getDhSectionRelativePositionForDetailLevel();
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// attempt to get/generate the data source for this section
|
||||
ILodDataSource dataSource = level.getFileHandler().read(sectionPos).get();
|
||||
if (dataSource == null)
|
||||
{
|
||||
return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + sectionPos + "].");
|
||||
}
|
||||
else
|
||||
{
|
||||
// attempt to get the LOD data from the data source
|
||||
FullDataPointIdMap mapping = dataSource.getMapping();
|
||||
SingleFullArrayView dataColumn = dataSource.tryGet(relativePos.x, relativePos.z);
|
||||
if (dataColumn != null)
|
||||
{
|
||||
int dataColumnIndexCount = dataColumn.getSingleLength();
|
||||
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.getSingle(i);
|
||||
|
||||
if (!getSpecificYCoordinate)
|
||||
{
|
||||
// if we aren't look for a specific datapoint, add each datapoint to the return array
|
||||
returnArray[i] = generateApiDatapoint(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 = FullDataPoint.getY(dataPoint) + levelMinimumHeight; // TODO rename getY to getBottomY
|
||||
int height = FullDataPoint.getDepth(dataPoint); // TODO rename to getHeight
|
||||
int topY = bottomY + height;
|
||||
|
||||
// does this datapoint contain the requested Y position?
|
||||
if (bottomY < requestedY && requestedY <= topY)
|
||||
{
|
||||
// this datapoint contains the requested block position, return it
|
||||
DhApiTerrainDataPoint apiTerrainData = generateApiDatapoint(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]);
|
||||
}
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e)
|
||||
{
|
||||
// shouldn't normally happen, but just in case
|
||||
e.printStackTrace();
|
||||
return DhApiResult.createFail("Unexpected exception: [" + e.getMessage() + "].");
|
||||
}
|
||||
}
|
||||
|
||||
private static DhApiTerrainDataPoint generateApiDatapoint(FullDataPointIdMap mapping, byte detailLevel, long dataPoint)
|
||||
{
|
||||
IBlockStateWrapper blockState = mapping.getBlockStateWrapper(FullDataPoint.getId(dataPoint));
|
||||
IBiomeWrapper biomeWrapper = mapping.getBiomeWrapper(FullDataPoint.getId(dataPoint));
|
||||
|
||||
int topY = FullDataPoint.getY(dataPoint);
|
||||
int depth = FullDataPoint.getDepth(dataPoint);
|
||||
int bottomY = topY - depth;
|
||||
|
||||
return new DhApiTerrainDataPoint(detailLevel,
|
||||
FullDataPoint.getLight(dataPoint), topY, bottomY,
|
||||
blockState, biomeWrapper);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -83,6 +240,32 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
// debug methods //
|
||||
//===============//
|
||||
|
||||
/** debug methods need to be async because pausing the main thread to debug and hot swapping will crash the program */
|
||||
public static void asyncDebugMethod(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ)
|
||||
{
|
||||
if (!debugThreadRunning)
|
||||
{
|
||||
debugThreadRunning = true;
|
||||
Thread thread = new Thread(() -> {
|
||||
try
|
||||
{
|
||||
DhApiResult<DhApiTerrainDataPoint> x = getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY);
|
||||
DhApiResult<DhApiTerrainDataPoint[]> y = getTerrainDataArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
debugThreadRunning = false;
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shouldn't be visible to API users, is only valid
|
||||
* for use on singleplayer worlds, and should only be
|
||||
@@ -114,6 +297,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
|
||||
DhLodPos relativePos = inputPos.getDhSectionRelativePositionForDetailLevel(LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
|
||||
|
||||
|
||||
// attempt to get the data source for this section
|
||||
ILodDataSource dataSource = serverLevel.dataFileHandler.read(sectionPos).get();
|
||||
if (dataSource != null)
|
||||
|
||||
@@ -85,6 +85,8 @@ public class DhLodPos implements Comparable<DhLodPos>
|
||||
/** Returns this position's child index in its parent */
|
||||
public int getChildIndexOfParent() { return (this.x & 1) + BitShiftUtil.square(this.z & 1); }
|
||||
|
||||
/** @see DhLodPos#getSectionPosWithSectionDetailLevel(byte) */
|
||||
public DhLodPos getDhSectionRelativePositionForDetailLevel() throws IllegalArgumentException { return this.getDhSectionRelativePositionForDetailLevel(this.detailLevel); }
|
||||
/**
|
||||
* Returns a DhLodPos with the given detail level and an X/Z position somewhere between (0,0) and (63,63).
|
||||
* This is done to access specific sections from a {@link ILodDataSource} where LOD columns are stored
|
||||
|
||||
Reference in New Issue
Block a user