diff --git a/api/src/main/java/com/seibel/lod/api/DhApiMain.java b/api/src/main/java/com/seibel/lod/api/DhApiMain.java
index ca04bc61c..941696fbd 100644
--- a/api/src/main/java/com/seibel/lod/api/DhApiMain.java
+++ b/api/src/main/java/com/seibel/lod/api/DhApiMain.java
@@ -3,7 +3,6 @@ package com.seibel.lod.api;
import com.seibel.lod.api.interfaces.config.IDhApiConfig;
import com.seibel.lod.api.interfaces.override.IDhApiOverrideable;
import com.seibel.lod.api.interfaces.override.worldGenerator.IDhApiWorldGeneratorOverrideRegister;
-import com.seibel.lod.api.methods.data.DhApiTerrainDataRepo;
import com.seibel.lod.api.methods.override.DhApiWorldGeneratorOverrideRegister;
import com.seibel.lod.core.DependencyInjection.DhApiEventInjector;
import com.seibel.lod.core.DependencyInjection.OverrideInjector;
@@ -25,7 +24,7 @@ import com.seibel.lod.core.interfaces.dependencyInjection.IOverrideInjector;
* the concrete object we replaced, there would be issues.
*
* @author James Seibel
- * @version 2022-9-16
+ * @version 2022-11-12
*/
public class DhApiMain
{
@@ -36,29 +35,40 @@ public class DhApiMain
*
* Use a {@link com.seibel.lod.api.methods.events.abstractEvents.DhApiAfterDhInitEvent DhApiAfterDhInitEvent}
* along with the {@link DhApiMain#events ApiCoreInjectors.events} to be notified when this can
- * be safely used.
+ * be safely used.
+ *
+ * Used to interact with Distant Horizons' Configs.
*/
- public static IDhApiConfig configs;
+ public static IDhApiConfig configs = null;
+
+ /**
+ * WARNING: will be null until after DH initializes for the first time.
+ *
+ * Use a {@link com.seibel.lod.api.methods.events.abstractEvents.DhApiAfterDhInitEvent DhApiAfterDhInitEvent}
+ * along with the {@link DhApiMain#events ApiCoreInjectors.events} to be notified when this can
+ * be safely used.
+ *
+ * Used to interact with Distant Horizons' terrain data.
+ */
+ public static IDhApiTerrainDataRepo terrainRepo = null;
+
// always available //
- /** Used to bind/unbind DH Api events. */
+ /** Used to bind/unbind Distant Horizons Api events. */
public static final IDhApiEventInjector events = DhApiEventInjector.INSTANCE;
- /** Used to bind/unbind DH Api events. */
+ /** Used to bind/unbind Distant Horizons Api events. */
public static final IDhApiWorldGeneratorOverrideRegister worldGenOverrides = DhApiWorldGeneratorOverrideRegister.INSTANCE;
- /** Used to bind overrides to change DH's core behavior. */
+ /** Used to bind overrides to change Distant Horizons' core behavior. */
public static final IOverrideInjector overrides = OverrideInjector.INSTANCE;
- /** Used to interact with DH's terrain data. */
- public static final IDhApiTerrainDataRepo terrainRepo = DhApiTerrainDataRepo.INSTANCE;
-
- /** This version should only be updated when breaking changes are introduced to the DH API. */
+ /** This version should only be updated when breaking changes are introduced to the Distant Horizons API. */
public static int getApiMajorVersion() { return ModInfo.API_MAJOR_VERSION; }
- /** This version should be updated whenever new methods are added to the DH API. */
+ /** This version should be updated whenever new methods are added to the Distant Horizons API. */
public static int getApiMinorVersion() { return ModInfo.API_MINOR_VERSION; }
/** Returns the mod's version number in the format: Major.Minor.Patch */
diff --git a/api/src/main/java/com/seibel/lod/api/methods/data/DhApiTerrainDataRepo.java b/api/src/main/java/com/seibel/lod/api/methods/data/DhApiTerrainDataRepo.java
deleted file mode 100644
index deb2855de..000000000
--- a/api/src/main/java/com/seibel/lod/api/methods/data/DhApiTerrainDataRepo.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.seibel.lod.api.methods.data;
-
-import com.seibel.lod.api.objects.DhApiResult;
-import com.seibel.lod.api.objects.data.DhApiTerrainDataPoint;
-import com.seibel.lod.api.interfaces.data.IDhApiTerrainDataRepo;
-
-
-/**
- * Allows getting and setting any terrain data Distant Horizons has stored.
- *
- * TODO once 1.7's data refactor is complete ask Leetom and/or Leonardo for help on setting these up
- *
- * @author James Seibel
- * @version 2022-9-16
- */
-public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
-{
- public static DhApiTerrainDataRepo INSTANCE = new DhApiTerrainDataRepo();
-
- private DhApiTerrainDataRepo() { }
-
-
-
- @Override
- public DhApiTerrainDataPoint getDataAtBlockPos(int blockPosX, int blockPosY, int blockPosZ) { throw new UnsupportedOperationException(); }
- @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(); }
-
- @Override
- public DhApiTerrainDataPoint getDataAtRegionPos(int regionPosX, int regionPosZ) { throw new UnsupportedOperationException(); }
- @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(); }
-
-}
diff --git a/api/src/main/java/com/seibel/lod/api/methods/data/Readme.md b/api/src/main/java/com/seibel/lod/api/methods/data/Readme.md
deleted file mode 100644
index a4089c832..000000000
--- a/api/src/main/java/com/seibel/lod/api/methods/data/Readme.md
+++ /dev/null
@@ -1 +0,0 @@
-The data api package holds objects and methods for getting/setting data stored by Distant Horizons.
\ No newline at end of file
diff --git a/core/src/main/java/com/seibel/lod/core/Initializer.java b/core/src/main/java/com/seibel/lod/core/Initializer.java
index ff038d29e..7790d6c9a 100644
--- a/core/src/main/java/com/seibel/lod/core/Initializer.java
+++ b/core/src/main/java/com/seibel/lod/core/Initializer.java
@@ -1,6 +1,7 @@
package com.seibel.lod.core;
import com.seibel.lod.core.api.external.methods.config.DhApiConfig;
+import com.seibel.lod.core.api.external.methods.data.DhApiTerrainDataRepo;
import com.seibel.lod.core.datatype.column.ColumnRenderLoader;
import com.seibel.lod.core.datatype.full.FullDataLoader;
import com.seibel.lod.core.datatype.full.SparseDataLoader;
@@ -11,7 +12,7 @@ import com.seibel.lod.core.datatype.full.SpottyDataLoader;
* Handles first time Core setup.
*
* @author Leetom
- * @version 2022-9-15
+ * @version 2022-11-12
*/
public class Initializer
{
@@ -24,6 +25,7 @@ public class Initializer
// link Core's config to the API
DhApiMain.configs = DhApiConfig.INSTANCE;
+ DhApiMain.terrainRepo = DhApiTerrainDataRepo.INSTANCE;
}
}
diff --git a/core/src/main/java/com/seibel/lod/core/api/external/methods/data/DhApiTerrainDataRepo.java b/core/src/main/java/com/seibel/lod/core/api/external/methods/data/DhApiTerrainDataRepo.java
new file mode 100644
index 000000000..f80f578fa
--- /dev/null
+++ b/core/src/main/java/com/seibel/lod/core/api/external/methods/data/DhApiTerrainDataRepo.java
@@ -0,0 +1,222 @@
+package com.seibel.lod.core.api.external.methods.data;
+
+import com.seibel.lod.api.objects.DhApiResult;
+import com.seibel.lod.api.objects.data.DhApiTerrainDataPoint;
+import com.seibel.lod.api.interfaces.data.IDhApiTerrainDataRepo;
+import com.seibel.lod.core.api.internal.SharedApi;
+import com.seibel.lod.core.datatype.ILodDataSource;
+import com.seibel.lod.core.datatype.full.FullDataPoint;
+import com.seibel.lod.core.datatype.full.FullDataPointIdMap;
+import com.seibel.lod.core.datatype.full.accessor.SingleFullArrayView;
+import com.seibel.lod.core.dependencyInjection.SingletonInjector;
+import com.seibel.lod.core.level.DhClientServerLevel;
+import com.seibel.lod.core.level.IDhLevel;
+import com.seibel.lod.core.pos.DhBlockPos;
+import com.seibel.lod.core.pos.DhLodPos;
+import com.seibel.lod.core.pos.DhSectionPos;
+import com.seibel.lod.core.util.BitShiftUtil;
+import com.seibel.lod.core.util.ColorUtil;
+import com.seibel.lod.core.util.LodUtil;
+import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
+import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
+import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
+import com.seibel.lod.core.wrapperInterfaces.world.IClientLevelWrapper;
+import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
+import org.apache.logging.log4j.LogManager;
+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
+ */
+public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
+{
+ public static DhApiTerrainDataRepo INSTANCE = new DhApiTerrainDataRepo();
+
+ private static final Logger LOGGER = LogManager.getLogger(DhApiTerrainDataRepo.class.getSimpleName());
+
+ // debugging values
+ private static final Lock debugThreadLock = new ReentrantLock();
+ private static String currentDebugBiomeName = "";
+ private static int currentDebugBlockColorInt = -1;
+
+
+
+ private DhApiTerrainDataRepo()
+ {
+
+ }
+
+
+
+ @Override
+ public DhApiTerrainDataPoint getDataAtBlockPos(int blockPosX, int blockPosY, int blockPosZ) { throw new UnsupportedOperationException(); }
+ @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(); }
+
+ @Override
+ public DhApiTerrainDataPoint getDataAtRegionPos(int regionPosX, int regionPosZ) { throw new UnsupportedOperationException(); }
+ @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(); }
+
+
+
+ //===============//
+ // debug methods //
+ //===============//
+
+ /**
+ * Shouldn't be visible to API users, is only valid
+ * for use on singleplayer worlds, and should only be
+ * used for debugging.
+ *
+ * Suggested use when testing is to call this during the ClientApi.render() method.
+ *
+ * @param levelWrapper which level the player is in
+ */
+ public static void logTopBlockAtBlockPosition(ILevelWrapper levelWrapper, DhBlockPos blockPos)
+ {
+ IMinecraftClientWrapper mcClientWrapper = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
+
+ if (debugThreadLock.tryLock())
+ {
+ // thread to prevent locking up the render thread
+ Thread thread = new Thread(() ->
+ {
+ try
+ {
+ IDhLevel level = SharedApi.currentWorld.getLevel(levelWrapper);
+ DhClientServerLevel serverLevel = (DhClientServerLevel) level;
+
+ int xBlockPos = blockPos.x;
+ int zBlockPos = blockPos.z;
+
+ byte inputDetailLevel = LodUtil.BLOCK_DETAIL_LEVEL;
+ byte outputDetailLevel = LodUtil.CHUNK_DETAIL_LEVEL;
+ byte detailLevelDifference = (byte) (outputDetailLevel - inputDetailLevel);
+
+ DhLodPos lodPos = new DhLodPos(inputDetailLevel, xBlockPos, zBlockPos);
+ lodPos = lodPos.convertUpwardsTo((byte) (DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL + detailLevelDifference));
+ DhSectionPos sectionPos = new DhSectionPos(lodPos.detailLevel, lodPos.x, lodPos.z);
+
+
+ // negative values need to be offset by the detail level difference
+ // in order to skip over -0 (relative position) to -1 (relative position)
+ int blockOffset = BitShiftUtil.powerOfTwo(detailLevelDifference) - 1;
+ xBlockPos += xBlockPos < 0 ? -blockOffset : 0;
+ zBlockPos += zBlockPos < 0 ? -blockOffset : 0;
+
+ int xRelativePos = xBlockPos / BitShiftUtil.powerOfTwo(detailLevelDifference);
+ int zRelativePos = zBlockPos / BitShiftUtil.powerOfTwo(detailLevelDifference);
+ xRelativePos = xBlockPos >= 0 ? (xRelativePos % 64) : 64 + (xRelativePos % 64);
+ zRelativePos = zBlockPos >= 0 ? (zRelativePos % 64) : 64 + (zRelativePos % 64);
+
+
+ // attempt to get the data source for this section
+ ILodDataSource dataSource = serverLevel.dataFileHandler.read(sectionPos).get();
+ if (dataSource != null)
+ {
+ // attempt to get the LOD data from the data source
+ FullDataPointIdMap mapping = dataSource.getMapping();
+ SingleFullArrayView dataColumn = dataSource.tryGet(xRelativePos, zRelativePos);
+ if (dataColumn == null)
+ {
+ logBlockBiomeDebugInfoIfDifferent("", -1);
+ }
+ else
+ {
+ int yIndex = 0;
+ int maxYIndex = dataColumn.getSingleLength() - 1;
+ long dataPoint = 0;
+ IBlockStateWrapper currentBlockState = null;
+
+ // search top down for the top-most (non-air) block
+ while (yIndex < maxYIndex && (currentBlockState == null || currentBlockState.serialize().equals("AIR")))
+ {
+ dataPoint = dataColumn.getSingle(yIndex);
+ if (dataPoint != 0)
+ {
+ currentBlockState = mapping.getBlockStateWrapper(FullDataPoint.getId(dataPoint));
+ }
+ yIndex++;
+ }
+
+
+ // log the LOD data if present
+ if (dataPoint != 0)
+ {
+ logBlockBiomeDebugInfoIfDifferent(levelWrapper, mcClientWrapper.getPlayerBlockPos(), dataPoint, mapping);
+ }
+ else
+ {
+ // no block data was found for this column
+ logBlockBiomeDebugInfoIfDifferent("[VOID]", -1);
+ }
+ }
+ }
+ }
+ catch (InterruptedException | ExecutionException e)
+ {
+ // shouldn't normally happen, but just in case
+ e.printStackTrace();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ debugThreadLock.unlock();
+ }
+ });
+
+ thread.start();
+ }
+ }
+ /** only logs the data if it was different than the currently stored debug data */
+ private static void logBlockBiomeDebugInfoIfDifferent(ILevelWrapper levelWrapper, DhBlockPos blockPos, long dataPoint, FullDataPointIdMap mapping)
+ {
+ int id = FullDataPoint.getId(dataPoint);
+
+ IBiomeWrapper biome = mapping.getBiomeWrapper(id);
+ IBlockStateWrapper blockState = mapping.getBlockStateWrapper(id);
+
+ String newBiomeName = biome.serialize();
+ int newBlockColorInt = ((IClientLevelWrapper)levelWrapper).computeBaseColor(blockPos, biome, blockState);
+ logBlockBiomeDebugInfoIfDifferent(newBiomeName, newBlockColorInt);
+ }
+ /** only logs the data if it was different than the currently stored debug data */
+ private static void logBlockBiomeDebugInfoIfDifferent(String newBiomeName, int newBlockColorInt)
+ {
+ if (!currentDebugBiomeName.equals(newBiomeName) || currentDebugBlockColorInt != newBlockColorInt)
+ {
+ Color newBlockColor = colorFromBlockInt(newBlockColorInt);
+
+ currentDebugBiomeName = newBiomeName;
+ currentDebugBlockColorInt = newBlockColorInt;
+ LOGGER.info(newBiomeName + " " + newBlockColor);
+ }
+ }
+ private static Color colorFromBlockInt(int colorInt) { return new Color(ColorUtil.getRed(colorInt), ColorUtil.getGreen(colorInt), ColorUtil.getBlue(colorInt), ColorUtil.getAlpha(colorInt)); }
+
+
+}
diff --git a/core/src/main/java/com/seibel/lod/core/file/datafile/DataFileHandler.java b/core/src/main/java/com/seibel/lod/core/file/datafile/DataFileHandler.java
index accdfbbe8..7963ceae3 100644
--- a/core/src/main/java/com/seibel/lod/core/file/datafile/DataFileHandler.java
+++ b/core/src/main/java/com/seibel/lod/core/file/datafile/DataFileHandler.java
@@ -225,7 +225,8 @@ public class DataFileHandler implements IDataSourceProvider {
{
this.topDetailLevel.updateAndGet(v -> Math.max(v, pos.sectionDetail));
DataMetaFile metaFile = this.atomicGetOrMakeFile(pos);
- if (metaFile == null) return CompletableFuture.completedFuture(null);
+ if (metaFile == null)
+ return CompletableFuture.completedFuture(null);
return metaFile.loadOrGetCached();
}