diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java
index 4d8b478f8..90bc2c5b8 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java
@@ -21,8 +21,14 @@ package com.seibel.distanthorizons.api.interfaces.world;
import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
+import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
+import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
+import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockColorOverrideEvent;
+import com.seibel.distanthorizons.api.objects.DhApiResult;
+import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
+import java.awt.*;
import java.io.File;
/**
@@ -90,6 +96,26 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper
*/
File getDhSaveFolder();
+ /**
+ * Returns the color DH would use for the given block/biome
+ * pair at the given world position before any API color overrides
+ * are considered.
+ * API color overrides are ignored to prevent infinite
+ * loops if this event is triggered inside said API override.
+ *
+ *
+ * Returns {@link DhApiResult#success} = false if {@link IDhApiLevelWrapper#getLevelType()} returns a {@link EDhApiLevelType#SERVER_LEVEL}
+ * (server levels have no concept of textures or colors).
+ *
+ * @see DhApiBlockColorOverrideEvent
+ * @since API 7.0.0
+ */
+ DhApiResult getBlockColorPreApi(
+ IDhApiBlockStateWrapper blockStateWrapper,
+ IDhApiBiomeWrapper biomeWrapper,
+ int blockWorldPosX, int blockWorldPosY, int blockWorldPosZ,
+ IDhApiFullDataSource dataSource);
+
}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockColorOverrideEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockColorOverrideEvent.java
index 4258e65d4..8b28c6254 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockColorOverrideEvent.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBlockColorOverrideEvent.java
@@ -19,16 +19,21 @@
package com.seibel.distanthorizons.api.methods.events.abstractEvents;
+import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
+import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
/**
- * Performance note: this event will be fired thousands of times on concurrent threads,
- * make it thread safe and as fast as possible.
+ * Performance note: this event will be fired millions of times on concurrent threads,
+ * make it thread safe and as fast as possible.
+ * (If every LOD block goes through this event, On a 512 render distance world,
+ * at the medium quality preset, it will be triggered around 40,000,000 times.)
+ *
*
* This event is fired when DH needs to convert a {@link IDhApiBlockStateWrapper}
* into a color for rendering. This event is fired after DH attempts to determine
@@ -41,7 +46,7 @@ import com.seibel.distanthorizons.coreapi.util.ColorUtil;
* via {@link DhApiBlockStateWrapperCreatedEvent.EventParam#setAllowApiColorOverride(boolean)}.
*
* @author James Seibel
- * @version 2026-04-14
+ * @version 2026-05-18
* @since API 6.0.0
* @see IDhApiBlockStateWrapper
*/
@@ -50,6 +55,7 @@ public abstract class DhApiBlockColorOverrideEvent implements IDhApiEvent event);
+
//=========================//
// internal DH API methods //
//=========================//
@@ -58,6 +64,7 @@ public abstract class DhApiBlockColorOverrideEvent implements IDhApiEvent event) { this.onBlockColorOverridden(event); }
+
//==================//
// parameter object //
//==================//
@@ -65,7 +72,9 @@ public abstract class DhApiBlockColorOverrideEvent implements IDhApiEvent
- // these constructors are private because the create... methods below are easier to understand
+ // these constructors are private because the create methods below are easier to understand
private DhApiResult(boolean success, String message) { this(success, message, null); }
private DhApiResult(boolean success, String message, T payload)
{
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java
index 7b23d02e7..c871a2bae 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java
@@ -19,12 +19,15 @@
package com.seibel.distanthorizons.core.api.external.methods.data;
+import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
+import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
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;
import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataRepo;
+import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3i;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
@@ -34,6 +37,8 @@ import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
+import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
+import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
import com.seibel.distanthorizons.core.util.DhApiTerrainDataPointUtil;
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
@@ -42,12 +47,16 @@ import com.seibel.distanthorizons.core.util.RayCastUtil;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
+import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3i;
+import com.seibel.distanthorizons.coreapi.util.ColorUtil;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable;
@@ -78,17 +87,21 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
//=============//
// constructor //
//=============//
+ //region
private DhApiTerrainDataRepo()
{
}
+ //endregion
+
//================//
// Getter Methods //
//================//
+ //region
@Override
public DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, IDhApiTerrainDataCache dataCache)
@@ -109,9 +122,12 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
public DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, IDhApiTerrainDataCache dataCache)
{ return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, DhSectionPos.encode(detailLevel, posX, posZ), dataCache); }
+ //endregion
+
// private getters //
+ //region
/** Returns a single API terrain datapoint that contains the given Y block position */
private static DhApiResult getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, long requestedColumnPos, Integer blockYPos, IDhApiTerrainDataCache dataCache)
@@ -340,11 +356,14 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
}
}
+ //endregion
+
//====================//
// raycasting methods //
//====================//
+ //region
@Override
public DhApiResult raycast(
@@ -490,11 +509,14 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
return returnList;
}
+ //endregion
+
//================//
// setter methods //
//================//
+ //region
@Override
public DhApiResult overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException
@@ -524,20 +546,26 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
return DhApiResult.createSuccess();
}
+ //endregion
+
//=============//
// API helpers //
//=============//
+ //region
@Override
public IDhApiTerrainDataCache createSoftCache() { return new DhApiTerrainDataCache(); }
+ //endregion
+
//===============//
// debug methods //
//===============//
+ //region
/**
* This method is here for debugging the repo and isn't intended for normal use.
@@ -618,5 +646,8 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
}
}
+ //endregion
+
+
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java
index e0bc88e0b..3e69a5aa3 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java
@@ -32,7 +32,9 @@ public interface IClientLevelWrapper extends ILevelWrapper
@Nullable
IServerLevelWrapper tryGetServerSideWrapper();
- int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockState);
+ default int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockState)
+ { return this.getBlockColor(pos, biome, fullDataSource, blockState, true); }
+ int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockState, boolean allowApiOverride);
/** @return -1 if there was a problem getting the color */
int getDirtBlockColor();
void clearBlockColorCache();