From 27cd001680bc7838e89285ed6e0b8de701b325dc Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Tue, 22 Aug 2023 11:36:45 +0200 Subject: [PATCH 1/2] feat: Update rendering block ignores Barrier blocks, structure void blocks, light blocks, and air blocks now share 2 `HashMap`s that define blocks that should be ignored by the LOD builder. --- .../common/wrappers/WrapperFactory.java | 6 +- .../wrappers/block/BlockStateWrapper.java | 133 +++++++++++++----- .../BatchGenerationEnvironment.java | 2 +- 3 files changed, 101 insertions(+), 40 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 85fee0a84..911a2378a 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 @@ -22,8 +22,8 @@ package com.seibel.distanthorizons.common.wrappers; import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; -import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; +import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhServerLevel; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; @@ -35,6 +35,7 @@ import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.chunk.ChunkAccess; import java.io.IOException; +import java.util.HashMap; /** * This handles creating abstract wrapper objects. @@ -71,6 +72,9 @@ public class WrapperFactory implements IWrapperFactory @Override public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; } + @Override + public HashMap getRendererIgnoredBlocks() { return BlockStateWrapper.RENDERER_IGNORED_BLOCKS; } + /** * Note: when this is updated for different MC versions, make sure you also update the documentation in 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 e19d0d9fb..b54fd8a1c 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 @@ -3,16 +3,15 @@ package com.seibel.distanthorizons.common.wrappers.block; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; +import net.minecraft.core.registries.BuiltInRegistries; 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 org.jetbrains.annotations.NotNull; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; #if MC_1_16_5 || MC_1_17_1 @@ -39,8 +38,11 @@ public class BlockStateWrapper implements IBlockStateWrapper // must be defined before AIR, otherwise a null pointer will be thrown private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public static final BlockStateWrapper AIR = new BlockStateWrapper(null); - public static final ConcurrentHashMap cache = new ConcurrentHashMap<>(); + public static final ConcurrentHashMap cache = new ConcurrentHashMap<>(); + public static final BlockStateWrapper AIR = fromBlockState(BuiltInRegistries.BLOCK.get(ResourceLocation.tryParse("minecraft:air")).defaultBlockState(), false); + public static final String[] RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS = {"minecraft:air", "minecraft:barrier", "minecraft:structure_void"}; + public static final HashMap RENDERER_IGNORED_BLOCKS_INTERNAL = getRendererIgnoredBlocksInternal(RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS); + public static final HashMap RENDERER_IGNORED_BLOCKS = getRendererIgnoredBlocks(RENDERER_IGNORED_BLOCKS_INTERNAL); /** * Cached so it can be quickly used as a semi-stable hashing method.
@@ -53,21 +55,75 @@ public class BlockStateWrapper implements IBlockStateWrapper // constructors // //==============// - public static BlockStateWrapper fromBlockState(BlockState blockState) + public static BlockStateWrapper fromBlockState(BlockState blockState) { - if (blockState == null || blockState.isAir()) - { + return fromBlockState(blockState, true); + } + + private static BlockStateWrapper fromBlockState(BlockState blockState, boolean nullCheck) + { + if (Objects.requireNonNull(blockState).isAir() && AIR != null) return AIR; - } - return cache.computeIfAbsent(blockState, BlockStateWrapper::new); + return cache.computeIfAbsent(blockState, blockState1 -> new BlockStateWrapper(blockState1, nullCheck)); } - public final BlockState blockState; - BlockStateWrapper(BlockState blockState) + /** + * Only meant for use in the {@code RENDERER_IGNORED_BLOCKS_INTERNAL} list. Do not use elsewhere, since the {@code levelWrapper} parameter of each {@code IBlockStateWrapper} will be {@code null}, causing issues. + * @param resourceLocations The resource location(s) of the block(s) that should be ignored by the renderer, may only contain {@code [a-z0-9/._-)} characters. + * @return The default blockstate(s) of the block(s), paired with the serialized resource location(s) of the block(s), which should be passed into the {@code RENDERER_IGNORED_BLOCKS_INTERNAL} map and the {@code getRendererIgnoredBlocks} method. + */ + @SuppressWarnings("SameParameterValue") + private static @NotNull HashMap getRendererIgnoredBlocksInternal(String @NotNull ... resourceLocations) { + HashMap blockStates = new HashMap<>(); + + for (String resourceLocation : resourceLocations) + { + ResourceLocation fetchedResourceLocation = Objects.requireNonNull(ResourceLocation.tryParse(resourceLocation), String.format("Supplied a resource location that couldn't be parsed by Minecraft: %s", resourceLocation)); + var splitResourceLocation = resourceLocation.split(":"); + + if (splitResourceLocation.length == 0) { + LOGGER.warn("A resource location that should be ignored by the renderer was in an invalid format: {}", resourceLocation); + continue; + } + + blockStates.put(BuiltInRegistries.BLOCK.get(fetchedResourceLocation).defaultBlockState(), splitResourceLocation[1].toUpperCase(Locale.ROOT)); + } + + return blockStates; + } + + /** + * Only meant for use in the {@code RENDERER_IGNORED_BLOCKS} list. Do not use elsewhere, since the {@code levelWrapper} parameter of each {@code IBlockStateWrapper} will be {@code null}, causing issues. + * @param rendererIgnoredBlocks A map containing the blockstate(s) of the block(s), paired with the resource location(s) of the block(s) that should be ignored by the renderer. + * @return The blockstate wrapper(s) of the blockstate(s), which should be passed into the {@code RENDERER_IGNORED_BLOCKS} list. + */ + @SuppressWarnings("SameParameterValue") + private static @NotNull HashMap getRendererIgnoredBlocks(@NotNull Map rendererIgnoredBlocks) + { + HashMap blockStateWrappers = new HashMap<>(); + + for (Map.Entry blockStateResourceLocations : rendererIgnoredBlocks.entrySet()) + { + blockStateWrappers.put(blockStateResourceLocations.getValue(), fromBlockState(blockStateResourceLocations.getKey(), false)); + } + + return blockStateWrappers; + } + + public final BlockState blockState; + + BlockStateWrapper(BlockState blockState) + { + this(blockState, true); + } + + // TODO: Pass in levelwrapper so nullCheck has a use + private BlockStateWrapper(BlockState blockState, boolean nullCheck) { this.blockState = blockState; - LOGGER.trace("Created BlockStateWrapper for [" + blockState + "]"); + + LOGGER.trace("Created BlockStateWrapper for [{}]", blockState); } @@ -95,49 +151,50 @@ public class BlockStateWrapper implements IBlockStateWrapper @Override public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; } - @Override public String serialize() // FIXME pass in level to prevent null pointers (or maybe just RegistryAccess?) { - // cache the serialization result so it can be quickly used as a semi-stable hashing method - if (this.serializationResult == null) + // the result can be quickly used as a semi-stable hashing method, so it's going to be cached + if (this.serializationResult != null) + return this.serializationResult; + + if (this.blockState == null) { - if (this.blockState == null) - { - return "AIR"; - } - - ResourceLocation resourceLocation; + return "AIR"; + } + + if (RENDERER_IGNORED_BLOCKS_INTERNAL.containsKey(this.blockState)) + return this.serializationResult = RENDERER_IGNORED_BLOCKS_INTERNAL.get(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()); + 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); + + 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); return this.serializationResult; } - public static BlockStateWrapper deserialize(String resourceStateString) throws IOException // FIXME pass in level to prevent null pointers (or maybe just RegistryAccess?) + public static IBlockStateWrapper deserialize(String resourceStateString) throws IOException // FIXME pass in level to prevent null pointers (or maybe just RegistryAccess?) { - if (resourceStateString.equals("AIR") || resourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case - { - return AIR; - } + if (resourceStateString.isEmpty()) + throw new IOException("resourceStateString is empty"); + if (RENDERER_IGNORED_BLOCKS_INTERNAL.containsValue(resourceStateString)) + return RENDERER_IGNORED_BLOCKS.get(resourceStateString); // parse the BlockState diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java index caacbd061..e54490bbb 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java @@ -484,7 +484,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv ChunkAccess chunk = totalChunks.get(x, z); if (chunk != null) { - chunkWrapperList.set(x, z, new ChunkWrapper(chunk, region, null)); + chunkWrapperList.set(x, z, new ChunkWrapper(chunk, region, serverlevel.getLevelWrapper())); } }); From 2f7852f103877b428004934413f7e083e9976c30 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 23 Aug 2023 21:25:40 -0500 Subject: [PATCH 2/2] Revert Air Block handling and use the old (de)serialization logic While having the ability to deserialize blockstates without a level is desired, I'm not sure if the method suggested here would work between MC versions or would support modded blocks (in the eventual case where someone wants to do that). --- .../common/wrappers/WrapperFactory.java | 5 +- .../wrappers/block/BlockStateWrapper.java | 259 ++++++++++-------- coreSubProjects | 2 +- 3 files changed, 154 insertions(+), 112 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 911a2378a..2ed92ea15 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 @@ -30,12 +30,13 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; 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.world.level.LevelReader; import net.minecraft.world.level.chunk.ChunkAccess; import java.io.IOException; -import java.util.HashMap; +import java.util.HashSet; /** * This handles creating abstract wrapper objects. @@ -73,7 +74,7 @@ public class WrapperFactory implements IWrapperFactory public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; } @Override - public HashMap getRendererIgnoredBlocks() { return BlockStateWrapper.RENDERER_IGNORED_BLOCKS; } + public HashSet getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); } /** 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 b54fd8a1c..6e2d03653 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 @@ -3,8 +3,10 @@ package com.seibel.distanthorizons.common.wrappers.block; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import org.apache.logging.log4j.Logger; @@ -31,18 +33,25 @@ public class BlockStateWrapper implements IBlockStateWrapper { /** example "minecraft:plains" */ public static final String RESOURCE_LOCATION_SEPARATOR = ":"; - /** example "minecraft:water_state_{level:0}" */ + /** example "minecraft:water_STATE_{level:0}" */ public static final String STATE_STRING_SEPARATOR = "_STATE_"; // must be defined before AIR, otherwise a null pointer will be thrown private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public static final ConcurrentHashMap cache = new ConcurrentHashMap<>(); - public static final BlockStateWrapper AIR = fromBlockState(BuiltInRegistries.BLOCK.get(ResourceLocation.tryParse("minecraft:air")).defaultBlockState(), false); - public static final String[] RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS = {"minecraft:air", "minecraft:barrier", "minecraft:structure_void"}; - public static final HashMap RENDERER_IGNORED_BLOCKS_INTERNAL = getRendererIgnoredBlocksInternal(RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS); - public static final HashMap RENDERER_IGNORED_BLOCKS = getRendererIgnoredBlocks(RENDERER_IGNORED_BLOCKS_INTERNAL); + 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 String[] RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS = { AIR_STRING, "minecraft:barrier", "minecraft:structure_void", "minecraft:light" }; + + public static HashSet rendererIgnoredBlocks = null; + + + + public final BlockState blockState; /** * Cached so it can be quickly used as a semi-stable hashing method.
@@ -51,86 +60,83 @@ public class BlockStateWrapper implements IBlockStateWrapper private String serializationResult = null; + //==============// // constructors // //==============// - public static BlockStateWrapper fromBlockState(BlockState blockState) + public static BlockStateWrapper fromBlockState(BlockState blockState) { - return fromBlockState(blockState, true); - } - - private static BlockStateWrapper fromBlockState(BlockState blockState, boolean nullCheck) - { - if (Objects.requireNonNull(blockState).isAir() && AIR != null) + if (blockState == null || blockState.isAir()) + { return AIR; - - return cache.computeIfAbsent(blockState, blockState1 -> new BlockStateWrapper(blockState1, nullCheck)); - } - - /** - * Only meant for use in the {@code RENDERER_IGNORED_BLOCKS_INTERNAL} list. Do not use elsewhere, since the {@code levelWrapper} parameter of each {@code IBlockStateWrapper} will be {@code null}, causing issues. - * @param resourceLocations The resource location(s) of the block(s) that should be ignored by the renderer, may only contain {@code [a-z0-9/._-)} characters. - * @return The default blockstate(s) of the block(s), paired with the serialized resource location(s) of the block(s), which should be passed into the {@code RENDERER_IGNORED_BLOCKS_INTERNAL} map and the {@code getRendererIgnoredBlocks} method. - */ - @SuppressWarnings("SameParameterValue") - private static @NotNull HashMap getRendererIgnoredBlocksInternal(String @NotNull ... resourceLocations) - { - HashMap blockStates = new HashMap<>(); - - for (String resourceLocation : resourceLocations) - { - ResourceLocation fetchedResourceLocation = Objects.requireNonNull(ResourceLocation.tryParse(resourceLocation), String.format("Supplied a resource location that couldn't be parsed by Minecraft: %s", resourceLocation)); - var splitResourceLocation = resourceLocation.split(":"); - - if (splitResourceLocation.length == 0) { - LOGGER.warn("A resource location that should be ignored by the renderer was in an invalid format: {}", resourceLocation); - continue; - } - - blockStates.put(BuiltInRegistries.BLOCK.get(fetchedResourceLocation).defaultBlockState(), splitResourceLocation[1].toUpperCase(Locale.ROOT)); } - return blockStates; + return WRAPPER_BY_BLOCK_STATE.computeIfAbsent(blockState, newBlockState -> new BlockStateWrapper(newBlockState)); } - /** - * Only meant for use in the {@code RENDERER_IGNORED_BLOCKS} list. Do not use elsewhere, since the {@code levelWrapper} parameter of each {@code IBlockStateWrapper} will be {@code null}, causing issues. - * @param rendererIgnoredBlocks A map containing the blockstate(s) of the block(s), paired with the resource location(s) of the block(s) that should be ignored by the renderer. - * @return The blockstate wrapper(s) of the blockstate(s), which should be passed into the {@code RENDERER_IGNORED_BLOCKS} list. - */ - @SuppressWarnings("SameParameterValue") - private static @NotNull HashMap getRendererIgnoredBlocks(@NotNull Map rendererIgnoredBlocks) + private BlockStateWrapper(BlockState blockState) { - HashMap blockStateWrappers = new HashMap<>(); - - for (Map.Entry blockStateResourceLocations : rendererIgnoredBlocks.entrySet()) - { - blockStateWrappers.put(blockStateResourceLocations.getValue(), fromBlockState(blockStateResourceLocations.getKey(), false)); - } - - return blockStateWrappers; - } - - public final BlockState blockState; - - BlockStateWrapper(BlockState blockState) - { - this(blockState, true); - } - - // TODO: Pass in levelwrapper so nullCheck has a use - private BlockStateWrapper(BlockState blockState, boolean nullCheck) { this.blockState = blockState; - - LOGGER.trace("Created BlockStateWrapper for [{}]", blockState); + LOGGER.trace("Created BlockStateWrapper for ["+blockState+"]"); } - //=========// - // methods // - //=========// + //================// + // helper methods // + //================// + + /** + * Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one. + * This way the method won't accidentally be called before the deserialization can be completed. + */ + public static HashSet getRendererIgnoredBlocks(ILevelWrapper levelWrapper) + { + // use the cached version if possible + if (rendererIgnoredBlocks != null) + { + return rendererIgnoredBlocks; + } + + + // deserialize each of the given resource locations + HashSet blockStateWrappers = new HashSet<>(); + for (String blockResourceLocation : RENDERER_IGNORED_BLOCKS_RESOURCE_LOCATIONS) + { + try + { + BlockStateWrapper DefaultBlockStateToIgnore = (BlockStateWrapper) deserialize(blockResourceLocation, levelWrapper); + blockStateWrappers.add(DefaultBlockStateToIgnore); + + if (DefaultBlockStateToIgnore == AIR) + { + continue; + } + + // add all possible blockstates (to account for light blocks with different light values and such) + List blockStatesToIgnore = DefaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates(); + for (BlockState blockState : blockStatesToIgnore) + { + BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState); + blockStateWrappers.add(newBlockToIgnore); + } + } + catch (IOException e) + { + LOGGER.warn("Unable to deserialize rendererIgnoredBlock with the resource location: ["+blockResourceLocation+"]. Error: "+e.getMessage(), e); + } + } + + rendererIgnoredBlocks = blockStateWrappers; + return rendererIgnoredBlocks; + } + + + + //=================// + // wrapper methods // + //=================// @Override public int getOpacity() @@ -152,30 +158,38 @@ public class BlockStateWrapper implements IBlockStateWrapper public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; } @Override - public String serialize() // FIXME pass in level to prevent null pointers (or maybe just RegistryAccess?) + public String serialize() { return this.serialize(null); } + public String serialize(ILevelWrapper levelWrapper) { - // the result can be quickly used as a semi-stable hashing method, so it's going to be cached + // 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) + { return this.serializationResult; + } if (this.blockState == null) { - return "AIR"; + return AIR_STRING; } - if (RENDERER_IGNORED_BLOCKS_INTERNAL.containsKey(this.blockState)) - return this.serializationResult = RENDERER_IGNORED_BLOCKS_INTERNAL.get(this.blockState); + + + #if MC_1_18_2 || MC_1_19_2 || MC_1_20_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 : null); + #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 = Minecraft.getInstance().level.registryAccess(); - resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock()); - #else - net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess(); + #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 + #endif if (resourceLocation == null) { @@ -188,23 +202,27 @@ public class BlockStateWrapper implements IBlockStateWrapper return this.serializationResult; } - public static IBlockStateWrapper deserialize(String resourceStateString) throws IOException // FIXME pass in level to prevent null pointers (or maybe just RegistryAccess?) + + /** 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.isEmpty()) - throw new IOException("resourceStateString is empty"); - - if (RENDERER_IGNORED_BLOCKS_INTERNAL.containsValue(resourceStateString)) - return RENDERER_IGNORED_BLOCKS.get(resourceStateString); - - - // parse the BlockState - int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR); - if (stateSeparatorIndex == -1) + if (resourceStateString.equals(AIR_STRING) || resourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case { - throw new IOException("Unable to parse BlockState out of string: [" + resourceStateString + "]."); + return AIR; + } + + + + // try to parse out the BlockState + String blockStatePropertiesString = null; // will be null if no properties were included + int stateSeparatorIndex = resourceStateString.indexOf(STATE_STRING_SEPARATOR); + if (stateSeparatorIndex != -1) + { + // blockstate properties found + blockStatePropertiesString = resourceStateString.substring(stateSeparatorIndex + STATE_STRING_SEPARATOR.length()); + resourceStateString = resourceStateString.substring(0, stateSeparatorIndex); } - String blockStatePropertiesString = resourceStateString.substring(stateSeparatorIndex + STATE_STRING_SEPARATOR.length()); - resourceStateString = resourceStateString.substring(0, stateSeparatorIndex); // parse the resource location int resourceSeparatorIndex = resourceStateString.indexOf(RESOURCE_LOCATION_SEPARATOR); @@ -219,35 +237,58 @@ public class BlockStateWrapper implements IBlockStateWrapper // attempt to get the BlockState from all possible BlockStates try { + + #if MC_1_18_2 || MC_1_19_2 || MC_1_20_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 : null); + #endif + Block block; #if MC_1_16_5 || MC_1_17_1 block = Registry.BLOCK.get(resourceLocation); #elif MC_1_18_2 || MC_1_19_2 - net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess(); + net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation); #else - net.minecraft.core.RegistryAccess registryAccess = Minecraft.getInstance().level.registryAccess(); + net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation); #endif - - BlockState foundState = null; - List possibleStateList = block.getStateDefinition().getPossibleStates(); - for (BlockState possibleState : possibleStateList) + if (block == null) { - String possibleStatePropertiesString = serializeBlockStateProperties(possibleState); - if (possibleStatePropertiesString.equals(blockStatePropertiesString)) + // shouldn't normally happen, but here to make the compiler happy + LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost."); + return AIR; + } + + + // attempt to find the blockstate from all possibilities + BlockState foundState = null; + if (blockStatePropertiesString != null) + { + List possibleStateList = block.getStateDefinition().getPossibleStates(); + for (BlockState possibleState : possibleStateList) { - foundState = possibleState; - break; + String possibleStatePropertiesString = serializeBlockStateProperties(possibleState); + if (possibleStatePropertiesString.equals(blockStatePropertiesString)) + { + foundState = possibleState; + break; + } } } - // use the default if no state was found + // use the default if no state was found or given if (foundState == null) { - LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]."); + if (blockStatePropertiesString != null) + { + // we should have found a blockstate, but didn't + LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]."); + } + foundState = block.defaultBlockState(); } return new BlockStateWrapper(foundState); diff --git a/coreSubProjects b/coreSubProjects index 2964acfc8..f6ee8048e 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 2964acfc8cc0558435989c2dd4aa4b6c81e0c292 +Subproject commit f6ee8048eb49a8b0e2ac702bb3ba0f60638bd6aa