From 18df87a4e8b2f3da815ecd3ce0e4a4ad36240f37 Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 20 Jul 2023 16:57:48 +0100 Subject: [PATCH] feat: implement a new "type swap" pattern (#2346) --- .../parser/pattern/TypeSwapPatternParser.java | 54 +++++++++ .../function/pattern/TypeSwapPattern.java | 103 ++++++++++++++++++ .../extension/factory/PatternFactory.java | 2 + 3 files changed, 159 insertions(+) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java new file mode 100644 index 000000000..5bf33d3a0 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/TypeSwapPatternParser.java @@ -0,0 +1,54 @@ +package com.fastasyncworldedit.core.extension.factory.parser.pattern; + +import com.fastasyncworldedit.core.configuration.Caption; +import com.fastasyncworldedit.core.extension.factory.parser.RichParser; +import com.fastasyncworldedit.core.function.pattern.TypeSwapPattern; +import com.fastasyncworldedit.core.util.Permission; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; +import com.sk89q.worldedit.function.pattern.Pattern; +import com.sk89q.worldedit.util.formatting.text.TextComponent; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.stream.Stream; + +public class TypeSwapPatternParser extends RichParser { + + private static final List SUGGESTIONS = List.of("oak", "spruce", "stone", "sandstone"); + + /** + * Create a new rich parser with a defined prefix for the result, e.g. {@code #simplex}. + * + * @param worldEdit the worldedit instance. + */ + public TypeSwapPatternParser(WorldEdit worldEdit) { + super(worldEdit, "#typeswap", "#ts", "#swaptype"); + } + + @Override + public Stream getSuggestions(String argumentInput, int index) { + if (index > 2) { + return Stream.empty(); + } + return SUGGESTIONS.stream(); + } + + @Override + public Pattern parseFromInput(@Nonnull String[] input, ParserContext context) throws InputParseException { + if (input.length != 2) { + throw new InputParseException(Caption.of( + "fawe.error.command.syntax", + TextComponent.of(getPrefix() + "[input][output] (e.g. " + getPrefix() + "[spruce][oak])") + )); + } + return new TypeSwapPattern( + context.requireExtent(), + input[0], + input[1], + Permission.hasPermission(context.requireActor(), "fawe.pattern.typeswap.regex") + ); + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java new file mode 100644 index 000000000..89876f658 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/TypeSwapPattern.java @@ -0,0 +1,103 @@ +package com.fastasyncworldedit.core.function.pattern; + +import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; +import com.fastasyncworldedit.core.util.StringMan; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; +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 java.util.regex.Pattern; + +/** + * Pattern that replaces blocks based on their ID, matching for an "input" and replacing with an "output" string. The "input" + * string may be regex. Keeps as much of the block state as possible, excluding NBT data. + * + * @since TODO + */ +public class TypeSwapPattern extends AbstractExtentPattern { + + private static final Pattern SPLITTER = Pattern.compile("[|,]"); + + private final String inputString; + private final String outputString; + private final String[] inputs; + private Pattern inputPattern = null; + + /** + * Create a new instance + * + * @param extent extent to use + * @param inputString string to replace. May be regex. + * @param outputString string to replace with + * @param allowRegex if regex should be allowed for input string matching + * @since TODO + */ + public TypeSwapPattern(Extent extent, String inputString, String outputString, boolean allowRegex) { + super(extent); + this.inputString = inputString; + this.outputString = outputString; + if (!StringMan.isAlphanumericUnd(inputString)) { + if (allowRegex) { + this.inputPattern = Pattern.compile(inputString.replace(",", "|")); + inputs = null; + } else { + inputs = SPLITTER.split(inputString); + } + } else { + inputs = null; + } + } + + @Override + public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException { + BlockState existing = get.getBlock(extent); + BlockState newBlock = getNewBlock(existing); + if (newBlock == null) { + return false; + } + return set.setBlock(extent, newBlock); + } + + @Override + public void applyBlock(final FilterBlock block) { + BlockState existing = block.getBlock(); + BlockState newState = getNewBlock(existing); + if (newState != null) { + block.setBlock(newState); + } + } + + @Override + public BaseBlock applyBlock(final BlockVector3 position) { + BaseBlock existing = position.getFullBlock(getExtent()); + BlockState newState = getNewBlock(existing.toBlockState()); + return newState == null ? existing : newState.toBaseBlock(); + } + + private BlockState getNewBlock(BlockState existing) { + String oldId = existing.getBlockType().getId(); + String newId = oldId; + if (inputPattern != null) { + newId = inputPattern.matcher(oldId).replaceAll(outputString); + } else if (inputs != null && inputs.length > 0) { + for (String input : inputs) { + newId = newId.replace(input, outputString); + } + } else { + newId = oldId.replace(inputString, outputString); + } + if (newId.equals(oldId)) { + return null; + } + BlockType newType = BlockTypes.get(newId); + if (newType == null) { + return null; + } + return newType.getDefaultState().withProperties(existing); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java index 420e58d2a..a76759833 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java @@ -42,6 +42,7 @@ import com.fastasyncworldedit.core.extension.factory.parser.pattern.OffsetPatter import com.fastasyncworldedit.core.extension.factory.parser.pattern.PerlinPatternParser; import com.fastasyncworldedit.core.extension.factory.parser.pattern.RandomFullClipboardPatternParser; import com.fastasyncworldedit.core.extension.factory.parser.pattern.RandomOffsetPatternParser; +import com.fastasyncworldedit.core.extension.factory.parser.pattern.TypeSwapPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser; import com.fastasyncworldedit.core.extension.factory.parser.pattern.RelativePatternParser; import com.fastasyncworldedit.core.extension.factory.parser.pattern.RichPatternParser; @@ -133,6 +134,7 @@ public final class PatternFactory extends AbstractFactory { register(new SimplexPatternParser(worldEdit)); register(new SolidRandomOffsetPatternParser(worldEdit)); register(new SurfaceRandomOffsetPatternParser(worldEdit)); + register(new TypeSwapPatternParser(worldEdit)); register(new VoronoiPatternParser(worldEdit)); //FAWE end }