Fix FullDataPointIdMap (de)serialization duplicate Entries

Potentially fix serializing Biomes/Blocks after a level has been shut down.
Also increase DATA_FORMAT_VERSION 2 -> 3
This commit is contained in:
James Seibel
2023-08-12 16:17:59 -05:00
parent 4fcd4cb2f7
commit 4ad9bb0d71
3 changed files with 116 additions and 56 deletions
@@ -33,8 +33,6 @@ import net.minecraft.resources.RegistryOps;
#endif
#if POST_MC_1_19_2
import net.minecraft.data.worldgen.biome.EndBiomes;
import net.minecraft.data.worldgen.biome.NetherBiomes;
#endif
@@ -54,12 +52,17 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
#if !PRE_MC_1_18_2
import net.minecraft.world.level.biome.Biomes;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
#endif
/** This class wraps the minecraft BlockPos.Mutable (and BlockPos) class */
public class BiomeWrapper implements IBiomeWrapper
{
private static final Logger LOGGER = LogManager.getLogger();
#if PRE_MC_1_18_2
public static final ConcurrentMap<Biome, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>();
public final Biome biome;
@@ -67,7 +70,19 @@ public class BiomeWrapper implements IBiomeWrapper
public static final ConcurrentMap<Holder<Biome>, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>();
public final Holder<Biome> biome;
#endif
/**
* Cached so it can be quickly used as a semi-stable hashing method. <br>
* This may also fix the issue where we can serialize and save after a level has been shut down.
*/
private String serializationResult = null;
//==============//
// constructors //
//==============//
static public IBiomeWrapper getBiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder<Biome> #endif biome)
{
return biomeWrapperMap.computeIfAbsent(biome, BiomeWrapper::new);
@@ -77,61 +92,80 @@ public class BiomeWrapper implements IBiomeWrapper
{
this.biome = biome;
}
@Override
//=========//
// methods //
//=========//
@Override
public String getName()
{
#if PRE_MC_1_18_2
return biome.toString();
#else
return biome.unwrapKey().orElse(Biomes.THE_VOID).registry().toString();
return this.biome.unwrapKey().orElse(Biomes.THE_VOID).registry().toString();
#endif
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BiomeWrapper that = (BiomeWrapper) o;
return Objects.equals(biome, that.biome);
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
else if (obj == null || this.getClass() != obj.getClass())
{
return false;
}
BiomeWrapper that = (BiomeWrapper) obj;
// the serialized value is used so we can test the contents instead of the references
return Objects.equals(this.serialize(), that.serialize());
}
@Override
public int hashCode() {
return Objects.hash(biome);
}
public int hashCode() { return Objects.hash(this.serialize()); }
@Override
public String serialize() // FIXME pass in level to prevent null pointers (or maybe just RegistryAccess?)
{
net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess();
ResourceLocation resourceLocation;
#if MC_1_16_5 || MC_1_17_1
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
#elif MC_1_18_2 || MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
#else
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
#endif
if (resourceLocation == null)
if (this.serializationResult == null)
{
// shouldn't normally happen, but just in case
return "";
}
else
{
String resourceLocationString = resourceLocation.getNamespace()+":"+resourceLocation.getPath();
return resourceLocationString;
net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess();
ResourceLocation resourceLocation;
#if MC_1_16_5 || MC_1_17_1
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
#elif MC_1_18_2 || MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
#else
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
#endif
if (resourceLocation == null)
{
LOGGER.warn("unable to serialize: "+this.biome.value());
// shouldn't normally happen, but just in case
this.serializationResult = "";
}
else
{
this.serializationResult = resourceLocation.getNamespace()+":"+resourceLocation.getPath();
}
}
return this.serializationResult;
}
public static IBiomeWrapper deserialize(String resourceLocationString) throws IOException // FIXME pass in level to prevent null pointers (or maybe just RegistryAccess?)
{
if (resourceLocationString.trim().isEmpty() || resourceLocationString.equals(""))
{
LOGGER.warn("null biome string deserialized");
// shouldn't normally happen, but just in case
new ResourceLocation("minecraft", "the_void"); // just "void" in MC 1.12 through 1.9 (inclusive)
}
@@ -157,6 +191,10 @@ public class BiomeWrapper implements IBiomeWrapper
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#else
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
if (unwrappedBiome == null)
{
LOGGER.warn("null biome string deserialized from string: "+resourceLocationString);
}
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#endif
@@ -172,4 +210,7 @@ public class BiomeWrapper implements IBiomeWrapper
@Override
public Object getWrappedMcObject() { return this.biome; }
@Override
public String toString() { return this.serialize(); }
}
@@ -6,6 +6,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
@@ -39,10 +40,13 @@ public class BlockStateWrapper implements IBlockStateWrapper
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final BlockStateWrapper AIR = new BlockStateWrapper(null);
public static final ConcurrentHashMap<BlockState, BlockStateWrapper> cache = new ConcurrentHashMap<>();
public static ConcurrentHashMap<BlockState, BlockStateWrapper> cache = new ConcurrentHashMap<>();
/**
* Cached so it can be quickly used as a semi-stable hashing method. <br>
* This may also fix the issue where we can serialize and save after a level has been shut down.
*/
private String serializationResult = null;
//==============//
@@ -95,25 +99,36 @@ public class BlockStateWrapper implements IBlockStateWrapper
@Override
public String serialize() // FIXME pass in level to prevent null pointers (or maybe just RegistryAccess?)
{
if (this.blockState == null)
// cache the serialization result so it can be quickly used as a semi-stable hashing method
if (this.serializationResult == null)
{
return "AIR";
if (this.blockState == null)
{
return "AIR";
}
ResourceLocation resourceLocation;
#if MC_1_16_5 || MC_1_17_1
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
#elif MC_1_18_2 || MC_1_19_2
net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess();
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
#else
net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess();
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
#endif
if (resourceLocation == null)
{
LOGGER.warn("unable to serialize: "+this.blockState);
}
this.serializationResult = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath()
+ STATE_STRING_SEPARATOR + serializeBlockStateProperties(this.blockState);
}
ResourceLocation resourceLocation;
#if MC_1_16_5 || MC_1_17_1
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
#elif MC_1_18_2 || MC_1_19_2
net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess();
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
#else
net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess();
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
#endif
String resourceStateString = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath()
+ STATE_STRING_SEPARATOR + serializeBlockStateProperties(this.blockState);
return resourceStateString;
return this.serializationResult;
}
public static BlockStateWrapper deserialize(String resourceStateString) throws IOException // FIXME pass in level to prevent null pointers (or maybe just RegistryAccess?)
@@ -175,7 +190,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// use the default if no state was found
if (foundState == null)
{
LOGGER.debug("Unable to find BlockState for Block ["+resourceLocation+"] with properties: ["+blockStatePropertiesString+"].");
LOGGER.warn("Unable to find BlockState for Block ["+resourceLocation+"] with properties: ["+blockStatePropertiesString+"].");
foundState = block.defaultBlockState();
}
return new BlockStateWrapper(foundState);
@@ -231,11 +246,12 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
BlockStateWrapper that = (BlockStateWrapper) obj;
return Objects.equals(this.blockState, that.blockState);
// the serialized value is used so we can test the contents instead of the references
return Objects.equals(this.serialize(), that.serialize());
}
@Override
public int hashCode() { return Objects.hash(this.blockState); }
public int hashCode() { return Objects.hash(this.serialize()); }
@Override
@@ -270,4 +286,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
#endif
}
@Override
public String toString() { return this.serialize(); }
}