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 e2093ff13..1088c3695 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 @@ -43,6 +43,7 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.FuzzyBlockState; import com.sk89q.worldedit.world.registry.LegacyMapper; import java.util.HashMap; @@ -268,12 +269,14 @@ public class DefaultBlockParser extends InputParser { // No wildcards allowed => eliminate them. (Start with default state) state = blockType.getDefaultState(); } else { - state = blockType.getDefaultState().toFuzzy(); + FuzzyBlockState.Builder fuzzyBuilder = FuzzyBlockState.builder(); + fuzzyBuilder.type(blockType); for (Map.Entry, Object> blockState : blockStates.entrySet()) { @SuppressWarnings("unchecked") Property objProp = (Property) blockState.getKey(); - state = state.with(objProp, blockState.getValue()); + fuzzyBuilder.withProperty(objProp, blockState.getValue()); } + state = fuzzyBuilder.build(); } state = applyProperties(state, stateProperties); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java index 1810f9178..bc913f2d7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BaseBlock.java @@ -142,7 +142,7 @@ public class BaseBlock implements BlockStateHolder, TileEntityBlock { final BaseBlock otherBlock = (BaseBlock) o; - return this.toImmutableState().equalsFuzzy(otherBlock.toImmutableState()) && Objects.equals(getNbtData(), otherBlock.getNbtData()); + return this.blockState.equalsFuzzy(otherBlock.blockState) && Objects.equals(getNbtData(), otherBlock.getNbtData()); } /** @@ -153,7 +153,7 @@ public class BaseBlock implements BlockStateHolder, TileEntityBlock { */ @Override public boolean equalsFuzzy(BlockStateHolder o) { - return this.toImmutableState().equalsFuzzy(o); + return this.blockState.equalsFuzzy(o); } @Override 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 565b208a0..d441e98d7 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 @@ -30,7 +30,6 @@ import com.sk89q.worldedit.registry.state.Property; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -46,30 +45,16 @@ public class BlockState implements BlockStateHolder { private final BlockType blockType; private final Map, Object> values; - private final boolean fuzzy; private BaseBlock emptyBaseBlock; // Neighbouring state table. private Table, Object, BlockState> states; - private BlockState(BlockType blockType) { + BlockState(BlockType blockType) { this.blockType = blockType; this.values = new LinkedHashMap<>(); this.emptyBaseBlock = new BaseBlock(this); - this.fuzzy = false; - } - - /** - * Creates a fuzzy BlockState. This can be used for partial matching. - * - * @param blockType The block type - * @param values The block state values - */ - private BlockState(BlockType blockType, Map, Object> values) { - this.blockType = blockType; - this.values = values; - this.fuzzy = true; } static Map, Object>, BlockState> generateStateMap(BlockType blockType) { @@ -144,12 +129,7 @@ public class BlockState implements BlockStateHolder { @Override public BlockState with(final Property property, final V value) { - if (fuzzy) { - return setState(property, value); - } else { - BlockState result = states.get(property, value); - return result == null ? this : result; - } + return states.row(property).getOrDefault(value, this); } @Override @@ -162,10 +142,6 @@ public class BlockState implements BlockStateHolder { return Collections.unmodifiableMap(this.values); } - public BlockState toFuzzy() { - return new BlockState(this.getBlockType(), new HashMap<>()); - } - @Override public boolean equalsFuzzy(BlockStateHolder o) { if (this == o) { @@ -207,9 +183,6 @@ public class BlockState implements BlockStateHolder { @Override public BaseBlock toBaseBlock() { - if (this.fuzzy) { - throw new IllegalArgumentException("Can't create a BaseBlock from a fuzzy BlockState!"); - } return this.emptyBaseBlock; } @@ -230,7 +203,7 @@ public class BlockState implements BlockStateHolder { * @param value The value * @return The blockstate, for chaining */ - private BlockState setState(final Property property, final Object value) { + BlockState setState(final Property property, final Object value) { this.values.put(property, value); return this; } @@ -251,6 +224,6 @@ public class BlockState implements BlockStateHolder { @Override public int hashCode() { - return Objects.hash(blockType, values, fuzzy); + return Objects.hash(blockType, values); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java index 13840aae6..633f00a3f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockType.java @@ -48,6 +48,7 @@ public class BlockType { private final String id; private final Function values; private final AtomicReference defaultState = new AtomicReference<>(); + private final AtomicReference emptyFuzzy = new AtomicReference<>(); private final AtomicReference>> properties = new AtomicReference<>(); private final AtomicReference blockMaterial = new AtomicReference<>(); private final AtomicReference, Object>, BlockState>> blockStatesMap = new AtomicReference<>(); @@ -156,6 +157,10 @@ public class BlockType { }); } + public FuzzyBlockState getFuzzyMatcher() { + return updateField(emptyFuzzy, () -> new FuzzyBlockState(this)); + } + /** * Gets a list of all possible states for this BlockType. * 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 new file mode 100644 index 000000000..b9c557fd4 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java @@ -0,0 +1,144 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.block; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.sk89q.worldedit.registry.state.Property; + +import java.util.HashMap; +import java.util.Map; + +/** + * A Fuzzy BlockState. Used for partial matching. + * + * Immutable, construct with {@link FuzzyBlockState.Builder}. + */ +public class FuzzyBlockState extends BlockState { + + FuzzyBlockState(BlockType blockType) { + super(blockType); + } + + private FuzzyBlockState(BlockType blockType, Map, Object> values) { + this(blockType); + for (Map.Entry, Object> entry : values.entrySet()) { + setState(entry.getKey(), entry.getValue()); + } + } + + /** + * Gets a full BlockState from this fuzzy one, filling in + * properties with default values where necessary. + * + * @return The full BlockState + */ + public BlockState getFullState() { + BlockState state = getBlockType().getDefaultState(); + for (Map.Entry, Object> entry : getStates().entrySet()) { + @SuppressWarnings("unchecked") + Property objKey = (Property) entry.getKey(); + state = state.with(objKey, entry.getValue()); + } + return getBlockType().getDefaultState(); + } + + /** + * Gets an instance of a builder. + * + * @return The builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for FuzzyBlockState + */ + public static class Builder { + private BlockType type; + private Map, Object> values = new HashMap<>(); + + /** + * The type of the Fuzzy BlockState + * + * @param type The type + * @return The builder, for chaining + */ + public Builder type(BlockType type) { + checkNotNull(type); + this.type = type; + return this; + } + + /** + * The type of the Fuzzy BlockState + * + * @param state The state + * @return The builder, for chaining + */ + public Builder type(BlockState state) { + checkNotNull(state); + this.type = state.getBlockType(); + return this; + } + + /** + * Adds a property to the fuzzy BlockState + * + * @param property The property + * @param value The value + * @param The property type + * @return The builder, for chaining + */ + public Builder withProperty(Property property, V value) { + checkNotNull(property); + checkNotNull(value); + checkNotNull(type, "The type must be set before the properties!"); + type.getProperty(property.getName()); // Verify the property is valid for this type + values.put(property, value); + return this; + } + + /** + * Builds a FuzzyBlockState from this builder. + * + * @return The fuzzy BlockState + */ + public FuzzyBlockState build() { + checkNotNull(type); + if (values.isEmpty()) { + return type.getFuzzyMatcher(); + } + return new FuzzyBlockState(type, values); + } + + /** + * Resets the builder. + * + * @return The builder, for chaining + */ + public Builder reset() { + this.type = null; + this.values.clear(); + return this; + } + } +}