From 5b4049e0ca9d9bff2a28dcb222dfcf9ece948cf1 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 24 Aug 2023 20:10:59 -0500 Subject: [PATCH] Require a ILevelWrapper when deserializing BlockStateWrappers --- .../common/wrappers/WrapperFactory.java | 36 +++- .../wrappers/block/BlockStateWrapper.java | 165 +++++++++--------- .../common/wrappers/chunk/ChunkWrapper.java | 9 +- .../wrappers/world/ClientLevelWrapper.java | 2 +- .../wrappers/world/ServerLevelWrapper.java | 2 +- coreSubProjects | 2 +- 6 files changed, 118 insertions(+), 98 deletions(-) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java index 2ed92ea15..81f95245b 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java @@ -23,6 +23,8 @@ import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiW import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; +import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; +import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhServerLevel; @@ -32,6 +34,9 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.chunk.ChunkAccess; @@ -68,7 +73,7 @@ public class WrapperFactory implements IWrapperFactory public IBiomeWrapper deserializeBiomeWrapper(String str) throws IOException { return BiomeWrapper.deserialize(str); } @Override - public IBlockStateWrapper deserializeBlockStateWrapper(String str) throws IOException { return BlockStateWrapper.deserialize(str); } + public IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BlockStateWrapper.deserialize(str, levelWrapper); } @Override public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; } @@ -114,15 +119,34 @@ public class WrapperFactory implements IWrapperFactory } ChunkAccess chunk = (ChunkAccess) objectArray[0]; - // light source - if (!(objectArray[1] instanceof LevelReader)) + // level / light source + if (!(objectArray[1] instanceof Level)) { throw new ClassCastException(createChunkWrapperErrorMessage(objectArray)); } - LevelReader lightSource = (LevelReader) objectArray[1]; + // the level is needed for the DH level wrapper... + Level level = (Level) objectArray[1]; + // ...the LevelReader is needed for chunk lighting + LevelReader lightSource = level; - return new ChunkWrapper(chunk, lightSource, /*A DH wrapped level isn't necessary*/null); + // level wrapper + ILevelWrapper levelWrapper; + if (level instanceof ServerLevel) + { + levelWrapper = ServerLevelWrapper.getWrapper((ServerLevel)level); + } + else if (level instanceof ClientLevel) + { + levelWrapper = ClientLevelWrapper.getWrapper((ClientLevel)level); + } + else + { + throw new ClassCastException(createChunkWrapperErrorMessage(objectArray)); + } + + + return new ChunkWrapper(chunk, lightSource, levelWrapper); } // incorrect number of parameters from the API else @@ -153,7 +177,7 @@ public class WrapperFactory implements IWrapperFactory // MC 1.16, 1.18, 1.19, 1.20 #if POST_MC_1_17_1 || MC_1_16_5 message.append("[" + ChunkAccess.class.getName() + "], \n"); - message.append("[" + LevelReader.class.getName() + "]. \n"); + message.append("[" + ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName() + "]. \n"); #else // See preprocessor comment in createChunkWrapper() for full documentation not implemented for this version of Minecraft! diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java index f239fed99..484842367 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java @@ -32,7 +32,7 @@ import net.minecraft.world.level.EmptyBlockGetter; public class BlockStateWrapper implements IBlockStateWrapper { - /** example "minecraft:plains" */ + /** example "minecraft:water" */ public static final String RESOURCE_LOCATION_SEPARATOR = ":"; /** example "minecraft:water_STATE_{level:0}" */ public static final String STATE_STRING_SEPARATOR = "_STATE_"; @@ -44,7 +44,7 @@ public class BlockStateWrapper implements IBlockStateWrapper public static final ConcurrentHashMap WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>(); public static String AIR_STRING = "AIR"; - public static final BlockStateWrapper AIR = new BlockStateWrapper(null); + public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null); public static final String[] RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS = { AIR_STRING, "minecraft:barrier", "minecraft:structure_void", "minecraft:light" }; @@ -53,12 +53,8 @@ public class BlockStateWrapper implements IBlockStateWrapper public final BlockState blockState; - - /** - * Cached so it can be quickly used as a semi-stable hashing method.
- * This may also fix the issue where we can serialize and save after a level has been shut down. - */ - private String serializationResult = null; + /** technically final, but since it requires a method call to generate it can't be marked as such */ + private String serialString; @@ -66,19 +62,20 @@ public class BlockStateWrapper implements IBlockStateWrapper // constructors // //==============// - public static BlockStateWrapper fromBlockState(BlockState blockState) + public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper) { if (blockState == null || blockState.isAir()) { return AIR; } - return WRAPPER_BY_BLOCK_STATE.computeIfAbsent(blockState, newBlockState -> new BlockStateWrapper(newBlockState)); + return WRAPPER_BY_BLOCK_STATE.computeIfAbsent(blockState, newBlockState -> new BlockStateWrapper(newBlockState, levelWrapper)); } - private BlockStateWrapper(BlockState blockState) + private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper) { this.blockState = blockState; + this.serialString = this.serialize(levelWrapper); LOGGER.trace("Created BlockStateWrapper for ["+blockState+"]"); } @@ -119,7 +116,7 @@ public class BlockStateWrapper implements IBlockStateWrapper List blockStatesToIgnore = DefaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates(); for (BlockState blockState : blockStatesToIgnore) { - BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState); + BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper); blockStateWrappers.add(newBlockToIgnore); } } @@ -159,15 +156,73 @@ public class BlockStateWrapper implements IBlockStateWrapper public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; } @Override - public String serialize() { return this.serialize(null); } - public String serialize(ILevelWrapper levelWrapper) + public String getSerialString() { return this.serialString; } + + @Override + public boolean equals(Object obj) { - // the serialization result can be quickly used as a semi-stable hashing method, so it needs to be cached for speed - if (this.serializationResult != null) + if (this == obj) { - return this.serializationResult; + return true; } + if (obj == null || this.getClass() != obj.getClass()) + { + return false; + } + + BlockStateWrapper that = (BlockStateWrapper) obj; + // the serialized value is used so we can test the contents instead of the references + return Objects.equals(this.getSerialString(), that.getSerialString()); + } + + @Override + public int hashCode() { return Objects.hash(this.getSerialString()); } + + + @Override + public Object getWrappedMcObject() { return this.blockState; } + + @Override + public boolean isAir() { return this.isAir(this.blockState); } + public boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); } + + @Override + public boolean isSolid() + { + #if PRE_MC_1_20_1 + return this.blockState.getMaterial().isSolid(); + #else + return !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty(); + #endif + } + + @Override + public boolean isLiquid() + { + if (this.isAir()) + { + return false; + } + + #if PRE_MC_1_20_1 + return this.blockState.getMaterial().isLiquid(); + #else + return !this.blockState.getFluidState().isEmpty(); + #endif + } + + @Override + public String toString() { return this.getSerialString(); } + + + + //=======================// + // serialization methods // + //=======================// + + private String serialize(ILevelWrapper levelWrapper) + { if (this.blockState == null) { return AIR_STRING; @@ -175,37 +230,37 @@ public class BlockStateWrapper implements IBlockStateWrapper + // older versions of MC have a static registry #if !(MC_1_16_5 || MC_1_17_1) - // use the given level if possible, otherwise try using the currently loaded one - Level level = (levelWrapper != null ? (Level)levelWrapper.getWrappedMcObject() : null); - level = (level == null ? Minecraft.getInstance().level : level); + Level level = (Level)levelWrapper.getWrappedMcObject(); + net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); #endif 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 = level.registryAccess(); resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock()); #else - net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock()); #endif + + if (resourceLocation == null) { - LOGGER.warn("unable to serialize: " + this.blockState); + LOGGER.warn("No ResourceLocation found, unable to serialize: " + this.blockState); + return AIR_STRING; } - this.serializationResult = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath() + this.serialString = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath() + STATE_STRING_SEPARATOR + serializeBlockStateProperties(this.blockState); - return this.serializationResult; + return this.serialString; } /** will only work if a level is currently loaded */ - public static IBlockStateWrapper deserialize(String resourceStateString) throws IOException { return deserialize(resourceStateString, null); } public static IBlockStateWrapper deserialize(String resourceStateString, ILevelWrapper levelWrapper) throws IOException { if (resourceStateString.equals(AIR_STRING) || resourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case @@ -292,7 +347,7 @@ public class BlockStateWrapper implements IBlockStateWrapper foundState = block.defaultBlockState(); } - return new BlockStateWrapper(foundState); + return new BlockStateWrapper(foundState, levelWrapper); } catch (Exception e) { @@ -331,61 +386,5 @@ public class BlockStateWrapper implements IBlockStateWrapper } - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - - if (obj == null || this.getClass() != obj.getClass()) - { - return false; - } - - BlockStateWrapper that = (BlockStateWrapper) 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(this.serialize()); } - - - @Override - public Object getWrappedMcObject() { return this.blockState; } - - @Override - public boolean isAir() { return this.isAir(this.blockState); } - public boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); } - - @Override - public boolean isSolid() - { - #if PRE_MC_1_20_1 - return this.blockState.getMaterial().isSolid(); - #else - return !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty(); - #endif - } - - @Override - public boolean isLiquid() - { - if (this.isAir()) - { - return false; - } - - #if PRE_MC_1_20_1 - return this.blockState.getMaterial().isLiquid(); - #else - return !this.blockState.getFluidState().isEmpty(); - #endif - } - - @Override - public String toString() { return this.serialize(); } } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java index f502b1d3d..0c717923f 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java @@ -95,7 +95,7 @@ public class ChunkWrapper implements IChunkWrapper // constructor // //=============// - public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource, @Nullable ILevelWrapper wrappedLevel) + public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource, ILevelWrapper wrappedLevel) { this.chunk = chunk; this.lightSource = lightSource; @@ -156,8 +156,6 @@ public class ChunkWrapper implements IChunkWrapper @Override public IBiomeWrapper getBiome(int relX, int relY, int relZ) { - //if (wrappedLevel != null) return wrappedLevel.getBiome(new DhBlockPos(x + getMinX(), y, z + getMinZ())); - #if PRE_MC_1_17_1 return BiomeWrapper.getBiomeWrapper(this.chunk.getBiomes().getNoiseBiome( relX >> 2, relY >> 2, relZ >> 2)); @@ -367,12 +365,11 @@ public class ChunkWrapper implements IChunkWrapper @Override public IBlockStateWrapper getBlockState(int relX, int relY, int relZ) { - //if (wrappedLevel != null) return wrappedLevel.getBlockState(new DhBlockPos(x + getMinX(), y, z + getMinZ())); - return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(new BlockPos(relX, relY, relZ))); + return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(new BlockPos(relX, relY, relZ)), this.wrappedLevel); } @Override - public boolean isStillValid() { return this.wrappedLevel == null || this.wrappedLevel.tryGetChunk(this.chunkPos) == this; } + public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; } #if POST_MC_1_20_1 private static boolean checkLightSectionsOnChunk(LevelChunk chunk, LevelLightEngine engine) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java index 377276816..545e8b3b1 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java @@ -165,7 +165,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper @Override public IBlockStateWrapper getBlockState(DhBlockPos pos) { - return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos))); + return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this); } @Override diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ServerLevelWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ServerLevelWrapper.java index c8aa907cb..b2779cf4f 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ServerLevelWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ServerLevelWrapper.java @@ -156,7 +156,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper @Override public IBlockStateWrapper getBlockState(DhBlockPos pos) { - return BlockStateWrapper.fromBlockState(level.getBlockState(McObjectConverter.Convert(pos))); + return BlockStateWrapper.fromBlockState(level.getBlockState(McObjectConverter.Convert(pos)), this); } @Override diff --git a/coreSubProjects b/coreSubProjects index 697abb520..e62ddc302 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 697abb520b56e7381f2c757e6288e909b9a55737 +Subproject commit e62ddc302b76b981cbe3635826f61510a5e5bbc1