diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java index 04d1e753e..1be7a4746 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java @@ -1,13 +1,21 @@ package com.fastasyncworldedit.core.configuration; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; +import com.fastasyncworldedit.core.limit.PropertyRemap; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.block.BlockTypesCache; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class Settings extends Config { @@ -192,7 +200,32 @@ public class Settings extends Config { "List of nbt tags to strip from blocks, e.g. Items", }) public List STRIP_NBT = new ArrayList<>(); - + @Comment({ + "If the disallowed blocks listed in config-legacy.yml should be disallowed in all edits,", + "not just where blocks patterns are used.", + " - Can prevent blocks being pasted from clipboards, etc.", + " - If fast-placement is disabled, this may cause edits to be slower." + }) + public boolean UNIVERSAL_DISALLOWED_BLOCKS = true; + @Comment({ + "List of blocks to deny use of. Can be either an entire block type or a block with a specific property value.", + "Where block properties are specified, any blockstate with the property will be disallowed (i.g. all directions", + "of a waterlogged fence). For blocking/remapping of all occurence of a property like waterlogged, see", + "remap-properties below.", + "Example block property blocking:", + " - \"minecraft:conduit[waterlogged=true]\"", + " - \"minecraft:piston[extended=false,facing=west]\"", + " - \"minecraft:wheat[age=7]\"" + }) + public List DISALLOWED_BLOCKS = Arrays.asList("\"minecraft:wheat\"", "\"minecraft:fire\"", "\"minecraft:redstone_wire\""); + @Comment({ + "List of block properties that should be remapped if used in an edit. Entries should take the form", + "\"property_name[value1_old:value1_new,value2_old:value2_new]\". For example:", + " - \"waterlogged[true:false]\"", + " - \"age[7:4,6:4,5:4]\"", + " - \"extended[true:false]\"" + }) + public List REMAP_PROPERTIES = new ArrayList<>(); } public static class HISTORY { @@ -399,7 +432,7 @@ public class Settings extends Config { @Comment({ "[SAFE] Keep entities that are positioned in non-air blocks when editing an area", - "Might cause client-side FPS lagg in some situations" + "Might cause client-side FPS lag in some situations" }) public boolean KEEP_ENTITIES_IN_BLOCKS = false; @@ -607,6 +640,84 @@ public class Settings extends Config { limit.STRIP_NBT = Collections.emptySet(); } } + limit.UNIVERSAL_DISALLOWED_BLOCKS &= newLimit.UNIVERSAL_DISALLOWED_BLOCKS; + + if (limit.DISALLOWED_BLOCKS == null) { + limit.DISALLOWED_BLOCKS = newLimit.DISALLOWED_BLOCKS.isEmpty() ? Collections.emptySet() : new HashSet<>( + newLimit.DISALLOWED_BLOCKS); + } else if (limit.DISALLOWED_BLOCKS.isEmpty() || newLimit.DISALLOWED_BLOCKS.isEmpty()) { + limit.DISALLOWED_BLOCKS = Collections.emptySet(); + } else { + limit.DISALLOWED_BLOCKS = new HashSet<>(limit.DISALLOWED_BLOCKS); + limit.DISALLOWED_BLOCKS.retainAll(newLimit.DISALLOWED_BLOCKS + .stream() + .map(s -> s.contains(":") ? s.toLowerCase(Locale.ROOT) : ("minecraft:" + s).toLowerCase(Locale.ROOT)) + .collect(Collectors.toSet())); + if (limit.DISALLOWED_BLOCKS.isEmpty()) { + limit.DISALLOWED_BLOCKS = Collections.emptySet(); + } + } + + if (limit.REMAP_PROPERTIES == null) { + limit.REMAP_PROPERTIES = newLimit.REMAP_PROPERTIES.isEmpty() ? Collections.emptySet() : + newLimit.REMAP_PROPERTIES.stream().flatMap(s -> { + String propertyStr = s.substring(0, s.indexOf('[')); + List> properties = + BlockTypesCache.getAllProperties().get(propertyStr.toLowerCase(Locale.ROOT)); + if (properties == null || properties.isEmpty()) { + return Stream.empty(); + } + String[] mappings = s.substring(s.indexOf('[') + 1, s.indexOf(']')).split(","); + Set> remaps = new HashSet<>(); + for (Property property : properties) { + for (String mapping : mappings) { + try { + String[] fromTo = mapping.split(":"); + remaps.add(property.getRemap( + property.getValueFor(fromTo[0]), + property.getValueFor(fromTo[1]) + )); + } catch (IllegalArgumentException ignored) { + // This property is unlikely to be the one being targeted. + break; + } + } + } + return remaps.stream(); + }).collect(Collectors.toSet()); + } else if (limit.REMAP_PROPERTIES.isEmpty() || newLimit.REMAP_PROPERTIES.isEmpty()) { + limit.REMAP_PROPERTIES = Collections.emptySet(); + } else { + limit.REMAP_PROPERTIES = new HashSet<>(limit.REMAP_PROPERTIES); + limit.REMAP_PROPERTIES.retainAll(newLimit.REMAP_PROPERTIES.stream().flatMap(s -> { + String propertyStr = s.substring(0, s.indexOf('[')); + List> properties = + BlockTypesCache.getAllProperties().get(propertyStr.toLowerCase(Locale.ROOT)); + if (properties == null || properties.isEmpty()) { + return Stream.empty(); + } + String[] mappings = s.substring(s.indexOf('[') + 1, s.indexOf(']')).split(","); + Set> remaps = new HashSet<>(); + for (Property property : properties) { + for (String mapping : mappings) { + try { + String[] fromTo = mapping.split(":"); + remaps.add(property.getRemap( + property.getValueFor(fromTo[0]), + property.getValueFor(fromTo[1]) + )); + } catch (IllegalArgumentException ignored) { + // This property is unlikely to be the one being targeted. + break; + } + } + } + return remaps.stream(); + }).collect(Collectors.toSet())); + if (limit.REMAP_PROPERTIES.isEmpty()) { + limit.REMAP_PROPERTIES = Collections.emptySet(); + } + } } } return limit; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java new file mode 100644 index 000000000..35db842f4 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java @@ -0,0 +1,188 @@ +package com.fastasyncworldedit.core.extent; + +import com.fastasyncworldedit.core.extent.processor.ProcessorScope; +import com.fastasyncworldedit.core.limit.PropertyRemap; +import com.fastasyncworldedit.core.queue.IBatchProcessor; +import com.fastasyncworldedit.core.queue.IChunk; +import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.util.ExtentTraverser; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extension.factory.parser.DefaultBlockParser; +import com.sk89q.worldedit.extent.AbstractDelegateExtent; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.FuzzyBlockState; + +import javax.annotation.Nullable; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.stream.Collectors; + +import static com.sk89q.worldedit.world.block.BlockTypesCache.states; + +public class DisallowedBlocksExtent extends AbstractDelegateExtent implements IBatchProcessor { + + private static final BlockState RESERVED = BlockTypes.__RESERVED__.getDefaultState(); + private final Set> remaps; + private Set blockedStates = null; + private Set blockedBlocks = null; + + /** + * Create a new instance. + * + * @param extent the extent + * @param blockedBlocks block types to disallow + * @param remaps property remaps to apply, e.g. waterlogged true -> false + */ + public DisallowedBlocksExtent(Extent extent, Set blockedBlocks, Set> remaps) { + super(extent); + this.remaps = remaps; + if (blockedBlocks != null && !blockedBlocks.isEmpty()) { + blockedBlocks = blockedBlocks.stream() + .map(s -> s.contains(":") ? s.toLowerCase(Locale.ROOT) : ("minecraft:" + s).toLowerCase(Locale.ROOT)) + .collect(Collectors.toSet()); + this.blockedBlocks = new HashSet<>(); + for (String block : blockedBlocks) { + if (block.indexOf('[') == -1 || block.indexOf(']') == -1) { + blockedBlocks.add(block); + continue; + } + String[] properties = block.substring(block.indexOf('[') + 1, block.indexOf(']')).split(","); + if (properties.length == 0) { + continue; + } + BlockType type = BlockTypes.get(block.substring(0, block.indexOf('['))); + Map, Object> values = + DefaultBlockParser.parseProperties(type, properties, null, true); + if (values == null || values.isEmpty()) { + continue; + } + if (blockedStates == null) { + blockedStates = new HashSet<>(); + } + blockedStates.add(new FuzzyBlockState(type.getDefaultState(), values)); + } + } + } + + @Override + public > boolean setBlock(BlockVector3 location, B block) throws WorldEditException { + if (block instanceof BaseBlock || block instanceof BlockState) { + B newBlock = checkBlock(block); + if (newBlock.getBlockType() == BlockTypes.__RESERVED__) { + return false; + } + return super.setBlock(location, newBlock); + } + return super.setBlock(location, block); + } + + @Override + public > boolean setBlock(int x, int y, int z, B block) throws WorldEditException { + if (block instanceof BaseBlock || block instanceof BlockState) { + B newBlock = checkBlock(block); + if (newBlock.getBlockType() == BlockTypes.__RESERVED__) { + return false; + } + return super.setBlock(x, y, z, newBlock); + } + return super.setBlock(x, y, z, block); + } + + private > B checkBlock(B block) { + if (blockedBlocks != null) { + if (blockedBlocks.contains(block.getBlockType().getId())) { + return (B) (block instanceof BlockState ? RESERVED : RESERVED.toBaseBlock()); // set to reserved/empty + } + } + if (blockedStates == null) { + return block; + } + for (FuzzyBlockState state : blockedStates) { + if (state.equalsFuzzy(block)) { + return (B) (block instanceof BlockState ? RESERVED : RESERVED.toBaseBlock()); + } + } + if (remaps == null || remaps.isEmpty()) { + return block; + } + for (PropertyRemap remap : remaps) { + block = remap.apply(block); + } + return block; + } + + @Override + public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { + if (blockedStates == null && blockedBlocks == null) { // Shouldn't be possible, but make sure + return set; + } + for (int layer = set.getMinSectionPosition(); layer <= set.getMaxSectionPosition(); layer++) { + if (!set.hasSection(layer)) { + continue; + } + char[] blocks = Objects.requireNonNull(set.loadIfPresent(layer)); + it: + for (int i = 0; i < blocks.length; i++) { + char block = blocks[i]; + BlockState state = states[block]; + if (blockedBlocks != null) { + if (blockedBlocks.contains(state.getBlockType().getId())) { + blocks[i] = 0; + continue; + } + } + if (blockedStates == null) { + continue; + } + for (FuzzyBlockState fuzzy : blockedStates) { + if (fuzzy.equalsFuzzy(state)) { + blocks[i] = 0; + continue it; + } + } + if (remaps == null || remaps.isEmpty()) { + blocks[i] = block; + continue; + } + for (PropertyRemap remap : remaps) { + state = remap.apply(state); + } + blocks[i] = state.getOrdinalChar(); + } + } + return set; + } + + @Override + public Future postProcessSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { + return CompletableFuture.completedFuture(set); + } + + @Nullable + @Override + public Extent construct(final Extent child) { + if (getExtent() != child) { + new ExtentTraverser(this).setNext(child); + } + return this; + } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.CHANGING_BLOCKS; + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/FaweRegionExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/FaweRegionExtent.java index 6daddccf7..b38a1b101 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/FaweRegionExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/FaweRegionExtent.java @@ -2,7 +2,7 @@ package com.fastasyncworldedit.core.extent; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.util.ExtentTraverser; import com.fastasyncworldedit.core.util.WEManager; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HeightBoundExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HeightBoundExtent.java index 4ce795c11..db5a03c6d 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HeightBoundExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HeightBoundExtent.java @@ -1,6 +1,6 @@ package com.fastasyncworldedit.core.extent; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java index 2dbac5afa..e4f2bc2b3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java @@ -4,7 +4,7 @@ import com.fastasyncworldedit.core.extent.filter.block.ExtentFilterBlock; import com.fastasyncworldedit.core.function.generator.GenBase; import com.fastasyncworldedit.core.function.generator.Resource; import com.fastasyncworldedit.core.internal.exception.FaweException; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.queue.Filter; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.MaxChangedBlocksException; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java index e934099f9..91ce69645 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java @@ -1,6 +1,6 @@ package com.fastasyncworldedit.core.extent; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java index 5ffa25a27..7420886a2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java @@ -5,7 +5,7 @@ import com.fastasyncworldedit.core.extent.processor.ProcessorScope; import com.fastasyncworldedit.core.function.generator.GenBase; import com.fastasyncworldedit.core.function.generator.Resource; import com.fastasyncworldedit.core.internal.exception.FaweException; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ProcessedWEExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ProcessedWEExtent.java index 8efb1bf5c..ab8c1d47b 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ProcessedWEExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ProcessedWEExtent.java @@ -1,7 +1,7 @@ package com.fastasyncworldedit.core.extent; import com.fastasyncworldedit.core.FaweCache; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.WEManager; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SingleRegionExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SingleRegionExtent.java index f30861ebc..4249faee7 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SingleRegionExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SingleRegionExtent.java @@ -1,6 +1,6 @@ package com.fastasyncworldedit.core.extent; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/object/FaweLimit.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java similarity index 88% rename from worldedit-core/src/main/java/com/fastasyncworldedit/core/object/FaweLimit.java rename to worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java index 7bbb86994..df9cfd16c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/object/FaweLimit.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/FaweLimit.java @@ -1,4 +1,4 @@ -package com.fastasyncworldedit.core.object; +package com.fastasyncworldedit.core.limit; import com.fastasyncworldedit.core.FaweCache; @@ -21,6 +21,9 @@ public class FaweLimit { public boolean CONFIRM_LARGE = true; public boolean RESTRICT_HISTORY_TO_REGIONS = true; public Set STRIP_NBT = null; + public boolean UNIVERSAL_DISALLOWED_BLOCKS = true; + public Set DISALLOWED_BLOCKS = null; + public Set> REMAP_PROPERTIES = null; public static FaweLimit MAX; @@ -112,6 +115,9 @@ public class FaweLimit { MAX.CONFIRM_LARGE = true; MAX.RESTRICT_HISTORY_TO_REGIONS = false; MAX.STRIP_NBT = null; + MAX.UNIVERSAL_DISALLOWED_BLOCKS = false; + MAX.DISALLOWED_BLOCKS = null; + MAX.REMAP_PROPERTIES = null; } public boolean MAX_CHANGES() { @@ -234,7 +240,10 @@ public class FaweLimit { && SPEED_REDUCTION == 0 && FAST_PLACEMENT && !RESTRICT_HISTORY_TO_REGIONS - && (STRIP_NBT == null || STRIP_NBT.isEmpty()); + && (STRIP_NBT == null || STRIP_NBT.isEmpty()) + && !UNIVERSAL_DISALLOWED_BLOCKS + && (DISALLOWED_BLOCKS == null || DISALLOWED_BLOCKS.isEmpty()) + && (REMAP_PROPERTIES == null || REMAP_PROPERTIES.isEmpty()); } public void set(FaweLimit limit) { @@ -252,6 +261,9 @@ public class FaweLimit { CONFIRM_LARGE = limit.CONFIRM_LARGE; RESTRICT_HISTORY_TO_REGIONS = limit.RESTRICT_HISTORY_TO_REGIONS; STRIP_NBT = limit.STRIP_NBT; + UNIVERSAL_DISALLOWED_BLOCKS = limit.UNIVERSAL_DISALLOWED_BLOCKS; + DISALLOWED_BLOCKS = limit.DISALLOWED_BLOCKS; + REMAP_PROPERTIES = limit.REMAP_PROPERTIES; } public FaweLimit copy() { @@ -270,6 +282,9 @@ public class FaweLimit { limit.CONFIRM_LARGE = CONFIRM_LARGE; limit.RESTRICT_HISTORY_TO_REGIONS = RESTRICT_HISTORY_TO_REGIONS; limit.STRIP_NBT = STRIP_NBT; + limit.UNIVERSAL_DISALLOWED_BLOCKS = UNIVERSAL_DISALLOWED_BLOCKS; + limit.DISALLOWED_BLOCKS = DISALLOWED_BLOCKS; + limit.REMAP_PROPERTIES = REMAP_PROPERTIES; return limit; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/PropertyRemap.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/PropertyRemap.java new file mode 100644 index 000000000..18fbb8c5e --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/limit/PropertyRemap.java @@ -0,0 +1,57 @@ +package com.fastasyncworldedit.core.limit; + +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; + +public class PropertyRemap { + + private final Property property; + private final T oldValue; + private final T newValue; + + /** + * New instance + * + * @param property property to remap values for + * @param oldValue value to remap from + * @param newValue value to remap to + */ + public PropertyRemap(Property property, T oldValue, T newValue) { + this.property = property; + this.oldValue = oldValue; + this.newValue = newValue; + } + + /** + * Apply remapping to a state. Will return original state if property is not present. + * + * @param state Block to apply remapping to + * @return new state + */ + public > B apply(B state) { + if (!state.getBlockType().hasProperty(property.getKey())) { + return state; + } + T current = state.getState(property); + if (current == oldValue) { + state = state.with(property.getKey(), newValue); + } + return state; + } + + /** + * Apply remapping to a given value if the given block type has the property associated with this remap instance. + * + * @param type block type to check + * @param value value to remap + * @return new value + */ + public T apply(BlockType type, T value) { + if (type.hasProperty(property.getKey())) { + return value == oldValue ? newValue : value; + } + return value; + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/EditSessionBuilder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/EditSessionBuilder.java index c9f36105e..8958ac2e2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/EditSessionBuilder.java @@ -23,7 +23,7 @@ import com.fastasyncworldedit.core.history.RollbackOptimizedHistory; import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet; import com.fastasyncworldedit.core.history.changeset.BlockBagChangeSet; import com.fastasyncworldedit.core.history.changeset.NullChangeSet; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 6bb4e4367..1c545e447 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -50,7 +50,7 @@ import com.fastasyncworldedit.core.math.MutableBlockVector2; import com.fastasyncworldedit.core.math.MutableBlockVector3; import com.fastasyncworldedit.core.math.MutableVector3; import com.fastasyncworldedit.core.math.random.SimplexNoise; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.queue.implementation.preloader.Preloader; import com.fastasyncworldedit.core.regions.RegionWrapper; import com.fastasyncworldedit.core.util.ExtentTraverser; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java index b6312f034..66881f44c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java @@ -23,6 +23,7 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.extent.DisallowedBlocksExtent; import com.fastasyncworldedit.core.extent.FaweRegionExtent; import com.fastasyncworldedit.core.extent.HistoryExtent; import com.fastasyncworldedit.core.extent.LimitExtent; @@ -42,7 +43,8 @@ import com.fastasyncworldedit.core.history.RollbackOptimizedHistory; import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet; import com.fastasyncworldedit.core.history.changeset.BlockBagChangeSet; import com.fastasyncworldedit.core.history.changeset.NullChangeSet; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; +import com.fastasyncworldedit.core.limit.PropertyRemap; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; @@ -68,7 +70,9 @@ import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.HashSet; import java.util.Locale; +import java.util.Set; import java.util.UUID; /** @@ -551,6 +555,23 @@ public final class EditSessionBuilder { this.extent = new StripNBTExtent(this.extent, this.limit.STRIP_NBT); } } + if (this.limit != null && !this.limit.isUnlimited()) { + Set limitBlocks = new HashSet<>(); + if ((getActor() == null || getActor().hasPermission("worldedit.anyblock") && this.limit.UNIVERSAL_DISALLOWED_BLOCKS)) { + limitBlocks.addAll(WorldEdit.getInstance().getConfiguration().disallowedBlocks); + } + if (this.limit.DISALLOWED_BLOCKS != null && !this.limit.DISALLOWED_BLOCKS.isEmpty()) { + limitBlocks.addAll(this.limit.DISALLOWED_BLOCKS); + } + Set> remaps = this.limit.REMAP_PROPERTIES; + if (!limitBlocks.isEmpty() || (remaps != null && !remaps.isEmpty())) { + if (placeChunks) { + extent.addProcessor(new DisallowedBlocksExtent(this.extent, limitBlocks, remaps)); + } else { + this.extent = new DisallowedBlocksExtent(this.extent, limitBlocks, remaps); + } + } + } this.extent = wrapExtent(this.extent, eventBus, event, EditSession.Stage.BEFORE_HISTORY); } return this; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index f875d1c7f..c7efa7cf3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -27,7 +27,7 @@ import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder; import com.fastasyncworldedit.core.history.DiskStorageHistory; import com.fastasyncworldedit.core.internal.io.FaweInputStream; import com.fastasyncworldedit.core.internal.io.FaweOutputStream; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.BrushCache; import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.StringMan; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index c189daaba..26b1b4856 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -54,7 +54,7 @@ import com.fastasyncworldedit.core.function.mask.IdMask; import com.fastasyncworldedit.core.function.mask.SingleBlockTypeMask; import com.fastasyncworldedit.core.math.heightmap.ScalableHeightMap; import com.fastasyncworldedit.core.math.heightmap.ScalableHeightMap.Shape; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.StringMan; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index 9f13fd7a7..61c220f68 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -29,7 +29,7 @@ import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder; import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard; import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder; import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.ImgurUtility; import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.MaskTraverser; @@ -45,7 +45,6 @@ import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.command.util.annotation.Preload; -import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 05e93c314..90ee83303 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -23,7 +23,7 @@ import com.fastasyncworldedit.core.FaweAPI; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.MaskTraverser; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.EditSession; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java index 3b77fee0e..ddfbdfd74 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java @@ -23,6 +23,8 @@ import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.extent.inventory.SlottableBlockBag; import com.fastasyncworldedit.core.jnbt.JSON2NBT; import com.fastasyncworldedit.core.jnbt.NBTException; +import com.fastasyncworldedit.core.limit.FaweLimit; +import com.fastasyncworldedit.core.limit.PropertyRemap; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.StringMan; import com.fastasyncworldedit.core.world.block.BlanketBaseBlock; @@ -66,6 +68,8 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -171,19 +175,57 @@ public class DefaultBlockParser extends InputParser { } } - private static Map, Object> parseProperties( + //FAWE start - make public + public static Map, Object> parseProperties( + //FAWE end BlockType type, String[] stateProperties, - ParserContext context + ParserContext context, + //FAWE start - if null should be returned instead of throwing an error + boolean nullNotError + //FAWE end ) throws NoMatchException { Map, Object> blockStates = new HashMap<>(); + //FAWE start - disallowed states + if (context != null && context.getActor() != null && !context.getActor().getLimit().isUnlimited()) { + for (String input : context.getActor().getLimit().DISALLOWED_BLOCKS) { + if (input.indexOf('[') == -1 && input.indexOf(']') == -1) { + continue; + } + if (!type.getId().equalsIgnoreCase(input.substring(0, input.indexOf('[')))) { + continue; + } + String[] properties = input.substring(input.indexOf('[') + 1, input.indexOf(']')).split(","); + Set blocked = Arrays.stream(properties).filter(s -> { + for (String in : stateProperties) { + if (in.equalsIgnoreCase(s)) { + return true; + } + } + return false; + }).collect(Collectors.toSet()); + if (!blocked.isEmpty()) { + throw new DisallowedUsageException(Caption.of( + "fawe.error.limit.disallowed-block", + TextComponent.of(input) + )); + } + } + } + //FAWE end + if (stateProperties.length > 0) { // Block data not yet detected // Parse the block data (optional) for (String parseableData : stateProperties) { try { String[] parts = parseableData.split("="); if (parts.length != 2) { + //FAWE start - if null should be returned instead of throwing an error + if (nullNotError) { + return null; + } + //FAWE end throw new InputParseException( Caption.of( "worldedit.error.parser.bad-state-format", @@ -195,7 +237,14 @@ public class DefaultBlockParser extends InputParser { @SuppressWarnings("unchecked") Property propertyKey = (Property) type.getPropertyMap().get(parts[0]); if (propertyKey == null) { - if (context.getActor() != null) { + //FAWE start - nullable context + if (context != null && context.getActor() != null) { + //FAWE end + //FAWE start - if null should be returned instead of throwing an error + if (nullNotError) { + return null; + } + //FAWE end throw new NoMatchException(Caption.of( "worldedit.error.parser.unknown-property", TextComponent.of(parts[0]), @@ -207,6 +256,11 @@ public class DefaultBlockParser extends InputParser { return Maps.newHashMap(); } if (blockStates.containsKey(propertyKey)) { + //FAWE start - if null should be returned instead of throwing an error + if (nullNotError) { + return null; + } + //FAWE end throw new InputParseException(Caption.of( "worldedit.error.parser.duplicate-property", TextComponent.of(parts[0]) @@ -216,6 +270,11 @@ public class DefaultBlockParser extends InputParser { try { value = propertyKey.getValueFor(parts[1]); } catch (IllegalArgumentException e) { + //FAWE start - if null should be returned instead of throwing an error + if (nullNotError) { + return null; + } + //FAWE end throw new NoMatchException(Caption.of( "worldedit.error.parser.unknown-value", TextComponent.of(parts[1]), @@ -223,10 +282,30 @@ public class DefaultBlockParser extends InputParser { )); } + //FAWE start - blocked states + if (context != null && context.getActor() != null && !context.getActor().getLimit().isUnlimited()) { + if (context.getActor().getLimit().REMAP_PROPERTIES != null + && !context.getActor().getLimit().REMAP_PROPERTIES.isEmpty()) { + for (PropertyRemap remap : context.getActor().getLimit().REMAP_PROPERTIES) { + Object newValue = remap.apply(type, value); + if (newValue != value) { + value = newValue; + break; + } + } + } + } + //FAWE end + blockStates.put(propertyKey, value); - } catch (NoMatchException e) { + } catch (NoMatchException | DisallowedUsageException e) { throw e; // Pass-through } catch (Exception e) { + //FAWE start - if null should be returned instead of throwing an error + if (nullNotError) { + return null; + } + //FAWE end throw new InputParseException(Caption.of( "worldedit.error.parser.bad-state-format", TextComponent.of(parseableData) @@ -387,7 +466,9 @@ public class DefaultBlockParser extends InputParser { } //FAWE end - blockStates.putAll(parseProperties(state.getBlockType(), stateProperties, context)); + //FAWE start - Not null if nullNotError false. + blockStates.putAll(parseProperties(state.getBlockType(), stateProperties, context, false)); + //FAWE end if (context.isPreferringWildcard()) { if (stateString == null || stateString.isEmpty()) { state = new FuzzyBlockState(state); @@ -428,10 +509,23 @@ public class DefaultBlockParser extends InputParser { if (context.isRestricted()) { Actor actor = context.requireActor(); - if (actor != null && !actor.hasPermission("worldedit.anyblock") - && worldEdit.getConfiguration().disallowedBlocks.contains(blockType.getId())) { - throw new DisallowedUsageException(Caption.of("worldedit.error.disallowed-block", TextComponent.of(input))); + //FAWE start - per-limit disallowed blocks + if (actor != null) { + if (!actor.hasPermission("worldedit.anyblock") + && worldEdit.getConfiguration().disallowedBlocks.contains(blockType.getId().toLowerCase(Locale.ROOT))) { + throw new DisallowedUsageException(Caption.of("worldedit.error.disallowed-block", TextComponent.of(blockType.getId()))); + } + FaweLimit limit = actor.getLimit(); + if (!limit.isUnlimited()) { + // No need to account for blocked states/properties as it will simply return false in the equality check + // during contains. + if (limit.DISALLOWED_BLOCKS.contains(blockType.getId().toLowerCase(Locale.ROOT))) { + throw new DisallowedUsageException(Caption.of("fawe.error.limit.disallowed-block", + TextComponent.of(blockType.getId()))); + } + } } + //FAWE end } if (nbt != null) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java index 79ba05a26..1f938ea82 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java @@ -22,7 +22,7 @@ package com.sk89q.worldedit.extension.platform; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.entity.MapMetadatable; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.task.InterruptableCondition; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.internal.cui.CUIEvent; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index 7b64ab693..5bfe8336f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -22,7 +22,7 @@ package com.sk89q.worldedit.regions; import com.fastasyncworldedit.core.extent.SingleRegionExtent; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; -import com.fastasyncworldedit.core.object.FaweLimit; +import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunk; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/Property.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/Property.java index 4e3a09473..308a39ec5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/Property.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/Property.java @@ -19,10 +19,12 @@ package com.sk89q.worldedit.registry.state; +import com.fastasyncworldedit.core.limit.PropertyRemap; import com.fastasyncworldedit.core.registry.state.PropertyKey; import javax.annotation.Nullable; import java.util.List; +import java.util.Objects; /** * Describes a state property of a block. @@ -57,16 +59,45 @@ public interface Property { T getValueFor(String string) throws IllegalArgumentException; //FAWE start + + /** + * Get the index of the given value in the list of values + * + * @param value value to get index for + * @throws IllegalArgumentException if value not applicable to this property + */ default int getIndex(T value) { return getValues().indexOf(value); } - default int getIndexFor(CharSequence string) throws IllegalArgumentException { - return getIndex(getValueFor(string.toString())); + /** + * Get the index of the given value in the list of values + * + * @param value value to get index for + * @throws IllegalArgumentException if value not applicable to this property + */ + default int getIndexFor(CharSequence value) throws IllegalArgumentException { + return getIndex(getValueFor(value.toString())); } + /** + * Get the {@link PropertyKey} associated with this property. + */ default PropertyKey getKey() { return PropertyKey.getOrCreate(getName()); } + + /** + * Get a {@link PropertyRemap} instance for this property with the given remap. + * + * @param from value to remap from + * @param to value to remap to + * @return new {@link PropertyRemap} instance + */ + default PropertyRemap getRemap(Object from, Object to) { + Objects.requireNonNull(from); + Objects.requireNonNull(to); + return new PropertyRemap(this, (T) from, (T) to); + } //FAWE end } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java index a9a8b88af..f1785b4f5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java @@ -42,6 +42,7 @@ import java.util.HashSet; import java.util.Locale; import java.util.Properties; import java.util.Set; +import java.util.stream.Collectors; /** * Simple LocalConfiguration that loads settings using @@ -92,7 +93,10 @@ public class PropertiesConfiguration extends LocalConfiguration { profile = getBool("profile", profile); traceUnflushedSessions = getBool("trace-unflushed-sessions", traceUnflushedSessions); - disallowedBlocks = getStringSet("disallowed-blocks", getDefaultDisallowedBlocks()); + disallowedBlocks = getStringSet("disallowed-blocks", getDefaultDisallowedBlocks()) + .stream() + .map(s -> s.contains(":") ? s.toLowerCase(Locale.ROOT) : ("minecraft:" + s).toLowerCase(Locale.ROOT)) + .collect(Collectors.toSet()); defaultChangeLimit = getInt("default-max-changed-blocks", defaultChangeLimit); maxChangeLimit = getInt("max-changed-blocks", maxChangeLimit); defaultVerticalHeight = getInt("default-vertical-height", defaultVerticalHeight); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java index ccca0ed2b..e10e4ac4f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java @@ -30,6 +30,7 @@ import org.apache.logging.log4j.Logger; import java.io.IOException; import java.util.HashSet; import java.util.Locale; +import java.util.stream.Collectors; /** * A less simple implementation of {@link LocalConfiguration} @@ -94,10 +95,10 @@ public class YAMLConfiguration extends LocalConfiguration { butcherDefaultRadius = Math.max(-1, config.getInt("limits.butcher-radius.default", butcherDefaultRadius)); butcherMaxRadius = Math.max(-1, config.getInt("limits.butcher-radius.maximum", butcherMaxRadius)); - disallowedBlocks = new HashSet<>(config.getStringList( - "limits.disallowed-blocks", - Lists.newArrayList(getDefaultDisallowedBlocks()) - )); + disallowedBlocks = config.getStringList("limits.disallowed-blocks", Lists.newArrayList(getDefaultDisallowedBlocks())) + .stream() + .map(s -> s.contains(":") ? s.toLowerCase(Locale.ROOT) : ("minecraft:" + s).toLowerCase(Locale.ROOT)) + .collect(Collectors.toSet()); allowedDataCycleBlocks = new HashSet<>(config.getStringList("limits.allowed-data-cycle-blocks", null)); registerHelp = config.getBoolean("register-help", true); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java index cb35f6587..3afa293d8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java @@ -152,8 +152,8 @@ public class BlockState implements BlockStateHolder, Pattern { String input = key.toString(); throw new SuggestInputParseException("Does not match a valid block type: " + input, input, () -> Stream.of( BlockTypesCache.values) - .filter(b -> StringMan.blockStateMatches(input, b.getId())) .map(BlockType::getId) + .filter(id -> StringMan.blockStateMatches(input, id)) .sorted(StringMan.blockStateComparator(input)) .collect(Collectors.toList()) ); @@ -163,7 +163,7 @@ public class BlockState implements BlockStateHolder, Pattern { return type.getDefaultState(); } - List propList = type.getProperties(); + List> propList = type.getProperties(); if (state.charAt(state.length() - 1) != ']') { state = state + "]"; @@ -172,7 +172,7 @@ public class BlockState implements BlockStateHolder, Pattern { charSequence.setString(state); if (propList.size() == 1) { - AbstractProperty property = (AbstractProperty) propList.get(0); + AbstractProperty property = (AbstractProperty) propList.get(0); String name = property.getName(); charSequence.setSubstring(propStrStart + name.length() + 2, state.length() - 1); @@ -188,7 +188,7 @@ public class BlockState implements BlockStateHolder, Pattern { stateId = type.getDefaultState().getInternalId(); } int length = state.length(); - AbstractProperty property = null; + AbstractProperty property = null; int last = propStrStart + 1; for (int i = last; i < length; i++) { @@ -200,7 +200,7 @@ public class BlockState implements BlockStateHolder, Pattern { if (property != null) { int index = property.getIndexFor(charSequence); if (index == -1) { - throw SuggestInputParseException.of(charSequence.toString(), property.getValues()); + throw SuggestInputParseException.of(charSequence.toString(), (List) property.getValues()); } stateId = property.modifyIndex(stateId, index); } else { @@ -288,7 +288,7 @@ public class BlockState implements BlockStateHolder, Pattern { public BlockState with(final Property property, final V value) { try { BlockType type = getBlockType(); - int newState = ((AbstractProperty) property).modify(this.getInternalId(), value); + int newState = ((AbstractProperty) property).modify(this.getInternalId(), value); return newState != this.getInternalId() ? type.withStateId(newState) : this; } catch (ClassCastException e) { throw new IllegalArgumentException("Property not found: " + property); @@ -298,7 +298,7 @@ public class BlockState implements BlockStateHolder, Pattern { @Override public V getState(final Property property) { try { - AbstractProperty ap = (AbstractProperty) property; + AbstractProperty ap = (AbstractProperty) property; return (V) ap.getValue(this.getInternalId()); } catch (ClassCastException e) { throw new IllegalArgumentException("Property not found: " + property); @@ -309,7 +309,11 @@ public class BlockState implements BlockStateHolder, Pattern { public BlockState with(final PropertyKey property, final V value) { try { BlockType type = getBlockType(); - int newState = ((AbstractProperty) type.getProperty(property)).modify(this.getInternalId(), value); + AbstractProperty abstractProperty = ((AbstractProperty) type.getProperty(property)); + if (abstractProperty == null) { + return this; + } + int newState = abstractProperty.modify(this.getInternalId(), value); return newState != this.getInternalId() ? type.withStateId(newState) : this; } catch (ClassCastException e) { throw new IllegalArgumentException("Property not found: " + property); @@ -340,7 +344,7 @@ public class BlockState implements BlockStateHolder, Pattern { //FAWE end BlockType type = this.getBlockType(); // Lazily initialize the map - Map map = Maps.asMap(type.getPropertiesSet(), (Function) this::getState); + Map, Object> map = Maps.asMap(type.getPropertiesSet(), (Function, Object>) this::getState); //noinspection RedundantCast - This is required for compilation, etc. return Collections.unmodifiableMap((Map, Object>) map); //FAWE end diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java index 9a20f9386..ee4ab12ad 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java @@ -52,8 +52,8 @@ public class FuzzyBlockState extends BlockState { } //FAWE end - private FuzzyBlockState(BlockState state, Map, Object> values) { - //FAWE start - use internal ids + //FAWE start - use internal ids, public constructor + public FuzzyBlockState(BlockState state, Map, Object> values) { super(state.getBlockType(), state.getInternalId(), state.getOrdinal()); if (values == null || values.isEmpty()) { props = Collections.emptyMap(); diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index e7264a8f8..2c48f95cd 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -148,6 +148,8 @@ "fawe.error.clipboard.invalid.info": "File: {0} (len: {1})", "fawe.error.clipboard.load.failure": "Could not load clipboard. Possible that the clipboard is still being written to from another server?!", "fawe.error.clipboard.on.disk.version.mismatch": "Clipboard version mismatch. Please delete your clipboards folder and restart the server.", + "fawe.error.limit.disallowed-block": "Your limit disallows use of block '{0}'", + "fawe.error.limit.disallowed-property": "Your limit disallows use of property '{0}'", "fawe.cancel.count": "Cancelled {0} edits.", "fawe.cancel.reason.confirm": "Use //confirm to execute {0}",