From 063ba01970611179fa45ac501ae25317a5252b33 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 8 Apr 2026 21:43:35 -0500 Subject: [PATCH] Fix tint color retrieval --- .../wrappers/block/AbstractDhTintGetter.java | 35 +++++++++-- .../block/ClientBlockStateColorCache.java | 62 ++++++++++++------- 2 files changed, 69 insertions(+), 28 deletions(-) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/AbstractDhTintGetter.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/AbstractDhTintGetter.java index 1c12cd54b..bed307bd0 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/AbstractDhTintGetter.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/AbstractDhTintGetter.java @@ -46,7 +46,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter private static final ConcurrentHashMap COLOR_BY_BLOCK_BIOME_PAIR = new ConcurrentHashMap<>(); /** returned if the color cache is incomplete */ - public static final int INVALID_COLOR = Integer.MIN_VALUE; + public static final int INVALID_COLOR = -1; protected BiomeWrapper biomeWrapper; @@ -60,6 +60,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter //=============// // constructor // //=============// + //region public AbstractDhTintGetter() { } @@ -76,11 +77,14 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter this.smoothingRadiusInBlocks = Config.Client.Advanced.Graphics.Quality.lodBiomeBlending.get(); } + //endregion - //================// - // shared methods // - //================// + + //===============// + // color getters // + //===============// + //region /** Called by MC's tint getter */ @Override @@ -195,7 +199,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter BlockBiomeWrapperPair pair = BlockBiomeWrapperPair.get(this.blockStateWrapper, biomeWrapper); // use the cached color if possible - Integer cachedColor = COLOR_BY_BLOCK_BIOME_PAIR.get(pair); // explicit Integer return here reduces unnecessary allocations + Integer cachedColor = COLOR_BY_BLOCK_BIOME_PAIR.get(pair); if (cachedColor != null) { return cachedColor; @@ -335,6 +339,27 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter }); } + //endregion + + + + //===========// + // set color // + //===========// + //region + + /** + * can be used in newer MC versions + * where the color getting logic is a bit more manual + */ + public static void setStaticColor(BlockStateWrapper blockStateWrapper, BiomeWrapper biomeWrapper, Integer colorInt) + { + BlockBiomeWrapperPair pair = BlockBiomeWrapperPair.get(blockStateWrapper, biomeWrapper); + COLOR_BY_BLOCK_BIOME_PAIR.put(pair, colorInt); + } + + //endregion + } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/ClientBlockStateColorCache.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/ClientBlockStateColorCache.java index f4acdb7a1..79259fc48 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/ClientBlockStateColorCache.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/ClientBlockStateColorCache.java @@ -56,7 +56,6 @@ import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; import net.minecraft.client.resources.model.geometry.BakedQuad; import net.minecraft.core.BlockPos; import net.minecraft.client.color.block.BlockTintSource; -import net.minecraft.client.multiplayer.ClientLevel; #endif /** @@ -123,6 +122,7 @@ public class ClientBlockStateColorCache //===========// // constants // //===========// + //region private static final int MIN_SRGB_BITS = 0x39000000; // 2^(-13) private static final int MAX_SRGB_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON @@ -186,14 +186,18 @@ public class ClientBlockStateColorCache //endregion }; + // these are threadlocals since AbstractDhTintGetter use local variables to handle color queries private static final ThreadLocal TintWithoutLevelOverrideGetter = ThreadLocal.withInitial(() -> new TintWithoutLevelOverrider()); private static final ThreadLocal TintOverrideGetter = ThreadLocal.withInitial(() -> new TintGetterOverride()); + //endregion + //=============// // constructor // //=============// + //region public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper clientLevelWrapper) { @@ -204,6 +208,8 @@ public class ClientBlockStateColorCache this.resolveColors(); } + //endregion + //===================// @@ -521,7 +527,7 @@ public class ClientBlockStateColorCache // attempt to get the tint - int tintColor = -1; + int tintColor = AbstractDhTintGetter.INVALID_COLOR; try { // try to use the fast tint getter logic first @@ -542,19 +548,18 @@ public class ClientBlockStateColorCache tintColor = Minecraft.getInstance() .getBlockColors() .getColor(this.blockState, - tintOverride, + tintOverride, // tintOverride will save the result of this query to speed up future queries McObjectConverter.Convert(blockPos), this.tintIndex); #else BlockTintSource tintSource = Minecraft.getInstance() .getBlockColors() .getTintSource(this.blockState, this.tintIndex); + // a tint source may be null for blocks that don't actually need tinting + // in that case the base color should be sufficient + // Example: cherry blossom leaves if (tintSource != null) { - // Try colorInWorld first (biome-aware for grass/foliage), - // fall back to colorAsTerrainParticle (biome-aware for water). - // Grass overrides colorAsTerrainParticle to return -1, - // water doesn't override colorInWorld (defaults to -1). BlockPos mcPos = McObjectConverter.Convert(blockPos); tintColor = tintSource.colorInWorld(this.blockState, tintOverride, mcPos); if (tintColor == -1) @@ -562,17 +567,39 @@ public class ClientBlockStateColorCache tintColor = tintSource.colorAsTerrainParticle(this.blockState, tintOverride, mcPos); } } + + if (tintColor == -1) + { + // no color found, use the base color + tintColor = AbstractDhTintGetter.INVALID_COLOR; + } + + // save this color to speed up future queries + TintWithoutLevelOverrider.setStaticColor(this.blockStateWrapper, biomeWrapper, tintColor); + // try to get the blended color with this new information + tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos)); #endif } } catch (Exception e) { + #if MC_VER <= MC_1_21_11 // this exception generally occurs if the tint requires other blocks besides itself LOGGER.debug("Unable to use ["+ TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e); BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState); + #else + // only display the error once per block/biome type to reduce log spam + if (!BROKEN_BLOCK_STATES.contains(this.blockState)) + { + LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e); + BROKEN_BLOCK_STATES.add(this.blockState); + } + #endif } } + // level-specific logic is only needed for MC 1.21.11 and older + #if MC_VER <= MC_1_21_11 // use the level logic only if requested if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState)) { @@ -585,29 +612,15 @@ public class ClientBlockStateColorCache tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos)); if (tintColor == AbstractDhTintGetter.INVALID_COLOR) { - #if MC_VER <= MC_1_21_11 tintColor = Minecraft.getInstance() .getBlockColors() .getColor(this.blockState, tintOverride, McObjectConverter.Convert(blockPos), this.tintIndex); - #else - BlockTintSource tintSource = Minecraft.getInstance() - .getBlockColors() - .getTintSource(this.blockState, this.tintIndex); - if (tintSource != null) - { - net.minecraft.core.BlockPos mcPos = McObjectConverter.Convert(blockPos); - tintColor = tintSource.colorInWorld(this.blockState, tintOverride, mcPos); - if (tintColor == -1) - { - tintColor = tintSource.colorAsTerrainParticle(this.blockState, tintOverride, mcPos); - } - } - #endif } } + #endif } catch (Exception e) { @@ -621,7 +634,7 @@ public class ClientBlockStateColorCache - if (tintColor != -1) + if (tintColor != AbstractDhTintGetter.INVALID_COLOR) { return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor); } @@ -637,6 +650,7 @@ public class ClientBlockStateColorCache //================// // helper classes // //================// + //region private enum EColorMode { @@ -668,6 +682,8 @@ public class ClientBlockStateColorCache } } + //endregion + }