From fd00635533f63897f16c1fa04f3d3e2d2c7b0a4c Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 16 Jun 2022 15:24:48 +0100 Subject: [PATCH] Fixes to BlockMask and "char" masks (#1787) * If a char mask is successfully created from the full input, return it * Don't double-up adding a block to a BlockMaskBuilder (if adding by regex is successful) - InputParseException is thrown if unsuccessful * Fix optimisation of BlockMask for negation of a single block type - Fixes #1755 * Allow early returning of an optimized MaskIntersection to avoid unnecessary work * Actually allow underscore in isAlphanumericUnd - Fixes #1626 * Replace a few more hard-coded air internal IDs * Don't fail silently if BlockMaskBuilder#addRegex doesn't work when testing all block types * Remove unused import --- .../factory/parser/mask/RichMaskParser.java | 11 +++-- .../core/function/mask/BlockMaskBuilder.java | 7 ++- .../core/util/StringMan.java | 2 +- .../worldedit/function/mask/BlockMask.java | 48 ++++++++++++------- .../function/mask/MaskIntersection.java | 8 ++++ 5 files changed, 53 insertions(+), 23 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java index 8d92ba90a..31c8f3d3e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java @@ -127,6 +127,9 @@ public class RichMaskParser extends FaweParser { case '%', '$', '<', '>', '!' -> { input = input.substring(input.indexOf(char0) + 1); mask = parseFromInput(char0 + "[" + input + "]", context); + if (mask != null) { + return mask; + } } case '#' -> { if (!(input.charAt(1) == '#')) { @@ -145,6 +148,10 @@ public class RichMaskParser extends FaweParser { try { builder.addRegex(full); } catch (InputParseException ignored) { + context.setPreferringWildcard(false); + context.setRestricted(false); + BaseBlock block = worldEdit.getBlockFactory().parseFromInput(full, context); + builder.add(block); } catch (PatternSyntaxException e) { throw new SuggestInputParseException( new NoMatchException(Caption.of("fawe.error.parse.unknown-mask", full, @@ -166,10 +173,6 @@ public class RichMaskParser extends FaweParser { } ); } - context.setPreferringWildcard(false); - context.setRestricted(false); - BaseBlock block = worldEdit.getBlockFactory().parseFromInput(full, context); - builder.add(block); mask = builder.build(extent); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/BlockMaskBuilder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/BlockMaskBuilder.java index f16ebe766..0b89e0ada 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/BlockMaskBuilder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/mask/BlockMaskBuilder.java @@ -123,7 +123,7 @@ public class BlockMaskBuilder { if (input.charAt(input.length() - 1) == ']') { int propStart = StringMan.findMatchingBracket(input, input.length() - 1); if (propStart == -1) { - return; + throw new InputParseException(Caption.of("fawe.error.no-block-found", TextComponent.of(input))); } MutableCharSequence charSequence = MutableCharSequence.getTemporal(); @@ -250,11 +250,16 @@ public class BlockMaskBuilder { if (StringMan.isAlphanumericUnd(input)) { add(BlockTypes.parse(input)); } else { + boolean success = false; for (BlockType myType : BlockTypesCache.values) { if (myType.getId().matches(input)) { add(myType); + success = true; } } + if (!success) { + throw new InputParseException(Caption.of("fawe.error.no-block-found", TextComponent.of(input))); + } } } }); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/StringMan.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/StringMan.java index e41a19662..595bbdcff 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/StringMan.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/StringMan.java @@ -295,7 +295,7 @@ public class StringMan { public static boolean isAlphanumericUnd(CharSequence str) { for (int i = 0; i < str.length(); i++) { final char c = str.charAt(i); - if (c < 0x30 || c >= 0x3a && c <= 0x40 || c > 0x5a && c <= 0x60 || c > 0x7a) { + if (c < 0x30 || c >= 0x3a && c <= 0x40 || c > 0x5a && c <= 0x60 && c != 0x5f || c > 0x7a) { return false; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java index 7e5a4abf1..9f191a4cc 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMask.java @@ -210,15 +210,14 @@ public class BlockMask extends ABlockMask { @Override public boolean replacesAir() { - return ordinals[1] - || ordinals[2] - || ordinals[3]; + return ordinals[BlockTypesCache.ReservedIDs.AIR] + || ordinals[BlockTypesCache.ReservedIDs.CAVE_AIR] + || ordinals[BlockTypesCache.ReservedIDs.VOID_AIR]; } @Override public Mask tryCombine(Mask mask) { - if (mask instanceof ABlockMask) { - ABlockMask other = (ABlockMask) mask; + if (mask instanceof ABlockMask other) { boolean modified = false; boolean hasAny = false; for (int i = 0; i < ordinals.length; i++) { @@ -241,8 +240,7 @@ public class BlockMask extends ABlockMask { @Override public Mask tryOr(Mask mask) { - if (mask instanceof ABlockMask) { - ABlockMask other = (ABlockMask) mask; + if (mask instanceof ABlockMask other) { boolean modified = false; for (int i = 0; i < ordinals.length; i++) { if (!ordinals[i]) { @@ -273,23 +271,27 @@ public class BlockMask extends ABlockMask { int setTypes = 0; BlockType setType = null; + BlockType unsetType = null; int totalTypes = 0; for (BlockType type : BlockTypesCache.values) { if (type != null) { totalTypes++; boolean hasAll = true; + boolean hasAny = false; List all = type.getAllStates(); for (BlockState state : all) { totalStates++; - hasAll &= test(state); + boolean result = test(state); + hasAll &= result; + hasAny |= result; } if (hasAll) { setTypes++; setType = type; setStates += all.size(); setState = type.getDefaultState(); - } else { + } else if (hasAny) { for (BlockState state : all) { if (test(state)) { setStates++; @@ -298,6 +300,10 @@ public class BlockMask extends ABlockMask { unsetState = state; } } + } else if (all.size() == 1) { + unsetState = all.get(0); + } else { + unsetType = type; } } } @@ -313,7 +319,11 @@ public class BlockMask extends ABlockMask { } if (setStates == totalStates - 1) { - return new InverseSingleBlockStateMask(getExtent(), unsetState); + if (unsetState != null) { + return new InverseSingleBlockStateMask(getExtent(), unsetState); + } else { + throw new IllegalArgumentException("unsetState cannot be null when passed to InverseSingleBlockStateMask"); + } } if (setTypes == 1) { @@ -321,7 +331,11 @@ public class BlockMask extends ABlockMask { } if (setTypes == totalTypes - 1) { - throw new IllegalArgumentException("unsetType cannot be null when passed to InverseSingleBlockTypeMask"); + if (unsetType != null) { + return new InverseSingleBlockTypeMask(getExtent(), unsetType); + } else { + throw new IllegalArgumentException("unsetType cannot be null when passed to InverseSingleBlockTypeMask"); + } } return null; @@ -329,15 +343,15 @@ public class BlockMask extends ABlockMask { @Override public Mask inverse() { - boolean[] cloned = ordinals.clone(); + boolean[] cloned = new boolean[ordinals.length]; for (int i = 0; i < cloned.length; i++) { - cloned[i] = !cloned[i]; + cloned[i] = !ordinals[i]; } if (replacesAir()) { - cloned[1] = false; - cloned[2] = false; - cloned[3] = false; - cloned[0] = false; + cloned[BlockTypesCache.ReservedIDs.__RESERVED__] = false; + cloned[BlockTypesCache.ReservedIDs.AIR] = false; + cloned[BlockTypesCache.ReservedIDs.CAVE_AIR] = false; + cloned[BlockTypesCache.ReservedIDs.VOID_AIR] = false; } return new BlockMask(getExtent(), cloned); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java index fd4bb1730..4b9b34594 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/MaskIntersection.java @@ -163,6 +163,14 @@ public class MaskIntersection extends AbstractMask { while (combineMasks(pairingFunction(), failedCombines)) { changed = true; } + if (masks.isEmpty()) { + formArray(); // This MaskIntersection instance may be reused for whatever reason, so ensure the array is correct. + return Masks.alwaysTrue(); + } + if (masks.size() == 1) { + formArray(); // This MaskIntersection instance may be reused for whatever reason, so ensure the array is correct. + return masks.iterator().next(); + } // Optimize / combine do { changed |= optimizeMasks(optimized);