From 8da984d9f9ca57c2ffc562345eb38bc00cbfbbad Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Thu, 27 Dec 2018 15:19:58 +1000 Subject: [PATCH 1/3] Fuzzier fuzzies --- .../factory/parser/DefaultBlockParser.java | 7 +- .../worldedit/world/block/BaseBlock.java | 4 +- .../worldedit/world/block/BlockState.java | 36 +---- .../world/block/FuzzyBlockState.java | 131 ++++++++++++++++++ 4 files changed, 143 insertions(+), 35 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java 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..a67235d0c 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,8 @@ 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; - } + BlockState result = states.get(property, value); + return result == null ? this : result; } @Override @@ -162,10 +143,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 +184,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 +204,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 +225,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/FuzzyBlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java new file mode 100644 index 000000000..a2082270e --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/FuzzyBlockState.java @@ -0,0 +1,131 @@ +/* + * 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); + } + + @SuppressWarnings("unchecked") + @Override + public BlockState toImmutableState() { + BlockState state = getBlockType().getDefaultState(); + for (Map.Entry, Object> entry : getStates().entrySet()) { + state = state.with((Property) entry.getKey(), 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 BlockState internalState; + 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); + internalState = type.getDefaultState(); + return this; + } + + /** + * The type of the Fuzzy BlockState + * + * @param state The state + * @return The builder, for chaining + */ + public Builder type(BlockState state) { + checkNotNull(state); + internalState = state; + 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(internalState, "The type must be set before the properties!"); + values.put(property, value); + return this; + } + + /** + * Builds a FuzzyBlockState from this builder. + * + * @return The fuzzy BlockState + */ + public FuzzyBlockState build() { + checkNotNull(internalState); + FuzzyBlockState blockState = new FuzzyBlockState(internalState.getBlockType()); + for (Map.Entry, Object> entry : values.entrySet()) { + blockState.setState(entry.getKey(), entry.getValue()); + } + return blockState; + } + + /** + * Resets the builder. + * + * @return The builder, for chaining + */ + public Builder reset() { + this.internalState = null; + this.values.clear(); + return this; + } + } +} From b544782f3b653b3998d52c02cd12695ab5c03882 Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Thu, 27 Dec 2018 15:33:19 +1000 Subject: [PATCH 2/3] Make the base fuzzy cached per block type --- .../com/sk89q/worldedit/world/block/BlockType.java | 5 +++++ .../worldedit/world/block/FuzzyBlockState.java | 14 +++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) 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 index a2082270e..708c50f3c 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 @@ -37,11 +37,16 @@ public class FuzzyBlockState extends BlockState { super(blockType); } - @SuppressWarnings("unchecked") - @Override - public BlockState toImmutableState() { + /** + * 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()) { + //noinspection unchecked state = state.with((Property) entry.getKey(), entry.getValue()); } return getBlockType().getDefaultState(); @@ -110,6 +115,9 @@ public class FuzzyBlockState extends BlockState { */ public FuzzyBlockState build() { checkNotNull(internalState); + if (values.isEmpty()) { + return internalState.getBlockType().getFuzzyMatcher(); + } FuzzyBlockState blockState = new FuzzyBlockState(internalState.getBlockType()); for (Map.Entry, Object> entry : values.entrySet()) { blockState.setState(entry.getKey(), entry.getValue()); From 54b6e571866a07f037b7f427c456777c2e0c373b Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Fri, 28 Dec 2018 15:05:05 +1000 Subject: [PATCH 3/3] Few minor improvements to the fuzzy system. --- .../worldedit/world/block/BlockState.java | 3 +- .../world/block/FuzzyBlockState.java | 33 +++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) 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 a67235d0c..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 @@ -129,8 +129,7 @@ public class BlockState implements BlockStateHolder { @Override public BlockState with(final Property property, final V value) { - BlockState result = states.get(property, value); - return result == null ? this : result; + return states.row(property).getOrDefault(value, this); } @Override 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 708c50f3c..b9c557fd4 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 @@ -37,6 +37,13 @@ public class FuzzyBlockState extends BlockState { 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. @@ -46,8 +53,9 @@ public class FuzzyBlockState extends BlockState { public BlockState getFullState() { BlockState state = getBlockType().getDefaultState(); for (Map.Entry, Object> entry : getStates().entrySet()) { - //noinspection unchecked - state = state.with((Property) entry.getKey(), entry.getValue()); + @SuppressWarnings("unchecked") + Property objKey = (Property) entry.getKey(); + state = state.with(objKey, entry.getValue()); } return getBlockType().getDefaultState(); } @@ -65,7 +73,7 @@ public class FuzzyBlockState extends BlockState { * Builder for FuzzyBlockState */ public static class Builder { - private BlockState internalState; + private BlockType type; private Map, Object> values = new HashMap<>(); /** @@ -76,7 +84,7 @@ public class FuzzyBlockState extends BlockState { */ public Builder type(BlockType type) { checkNotNull(type); - internalState = type.getDefaultState(); + this.type = type; return this; } @@ -88,7 +96,7 @@ public class FuzzyBlockState extends BlockState { */ public Builder type(BlockState state) { checkNotNull(state); - internalState = state; + this.type = state.getBlockType(); return this; } @@ -103,7 +111,8 @@ public class FuzzyBlockState extends BlockState { public Builder withProperty(Property property, V value) { checkNotNull(property); checkNotNull(value); - checkNotNull(internalState, "The type must be set before the properties!"); + 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; } @@ -114,15 +123,11 @@ public class FuzzyBlockState extends BlockState { * @return The fuzzy BlockState */ public FuzzyBlockState build() { - checkNotNull(internalState); + checkNotNull(type); if (values.isEmpty()) { - return internalState.getBlockType().getFuzzyMatcher(); + return type.getFuzzyMatcher(); } - FuzzyBlockState blockState = new FuzzyBlockState(internalState.getBlockType()); - for (Map.Entry, Object> entry : values.entrySet()) { - blockState.setState(entry.getKey(), entry.getValue()); - } - return blockState; + return new FuzzyBlockState(type, values); } /** @@ -131,7 +136,7 @@ public class FuzzyBlockState extends BlockState { * @return The builder, for chaining */ public Builder reset() { - this.internalState = null; + this.type = null; this.values.clear(); return this; }