diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java index 9357e693f..14977e160 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/FaweBukkit.java @@ -18,7 +18,6 @@ import com.boydti.fawe.bukkit.regions.TownyFeature; import com.boydti.fawe.bukkit.regions.Worldguard; import com.boydti.fawe.bukkit.util.BukkitTaskMan; import com.boydti.fawe.bukkit.util.ItemUtil; -import com.boydti.fawe.bukkit.util.VaultUtil; import com.boydti.fawe.bukkit.util.image.BukkitImageViewer; import com.boydti.fawe.config.Settings; import com.boydti.fawe.regions.FaweMaskManager; diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java index 07b06075c..a9aef70f4 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15_2/BukkitGetBlocks_1_15_2_Copy.java @@ -106,7 +106,7 @@ public class BukkitGetBlocks_1_15_2_Copy extends BukkitGetBlocks_1_15_2 { } protected void storeSection(int layer) { - update(layer, blocks[layer]); + blocks[layer] = update(layer, null).clone(); } @Override diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java index 42364ed89..9b9dc3697 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_1/BukkitGetBlocks_1_16_1_Copy.java @@ -107,7 +107,7 @@ public class BukkitGetBlocks_1_16_1_Copy extends BukkitGetBlocks_1_16_1 { } protected void storeSection(int layer) { - update(layer, blocks[layer]); + blocks[layer] = update(layer, null).clone(); } @Override diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java index 6981d63a7..ac80fe3f1 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_16_2/BukkitGetBlocks_1_16_2_Copy.java @@ -107,7 +107,7 @@ public class BukkitGetBlocks_1_16_2_Copy extends BukkitGetBlocks_1_16_2 { } protected void storeSection(int layer) { - update(layer, blocks[layer]); + blocks[layer] = update(layer, null).clone(); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java index 834fca3ae..ffd2b4466 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharBlocks.java @@ -17,7 +17,7 @@ public abstract class CharBlocks implements IBlocks { }; public static final Section EMPTY = new Section() { @Override - public synchronized final char[] get(CharBlocks blocks, int layer) { + public final char[] get(CharBlocks blocks, int layer) { char[] arr = blocks.blocks[layer]; if (arr == null) { arr = blocks.blocks[layer] = blocks.update(layer, null); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java index ee9b3b7c6..88d9a923d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/AbstractChangeSet.java @@ -303,8 +303,8 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { MainUtil.setPosition(nbt, x, y, z); addTileCreate(nbt); } - int combinedFrom = from.getInternalId(); - int combinedTo = to.getInternalId(); + int combinedFrom = from.getOrdinal(); + int combinedTo = to.getOrdinal(); add(x, y, z, combinedFrom, combinedTo); } catch (Exception e) { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/Linear2DBlockPattern.java b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/Linear2DBlockPattern.java index 5442510f9..0bed3a376 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/Linear2DBlockPattern.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/Linear2DBlockPattern.java @@ -10,14 +10,18 @@ import com.sk89q.worldedit.world.block.BaseBlock; public class Linear2DBlockPattern extends AbstractPattern { private final Pattern[] patternsArray; + private final int xScale; + private final int zScale; - public Linear2DBlockPattern(Pattern[] patterns) { + public Linear2DBlockPattern(Pattern[] patterns, int xScale, int zScale) { this.patternsArray = patterns; + this.xScale = xScale; + this.zScale = zScale; } @Override public BaseBlock apply(BlockVector3 position) { - int index = (position.getBlockX() + position.getBlockZ()) % patternsArray.length; + int index = (position.getBlockX() / this.xScale + position.getBlockZ() / this.zScale) % patternsArray.length; if (index < 0) { index += patternsArray.length; } @@ -26,7 +30,7 @@ public class Linear2DBlockPattern extends AbstractPattern { @Override public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException { - int index = (get.getBlockX() + get.getBlockZ()) % patternsArray.length; + int index = (get.getBlockX() / this.xScale + get.getBlockZ() / this.zScale) % patternsArray.length; if (index < 0) { index += patternsArray.length; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/Linear3DBlockPattern.java b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/Linear3DBlockPattern.java index cbf7d0002..9f70debe9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/Linear3DBlockPattern.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/Linear3DBlockPattern.java @@ -10,14 +10,21 @@ import com.sk89q.worldedit.world.block.BaseBlock; public class Linear3DBlockPattern extends AbstractPattern { private final Pattern[] patternsArray; + private final int xScale; + private final int yScale; + private final int zScale; - public Linear3DBlockPattern(Pattern[] patterns) { + public Linear3DBlockPattern(Pattern[] patterns, int xScale, int yScale, int zScale) { this.patternsArray = patterns; + this.xScale = xScale; + this.yScale = yScale; + this.zScale = zScale; } @Override public BaseBlock apply(BlockVector3 position) { - int index = (position.getBlockX() + position.getBlockY() + position.getBlockZ()) % patternsArray.length; + int index = (position.getBlockX() / this.xScale + + position.getBlockY() / this.yScale + position.getBlockZ() / this.zScale) % patternsArray.length; if (index < 0) { index += patternsArray.length; } @@ -26,7 +33,8 @@ public class Linear3DBlockPattern extends AbstractPattern { @Override public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException { - int index = (get.getBlockX() + get.getBlockY() + get.getBlockZ()) % patternsArray.length; + int index = (get.getBlockX() / this.xScale + + get.getBlockY() / this.yScale + get.getBlockZ() / this.zScale) % patternsArray.length; if (index < 0) { index += patternsArray.length; } 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 4b8345798..5094f4827 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -92,9 +92,12 @@ import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Random; import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -116,7 +119,7 @@ public class LocalSession implements TextureHolder { // Session related private transient RegionSelector selector = new CuboidRegionSelector(); private transient boolean placeAtPos1 = false; - private transient List history = Collections.synchronizedList(new LinkedList() { + private final transient List history = Collections.synchronizedList(new LinkedList() { @Override public Object get(int index) { Object value = super.get(index); @@ -135,6 +138,7 @@ public class LocalSession implements TextureHolder { private transient volatile Integer historyNegativeIndex; private transient ClipboardHolder clipboard; private transient final Object clipboardLock = new Object(); + private transient final Lock historyWriteLock = new ReentrantLock(true); private transient boolean superPickaxe = false; private transient BlockTool pickaxeMode = new SinglePickaxe(); private final transient Int2ObjectOpenHashMap tools = new Int2ObjectOpenHashMap<>(0); @@ -439,109 +443,113 @@ public class LocalSession implements TextureHolder { return null; } - public synchronized void remember(Identifiable player, World world, ChangeSet changeSet, FaweLimit limit) { - if (Settings.IMP.HISTORY.USE_DISK) { - LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE; - } - if (changeSet.size() == 0) { - return; - } - loadSessionHistoryFromDisk(player.getUniqueId(), world); - if (changeSet instanceof ChangeSet) { - ListIterator iter = history.listIterator(); - int i = 0; - int cutoffIndex = history.size() - getHistoryNegativeIndex(); - while (iter.hasNext()) { - Object item = iter.next(); - if (++i > cutoffIndex) { - ChangeSet oldChangeSet; - if (item instanceof ChangeSet) { - oldChangeSet = (ChangeSet) item; - } else { - oldChangeSet = getChangeSet(item); + public void remember(Identifiable player, World world, ChangeSet changeSet, FaweLimit limit) { + historyWriteLock.lock(); + try { + if (Settings.IMP.HISTORY.USE_DISK) { + LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE; + } + if (changeSet.size() == 0) { + return; + } + loadSessionHistoryFromDisk(player.getUniqueId(), world); + if (changeSet instanceof ChangeSet) { + ListIterator iter = history.listIterator(); + int i = 0; + int cutoffIndex = history.size() - getHistoryNegativeIndex(); + while (iter.hasNext()) { + Object item = iter.next(); + if (++i > cutoffIndex) { + ChangeSet oldChangeSet; + if (item instanceof ChangeSet) { + oldChangeSet = (ChangeSet) item; + } else { + oldChangeSet = getChangeSet(item); + } + historySize -= MainUtil.getSize(oldChangeSet); + iter.remove(); } - historySize -= MainUtil.getSize(oldChangeSet); - iter.remove(); } } + historySize += MainUtil.getSize(changeSet); + history.add(changeSet); + if (getHistoryNegativeIndex() != 0) { + setDirty(); + historyNegativeIndex = 0; + } + if (limit != null) { + int limitMb = limit.MAX_HISTORY; + while (((!Settings.IMP.HISTORY.USE_DISK && history.size() > MAX_HISTORY_SIZE) || (historySize >> 20) > limitMb) && history.size() > 1) { + ChangeSet item = (ChangeSet) history.remove(0); + item.delete(); + long size = MainUtil.getSize(item); + historySize -= size; + } + } + } finally { + historyWriteLock.unlock(); } - historySize += MainUtil.getSize(changeSet); - history.add(changeSet); - if (getHistoryNegativeIndex() != 0) { - setDirty(); - historyNegativeIndex = 0; - } - if (limit != null) { - int limitMb = limit.MAX_HISTORY; + } + + public void remember(EditSession editSession, boolean append, int limitMb) { + historyWriteLock.lock(); + try { + if (Settings.IMP.HISTORY.USE_DISK) { + LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE; + } + // It should have already been flushed, but just in case! + editSession.flushQueue(); + if (editSession.getChangeSet() == null || limitMb == 0 || historySize >> 20 > limitMb && !append) { + return; + } + + ChangeSet changeSet = editSession.getChangeSet(); + if (changeSet.isEmpty()) { + return; + } + + Player player = editSession.getPlayer(); + if (player != null) { + loadSessionHistoryFromDisk(player.getUniqueId(), editSession.getWorld()); + } + // Destroy any sessions after this undo point + if (append) { + ListIterator iter = history.listIterator(); + int i = 0; + int cutoffIndex = history.size() - getHistoryNegativeIndex(); + while (iter.hasNext()) { + Object item = iter.next(); + if (++i > cutoffIndex) { + ChangeSet oldChangeSet; + if (item instanceof ChangeSet) { + oldChangeSet = (ChangeSet) item; + } else { + oldChangeSet = getChangeSet(item); + } + historySize -= MainUtil.getSize(oldChangeSet); + iter.remove(); + } + } + } + + historySize += MainUtil.getSize(changeSet); + if (append) { + history.add(changeSet); + if (getHistoryNegativeIndex() != 0) { + setDirty(); + historyNegativeIndex = 0; + } + } else { + history.add(0, changeSet); + } while (((!Settings.IMP.HISTORY.USE_DISK && history.size() > MAX_HISTORY_SIZE) || (historySize >> 20) > limitMb) && history.size() > 1) { ChangeSet item = (ChangeSet) history.remove(0); item.delete(); long size = MainUtil.getSize(item); historySize -= size; } - } - } - - public synchronized void remember(EditSession editSession, boolean append, int limitMb) { - if (Settings.IMP.HISTORY.USE_DISK) { - LocalSession.MAX_HISTORY_SIZE = Integer.MAX_VALUE; - } - // It should have already been flushed, but just in case! - editSession.flushQueue(); - if (editSession.getChangeSet() == null || limitMb == 0 || historySize >> 20 > limitMb && !append) { - return; - } - /* - // Don't store anything if no changes were made - if (editSession.size() == 0) { - return; - } - */ - - ChangeSet changeSet = editSession.getChangeSet(); - if (changeSet.isEmpty()) { - return; - } - - Player player = editSession.getPlayer(); - if (player != null) { - loadSessionHistoryFromDisk(player.getUniqueId(), editSession.getWorld()); - } - // Destroy any sessions after this undo point - if (append) { - ListIterator iter = history.listIterator(); - int i = 0; - int cutoffIndex = history.size() - getHistoryNegativeIndex(); - while (iter.hasNext()) { - Object item = iter.next(); - if (++i > cutoffIndex) { - ChangeSet oldChangeSet; - if (item instanceof ChangeSet) { - oldChangeSet = (ChangeSet) item; - } else { - oldChangeSet = getChangeSet(item); - } - historySize -= MainUtil.getSize(oldChangeSet); - iter.remove(); - } - } - } - - historySize += MainUtil.getSize(changeSet); - if (append) { - history.add(changeSet); - if (getHistoryNegativeIndex() != 0) { - setDirty(); - historyNegativeIndex = 0; - } - } else { - history.add(0, changeSet); - } - while (((!Settings.IMP.HISTORY.USE_DISK && history.size() > MAX_HISTORY_SIZE) || (historySize >> 20) > limitMb) && history.size() > 1) { - ChangeSet item = (ChangeSet) history.remove(0); - item.delete(); - long size = MainUtil.getSize(item); - historySize -= size; + } finally { + historyWriteLock.unlock(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/LocationConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/LocationConverter.java new file mode 100644 index 000000000..5cf84a152 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/LocationConverter.java @@ -0,0 +1,88 @@ +/* + * 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.world.World; +import org.enginehub.piston.CommandManager; +import org.enginehub.piston.converter.ArgumentConverter; +import org.enginehub.piston.converter.ConversionResult; +import org.enginehub.piston.converter.FailedConversion; +import org.enginehub.piston.converter.SuccessfulConversion; +import org.enginehub.piston.inject.InjectedValueAccess; +import org.enginehub.piston.inject.Key; + +import java.util.Collections; +import java.util.List; + +public class LocationConverter implements ArgumentConverter { + + public static void register(CommandManager commandManager) { + commandManager.registerConverter(Key.of(Location.class), LOCATION_CONVERTER); + } + + public static final LocationConverter LOCATION_CONVERTER = new LocationConverter(); + + private final WorldConverter worldConverter = WorldConverter.WORLD_CONVERTER; + private final VectorConverter vectorConverter = VectorConverter.BLOCK_VECTOR_3_CONVERTER; + private final Component desc = TextComponent.of("any world, x, y, and z"); + + private LocationConverter() { + } + + @Override + public Component describeAcceptableArguments() { + return desc; + } + + @Override + public List getSuggestions(String input, InjectedValueAccess context) { + if (input.contains(",")) { + return Collections.emptyList(); + } + return worldConverter.getSuggestions(input, context); + } + + @Override + public ConversionResult convert(String s, InjectedValueAccess injectedValueAccess) { + String[] split4 = s.split(",", 4); + if (split4.length != 4) { + return FailedConversion.from(new IllegalArgumentException( + "Must have exactly 1 world and 3 vector components")); + } + + String[] split2 = s.split(",", 2); + + ConversionResult world = worldConverter.convert(split2[0], injectedValueAccess); + if (!world.isSuccessful()) { + return (FailedConversion) world; + } + ConversionResult vector = vectorConverter.convert(split2[1], injectedValueAccess); + if (!vector.isSuccessful()) { + return (FailedConversion) vector; + } + + Location location = new Location(world.get().iterator().next(), vector.get().iterator().next().toVector3()); + return SuccessfulConversion.fromSingle(location); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/WorldConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/WorldConverter.java index af6ce8b41..d9243cba4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/WorldConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/WorldConverter.java @@ -39,10 +39,10 @@ import java.util.stream.Stream; public class WorldConverter implements ArgumentConverter { public static void register(CommandManager commandManager) { - commandManager.registerConverter(Key.of(World.class), - new WorldConverter() - ); + commandManager.registerConverter(Key.of(World.class), WORLD_CONVERTER); } + + public static final WorldConverter WORLD_CONVERTER = new WorldConverter(); private final TextComponent choices; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java index 87ea41d68..28bbea434 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.command.util; +import com.boydti.fawe.util.MathMan; import com.sk89q.worldedit.registry.Keyed; import com.sk89q.worldedit.registry.NamespacedRegistry; import com.sk89q.worldedit.registry.Registry; @@ -35,6 +36,7 @@ import java.util.Map; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import static org.enginehub.piston.converter.SuggestionHelper.byPrefix; @@ -173,4 +175,57 @@ public final class SuggestionHelper { Predicate search = byPrefix(input.toLowerCase(Locale.ROOT)); return registry.keySet().stream().filter(search); } + + /** + * Returns a stream of suggestions for positive doubles. + * + * @param argumentInput the given input to filter with. + * @return a stream of suggestions. + */ + public static Stream suggestPositiveDoubles(String argumentInput) { + if (argumentInput.isEmpty()) { + return Stream.of("1", "2", "3", "4", "5", "6", "7", "8", "9"); + } + // if already a valid number, suggest more digits + if (isDouble(argumentInput)) { + Stream numbers = Stream.of("", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); + if (argumentInput.indexOf('.') == -1) { + numbers = Stream.concat(numbers, Stream.of(".")); + } + return numbers.map(s -> argumentInput + s); + } + // no valid input anymore + return Stream.empty(); + } + + /** + * Returns a stream of suggestions for positive integers. + * + * @param argumentInput the given input to filter with. + * @return a stream of suggestions. + */ + public static Stream suggestPositiveIntegers(String argumentInput) { + if (argumentInput.isEmpty()) { + return IntStream.rangeClosed(1, 9).mapToObj(Integer::toString); + } + if (MathMan.isInteger(argumentInput)) { + return IntStream.rangeClosed(0, 9).mapToObj(Integer::toString).map(s -> argumentInput + s); + } + // no valid input anymore + return Stream.empty(); + } + + private static boolean isDouble(String input) { + boolean point = false; + for (char c : input.toCharArray()) { + if (!Character.isDigit(c)) { + if (c == '.' && !point) { + point = true; + } else { + return false; + } + } + } + return true; + } } 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 e8bad9814..3a392b8d8 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 @@ -22,7 +22,11 @@ package com.sk89q.worldedit.extension.factory; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.factory.parser.pattern.BiomePatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.BlockCategoryPatternParser; +import com.sk89q.worldedit.extension.factory.parser.pattern.BufferedPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.ClipboardPatternParser; +import com.sk89q.worldedit.extension.factory.parser.pattern.ExistingPatternParser; +import com.sk89q.worldedit.extension.factory.parser.pattern.Linear2DPatternParser; +import com.sk89q.worldedit.extension.factory.parser.pattern.Linear3DPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.PerlinPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser; import com.sk89q.worldedit.extension.factory.parser.pattern.RandomStatePatternParser; @@ -66,6 +70,10 @@ public final class PatternFactory extends AbstractFactory { register(new PerlinPatternParser(worldEdit)); register(new RidgedMultiFractalPatternParser(worldEdit)); register(new BiomePatternParser(worldEdit)); + register(new Linear2DPatternParser(worldEdit)); + register(new Linear3DPatternParser(worldEdit)); + register(new BufferedPatternParser(worldEdit)); + register(new ExistingPatternParser(worldEdit)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java index 66fe34d98..fae4b740d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/RichParser.java @@ -1,5 +1,6 @@ package com.sk89q.worldedit.extension.factory.parser; +import com.google.common.base.Preconditions; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; @@ -7,8 +8,10 @@ import com.sk89q.worldedit.internal.registry.InputParser; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import java.util.StringJoiner; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Stream; /** @@ -18,51 +21,75 @@ import java.util.stream.Stream; * @param the parse result. */ public abstract class RichParser extends InputParser { - private final String prefix; - private final String required; + private final String[] prefixes; /** * Create a new rich parser with a defined prefix for the result, e.g. {@code #simplex}. * * @param worldEdit the worldedit instance. - * @param prefix the prefix of this parser result. + * @param aliases the prefix of this parser result. */ - protected RichParser(WorldEdit worldEdit, String prefix) { + protected RichParser(WorldEdit worldEdit, String... aliases) { super(worldEdit); - this.prefix = prefix; - this.required = prefix + "["; + Preconditions.checkArgument(aliases.length >= 1, "Aliases may not be empty"); + this.prefixes = aliases; + } + + @NotNull + private static Predicate validPrefix(String other) { + return prefix -> { + if (prefix.length() > other.length()) { + return prefix.startsWith(other); + } + return other.startsWith(prefix); + }; + } + + @NotNull + private Function> extractArguments(String input) { + return prefix -> { + if (input.length() > prefix.length()) { + // input already contains argument(s) -> extract them + String[] strings = extractArguments(input.substring(prefix.length()), false); + // rebuild the argument string without the last argument + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < strings.length - 1; i++) { + builder.append('[').append(strings[i]).append(']'); + } + String previous = prefix + builder; + // read the suggestions for the last argument + return getSuggestions(strings[strings.length - 1], strings.length - 1) + .map(suggestion -> previous + "[" + suggestion); + } else { + return Stream.of(prefix); + } + }; + } + + public String getPrefix() { + return this.prefixes[0]; } @Override public Stream getSuggestions(String input) { - // we don't even want to start suggesting if it's not meant to be this parser result - if (input.length() >= this.required.length() && !input.startsWith(this.required)) { - return Stream.empty(); - } - // suggest until the first [ as long as it isn't fully typed - if (input.length() < this.required.length()) { - return Stream.of(this.required).filter(s -> s.startsWith(input)); - } - // we know that it is at least "" - String[] strings = extractArguments(input.substring(this.prefix.length()), false); - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < strings.length - 1; i++) { - builder.append('[').append(strings[i]).append(']'); - } - String previous = this.prefix + builder; - return getSuggestions(strings[strings.length - 1], strings.length - 1).map(s -> previous + "[" + s + "]"); + return Arrays.stream(this.prefixes) + .filter(validPrefix(input)) + .flatMap(extractArguments(input)); } @Override public E parseFromInput(String input, ParserContext context) throws InputParseException { - if (!input.startsWith(this.prefix)) { - return null; + for (String prefix : this.prefixes) { + if (!input.startsWith(prefix)) { + continue; + } + if (input.length() < prefix.length()) { + continue; + } + String[] arguments = extractArguments(input.substring(prefix.length()), true); + return parseFromInput(arguments, context); } - if (input.length() < this.prefix.length()) { - return null; - } - String[] arguments = extractArguments(input.substring(prefix.length()), true); - return parseFromInput(arguments, context); + return null; } /** @@ -118,40 +145,4 @@ public abstract class RichParser extends InputParser { } return arguments.toArray(new String[0]); } - - /** - * Returns a stream of suggestions for positive doubles. - * - * @param argumentInput the given input to filter with. - * @return a stream of suggestions. - */ - protected Stream suggestPositiveDoubles(String argumentInput) { - if (argumentInput.isEmpty()) { - return Stream.of("1", "2", "3", "4", "5", "6", "7", "8", "9"); - } - // if already a valid number, suggest more digits - if (isDouble(argumentInput)) { - Stream numbers = Stream.of("", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"); - if (argumentInput.indexOf('.') == -1) { - numbers = Stream.concat(numbers, Stream.of(".")); - } - return numbers.map(s -> argumentInput + s); - } - // no valid input anymore - return Stream.empty(); - } - - private static boolean isDouble(String input) { - boolean point = false; - for (char c : input.toCharArray()) { - if (!Character.isDigit(c)) { - if (c == '.' && !point) { - point = true; - } else { - return false; - } - } - } - return true; - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/AdjacentMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/AdjacentMaskParser.java index c9d24e1a8..d60ef0d9a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/AdjacentMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/AdjacentMaskParser.java @@ -3,6 +3,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.boydti.fawe.object.mask.AdjacentAnyMask; import com.boydti.fawe.object.mask.AdjacentMask; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.factory.parser.RichParser; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; @@ -22,7 +23,7 @@ public class AdjacentMaskParser extends RichParser { if (index == 0) { return worldEdit.getMaskFactory().getSuggestions(argumentInput).stream(); } else if (index == 1 || index == 2) { - return this.suggestPositiveDoubles(argumentInput); + return SuggestionHelper.suggestPositiveDoubles(argumentInput); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/AngleMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/AngleMaskParser.java index 0c7acf214..f2d9867ee 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/AngleMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/AngleMaskParser.java @@ -2,10 +2,13 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.boydti.fawe.object.mask.AngleMask; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.factory.parser.RichParser; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import org.jetbrains.annotations.NotNull; import java.util.stream.Stream; @@ -21,7 +24,7 @@ public class AngleMaskParser extends RichParser { @Override protected Stream getSuggestions(String argumentInput, int index) { if (index == 0 || index == 1) { - return suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); + return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); } else if (index > 1 && index <= 1 + flags.length) { return Stream.of(flags); } @@ -37,7 +40,7 @@ public class AngleMaskParser extends RichParser { String maxArg = arguments[1]; boolean degree = minArg.endsWith("d"); if (degree ^ maxArg.endsWith("d")) { - throw new InputParseException("Cannot combine degree with block-step"); + throw new InputParseException(TranslatableComponent.of("fawe.error.mask.angle")); } boolean overlay = false; if (arguments.length > 2) { @@ -46,7 +49,8 @@ public class AngleMaskParser extends RichParser { if (flag.equals("-o")) { overlay = true; } else { - throw new InputParseException("The flag " + flag + " is not applicable for this mask!"); + throw new InputParseException(TranslatableComponent.of("fawe.error.invalid-flag", + TextComponent.of(flag))); } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExtremaMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExtremaMaskParser.java index 50fa9b849..479b6f71c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExtremaMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExtremaMaskParser.java @@ -2,10 +2,13 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.boydti.fawe.object.mask.ExtremaMask; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.factory.parser.RichParser; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import org.jetbrains.annotations.NotNull; import java.util.stream.Stream; @@ -21,7 +24,7 @@ public class ExtremaMaskParser extends RichParser { @Override protected Stream getSuggestions(String argumentInput, int index) { if (index == 0 || index == 1) { - return suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); + return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); } else if (index > 1 && index <= 1 + flags.length) { return Stream.of(flags); } @@ -37,7 +40,7 @@ public class ExtremaMaskParser extends RichParser { String maxArg = arguments[1]; boolean degree = minArg.endsWith("d"); if (degree ^ maxArg.endsWith("d")) { - throw new InputParseException("Cannot combine degree with block-step"); + throw new InputParseException(TranslatableComponent.of("fawe.error.mask.angle")); } double min; double max; @@ -48,7 +51,8 @@ public class ExtremaMaskParser extends RichParser { if (flag.equals("-o")) { overlay = true; } else { - throw new InputParseException("The flag " + flag + " is not applicable for this mask!"); + throw new InputParseException(TranslatableComponent.of("fawe.error.invalid-flag", + TextComponent.of(flag))); } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ROCAngleMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ROCAngleMaskParser.java index fd4672ae7..b99274084 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ROCAngleMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ROCAngleMaskParser.java @@ -2,10 +2,13 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.boydti.fawe.object.mask.ROCAngleMask; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.factory.parser.RichParser; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import org.jetbrains.annotations.NotNull; import java.util.stream.Stream; @@ -21,7 +24,7 @@ public class ROCAngleMaskParser extends RichParser { @Override protected Stream getSuggestions(String argumentInput, int index) { if (index == 0 || index == 1) { - return suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); + return SuggestionHelper.suggestPositiveDoubles(argumentInput).flatMap(s -> Stream.of(s, s + "d")); } else if (index > 1 && index <= 1 + flags.length) { return Stream.of(flags); } @@ -37,7 +40,7 @@ public class ROCAngleMaskParser extends RichParser { String maxArg = arguments[1]; boolean degree = minArg.endsWith("d"); if (degree ^ maxArg.endsWith("d")) { - throw new InputParseException("Cannot combine degree with block-step"); + throw new InputParseException(TranslatableComponent.of("fawe.error.mask.angle")); } double min; double max; @@ -48,7 +51,8 @@ public class ROCAngleMaskParser extends RichParser { if (flag.equals("-o")) { overlay = true; } else { - throw new InputParseException("The flag " + flag + " is not applicable for this mask!"); + throw new InputParseException(TranslatableComponent.of("fawe.error.invalid-flag", + TextComponent.of(flag))); } } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RadiusMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RadiusMaskParser.java index 914159218..ae0f7182e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RadiusMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RadiusMaskParser.java @@ -2,6 +2,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.boydti.fawe.object.mask.RadiusMask; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.factory.parser.RichParser; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; @@ -19,7 +20,7 @@ public class RadiusMaskParser extends RichParser { @Override protected Stream getSuggestions(String argumentInput, int index) { if (index == 0 || index == 1) { - return suggestPositiveDoubles(argumentInput); + return SuggestionHelper.suggestPositiveDoubles(argumentInput); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SimplexMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SimplexMaskParser.java index 09424b6c8..4e96690f0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SimplexMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SimplexMaskParser.java @@ -2,6 +2,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.boydti.fawe.object.mask.SimplexMask; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.factory.parser.RichParser; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; @@ -20,7 +21,7 @@ public class SimplexMaskParser extends RichParser { @Override protected Stream getSuggestions(String argumentInput, int index) { if (index < 3) { - suggestPositiveDoubles(argumentInput); + return SuggestionHelper.suggestPositiveDoubles(argumentInput); } return Stream.empty(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BufferedPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BufferedPatternParser.java new file mode 100644 index 000000000..d7e48ccf8 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BufferedPatternParser.java @@ -0,0 +1,43 @@ +package com.sk89q.worldedit.extension.factory.parser.pattern; + +import com.boydti.fawe.object.pattern.BufferedPattern; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.factory.parser.RichParser; +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 com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import org.jetbrains.annotations.NotNull; + +import java.util.stream.Stream; + +public class BufferedPatternParser extends RichParser { + + /** + * Create a new rich parser with a defined prefix for the result, e.g. {@code #simplex}. + * + * @param worldEdit the worldedit instance. + */ + public BufferedPatternParser(WorldEdit worldEdit) { + super(worldEdit, "#buffer"); + } + + @Override + protected Stream getSuggestions(String argumentInput, int index) { + if (index == 0) { + return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + } + return Stream.empty(); + } + + @Override + protected Pattern parseFromInput(@NotNull String[] arguments, ParserContext context) throws InputParseException { + if (arguments.length != 1) { + throw new InputParseException(TranslatableComponent.of("fawe.error.command.syntax", + TextComponent.of(getPrefix() + "[pattern] (e.g. " + getPrefix() + "[stone,dirt])"))); + } + Pattern inner = this.worldEdit.getPatternFactory().parseFromInput(arguments[0], context); + return new BufferedPattern(context.requireActor(), inner); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ExistingPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ExistingPatternParser.java new file mode 100644 index 000000000..f69981b9a --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ExistingPatternParser.java @@ -0,0 +1,29 @@ +package com.sk89q.worldedit.extension.factory.parser.pattern; + +import com.boydti.fawe.object.pattern.ExistingPattern; +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.internal.registry.SimpleInputParser; + +import java.util.Collections; +import java.util.List; + +public class ExistingPatternParser extends SimpleInputParser { + private final List aliases = Collections.singletonList("#existing"); + + public ExistingPatternParser(WorldEdit worldEdit) { + super(worldEdit); + } + + @Override + public List getMatchedAliases() { + return this.aliases; + } + + @Override + public Pattern parseFromSimpleInput(String input, ParserContext context) throws InputParseException { + return new ExistingPattern(context.requireExtent()); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/Linear2DPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/Linear2DPatternParser.java new file mode 100644 index 000000000..34922b994 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/Linear2DPatternParser.java @@ -0,0 +1,71 @@ +package com.sk89q.worldedit.extension.factory.parser.pattern; + +import com.boydti.fawe.object.pattern.Linear2DBlockPattern; +import com.google.common.base.Preconditions; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; +import com.sk89q.worldedit.extension.factory.parser.RichParser; +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.function.pattern.RandomPattern; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; +import java.util.stream.Stream; + +public class Linear2DPatternParser extends RichParser { + + /** + * Create a new rich parser with a defined prefix for the result, e.g. {@code #simplex}. + * + * @param worldEdit the worldedit instance. + */ + public Linear2DPatternParser(WorldEdit worldEdit) { + super(worldEdit, "#linear2d", "#l2d"); + } + + @Override + protected Stream getSuggestions(String argumentInput, int index) { + switch (index) { + case 0: + return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + case 1: + case 2: + return SuggestionHelper.suggestPositiveIntegers(argumentInput); + default: + return Stream.empty(); + } + } + + @Override + protected Pattern parseFromInput(@NotNull String[] arguments, ParserContext context) throws InputParseException { + if (arguments.length == 0 || arguments.length > 3) { + throw new InputParseException(TranslatableComponent.of("fawe.error.command.syntax", + TextComponent.of(getPrefix() + "[pattern] (e.g. " + getPrefix() + "[stone,dirt])"))); + } + Pattern inner = this.worldEdit.getPatternFactory().parseFromInput(arguments[0], context); + if (inner instanceof BlockStateHolder) { + return inner; + } + int xScale = 1; + int zScale = 1; + if (arguments.length > 1) { + xScale = Integer.parseInt(arguments[1]); + Preconditions.checkArgument(xScale != 0); + } + if (arguments.length > 2) { + zScale = Integer.parseInt(arguments[2]); + Preconditions.checkArgument(zScale != 0); + } + if (inner instanceof RandomPattern) { + Set patterns = ((RandomPattern) inner).getPatterns(); + return new Linear2DBlockPattern(patterns.toArray(new Pattern[0]), xScale, zScale); + } + throw new InputParseException(TextComponent.of("Pattern " + inner.getClass().getSimpleName() + + " cannot be used with " + getPrefix())); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/Linear3DPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/Linear3DPatternParser.java new file mode 100644 index 000000000..72b8480ea --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/Linear3DPatternParser.java @@ -0,0 +1,77 @@ +package com.sk89q.worldedit.extension.factory.parser.pattern; + +import com.boydti.fawe.object.pattern.Linear3DBlockPattern; +import com.google.common.base.Preconditions; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; +import com.sk89q.worldedit.extension.factory.parser.RichParser; +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.function.pattern.RandomPattern; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; +import java.util.stream.Stream; + +public class Linear3DPatternParser extends RichParser { + + /** + * Create a new rich parser with a defined prefix for the result, e.g. {@code #simplex}. + * + * @param worldEdit the worldedit instance. + */ + public Linear3DPatternParser(WorldEdit worldEdit) { + super(worldEdit, "#linear3d", "#l3d"); + } + + @Override + protected Stream getSuggestions(String argumentInput, int index) { + switch (index) { + case 0: + return this.worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); + case 1: + case 2: + case 3: + return SuggestionHelper.suggestPositiveIntegers(argumentInput); + default: + return Stream.empty(); + } + } + + @Override + protected Pattern parseFromInput(@NotNull String[] arguments, ParserContext context) throws InputParseException { + if (arguments.length == 0 || arguments.length > 4) { + throw new InputParseException(TranslatableComponent.of("fawe.error.command.syntax", + TextComponent.of(getPrefix() + "[pattern] (e.g. " + getPrefix() + "[stone,dirt])"))); + } + Pattern inner = this.worldEdit.getPatternFactory().parseFromInput(arguments[0], context); + if (inner instanceof BlockStateHolder) { + return inner; + } + int xScale = 1; + int yScale = 1; + int zScale = 1; + if (arguments.length > 1) { + xScale = Integer.parseInt(arguments[1]); + Preconditions.checkArgument(xScale != 0); + } + if (arguments.length > 2) { + yScale = Integer.parseInt(arguments[2]); + Preconditions.checkArgument(yScale != 0); + } + if (arguments.length > 3) { + zScale = Integer.parseInt(arguments[3]); + Preconditions.checkArgument(zScale != 0); + } + if (inner instanceof RandomPattern) { + Set patterns = ((RandomPattern) inner).getPatterns(); + return new Linear3DBlockPattern(patterns.toArray(new Pattern[0]), xScale, yScale,zScale); + } + throw new InputParseException(TextComponent.of("Pattern " + inner.getClass().getSimpleName() + + " cannot be used with " + getPrefix())); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/NoisePatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/NoisePatternParser.java index e8b2a0e30..d9b0ee56a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/NoisePatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/NoisePatternParser.java @@ -2,12 +2,15 @@ package com.sk89q.worldedit.extension.factory.parser.pattern; import com.boydti.fawe.object.random.NoiseRandom; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.factory.parser.RichParser; 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.function.pattern.RandomPattern; import com.sk89q.worldedit.math.noise.NoiseGenerator; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.world.block.BlockStateHolder; import org.jetbrains.annotations.NotNull; @@ -35,7 +38,7 @@ public abstract class NoisePatternParser extends RichParser { @Override protected Stream getSuggestions(String argumentInput, int index) { if (index == 0) { - return suggestPositiveDoubles(argumentInput); + return SuggestionHelper.suggestPositiveDoubles(argumentInput); } if (index == 1) { return worldEdit.getPatternFactory().getSuggestions(argumentInput).stream(); @@ -46,8 +49,8 @@ public abstract class NoisePatternParser extends RichParser { @Override protected Pattern parseFromInput(@NotNull String[] arguments, ParserContext context) { if (arguments.length != 2) { - throw new InputParseException(this.name + " requires a scale and a pattern, e.g. #" - + this.name + "[5][dirt,stone]"); + throw new InputParseException(TranslatableComponent.of("fawe.error.command.syntax", + TextComponent.of(getPrefix() + "[scale][pattern] (e.g. " + getPrefix() + "[5][dirt,stone])"))); } double scale = parseScale(arguments[0]); Pattern inner = worldEdit.getPatternFactory().parseFromInput(arguments[1], context); @@ -56,8 +59,8 @@ public abstract class NoisePatternParser extends RichParser { } else if (inner instanceof BlockStateHolder) { return inner; // single blocks won't have any impact on how a noise behaves } else { - throw new InputParseException("Pattern " + inner.getClass().getSimpleName() - + " cannot be used with #" + this.name); + throw new InputParseException(TextComponent.of("Pattern " + inner.getClass().getSimpleName() + + " cannot be used with #" + this.name)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java index d180ef31a..ce8be7bbd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -79,8 +79,8 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { private final Map meta; // Queue for async tasks - private AtomicInteger runningCount = new AtomicInteger(); - private AsyncNotifyQueue asyncNotifyQueue = new AsyncNotifyQueue( + private final AtomicInteger runningCount = new AtomicInteger(); + private final AsyncNotifyQueue asyncNotifyQueue = new AsyncNotifyQueue( (thread, throwable) -> { while (throwable.getCause() != null) { throwable = throwable.getCause(); @@ -684,6 +684,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { * @param async TODO description * @return false if the task was ran or queued */ + @Override public boolean runAction(Runnable ifFree, boolean checkFree, boolean async) { if (checkFree) { if (runningCount.get() != 0) { 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 7761aaf9f..5a222f0cf 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 @@ -189,11 +189,11 @@ public interface Actor extends Identifiable, SessionOwner, Subject, MapMetadatab */ default boolean confirm() { InterruptableCondition confirm = deleteMeta("cmdConfirm"); - if (confirm != null) { - confirm.signal();; - return true; + if (confirm == null) { + return false; } - return false; + queueAction(confirm::signal); + return true; } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 22cd184ef..2bb552c74 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -90,6 +90,7 @@ import com.sk89q.worldedit.command.argument.EntityRemoverConverter; import com.sk89q.worldedit.command.argument.EnumConverter; import com.sk89q.worldedit.command.argument.ExpressionConverter; import com.sk89q.worldedit.command.argument.FactoryConverter; +import com.sk89q.worldedit.command.argument.LocationConverter; import com.sk89q.worldedit.command.argument.RegionFactoryConverter; import com.sk89q.worldedit.command.argument.RegistryConverter; import com.sk89q.worldedit.command.argument.SideEffectConverter; @@ -256,6 +257,7 @@ public final class PlatformCommandManager { EntityRemoverConverter.register(commandManager); RegionFactoryConverter.register(commandManager); WorldConverter.register(commandManager); + LocationConverter.register(commandManager); ExpressionConverter.register(commandManager); SideEffectConverter.register(commandManager); @@ -652,13 +654,12 @@ public final class PlatformCommandManager { } else { actor.decline(); } - LocalSession session = worldEdit.getSessionManager().get(actor); - synchronized (session) { + actor.runAction(() -> { SessionKey key = actor.getSessionKey(); if (key.isActive()) { PlatformCommandManager.this.handleCommandOnCurrentThread(event); } - } + }, false, true); }, Fawe.isMainThread()); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java index 762c462d4..62910011c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformManager.java @@ -437,7 +437,7 @@ public class PlatformManager { Tool tool = session.getTool(player); if (tool instanceof DoubleActionTraceTool && tool.canUse(player)) { player.runAsyncIfFree(() -> reset((DoubleActionTraceTool) tool).actSecondary(queryCapability(Capability.WORLD_EDITING), - getConfiguration(), player, session)); + getConfiguration(), player, session)); event.setCancelled(true); return; } @@ -450,7 +450,7 @@ public class PlatformManager { if (tool instanceof TraceTool && tool.canUse(player)) { //todo this needs to be fixed so the event is canceled after actPrimary is used and returns true player.runAction(() -> reset((TraceTool) tool).actPrimary(queryCapability(Capability.WORLD_EDITING), - getConfiguration(), player, session), false, true); + getConfiguration(), player, session), false, true); event.setCancelled(true); return; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java index c3a2dad04..3a8732255 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java @@ -83,6 +83,11 @@ public class PlayerProxy extends AbstractPlayerActor { return basePlayer.getBlockInHand(handSide); } + @Override + public boolean runAction(Runnable ifFree, boolean checkFree, boolean async) { + return basePlayer.runAction(ifFree, checkFree, async); + } + @Override public UUID getUniqueId() { return basePlayer.getUniqueId(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java index 6433ce318..c0c413689 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/FastSchematicReader.java @@ -32,10 +32,13 @@ import com.sk89q.jnbt.IntTag; import com.sk89q.jnbt.NBTInputStream; import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.DataFixer; @@ -99,6 +102,7 @@ public class FastSchematicReader extends NBTSchematicReader { public FastSchematicReader(NBTInputStream inputStream) { checkNotNull(inputStream); this.inputStream = inputStream; + this.fixer = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataFixer(); } private String fix(String palettePart) { @@ -133,7 +137,12 @@ public class FastSchematicReader extends NBTSchematicReader { StreamDelegate root = new StreamDelegate(); StreamDelegate schematic = root.add("Schematic"); schematic.add("DataVersion").withInt((i, v) -> dataVersion = v); - schematic.add("Version").withInt((i, v) -> version = v); + schematic.add("Version").withInt((i, v) -> { + version = v; + if (v == 1 && dataVersion == -1) { // DataVersion might not be present, assume 1.13.2 + dataVersion = Constants.DATA_VERSION_MC_1_13_2; + } + }); schematic.add("Width").withInt((i, v) -> width = v); schematic.add("Height").withInt((i, v) -> height = v); schematic.add("Length").withInt((i, v) -> length = v); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java index 44ac3c8bf..14f363009 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicReader.java @@ -33,6 +33,8 @@ import com.sk89q.jnbt.NBTInputStream; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.BannerBlockCompatibilityHandler; +import com.sk89q.worldedit.extent.clipboard.io.legacycompat.BedBlockCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.EntityNBTCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.FlowerPotCompatibilityHandler; import com.sk89q.worldedit.extent.clipboard.io.legacycompat.NBTCompatibilityHandler; @@ -86,7 +88,9 @@ public class SchematicReader implements ClipboardReader { new SignCompatibilityHandler(), new FlowerPotCompatibilityHandler(), new NoteBlockCompatibilityHandler(), - new SkullBlockCompatibilityHandler() + new SkullBlockCompatibilityHandler(), + new BannerBlockCompatibilityHandler(), + new BedBlockCompatibilityHandler() }; private static final EntityNBTCompatibilityHandler[] ENTITY_COMPATIBILITY_HANDLERS = { new Pre13HangingCompatibilityHandler() diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index b86e09a95..de060a1ed 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -102,6 +102,8 @@ "fawe.error.player.not.found": "Player not found: {0}", "fawe.error.worldedit.some.fails": "{0} blocks weren't placed because they were outside your allowed region.", "fawe.error.worldedit.some.fails.blockbag": "Missing blocks: {0}", + "fawe.error.mask.angle": "Cannot combine degree with block-step", + "fawe.error.invalid-flag": "The flag {0} is not applicable here", "fawe.cancel.worldedit.cancel.count": "Cancelled {0} edits.", "fawe.cancel.worldedit.cancel.reason.confirm": "Use //confirm to execute {2}",