Close #776 (fix Polytone client biome colors)

This commit is contained in:
James Seibel
2025-06-05 07:53:23 -05:00
parent 8a3175f345
commit baebb7323d
16 changed files with 202 additions and 150 deletions
@@ -47,6 +47,7 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.biome.Biomes;
#endif
@@ -130,14 +131,6 @@ public class BiomeWrapper implements IBiomeWrapper
//LOGGER.trace("Created BiomeWrapper ["+this.serialString+"] for ["+biome+"]");
}
/** should only be used to create {@link BiomeWrapper#EMPTY_WRAPPER} */
private BiomeWrapper()
{
this.biome = null;
this.serialString = EMPTY_BIOME_STRING;
this.hashCode = Objects.hash(this.serialString);
}
//=========//
@@ -285,6 +278,42 @@ public class BiomeWrapper implements IBiomeWrapper
// if no wrapper is found, default to the empty wrapper
BiomeWrapper foundWrapper = EMPTY_WRAPPER;
try
{
try
{
Level level = (Level) levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
BiomeDeserializeResult deserializeResult = deserializeBiome(resourceLocationString, registryAccess);
if (!deserializeResult.success)
{
if (!brokenResourceLocationStrings.contains(resourceLocationString))
{
brokenResourceLocationStrings.add(resourceLocationString);
LOGGER.warn("Unable to deserialize biome from string: [" + resourceLocationString + "]");
}
return EMPTY_WRAPPER;
}
foundWrapper = (BiomeWrapper) getBiomeWrapper(deserializeResult.biome, levelWrapper);
return foundWrapper;
}
catch (Exception e)
{
throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BiomeWrapper: " + e.getMessage(), e);
}
}
finally
{
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
}
}
public static BiomeDeserializeResult deserializeBiome(String resourceLocationString, net.minecraft.core.RegistryAccess registryAccess) throws IOException
{
// parse the resource location
int separatorIndex = resourceLocationString.indexOf(":");
@@ -308,11 +337,6 @@ public class BiomeWrapper implements IBiomeWrapper
}
try
{
Level level = (Level) levelWrapper.getWrappedMcObject();
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
boolean success;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
@@ -341,31 +365,30 @@ public class BiomeWrapper implements IBiomeWrapper
}
#endif
return new BiomeDeserializeResult(success, biome);
}
if (!success)
//================//
// helper classes //
//================//
public static class BiomeDeserializeResult
{
if (!brokenResourceLocationStrings.contains(resourceLocationString))
public final boolean success;
#if MC_VER < MC_1_18_2
public final Biome biome;
#else
public final Holder<Biome> biome;
#endif
public BiomeDeserializeResult(boolean success, #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome)
{
brokenResourceLocationStrings.add(resourceLocationString);
LOGGER.warn("Unable to deserialize biome from string: [" + resourceLocationString + "]");
this.success = success;
this.biome = biome;
}
return EMPTY_WRAPPER;
}
foundWrapper = (BiomeWrapper) getBiomeWrapper(biome, levelWrapper);
return foundWrapper;
}
catch (Exception e)
{
throw new IOException("Failed to deserialize the string [" + finalResourceStateString + "] into a BiomeWrapper: " + e.getMessage(), e);
}
}
finally
{
WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper);
}
}
}
@@ -42,7 +42,6 @@ import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
@@ -91,7 +90,7 @@ public class ClientBlockStateColorCache
private static final RandomSource RANDOM = RandomSource.create();
#endif
private final IClientLevelWrapper levelWrapper;
private final IClientLevelWrapper clientLevelWrapper;
private final BlockState blockState;
private final LevelReader level;
@@ -174,7 +173,7 @@ public class ClientBlockStateColorCache
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel)
{
this.blockState = blockState;
this.levelWrapper = samplingLevel;
this.clientLevelWrapper = samplingLevel;
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
this.resolveColors();
}
@@ -471,7 +470,7 @@ public class ClientBlockStateColorCache
try
{
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState, new TintWithoutLevelOverrider(biome, this.levelWrapper), McObjectConverter.Convert(pos), this.tintIndex);
.getColor(this.blockState, new TintWithoutLevelOverrider(biome, this.clientLevelWrapper), McObjectConverter.Convert(pos), this.tintIndex);
}
catch (UnsupportedOperationException e)
{
@@ -19,8 +19,10 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.*;
@@ -29,6 +31,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -36,22 +39,64 @@ import org.jetbrains.annotations.Nullable;
import net.minecraft.core.Holder;
#endif
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class TintWithoutLevelOverrider implements BlockAndTintGetter
{
/**
* This will only ever be null if there was an issue with {@link IClientLevelWrapper#getPlainsBiomeWrapper()}
* but {@link Nullable} is there just in case.
*/
@Nullable
#if MC_VER >= MC_1_18_2
public final Holder<Biome> biome;
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
#if MC_VER < MC_1_18_2
public static final ConcurrentMap<String, Biome> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
#else
public final Biome biome;
public static final ConcurrentMap<String, Holder<Biome>> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
#endif
@NotNull
private final BiomeWrapper biomeWrapper;
//=============//
// constructor //
//=============//
public TintWithoutLevelOverrider(@NotNull BiomeWrapper biomeWrapper, IClientLevelWrapper clientLevelWrapper)
{ this.biomeWrapper = biomeWrapper; }
//=========//
// methods //
//=========//
@Override
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
{
String biomeString = this.biomeWrapper.getSerialString();
if (biomeString == null
|| biomeString.isEmpty()
|| biomeString.equals(BiomeWrapper.EMPTY_BIOME_STRING))
{
// default to "plains" for empty/invalid biomes
biomeString = "minecraft:plains";
}
return colorResolver.getColor(unwrap(getClientBiome(biomeString)), blockPos.getX(), blockPos.getZ());
}
private static Biome unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{
#if MC_VER >= MC_1_18_2
return biome.value();
#else
return biome;
#endif
}
/**
* Constructs the TintWithoutLevelOverrider, storing the provided Biome Holder for late-binding access.
*
* <p>Previously, this class might have immediately unwrapped the Holder like this:</p>
* <pre>{@code
* // Inside constructor (OLD WAY - PROBLEMATIC):
@@ -85,41 +130,52 @@ public class TintWithoutLevelOverrider implements BlockAndTintGetter
* whenever the biome information is needed, ensuring it always retrieves the most current {@code Biome}
* instance associated with the holder at that time.</p>
*/
public TintWithoutLevelOverrider(BiomeWrapper biomeWrapper, IClientLevelWrapper clientLevelWrapper)
private static #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif getClientBiome(String biomeResourceString)
{
#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome = biomeWrapper.biome;
if (biome == null) // We are looking at the empty biome wrapper
// cache the client biomes so we don't have to re-parse the resource location every time
return BIOME_BY_RESOURCE_STRING.compute(biomeResourceString,
(resourceString, existingBiome) ->
{
BiomeWrapper plainsBiomeWrapper = ((BiomeWrapper) clientLevelWrapper.getPlainsBiomeWrapper());
if (plainsBiomeWrapper != null)
if (existingBiome != null)
{
biome = plainsBiomeWrapper.biome;
return existingBiome;
}
ClientLevel clientLevel = Minecraft.getInstance().level;
if (clientLevel == null)
{
// shouldn't happen, but just in case
throw new IllegalStateException("Attempted to get client biome when no client level was loaded.");
}
BiomeWrapper.BiomeDeserializeResult result;
try
{
result = BiomeWrapper.deserializeBiome(resourceString, clientLevel.registryAccess());
}
catch (Exception e)
{
LOGGER.warn("Unable to deserialize client biome ["+resourceString+"], using fallback...");
try
{
result = BiomeWrapper.deserializeBiome("minecraft:plains", clientLevel.registryAccess());
}
catch (IOException ex)
{
// should never happen, if it does this log will explode, but just in case
LOGGER.error("Unable to deserialize fallback client biome [minecraft:plains], returning NULL.");
return null;
}
}
this.biome = biome;
if (result.success)
{
existingBiome = result.biome;
}
@Override
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
{
if (this.biome == null)
{
// hopefully unneeded debug color
return ColorUtil.CYAN;
}
return colorResolver.getColor(unwrap(biome), blockPos.getX(), blockPos.getZ());
}
private static Biome unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{
#if MC_VER >= MC_1_18_2
return biome.value();
#else
return biome;
#endif
return existingBiome;
});
}
+1 -3
View File
@@ -25,9 +25,7 @@ fabric_api_version=0.42.0+1.16
canvas_version=
# iris - needs 1.7.4+ to support the DH API
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "*", "polytone": "*" }
fabric_incompatibility_list={ "iris": "*" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -25,9 +25,7 @@ fabric_api_version=0.46.1+1.17
canvas_version=
# iris - needs 1.7.4+ to support the DH API
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "*", "polytone": "*" }
fabric_incompatibility_list={ "iris": "*" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -26,9 +26,7 @@ fabric_api_version=0.76.0+1.18.2
canvas_version=mc118:1.0.2616
# iris - needs 1.7.4+ to support the DH API
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "*", "polytone": "*" }
fabric_incompatibility_list={ "iris": "*" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -25,9 +25,7 @@ fabric_api_version=0.76.1+1.19.2
canvas_version=mc119-1.0.2480
# iris - needs 1.7.4+ to support the DH API
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "*", "polytone": "*" }
fabric_incompatibility_list={ "iris": "*" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -24,9 +24,7 @@ fabric_api_version=0.87.1+1.19.4
canvas_version=
# iris - needs 1.7.4+ to support the DH API
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "*", "polytone": "*" }
fabric_incompatibility_list={ "iris": "*" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -24,9 +24,7 @@ fabric_api_version=0.90.4+1.20.1
canvas_version=
# iris - needs 1.7.4+ to support the DH API
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "<=1.7.4", "polytone": "*" }
fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -24,9 +24,7 @@ fabric_api_version=0.90.4+1.20.2
canvas_version=
# iris - needs 1.7.4+ to support the DH API
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "<=1.7.4", "polytone": "*" }
fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -25,9 +25,7 @@ fabric_api_version=0.91.2+1.20.4
canvas_version=
# iris - needs 1.7.4+ to support the DH API
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "<=1.7.4", "polytone": "*" }
fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -25,9 +25,7 @@ fabric_api_version=0.97.8+1.20.6
canvas_version=
# iris - needs 1.7.4+ to support the DH API
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "<=1.7.4", "polytone": "*" }
fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -25,9 +25,7 @@ fabric_api_version=0.115.0+1.21.1
canvas_version=
# iris - needs 1.7.4+ to support the DH API
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "<=1.7.4", "polytone": "*" }
fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -25,9 +25,7 @@ fabric_api_version=0.110.0+1.21.3
canvas_version=
# fabric-api 0.110.0 fixed a bug in MC 1.21.3 with the rendering API DH relied on
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "fabric-api": "<0.110.0", "polytone": "*" }
fabric_incompatibility_list={ "fabric-api": "<0.110.0" }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -24,9 +24,7 @@ fabric_api_version=0.110.5+1.21.4
immersive_portals_version=
canvas_version=
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "polytone": "*" }
fabric_incompatibility_list={ }
fabric_recommend_list={}
# Fabric mod run
+1 -3
View File
@@ -24,9 +24,7 @@ fabric_api_version=0.119.5+1.21.5
canvas_version=
# Iris - some versions of 1.8.11 nightly builds may not work, but the ones after 2025-03-30 should
# polytone - sometimes has biomes that aren't synced between server/client
# so it doesn't accept DH provided biomes, resulting in gray blocks and log spam
fabric_incompatibility_list={ "iris": "<=1.8.10", "polytone": "*" }
fabric_incompatibility_list={ "iris": "<=1.8.10" }
fabric_recommend_list={}
# Fabric mod run