Implement DhApiTerrainDataPointRepo get methods

This commit is contained in:
James Seibel
2022-11-13 21:49:26 -06:00
parent 1099f5ec24
commit cd1c12be12
4 changed files with 257 additions and 58 deletions
@@ -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;
}
}
@@ -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