From 3dfaed4409a11631474069b74333af05e542b379 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 12 Nov 2022 12:57:40 -0600 Subject: [PATCH] Add a demo/debug method to DhApiTerrainDataRepo --- .../java/com/seibel/lod/api/DhApiMain.java | 34 ++- .../methods/data/DhApiTerrainDataRepo.java | 45 ---- .../com/seibel/lod/api/methods/data/Readme.md | 1 - .../java/com/seibel/lod/core/Initializer.java | 4 +- .../methods/data/DhApiTerrainDataRepo.java | 222 ++++++++++++++++++ .../core/file/datafile/DataFileHandler.java | 3 +- 6 files changed, 249 insertions(+), 60 deletions(-) delete mode 100644 api/src/main/java/com/seibel/lod/api/methods/data/DhApiTerrainDataRepo.java delete mode 100644 api/src/main/java/com/seibel/lod/api/methods/data/Readme.md create mode 100644 core/src/main/java/com/seibel/lod/core/api/external/methods/data/DhApiTerrainDataRepo.java 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(); }