From a680aa97d2a40f27de835e6d96828d969b325706 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Tue, 21 Feb 2023 21:25:17 -0600 Subject: [PATCH] Fix exceptions when closing a world --- .../common/wrappers/block/BiomeWrapper.java | 138 ++++++++++++------ .../wrappers/block/BlockStateWrapper.java | 131 ++++++++++------- coreSubProjects | 2 +- 3 files changed, 172 insertions(+), 99 deletions(-) diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/BiomeWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BiomeWrapper.java index 3b2daf83d..fadfbad9c 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/block/BiomeWrapper.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/BiomeWrapper.java @@ -23,26 +23,24 @@ import java.io.IOException; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; -import com.google.common.collect.ImmutableBiMap; +import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.mojang.serialization.JsonOps; -import com.mojang.serialization.Lifecycle; import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper; import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.Holder; #if POST_MC_1_19 import net.minecraft.data.worldgen.biome.EndBiomes; import net.minecraft.data.worldgen.biome.NetherBiomes; #endif -import net.minecraft.resources.RegistryFixedCodec; +import net.minecraft.core.RegistryAccess; import net.minecraft.resources.RegistryOps; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biomes; - /** This class wraps the minecraft BlockPos.Mutable (and BlockPos) class */ public class BiomeWrapper implements IBiomeWrapper { @@ -50,20 +48,55 @@ public class BiomeWrapper implements IBiomeWrapper public static final ConcurrentMap biomeWrapperMap = new ConcurrentHashMap<>(); public final Biome biome; #else - public static final ConcurrentMap, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>(); + public static ConcurrentMap, BiomeWrapper> WRAPPER_BY_BIOME = new ConcurrentHashMap<>(); + public static ConcurrentHashMap, String> SERIAL_BY_BIOME = new ConcurrentHashMap<>(); + public final Holder biome; #endif - - static public IBiomeWrapper getBiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder #endif biome) - { - return biomeWrapperMap.computeIfAbsent(biome, BiomeWrapper::new); - } - + + private static boolean registryOpsOutdated = false; + private static RegistryOps registryOps = null; + private static RegistryOps getRegistryOps() + { + ClientLevel level = Minecraft.getInstance().level; + if (registryOps != null && level == null) + { + // request a new registryOps the next time a world is loaded, + // if the world is reset the old registryOps will throw exceptions + registryOpsOutdated = true; + } + + if (registryOps == null || (registryOpsOutdated && level != null)) + { + registryOps = RegistryOps.create(JsonOps.INSTANCE, level.registryAccess()); + } + return registryOps; + } + + + + //==============// + // constructors // + //==============// + private BiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder #endif biome) { this.biome = biome; + + SERIAL_BY_BIOME.put(this.biome, this.serialize()); } - + + static public IBiomeWrapper getBiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder #endif biome) + { + return WRAPPER_BY_BIOME.computeIfAbsent(biome, BiomeWrapper::new); + } + + + + //=========// + // methods // + //=========// + @Override public String getName() { @@ -73,40 +106,53 @@ public class BiomeWrapper implements IBiomeWrapper return biome.unwrapKey().orElse(Biomes.THE_VOID).registry().toString(); #endif } - - @Override - public String serialize() { - //FIXME: Pass in a level obj - String data = Biome.CODEC.encodeStart(RegistryOps.create(JsonOps.INSTANCE, Minecraft.getInstance().level.registryAccess()), - biome).get().orThrow().toString(); - return data; - } - - @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); - } - - @Override - public int hashCode() { - return Objects.hash(biome); - } - - public static IBiomeWrapper deserialize(String str) throws IOException { - try { - #if PRE_MC_1_18_2 Biome #else - Holder #endif - biome = Biome.CODEC.decode(RegistryOps.create(JsonOps.INSTANCE, Minecraft.getInstance().level.registryAccess()), - JsonParser.parseString(str)).get().orThrow().getFirst(); - return getBiomeWrapper(biome); - } catch (Exception e) { - throw new IOException("Failed to deserialize biome wrapper", e); - } - } + @Override + public String serialize() + { + if (!SERIAL_BY_BIOME.containsKey(this.biome)) + { + String newSerial = Biome.CODEC.encodeStart(getRegistryOps(), biome).get().orThrow().toString(); + SERIAL_BY_BIOME.put(this.biome, newSerial); + } + String serial = SERIAL_BY_BIOME.get(this.biome); + return serial; + } + + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj == null || getClass() != obj.getClass()) + { + return false; + } + + BiomeWrapper that = (BiomeWrapper) obj; + return Objects.equals(biome, that.biome); + } + + @Override + public int hashCode() { return Objects.hash(biome); } + + public static IBiomeWrapper deserialize(String serial) throws IOException + { + try + { + #if PRE_MC_1_18_2 Biome #else + Holder #endif + biome = Biome.CODEC.decode(getRegistryOps(), JsonParser.parseString(serial)).get().orThrow().getFirst(); + return getBiomeWrapper(biome); + } + catch (Exception e) + { + throw new IOException("Failed to deserialize biome wrapper", e); + } + } @Override public Object getWrappedMcObject_UNSAFE() { return this.biome; } diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockStateWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockStateWrapper.java index d42aff466..9beca6976 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockStateWrapper.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/block/BlockStateWrapper.java @@ -8,7 +8,6 @@ import net.minecraft.world.level.block.state.BlockState; import org.apache.logging.log4j.Logger; import java.io.IOException; -import java.util.HashMap; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -16,63 +15,97 @@ public class BlockStateWrapper implements IBlockStateWrapper { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); public static final BlockStateWrapper AIR = new BlockStateWrapper(null); - - public static ConcurrentHashMap cache = new ConcurrentHashMap<>(); - - public static BlockStateWrapper fromBlockState(BlockState blockState) - { - if (blockState == null || blockState.isAir()) - return AIR; - - if (blockState.getFluidState().isEmpty()) - return cache.computeIfAbsent(blockState, BlockStateWrapper::new); - else - return cache.computeIfAbsent(blockState.getFluidState().createLegacyBlock(), BlockStateWrapper::new); - } - + + public static ConcurrentHashMap WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>(); + public static ConcurrentHashMap SERIAL_BY_BLOCK_STATE = new ConcurrentHashMap<>(); + public final BlockState blockState; - BlockStateWrapper(BlockState blockState) { - this.blockState = blockState; - //LOGGER.info("Created BlockStateWrapper for {}", blockState); - } - - @Override + + + + BlockStateWrapper(BlockState blockState) + { + this.blockState = blockState; + + // blockState shouldn't be null, but just in case + if (this.blockState != null) + { + SERIAL_BY_BLOCK_STATE.put(this.blockState, this.serialize()); + } + + //LOGGER.info("Created BlockStateWrapper for {}", blockState); + } + + public static BlockStateWrapper fromBlockState(BlockState blockState) + { + if (blockState == null || blockState.isAir()) + { + return AIR; + } + + if (blockState.getFluidState().isEmpty()) + { + return WRAPPER_BY_BLOCK_STATE.computeIfAbsent(blockState, BlockStateWrapper::new); + } + else + { + return WRAPPER_BY_BLOCK_STATE.computeIfAbsent(blockState.getFluidState().createLegacyBlock(), BlockStateWrapper::new); + } + } + + + + @Override public String serialize() { if (this.blockState == null) { return "AIR"; } + + if (!SERIAL_BY_BLOCK_STATE.containsKey(this.blockState)) + { + String newSerial = BlockState.CODEC.encodeStart(JsonOps.COMPRESSED, this.blockState).get().orThrow().toString(); + SERIAL_BY_BLOCK_STATE.put(this.blockState, newSerial); + } + String serial = SERIAL_BY_BLOCK_STATE.get(this.blockState); + return serial; + } - return BlockState.CODEC.encodeStart(JsonOps.COMPRESSED, this.blockState).get().orThrow().toString(); + public static BlockStateWrapper deserialize(String str) throws IOException + { + if (str.equals("AIR")) + { + return AIR; + } + try + { + return new BlockStateWrapper(BlockState.CODEC.decode(JsonOps.COMPRESSED, JsonParser.parseString(str)).get().orThrow().getFirst()); + } + catch (Exception e) + { + throw new IOException("Failed to deserialize BlockStateWrapper", e); + } } - public static BlockStateWrapper deserialize(String str) throws IOException { - if (str.equals("AIR")) { - return AIR; - } - try { - return new BlockStateWrapper( - BlockState.CODEC.decode(JsonOps.COMPRESSED, JsonParser.parseString(str)).get().orThrow().getFirst() - ); - } catch (Exception e) { - throw new IOException("Failed to deserialize BlockStateWrapper", e); - } - } + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + else if (o == null || getClass() != o.getClass()) + { + return false; + } + + BlockStateWrapper that = (BlockStateWrapper) o; + return Objects.equals(blockState, that.blockState); + } @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BlockStateWrapper that = (BlockStateWrapper) o; - return Objects.equals(blockState, that.blockState); - } - - @Override - public int hashCode() { - return Objects.hash(blockState); - } - + public int hashCode() { return Objects.hash(blockState); } @Override public Object getWrappedMcObject_UNSAFE() { return this.blockState; } @@ -81,10 +114,4 @@ public class BlockStateWrapper implements IBlockStateWrapper public boolean isAir() { return this.isAir(this.blockState); } public boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); } - - - - - - } diff --git a/coreSubProjects b/coreSubProjects index 924465a78..af9215c84 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 924465a788f94bfb4f162aa4884ff45b520fd02b +Subproject commit af9215c849fcce65f40d09ca0afe2f0ef09a9503