From c55353e5b193ec009239b2df5c2eb0c28b3971ec Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Wed, 22 Aug 2018 03:22:37 +1000 Subject: [PATCH] Various command (use es6 if java9 + nashorn) --- build.gradle | 2 + .../com/boydti/fawe/bukkit/FaweBukkit.java | 13 +- .../bukkit/listener/ATabCompleteListener.java | 39 ++ .../listener/AsyncTabCompleteListener.java | 48 +++ .../listener/SyncTabCompleteListener.java | 23 ++ .../bukkit/util/DynamicPluginCommand.java | 16 +- .../sk89q/worldedit/bukkit/BukkitPlayer.java | 7 +- .../worldedit/bukkit/WorldEditPlugin.java | 29 +- .../src/main/java/com/boydti/fawe/Fawe.java | 56 +-- .../boydti/fawe/command/AnvilCommands.java | 164 ++++---- .../com/boydti/fawe/command/FaweParser.java | 39 +- .../com/boydti/fawe/command/MaskBinding.java | 29 +- .../boydti/fawe/command/PatternBinding.java | 33 +- .../command/SuggestInputParseException.java | 104 +++-- .../java/com/boydti/fawe/config/Commands.java | 5 +- .../java/com/boydti/fawe/config/Settings.java | 3 +- .../com/boydti/fawe/object/RunnableVal2.java | 9 +- .../object/clipboard/OffsetFaweClipboard.java | 3 - .../boydti/fawe/object/pattern/IdPattern.java | 6 - .../object/string/MutableCharSequence.java | 7 + .../java/com/boydti/fawe/util/MainUtil.java | 5 +- .../java/com/boydti/fawe/util/StringMan.java | 9 + .../com/boydti/fawe/util/TextureUtil.java | 2 +- .../com/sk89q/worldedit/CuboidClipboard.java | 13 +- .../java/com/sk89q/worldedit/EditSession.java | 6 +- .../com/sk89q/worldedit/blocks/BlockType.java | 9 +- .../sk89q/worldedit/command/MaskCommands.java | 28 +- .../worldedit/command/PatternCommands.java | 2 +- .../worldedit/command/SchematicCommands.java | 4 +- .../worldedit/command/WorldEditCommands.java | 7 +- .../extension/factory/DefaultBlockParser.java | 16 +- .../extension/factory/DefaultMaskParser.java | 164 ++++---- .../factory/HashTagPatternParser.java | 73 +++- .../extension/factory/MaskFactory.java | 1 - .../extension/factory/PatternFactory.java | 2 - .../platform/AbstractPlayerActor.java | 3 +- .../extension/platform/CommandManager.java | 15 +- .../transform/BlockTransformExtent.java | 13 +- .../function/mask/BlockMaskBuilder.java | 103 ++++- .../internal/command/WorldEditBinding.java | 14 +- .../registry/state/DirectionalProperty.java | 6 +- .../registry/state/EnumProperty.java | 3 +- .../scripting/CommandScriptLoader.java | 106 ++++++ .../scripting/NashornCraftScriptEngine.java | 79 ++++ .../util/command/SimpleDispatcher.java | 1 + .../command/binding/PrimitiveBindings.java | 5 +- .../parametric/AParametricCallable.java | 170 ++++----- .../command/parametric/BindingHelper.java | 6 +- .../parametric/ContextArgumentStack.java | 2 +- .../command/parametric/FaweParanamer.java | 4 +- .../FunctionParametricCallable.java | 77 ++-- .../command/parametric/ParametricBuilder.java | 1 - .../parametric/StringArgumentStack.java | 2 +- .../worldedit/world/block/BlockState.java | 54 ++- .../worldedit/world/block/BlockTypes.java | 21 +- .../worldedit/world/registry/legacy.json | 14 +- worldedit-core/src/main/resources/cs_adv.js | 49 +++ .../src/main/resources/fr/message.yml | 356 ++++++++++++++++++ .../src/main/resources/nl/message.yml | 331 ++++++++++++++++ 59 files changed, 1799 insertions(+), 612 deletions(-) create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ATabCompleteListener.java create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/AsyncTabCompleteListener.java create mode 100644 worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/SyncTabCompleteListener.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CommandScriptLoader.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/scripting/NashornCraftScriptEngine.java create mode 100644 worldedit-core/src/main/resources/cs_adv.js create mode 100644 worldedit-core/src/main/resources/fr/message.yml create mode 100644 worldedit-core/src/main/resources/nl/message.yml diff --git a/build.gradle b/build.gradle index 0117fa686..e40acea66 100644 --- a/build.gradle +++ b/build.gradle @@ -68,6 +68,8 @@ subprojects { version = rootProject.version group = 'com.boydti.fawe' + compileJava { options.compilerArgs += ["-parameters"] } + repositories { mavenCentral() maven { url "http://ci.athion.net/job/FAWE-WorldGuard-1.13/ws/mvn/" } 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 09487470d..f7bc90e4b 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 @@ -3,10 +3,7 @@ package com.boydti.fawe.bukkit; import com.boydti.fawe.Fawe; import com.boydti.fawe.IFawe; import com.boydti.fawe.bukkit.chat.BukkitChatManager; -import com.boydti.fawe.bukkit.listener.BrushListener; -import com.boydti.fawe.bukkit.listener.BukkitImageListener; -import com.boydti.fawe.bukkit.listener.CFIPacketListener; -import com.boydti.fawe.bukkit.listener.RenderListener; +import com.boydti.fawe.bukkit.listener.*; import com.boydti.fawe.bukkit.regions.*; import com.boydti.fawe.bukkit.util.BukkitReflectionUtils; import com.boydti.fawe.bukkit.util.BukkitTaskMan; @@ -32,7 +29,6 @@ import com.boydti.fawe.util.TaskManager; import com.boydti.fawe.util.cui.CUI; import com.boydti.fawe.util.image.ImageViewer; import com.boydti.fawe.util.metrics.BStats; -import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.world.World; import org.bukkit.Bukkit; @@ -123,6 +119,13 @@ public class FaweBukkit implements IFawe, Listener { } catch (ClassNotFoundException e) { new ChunkListener_9(); } + + try { + new AsyncTabCompleteListener(WorldEditPlugin.getInstance()); + } catch (Throwable ignore) + { + Bukkit.getPluginManager().registerEvents(new SyncTabCompleteListener(WorldEditPlugin.getInstance()), plugin); + } } }); } diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ATabCompleteListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ATabCompleteListener.java new file mode 100644 index 000000000..9b772fc14 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/ATabCompleteListener.java @@ -0,0 +1,39 @@ +package com.boydti.fawe.bukkit.listener; + +import com.boydti.fawe.object.string.MutableCharSequence; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; +import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.util.command.CommandMapping; +import com.sk89q.worldedit.util.command.Dispatcher; +import org.bukkit.command.CommandSender; +import org.bukkit.event.Listener; + +import java.util.List; + +public class ATabCompleteListener implements Listener { + private final WorldEditPlugin worldEdit; + + public ATabCompleteListener(WorldEditPlugin worldEdit) { + this.worldEdit = worldEdit; + } + public List onTab(String buffer, CommandSender sender) { + int firstSpace = buffer.indexOf(' '); + if (firstSpace == -1) return null; + MutableCharSequence mBuffer = MutableCharSequence.getTemporal(); + mBuffer.setString(buffer); + mBuffer.setSubstring(0, firstSpace); + String label = buffer.substring(mBuffer.indexOf(':') + 1, firstSpace); + Dispatcher dispatcher = CommandManager.getInstance().getDispatcher(); + CommandMapping weCommand = dispatcher.get(label); + if (weCommand != null) { + CommandSuggestionEvent event = new CommandSuggestionEvent(worldEdit.wrapCommandSender(sender), buffer); + worldEdit.getWorldEdit().getEventBus().post(event); + List suggestions = event.getSuggestions(); + if (suggestions != null) { + return suggestions; + } + } + return null; + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/AsyncTabCompleteListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/AsyncTabCompleteListener.java new file mode 100644 index 000000000..581729fd0 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/AsyncTabCompleteListener.java @@ -0,0 +1,48 @@ +package com.boydti.fawe.bukkit.listener; + +import com.boydti.fawe.util.TaskManager; +import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.event.EventHandler; +import org.bukkit.event.server.TabCompleteEvent; + +import java.util.Collections; +import java.util.List; + +public class AsyncTabCompleteListener extends ATabCompleteListener { + public AsyncTabCompleteListener(WorldEditPlugin worldEdit) { + super(worldEdit); + Bukkit.getPluginManager().registerEvents(this, worldEdit); + } + + @EventHandler + public void onTabComplete(AsyncTabCompleteEvent event) { + if (event.isCommand()) { + List result = this.onTab(event.getBuffer(), event.getSender()); + if (result != null) { + event.setCompletions(result); + event.setHandled(true); // Doesn't work + } + } + } + + // Fix for the event being borked with paper + private List completions = null; + private CommandSender sender; + + @EventHandler + public void onTabComplete(TabCompleteEvent event) { + if (event.isCommand()) { + if (sender == event.getSender()) { + event.setCompletions(completions); + sender = null; + } else { + sender = event.getSender(); + completions = event.getCompletions(); + event.setCompletions(Collections.emptyList()); + } + } + } +} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/SyncTabCompleteListener.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/SyncTabCompleteListener.java new file mode 100644 index 000000000..323ac0db4 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/listener/SyncTabCompleteListener.java @@ -0,0 +1,23 @@ +package com.boydti.fawe.bukkit.listener; + +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import org.bukkit.event.EventHandler; +import org.bukkit.event.server.TabCompleteEvent; + +import java.util.List; + +public class SyncTabCompleteListener extends ATabCompleteListener { + public SyncTabCompleteListener(WorldEditPlugin worldEdit) { + super(worldEdit); + } + + @EventHandler + public void onTabComplete(TabCompleteEvent event) { + if (event.isCommand()) { + List result = this.onTab(event.getBuffer(), event.getSender()); + if (result != null) { + event.setCompletions(result); + } + } + } +} diff --git a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java index 479681c6b..dc65e60d8 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/DynamicPluginCommand.java @@ -78,14 +78,14 @@ public class DynamicPluginCommand extends org.bukkit.command.Command implements return owningPlugin; } - @Override - public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { - if (registeredWith instanceof CommandInspector) { - return ((TabCompleter) owner).onTabComplete(sender, this, alias, args); - } else { - return super.tabComplete(sender, alias, args); - } - } +// @Override +// public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { +// if (registeredWith instanceof CommandInspector) { +// return ((TabCompleter) owner).onTabComplete(sender, this, alias, args); +// } else { +// return super.tabComplete(sender, alias, args); +// } +// } @SuppressWarnings("unchecked") @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index c6c5a9fde..be3f6ff1f 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -25,8 +25,6 @@ import com.sk89q.util.StringUtil; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extension.platform.AbstractPlayerActor; @@ -35,9 +33,9 @@ import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameModes; -import jdk.nashorn.internal.ir.Block; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -47,12 +45,11 @@ import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import javax.annotation.Nullable; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import javax.annotation.Nullable; - public class BukkitPlayer extends AbstractPlayerActor { private Player player; diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index eb4a70bd0..51a5b2fed 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -71,7 +71,8 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * Plugin for Bukkit. */ -public class WorldEditPlugin extends JavaPlugin implements TabCompleter { +public class WorldEditPlugin extends JavaPlugin //implements TabCompleter +{ private static final Logger log = Logger.getLogger("FastAsyncWorldEdit"); public static final String CUI_PLUGIN_CHANNEL = "worldedit:cui"; @@ -390,18 +391,20 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { return true; } - @Override - public List onTabComplete(CommandSender sender, Command cmd, String commandLabel, String[] args) { - // Add the command to the array because the underlying command handling - // code of WorldEdit expects it - String[] split = new String[args.length + 1]; - System.arraycopy(args, 0, split, 1, args.length); - split[0] = cmd.getName(); - - CommandSuggestionEvent event = new CommandSuggestionEvent(wrapCommandSender(sender), Joiner.on(" ").join(split)); - getWorldEdit().getEventBus().post(event); - return event.getSuggestions(); - } +// @Deprecated Using Async tab complete (rather than main thread) +// @Override +// public List onTabComplete(CommandSender sender, Command cmd, String commandLabel, String[] args) { +// // Add the command to the array because the underlying command handling +// // code of WorldEdit expects it +// String[] split = new String[args.length + 1]; +// System.arraycopy(args, 0, split, 1, args.length); +// split[0] = cmd.getName(); +// +// CommandSuggestionEvent event = new CommandSuggestionEvent(wrapCommandSender(sender), Joiner.on(" ").join(split)); +// getWorldEdit().getEventBus().post(event); +// +// return event.getSuggestions(); +// } /** * Gets the session for the player. diff --git a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java index 8f0338599..03e28d817 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/Fawe.java @@ -12,62 +12,10 @@ import com.boydti.fawe.util.chat.ChatManager; import com.boydti.fawe.util.chat.PlainChatManager; import com.boydti.fawe.util.cui.CUI; import com.boydti.fawe.util.metrics.BStats; -import com.boydti.fawe.wrappers.FakePlayer; -import com.sk89q.jnbt.*; -import com.sk89q.worldedit.*; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.command.*; -import com.sk89q.worldedit.command.composition.SelectionCommand; -import com.sk89q.worldedit.command.tool.*; -import com.sk89q.worldedit.command.tool.brush.GravityBrush; -import com.sk89q.worldedit.event.extent.EditSessionEvent; -import com.sk89q.worldedit.extension.factory.DefaultMaskParser; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.factory.DefaultTransformParser; -import com.sk89q.worldedit.extension.factory.HashTagPatternParser; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.extension.platform.CommandManager; -import com.sk89q.worldedit.extension.platform.PlatformManager; -import com.sk89q.worldedit.extent.AbstractDelegateExtent; -import com.sk89q.worldedit.extent.MaskingExtent; -import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; -import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; -import com.sk89q.worldedit.extent.clipboard.io.SchematicReader; -import com.sk89q.worldedit.extent.inventory.BlockBagExtent; -import com.sk89q.worldedit.extent.transform.BlockTransformExtent; -import com.sk89q.worldedit.function.CombinedRegionFunction; -import com.sk89q.worldedit.function.block.BlockReplace; -import com.sk89q.worldedit.function.block.ExtentBlockCopy; -import com.sk89q.worldedit.function.entity.ExtentEntityCopy; -import com.sk89q.worldedit.function.mask.*; -import com.sk89q.worldedit.function.operation.ChangeSetExecutor; -import com.sk89q.worldedit.function.operation.ForwardExtentCopy; -import com.sk89q.worldedit.function.operation.Operations; -import com.sk89q.worldedit.function.pattern.*; -import com.sk89q.worldedit.function.visitor.*; -import com.sk89q.worldedit.internal.command.WorldEditBinding; -import com.sk89q.worldedit.internal.expression.Expression; -import com.sk89q.worldedit.internal.expression.runtime.*; -import com.sk89q.worldedit.math.convolution.HeightMap; -import com.sk89q.worldedit.math.interpolation.KochanekBartelsInterpolation; -import com.sk89q.worldedit.math.transform.AffineTransform; -import com.sk89q.worldedit.regions.CuboidRegion; -import com.sk89q.worldedit.regions.CylinderRegion; -import com.sk89q.worldedit.regions.EllipsoidRegion; -import com.sk89q.worldedit.regions.selector.*; -import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; -import com.sk89q.worldedit.session.ClipboardHolder; -import com.sk89q.worldedit.session.PasteBuilder; -import com.sk89q.worldedit.session.SessionManager; import com.sk89q.worldedit.session.request.Request; -import com.sk89q.worldedit.util.command.SimpleCommandMapping; -import com.sk89q.worldedit.util.command.SimpleDispatcher; -import com.sk89q.worldedit.util.command.fluent.DispatcherNode; -import com.sk89q.worldedit.util.command.parametric.*; -import com.sk89q.worldedit.util.formatting.Fragment; -import com.sk89q.worldedit.util.formatting.component.CommandListBox; -import com.sk89q.worldedit.util.formatting.component.CommandUsageBox; -import com.sk89q.worldedit.util.formatting.component.MessageBox; -import com.sk89q.worldedit.world.biome.BaseBiome; import javax.annotation.Nullable; import javax.management.InstanceAlreadyExistsException; @@ -418,6 +366,8 @@ public class Fawe { MainUtil.copyFile(MainUtil.getJarFile(), "tr/message.yml", null); MainUtil.copyFile(MainUtil.getJarFile(), "es/message.yml", null); MainUtil.copyFile(MainUtil.getJarFile(), "es/commands.yml", null); + MainUtil.copyFile(MainUtil.getJarFile(), "nl/message.yml", null); + MainUtil.copyFile(MainUtil.getJarFile(), "fr/message.yml", null); // Setting up config.yml File file = new File(this.IMP.getDirectory(), "config.yml"); Settings.IMP.PLATFORM = IMP.getPlatform().replace("\"", ""); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java b/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java index 707bf8beb..a39c0f203 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/AnvilCommands.java @@ -135,18 +135,18 @@ public class AnvilCommands { } } -// @Command( -// aliases = {"replaceall", "rea", "repall"}, -// usage = " [from-block] ", -// desc = "Replace all blocks in the selection with another", -// help = "Replace all blocks in the selection with another\n" + -// "The -d flag disabled wildcard data matching\n", -// flags = "df", -// min = 2, -// max = 4 -// ) -// @CommandPermissions("worldedit.anvil.replaceall") -// public void replaceAll(Player player, String folder, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException { + @Command( + aliases = {"replaceall", "rea", "repall"}, + usage = " [from-block] ", + desc = "Replace all blocks in the selection with another", + help = "Replace all blocks in the selection with another\n" + + "The -d flag disabled wildcard data matching\n", + flags = "df", + min = 2, + max = 4 + ) + @CommandPermissions("worldedit.anvil.replaceall") + public void replaceAll(Player player, String folder, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException { // final FaweBlockMatcher matchFrom; // if (from == null) { // matchFrom = FaweBlockMatcher.NOT_AIR; @@ -160,7 +160,7 @@ public class AnvilCommands { // ReplaceSimpleFilter filter = new ReplaceSimpleFilter(matchFrom, matchTo); // ReplaceSimpleFilter result = runWithWorld(player, folder, filter, true); // if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); -// } + } @Command( aliases = {"remapall"}, @@ -333,16 +333,16 @@ public class AnvilCommands { if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); } -// @Command( -// aliases = {"replaceallpattern", "reap", "repallpat"}, -// usage = " [from-block] ", -// desc = "Replace all blocks in the selection with another", -// flags = "dm", -// min = 2, -// max = 4 -// ) -// @CommandPermissions("worldedit.anvil.replaceall") -// public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException { + @Command( + aliases = {"replaceallpattern", "reap", "repallpat"}, + usage = " [from-block] ", + desc = "Replace all blocks in the selection with another", + flags = "dm", + min = 2, + max = 4 + ) + @CommandPermissions("worldedit.anvil.replaceall") + public void replaceAllPattern(Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException { // MCAFilterCounter filter; // if (useMap) { // if (to instanceof RandomPattern) { @@ -363,18 +363,18 @@ public class AnvilCommands { // } // MCAFilterCounter result = runWithWorld(player, folder, filter, true); // if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); -// } + } // -// @Command( -// aliases = {"countall"}, -// usage = " [hasSky] ", -// desc = "Count all blocks in a world", -// flags = "d", -// min = 2, -// max = 3 -// ) -// @CommandPermissions("worldedit.anvil.countall") -// public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData) throws WorldEditException { + @Command( + aliases = {"countall"}, + usage = " [hasSky] ", + desc = "Count all blocks in a world", + flags = "d", + min = 2, + max = 3 + ) + @CommandPermissions("worldedit.anvil.countall") + public void countAll(Player player, EditSession editSession, String folder, String arg, @Switch('d') boolean useData) throws WorldEditException { // Set searchBlocks = worldEdit.getBlocks(player, arg, true); // MCAFilterCounter filter; // if (useData || arg.contains(":")) { // Optimize for both cases @@ -388,7 +388,7 @@ public class AnvilCommands { // } // MCAFilterCounter result = runWithWorld(player, folder, filter, true); // if (result != null) player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(result.getTotal())); -// } + } @Command( aliases = {"clear", "unset"}, @@ -440,16 +440,16 @@ public class AnvilCommands { if (result != null) player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); } -// @Command( -// aliases = {"count"}, -// usage = "", -// desc = "Count blocks in a selection", -// flags = "d", -// min = 1, -// max = 2 -// ) -// @CommandPermissions("worldedit.anvil.count") -// public void count(Player player, EditSession editSession, @Selection Region selection, String arg, @Switch('d') boolean useData) throws WorldEditException { + @Command( + aliases = {"count"}, + usage = "", + desc = "Count blocks in a selection", + flags = "d", + min = 1, + max = 2 + ) + @CommandPermissions("worldedit.anvil.count") + public void count(Player player, EditSession editSession, @Selection Region selection, String arg, @Switch('d') boolean useData) throws WorldEditException { // Set searchBlocks = worldEdit.getBlocks(player, arg, true); // MCAFilterCounter filter; // if (useData || arg.contains(":")) { // Optimize for both cases @@ -463,14 +463,14 @@ public class AnvilCommands { // } // MCAFilterCounter result = runWithSelection(player, editSession, selection, filter); // if (result != null) player.print(BBC.getPrefix() + BBC.SELECTION_COUNT.format(result.getTotal())); -// } + } // -// @Command( -// aliases = {"distr"}, -// desc = "Replace all blocks in the selection with another" -// ) -// @CommandPermissions("worldedit.anvil.distr") -// public void distr(Player player, EditSession editSession, @Selection Region selection, @Switch('d') boolean useData) throws WorldEditException { + @Command( + aliases = {"distr"}, + desc = "Replace all blocks in the selection with another" + ) + @CommandPermissions("worldedit.anvil.distr") + public void distr(Player player, EditSession editSession, @Selection Region selection, @Switch('d') boolean useData) throws WorldEditException { // long total = 0; // long[] count; // MCAFilter counts; @@ -540,15 +540,15 @@ public class AnvilCommands { // player.print(BBC.getPrefix() + str); // } // } -// } + } // -// @Command( -// aliases = {"replace", "r"}, -// usage = "[from-block] ", -// desc = "Replace all blocks in the selection with another" -// ) -// @CommandPermissions("worldedit.anvil.replace") -// public void replace(Player player, EditSession editSession, @Selection Region selection, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException { + @Command( + aliases = {"replace", "r"}, + usage = "[from-block] ", + desc = "Replace all blocks in the selection with another" + ) + @CommandPermissions("worldedit.anvil.replace") + public void replace(Player player, EditSession editSession, @Selection Region selection, @Optional String from, String to, @Switch('d') boolean useData) throws WorldEditException { // final FaweBlockMatcher matchFrom; // if (from == null) { // matchFrom = FaweBlockMatcher.NOT_AIR; @@ -561,16 +561,16 @@ public class AnvilCommands { // if (result != null) { // player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); // } -// } + } // -// @Command( -// aliases = {"replacepattern", "preplace", "rp"}, -// usage = "[from-mask] ", -// desc = "Replace all blocks in the selection with a pattern" -// ) -// @CommandPermissions("worldedit.anvil.replace") -// // Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap -// public void replacePattern(Player player, EditSession editSession, @Selection Region selection, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException { + @Command( + aliases = {"replacepattern", "preplace", "rp"}, + usage = "[from-mask] ", + desc = "Replace all blocks in the selection with a pattern" + ) + @CommandPermissions("worldedit.anvil.replace") + // Player player, String folder, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap + public void replacePattern(Player player, EditSession editSession, @Selection Region selection, @Optional String from, final Pattern to, @Switch('d') boolean useData, @Switch('m') boolean useMap) throws WorldEditException { // MCAFilterCounter filter; // if (useMap) { // if (to instanceof RandomPattern) { @@ -593,7 +593,7 @@ public class AnvilCommands { // if (result != null) { // player.print(BBC.getPrefix() + BBC.VISITOR_BLOCK.format(result.getTotal())); // } -// } + } @Command( aliases = {"set"}, @@ -651,17 +651,17 @@ public class AnvilCommands { BBC.COMMAND_COPY.send(player, selection.getArea()); } -// @Command( -// aliases = {"paste"}, -// desc = "Paste chunks from your anvil clipboard", -// help = -// "Paste the chunks from your anvil clipboard.\n" + -// "The -c flag will align the paste to the chunks.", -// flags = "c" -// -// ) -// @CommandPermissions("worldedit.anvil.pastechunks") -// public void paste(Player player, LocalSession session, EditSession editSession, @Switch('c') boolean alignChunk) throws WorldEditException, IOException { + @Command( + aliases = {"paste"}, + desc = "Paste chunks from your anvil clipboard", + help = + "Paste the chunks from your anvil clipboard.\n" + + "The -c flag will align the paste to the chunks.", + flags = "c" + + ) + @CommandPermissions("worldedit.anvil.pastechunks") + public void paste(Player player, LocalSession session, EditSession editSession, @Switch('c') boolean alignChunk) throws WorldEditException, IOException { // FawePlayer fp = FawePlayer.wrap(player); // MCAClipboard clipboard = fp.getMeta(FawePlayer.METADATA_KEYS.ANVIL_CLIPBOARD); // if (clipboard == null) { @@ -689,5 +689,5 @@ public class AnvilCommands { // } catch (IOException e) { throw new RuntimeException(e); } // }); // BBC.COMMAND_PASTE.send(player, player.getPosition().toBlockVector()); -// } + } } \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/FaweParser.java b/worldedit-core/src/main/java/com/boydti/fawe/command/FaweParser.java index bd8ab2968..d312dcdd2 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/FaweParser.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/FaweParser.java @@ -25,21 +25,21 @@ public abstract class FaweParser extends InputParser { public abstract Dispatcher getDispatcher(); - public List suggestRemaining(String input, String... expected) throws InputParseException { - List remainder = StringMan.split(input, ':'); - int len = remainder.size(); - if (len != expected.length - 1) { - if (len <= expected.length - 1 && len != 0) { - if (remainder.get(len - 1).endsWith(":")) { - throw new SuggestInputParseException(null, StringMan.join(expected, ":")); - } - throw new SuggestInputParseException(null, expected[0] + ":" + input + ":" + StringMan.join(Arrays.copyOfRange(expected, len + 1, 3), ":")); - } else { - throw new SuggestInputParseException(null, StringMan.join(expected, ":")); - } - } - return remainder; - } +// public List suggestRemaining(String input, String... expected) throws InputParseException { +// List remainder = StringMan.split(input, ':'); +// int len = remainder.size(); +// if (len != expected.length - 1) { +// if (len <= expected.length - 1 && len != 0) { +// if (remainder.get(len - 1).endsWith(":")) { +// throw new SuggestInputParseException(null, StringMan.join(expected, ":")); +// } +// throw new SuggestInputParseException(null, expected[0] + ":" + input + ":" + StringMan.join(Arrays.copyOfRange(expected, len + 1, 3), ":")); +// } else { +// throw new SuggestInputParseException(null, StringMan.join(expected, ":")); +// } +// } +// return remainder; +// } protected static class ParseEntry { public boolean and; @@ -58,7 +58,7 @@ public abstract class FaweParser extends InputParser { } } - public List>> parse(String toParse) throws InputParseException { + public static List>> parse(String toParse) throws InputParseException { List>> keys = new ArrayList<>(); List inputs = new ArrayList<>(); List and = new ArrayList<>(); @@ -83,8 +83,11 @@ public abstract class FaweParser extends InputParser { int next = StringMan.findMatchingBracket(toParse, i); if (next != -1) { i = next; - continue outer; + } else { + toParse += "]"; + i = toParse.length(); } + continue outer; } } } @@ -93,7 +96,7 @@ public abstract class FaweParser extends InputParser { String full = inputs.get(i); String command = full; List args = new ArrayList<>(); - while (command.charAt(command.length() - 1) == ']') { + while (!command.isEmpty() && command.charAt(command.length() - 1) == ']') { int startPos = StringMan.findMatchingBracket(command, command.length() - 1); if (startPos == -1) break; String arg = command.substring(startPos + 1, command.length() - 1); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/MaskBinding.java b/worldedit-core/src/main/java/com/boydti/fawe/command/MaskBinding.java index 09dd76af4..2173aff6b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/MaskBinding.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/MaskBinding.java @@ -1,9 +1,16 @@ package com.boydti.fawe.command; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extension.factory.DefaultMaskParser; import com.sk89q.worldedit.util.command.parametric.ParameterData; +import com.sk89q.worldedit.world.block.BlockTypes; + import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class MaskBinding extends FaweBinding { private final WorldEdit worldEdit; @@ -15,25 +22,9 @@ public class MaskBinding extends FaweBinding { @Override public List getSuggestions(ParameterData parameter, String prefix) { -// int index = prefix.lastIndexOf(","); -// String start = index != -1 ? prefix.substring(0, index) : ""; -// String current = index != -1 ? prefix.substring(index) : prefix; -// if (current.isEmpty()) { -// return MainUtil.prepend(start, Arrays.asList(DefaultMaskParser.ALL_MASKS)); -// } -// if (current.startsWith("#") || current.startsWith("=")) { -// return new ArrayList<>(); -// } -// if (StringMan.isAlphanumeric(current.charAt(0) + "")) { -// String[] split2 = current.split(":"); -// if (split2.length == 2 || current.endsWith(":")) { -// start = (start.isEmpty() ? split2[0] : start + " " + split2[0]) + ":"; -// current = split2.length == 2 ? split2[1] : ""; -// return MainUtil.prepend(start, MainUtil.filter(current, BundledBlockData.getInstance().getBlockStates(split2[0]))); -// } -// List blocks = BundledBlockData.getInstance().getBlockNames(split2[0]); -// return MainUtil.prepend(start, blocks); -// } + if (prefix.isEmpty()) { + return Stream.concat(Stream.of("#"), BlockTypes.getNameSpaces().stream().map(n -> n + ":")).collect(Collectors.toList()); + } return super.getSuggestions(parameter, prefix); } } \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/PatternBinding.java b/worldedit-core/src/main/java/com/boydti/fawe/command/PatternBinding.java index 9c9d90055..078042d21 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/PatternBinding.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/PatternBinding.java @@ -2,9 +2,13 @@ package com.boydti.fawe.command; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.util.command.parametric.ParameterData; +import com.sk89q.worldedit.world.block.BlockTypes; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class PatternBinding extends FaweBinding { private final WorldEdit worldEdit; @@ -16,32 +20,9 @@ public class PatternBinding extends FaweBinding { @Override public List getSuggestions(ParameterData parameter, String prefix) { + if (prefix.isEmpty()) { + return Stream.concat(Stream.of("#"), BlockTypes.getNameSpaces().stream()).collect(Collectors.toList()); + } return super.getSuggestions(parameter, prefix); -// int index = prefix.lastIndexOf(",|%"); -// String start = index != -1 ? prefix.substring(0, index) : ""; -// String current = index != -1 ? prefix.substring(index) : prefix; -// if (current.isEmpty()) { -// return MainUtil.prepend(start, Arrays.asList(HashTagPatternParser.ALL_PATTERNS)); -// } -// if (current.startsWith("#") || current.startsWith("=")) { -// return new ArrayList<>(); -// } -// if ("hand".startsWith(prefix)) { -// return MainUtil.prepend(start, Arrays.asList("hand")); -// } -// if ("pos1".startsWith(prefix)) { -// return MainUtil.prepend(start, Arrays.asList("pos1")); -// } -// if (current.contains("|")) { -// return new ArrayList<>(); -// } -// String[] split2 = current.split(":"); -// if (split2.length == 2 || current.endsWith(":")) { -// start = (start.isEmpty() ? split2[0] : start + " " + split2[0]) + ":"; -// current = split2.length == 2 ? split2[1] : ""; -// return MainUtil.prepend(start, MainUtil.filter(current, BundledBlockData.getInstance().getBlockStates(split2[0]))); -// } -// List blocks = BundledBlockData.getInstance().getBlockNames(split2[0]); -// return MainUtil.prepend(start, blocks); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/command/SuggestInputParseException.java b/worldedit-core/src/main/java/com/boydti/fawe/command/SuggestInputParseException.java index 98dc8ff61..76e9243ff 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/command/SuggestInputParseException.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/command/SuggestInputParseException.java @@ -2,87 +2,73 @@ package com.boydti.fawe.command; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.StringMan; +import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extension.input.InputParseException; + +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.function.Supplier; + +import static com.google.common.base.Preconditions.checkNotNull; public class SuggestInputParseException extends InputParseException { - private final String message; + private final InputParseException cause; + private final Supplier> getSuggestions; private String prefix; - private ArrayList suggestions = new ArrayList<>(); - public SuggestInputParseException(String input, Collection inputs) { - super(""); - this.message = "Suggested input: " + StringMan.join(suggestions = getSuggestions(input, inputs), ", "); - this.prefix = ""; + public SuggestInputParseException(String msg, String prefix, Supplier> getSuggestions) { + this(new InputParseException(msg), prefix, getSuggestions); } - public SuggestInputParseException(String input, String... inputs) { - super(""); - this.message = "Suggested input: " + StringMan.join(suggestions = getSuggestions(input, inputs), ", "); - this.prefix = ""; + public static SuggestInputParseException of(Throwable other, String prefix, Supplier> getSuggestions) { + if (other instanceof InputParseException) return of((InputParseException) other, prefix, getSuggestions); + return of(new InputParseException(other.getMessage()), prefix, getSuggestions); + } + + public static SuggestInputParseException of(InputParseException other, String prefix, Supplier> getSuggestions) { + if (other instanceof SuggestInputParseException) return (SuggestInputParseException) other; + return new SuggestInputParseException(other, prefix, getSuggestions); + } + + public SuggestInputParseException(InputParseException other, String prefix, Supplier> getSuggestions) { + super(other.getMessage()); + checkNotNull(getSuggestions); + checkNotNull(other); + this.cause = other; + this.getSuggestions = getSuggestions; + this.prefix = prefix; + } + + public static SuggestInputParseException get(InvocationTargetException e) { + Throwable t = e; + while (t.getCause() != null) { + t = t.getCause(); + if (t instanceof SuggestInputParseException) return (SuggestInputParseException) t; + } + return null; + } + + @Override + public synchronized Throwable getCause() { + return cause.getCause(); } @Override public String getMessage() { - return message; + return cause.getMessage(); } + public List getSuggestions() { - return MainUtil.prepend(prefix, suggestions); + return getSuggestions.get(); } public SuggestInputParseException prepend(String input) { this.prefix = input + prefix; return this; } - - public static SuggestInputParseException get(Throwable e) { - if (e instanceof SuggestInputParseException) { - return (SuggestInputParseException) e; - } - Throwable cause = e.getCause(); - if (cause == null) { - return null; - } - return get(cause); - } - - private static ArrayList getSuggestions(String input, Collection inputs) { - ArrayList suggestions = new ArrayList<>(); - if (input != null) { - String tmp = input.toLowerCase(); - for (String s : inputs) { - if (s.startsWith(tmp)) { - suggestions.add(s); - } - - } - } - if (suggestions.isEmpty()) { - suggestions.addAll(inputs); - } - return suggestions; - } - - private static ArrayList getSuggestions(String input, String... inputs) { - ArrayList suggestions = new ArrayList<>(); - if (input != null) { - String tmp = input.toLowerCase(); - for (String s : inputs) { - if (s.startsWith(tmp)) { - suggestions.add(s); - } - - } - } - if (suggestions.isEmpty()) { - for (String s : inputs) { - suggestions.add(s); - } - } - return suggestions; - } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/config/Commands.java b/worldedit-core/src/main/java/com/boydti/fawe/config/Commands.java index a7f039b09..bc610002c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/config/Commands.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/config/Commands.java @@ -30,7 +30,8 @@ public class Commands { } } - public static Command fromArgs(String[] aliases, String usage, String desc, int min, int max, String flags, String help) { + public static Command fromArgs(String[] aliases, String usage, String desc, int min, Integer max, String flags, String help) { + int finalMax = max == null ? -1 : max; return new Command() { @Override public Class annotationType() { @@ -54,7 +55,7 @@ public class Commands { } @Override public int max() { - return max; + return finalMax; } @Override public String flags() { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java b/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java index 169df3132..3dfca6eff 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/config/Settings.java @@ -76,8 +76,6 @@ public class Settings extends Config { @Comment({ "Put any minecraft or mod jars for FAWE to be aware of block textures", }) - public String PATTERNS = "patterns"; - public String MASKS = "masks"; public String TEXTURES = "textures"; public String HEIGHTMAP = "heightmap"; public String HISTORY = "history"; @@ -88,6 +86,7 @@ public class Settings extends Config { public String CLIPBOARD = "clipboard"; @Comment("Each player has their own sub directory for schematics") public boolean PER_PLAYER_SCHEMATICS = true; + public String COMMANDS = "commands"; } @Comment("Region restriction settings") diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/RunnableVal2.java b/worldedit-core/src/main/java/com/boydti/fawe/object/RunnableVal2.java index 51a4e7841..4c3d7ca0e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/RunnableVal2.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/RunnableVal2.java @@ -1,6 +1,8 @@ package com.boydti.fawe.object; -public abstract class RunnableVal2 implements Runnable { +import java.util.function.BiConsumer; + +public abstract class RunnableVal2 implements Runnable, BiConsumer { public T value1; public U value2; @@ -23,4 +25,9 @@ public abstract class RunnableVal2 implements Runnable { run(value1, value2); return this; } + + @Override + public void accept(T t, U u) { + run(t, u); + } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/OffsetFaweClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/OffsetFaweClipboard.java index 83a86cf70..abdaf65c1 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/OffsetFaweClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/OffsetFaweClipboard.java @@ -1,12 +1,9 @@ package com.boydti.fawe.object.clipboard; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -import jdk.nashorn.internal.ir.Block; public class OffsetFaweClipboard extends AbstractDelegateFaweClipboard { private final int ox, oy, oz; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/IdPattern.java b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/IdPattern.java index 06f66961d..9634eb862 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/IdPattern.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/pattern/IdPattern.java @@ -1,15 +1,9 @@ package com.boydti.fawe.object.pattern; -import com.boydti.fawe.FaweCache; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockTypes; -import jdk.nashorn.internal.ir.Block; - import static com.google.common.base.Preconditions.checkNotNull; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/string/MutableCharSequence.java b/worldedit-core/src/main/java/com/boydti/fawe/object/string/MutableCharSequence.java index 34dba8af3..5785f9885 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/string/MutableCharSequence.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/string/MutableCharSequence.java @@ -56,6 +56,13 @@ public class MutableCharSequence implements CharSequence { return str.substring(start, start + length); } + public int indexOf(char c) { + for (int i = 0; i < length; i++) { + if (charAt(i) == c) return i; + } + return -1; + } + @Override public boolean equals(Object obj) { CharSequence anotherString = (CharSequence) obj; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java b/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java index 0027cf005..6dd0f91c7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/MainUtil.java @@ -31,6 +31,7 @@ import java.util.Map.Entry; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiConsumer; import java.util.regex.Pattern; import java.util.zip.*; import javax.imageio.ImageIO; @@ -151,13 +152,13 @@ public class MainUtil { new Exception().printStackTrace(); } - public static void traverse(Path path, final RunnableVal2 onEach) { + public static void traverse(Path path, final BiConsumer onEach) { try { Files.walkFileTree(path, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { - onEach.run(file, attrs); + onEach.accept(file, attrs); return FileVisitResult.CONTINUE; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/StringMan.java b/worldedit-core/src/main/java/com/boydti/fawe/util/StringMan.java index 256b5f278..b02de918d 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/StringMan.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/StringMan.java @@ -10,6 +10,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.function.Function; +import java.util.function.IntConsumer; +import java.util.function.IntFunction; public class StringMan { public static String replaceFromMap(final String string, final Map replacements) { @@ -33,6 +35,13 @@ public class StringMan { return sb.toString(); } + public static boolean containsAny(CharSequence sequence, String any) { + for (int i = 0; i < sequence.length(); i++) { + if (any.indexOf(sequence.charAt(i)) != -1) return true; + } + return false; + } + public static int findMatchingBracket(CharSequence sequence, int index) { char startC = sequence.charAt(index); char lookC = getMatchingBracket(startC); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java b/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java index 663d11bd6..8aa1fd03b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/util/TextureUtil.java @@ -504,7 +504,7 @@ public class TextureUtil implements TextureHolder{ } public BiomeColor getNearestBiome(int color) { - int grass = blockColors[2 << 4]; + int grass = blockColors[BlockTypes.GRASS_BLOCK.getInternalId()]; if (grass == 0) { return null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/CuboidClipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/CuboidClipboard.java index 8cf3d7d6d..3441b83b9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/CuboidClipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/CuboidClipboard.java @@ -25,12 +25,14 @@ import com.boydti.fawe.util.MainUtil; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.command.ClipboardCommands; +import com.sk89q.worldedit.command.FlattenedClipboardTransform; import com.sk89q.worldedit.command.SchematicCommands; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.regions.CuboidRegion; @@ -84,7 +86,7 @@ public class CuboidClipboard { } } - private final BlockArrayClipboard clipboard; + private BlockArrayClipboard clipboard; private AffineTransform transform; public Vector size; @@ -441,7 +443,14 @@ public class CuboidClipboard { @Deprecated public void saveSchematic(File path) throws IOException, DataException { checkNotNull(path); - SchematicFormat.MCEDIT.save(this, path); + if (transform != null && !transform.isIdentity()) { + final FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboard, transform); + BlockArrayClipboard target = new BlockArrayClipboard(result.getTransformedRegion(), UUID.randomUUID()); + target.setOrigin(clipboard.getOrigin()); + Operations.completeLegacy(result.copyTo(target)); + this.clipboard = target; + } + new Schematic(clipboard).save(path, ClipboardFormat.SPONGE_SCHEMATIC); } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index 995dfbef8..ba359ed6a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -1548,7 +1548,7 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, checkNotNull(region); checkNotNull(block); if (canBypassAll(region, false, true) && !block.hasNbtData()) { - return changes = queue.setBlocks((CuboidRegion) region, block.getInternalPropertiesId()); + return changes = queue.setBlocks((CuboidRegion) region, block.getInternalId()); } try { if (hasExtraExtents()) { @@ -1584,8 +1584,8 @@ public class EditSession extends AbstractDelegateExtent implements HasFaweQueue, if (pattern instanceof BlockPattern) { return setBlocks(region, ((BlockPattern) pattern).getBlock()); } - if (pattern instanceof BaseBlock) { - return setBlocks(region, (BaseBlock) pattern); + if (pattern instanceof BlockStateHolder) { + return setBlocks(region, (BlockStateHolder) pattern); } final BlockReplace replace = new BlockReplace(EditSession.this, pattern); final RegionVisitor visitor = new RegionVisitor(region, replace, queue instanceof MappedFaweQueue ? (MappedFaweQueue) queue : null); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BlockType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BlockType.java index 16fea362a..e76f61aa0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BlockType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BlockType.java @@ -19,19 +19,12 @@ package com.sk89q.worldedit.blocks; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.sk89q.worldedit.PlayerDirection; import com.sk89q.worldedit.registry.state.PropertyGroup; import com.sk89q.worldedit.registry.state.PropertyKey; import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypes; -import jdk.nashorn.internal.ir.Block; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.Map; +import static com.google.common.base.Preconditions.checkNotNull; /** * Block types. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java index c80e660bc..cdd5bdeb4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/MaskCommands.java @@ -62,7 +62,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"false"}, + aliases = {"false", "#false"}, desc = "Always false" ) public Mask falseMask(Extent extent) { @@ -70,7 +70,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"true"}, + aliases = {"true", "#true"}, desc = "Always true" ) public Mask trueMask(Extent extent) { @@ -262,7 +262,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"\\", "/", "#angle"}, + aliases = {"\\", "/", "#angle", "#\\", "#/"}, desc = "Restrict to specific terrain angle", help = "Restrict to specific terrain angle\n" + "The -o flag will only overlay\n" + @@ -289,7 +289,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"(", ")", "#roc"}, + aliases = {"(", ")", "#roc", "#(", "#)"}, desc = "Restrict to near specific terrain slope rate of change", help = "Restrict to near specific terrain slope rate of change\n" + "The -o flag will only overlay\n" + @@ -315,7 +315,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"^", "#extrema"}, + aliases = {"^", "#extrema", "#^"}, desc = "Restrict to near specific terrain extrema", help = "Restrict to near specific terrain extrema\n" + "The -o flag will only overlay\n" + @@ -342,7 +342,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"{"}, + aliases = {"{", "#{"}, desc = "Restricts blocks to within a specific radius range of the initial block", usage = " ", min = 2, @@ -353,7 +353,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"|"}, + aliases = {"|", "#|", "#side"}, desc = "sides with a specific number of other blocks", usage = " ", min = 3, @@ -364,7 +364,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"~"}, + aliases = {"~", "#~", "#adjacent"}, desc = "Adjacent to a specific number of other blocks", usage = " [min=1] [max=8]", min = 1, @@ -382,7 +382,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"<"}, + aliases = {"<", "#<", "#below"}, desc = "below a specific block", usage = "", min = 1, @@ -394,7 +394,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {">"}, + aliases = {">", "#>", "#above"}, desc = "above a specific block", usage = "", min = 1, @@ -406,7 +406,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"$", "#biome"}, + aliases = {"$", "#biome", "#$"}, desc = "in a specific biome", help = "in a specific biome. For a list of biomes use //biomelist", usage = "", @@ -418,7 +418,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"%"}, + aliases = {"%", "#%", "#percent"}, desc = "percentage chance", usage = "", min = 1, @@ -430,7 +430,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"="}, + aliases = {"=", "#=", "#expression"}, desc = "expression mask", usage = "", min = 1, @@ -444,7 +444,7 @@ public class MaskCommands extends MethodCommands { } @Command( - aliases = {"!", "#not", "#negate"}, + aliases = {"!", "#not", "#negate", "#!"}, desc = "Negate another mask", usage = "", min = 1, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/PatternCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/PatternCommands.java index ca847e07a..4f7aa536f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/PatternCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/PatternCommands.java @@ -418,7 +418,7 @@ public class PatternCommands extends MethodCommands { } @Command( - aliases = {"="}, + aliases = {"=", "#=", "#expression"}, desc = "Expression pattern: http://wiki.sk89q.com/wiki/WorldEdit/Expression_syntax", usage = "", min = 1, diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java index 796652140..932a8d8e4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java @@ -382,11 +382,11 @@ public class SchematicCommands extends MethodCommands { @Command(aliases = {"move", "m"}, usage = "", desc = "Move your loaded schematic", help = "Move your currently loaded schematics", min = 1, max = 1) @CommandPermissions({"worldedit.schematic.move", "worldedit.schematic.move.other"}) - public void move(final Player player, final LocalSession session, final CommandContext args) throws WorldEditException { + public void move(final Player player, final LocalSession session, String directory) throws WorldEditException { final LocalConfiguration config = this.worldEdit.getConfiguration(); final File working = this.worldEdit.getWorkingDirectoryFile(config.saveDir); final File dir = Settings.IMP.PATHS.PER_PLAYER_SCHEMATICS ? new File(working, player.getUniqueId().toString()) : working; - File destDir = new File(dir, args.getString(0)); + File destDir = new File(dir, directory); if (!MainUtil.isInSubDirectory(working, destDir)) { player.printError("Directory " + destDir + " does not exist!"); return; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java index eebadfc38..7a6a859bf 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/WorldEditCommands.java @@ -36,10 +36,8 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.event.platform.ConfigurationLoadEvent; -import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.extension.platform.Capability; -import com.sk89q.worldedit.extension.platform.Platform; -import com.sk89q.worldedit.extension.platform.PlatformManager; +import com.sk89q.worldedit.extension.platform.*; + import java.io.IOException; import java.net.URL; import java.text.DateFormat; @@ -113,6 +111,7 @@ public class WorldEditCommands { we.getPlatformManager().queryCapability(Capability.CONFIGURATION).reload(); we.getEventBus().post(new ConfigurationLoadEvent(we.getPlatformManager().queryCapability(Capability.CONFIGURATION).getConfiguration())); Fawe.get().setupConfigs(); + CommandManager.getInstance().register(we.getPlatformManager().queryCapability(Capability.USER_COMMANDS)); actor.print(BBC.getPrefix() + "Reloaded WorldEdit " + we.getVersion() + " and FAWE (" + Fawe.get().getVersion() + ")"); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultBlockParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultBlockParser.java index d028ec40b..2d3a74288 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultBlockParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultBlockParser.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extension.factory; +import com.boydti.fawe.command.SuggestInputParseException; import com.boydti.fawe.jnbt.JSON2NBT; import com.boydti.fawe.jnbt.NBTException; import com.boydti.fawe.util.MathMan; @@ -26,6 +27,7 @@ import com.boydti.fawe.util.StringMan; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.extension.platform.Platform; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.MobSpawnerBlock; @@ -50,6 +52,11 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.registry.LegacyMapper; import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Parses block input strings. @@ -283,8 +290,13 @@ public class DefaultBlockParser extends InputParser { break; } } - if (!worldEdit.getPlatformManager().queryCapability(Capability.USER_COMMANDS).isValidMobType(mobName)) { - throw new NoMatchException("Unknown mob type '" + mobName + "'"); + Platform capability = worldEdit.getPlatformManager().queryCapability(Capability.USER_COMMANDS); + if (!capability.isValidMobType(mobName)) { + final String finalMobName = mobName.toLowerCase(); + throw new SuggestInputParseException("Unknown mob type '" + mobName + "'", mobName, () -> Stream.of(MobType.values()) + .map(m -> m.getName().toLowerCase()) + .filter(s -> s.startsWith(finalMobName)) + .collect(Collectors.toList())); } return new MobSpawnerBlock(state, mobName); } else { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java index 6043d91c0..a38012653 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/DefaultMaskParser.java @@ -1,11 +1,14 @@ package com.sk89q.worldedit.extension.factory; import com.boydti.fawe.command.FaweParser; +import com.boydti.fawe.command.SuggestInputParseException; import com.boydti.fawe.config.BBC; import com.boydti.fawe.util.StringMan; +import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandLocals; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.blocks.BaseBlock; +import com.sk89q.worldedit.util.command.*; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.command.MaskCommands; import com.sk89q.worldedit.extension.input.InputParseException; @@ -17,14 +20,18 @@ import com.sk89q.worldedit.function.mask.*; import com.sk89q.worldedit.internal.command.ActorAuthorizer; import com.sk89q.worldedit.internal.command.WorldEditBinding; import com.sk89q.worldedit.session.request.Request; -import com.sk89q.worldedit.util.command.Dispatcher; -import com.sk89q.worldedit.util.command.SimpleDispatcher; import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockTypes; import java.util.*; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class DefaultMaskParser extends FaweParser { private final Dispatcher dispatcher; @@ -50,11 +57,11 @@ public class DefaultMaskParser extends FaweParser { @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { - if (input.isEmpty()) return null; + if (input.isEmpty()) { + throw new SuggestInputParseException("No input provided", "", () -> Stream.concat(Stream.of("#", ",", "&"), BlockTypes.getNameSpaces().stream().map(n -> n + ":")).collect(Collectors.toList())); + } Extent extent = Request.request().getExtent(); if (extent == null) extent = context.getExtent(); -// List intersection = new ArrayList<>(); -// List union = new ArrayList<>(); List> masks = new ArrayList<>(); masks.add(new ArrayList<>()); @@ -63,12 +70,12 @@ public class DefaultMaskParser extends FaweParser { if (actor != null) { locals.put(Actor.class, actor); } - // try { List>> parsed = parse(input); for (Map.Entry> entry : parsed) { ParseEntry pe = entry.getKey(); - String command = pe.input; + final String command = pe.input; + String full = pe.full; Mask mask = null; if (command.isEmpty()) { mask = parseFromInput(StringMan.join(entry.getValue(), ','), context); @@ -79,76 +86,107 @@ public class DefaultMaskParser extends FaweParser { if (charMask && input.charAt(0) == '=') { return parseFromInput(char0 + "[" + input.substring(1) + "]", context); } - if (mask == null) { - // Legacy syntax - if (charMask) { - switch (char0) { - case '\\': // - case '/': // - case '{': // - case '$': // - case '%': { - command = command.substring(1); - String value = command + ((entry.getValue().isEmpty()) ? "" : "[" + StringMan.join(entry.getValue(), "][") + "]"); - if (value.contains(":")) { - if (value.charAt(0) == ':') value.replaceFirst(":", ""); - value = value.replaceAll(":", "]["); - } - mask = parseFromInput(char0 + "[" + value + "]", context); - break; + if (char0 == '#') { + throw new SuggestInputParseException(new NoMatchException("Unkown mask: " + full + ", See: //masks"), full, + () -> { + if (full.length() == 1) return new ArrayList<>(dispatcher.getPrimaryAliases()); + return dispatcher.getAliases().stream().filter( + s -> s.startsWith(command.toLowerCase()) + ).collect(Collectors.toList()); } - case '|': - case '~': - case '<': - case '>': - case '!': - input = input.substring(input.indexOf(char0) + 1); - mask = parseFromInput(char0 + "[" + input + "]", context); - if (actor != null) { - BBC.COMMAND_CLARIFYING_BRACKET.send(actor, char0 + "[" + input + "]"); - } - return mask; + ); + } + // Legacy syntax + if (charMask) { + switch (char0) { + case '\\': // + case '/': // + case '{': // + case '$': // + case '%': { + String value = command.substring(1) + ((entry.getValue().isEmpty()) ? "" : "[" + StringMan.join(entry.getValue(), "][") + "]"); + if (value.contains(":")) { + if (value.charAt(0) == ':') value.replaceFirst(":", ""); + value = value.replaceAll(":", "]["); + } + mask = parseFromInput("#" + char0 + "[" + value + "]", context); + break; } - } - if (mask == null) { - if (command.startsWith("[")) { - int end = command.lastIndexOf(']'); - mask = parseFromInput(command.substring(1, end == -1 ? command.length() : end), context); - } else { - List entries = entry.getValue(); - try { - BlockMaskBuilder builder = new BlockMaskBuilder().addRegex(pe.full); - if (builder.isEmpty()) { - try { - context.setPreferringWildcard(true); - context.setRestricted(false); - BlockStateHolder block = worldEdit.getBlockFactory().parseFromInput(pe.full, context); - builder.add(block); - } catch (NoMatchException e) { - throw new NoMatchException(e.getMessage() + " See: //masks"); - } - } - mask = builder.build(extent); - } catch (PatternSyntaxException regex) { - throw new InputParseException(regex.getMessage()); + case '|': + case '~': + case '<': + case '>': + case '!': + input = input.substring(input.indexOf(char0) + 1); + mask = parseFromInput(char0 + "[" + input + "]", context); + if (actor != null) { + BBC.COMMAND_CLARIFYING_BRACKET.send(actor, char0 + "[" + input + "]"); } + return mask; + } + } + if (mask == null) { + if (command.startsWith("[")) { + int end = command.lastIndexOf(']'); + mask = parseFromInput(command.substring(1, end == -1 ? command.length() : end), context); + } else { + List entries = entry.getValue(); + BlockMaskBuilder builder = new BlockMaskBuilder(); +// if (StringMan.containsAny(full, "\\^$.|?+(){}<>~$!%^&*+-/")) + { + try { + builder.addRegex(full); + } catch (SuggestInputParseException rethrow) { + throw rethrow; + } catch (InputParseException ignore) {} + } + if (mask == null) { + context.setPreferringWildcard(true); + context.setRestricted(false); + BlockStateHolder block = worldEdit.getBlockFactory().parseFromInput(full, context); + builder.add(block); + mask = builder.build(extent); } } } } else { List args = entry.getValue(); - if (!args.isEmpty()) { - command += " " + StringMan.join(args, " "); + String cmdArgs = ((args.isEmpty()) ? "" : " " + StringMan.join(args, " ")); + try { + mask = (Mask) dispatcher.call(command + cmdArgs, locals, new String[0]); + } catch (SuggestInputParseException rethrow) { + throw rethrow; + } catch (Throwable e) { + throw SuggestInputParseException.of(e, full, () -> { + try { + List suggestions = dispatcher.get(command).getCallable().getSuggestions(cmdArgs, locals); + if (suggestions.size() <= 2) { + for (int i = 0; i < suggestions.size(); i++) { + String suggestion = suggestions.get(i); + if (suggestion.indexOf(' ') != 0) { + String[] split = suggestion.split(" "); + suggestion = BBC.color("[" + StringMan.join(split, "][") + "]"); + suggestions.set(i, suggestion); + } + } + } + return suggestions; + } catch (CommandException e1) { + throw new InputParseException(e1.getMessage()); + } catch (Throwable e2) { + e2.printStackTrace(); + throw new InputParseException(e2.getMessage()); + } + }); } - mask = (Mask) dispatcher.call(command, locals, new String[0]); } if (pe.and) { masks.add(new ArrayList<>()); } masks.get(masks.size() - 1).add(mask); } - } catch (InputParseException ignore) { - throw ignore; + } catch (InputParseException rethrow) { + throw rethrow; } catch (Throwable e) { e.printStackTrace(); throw new InputParseException(e.getMessage(), e); @@ -169,6 +207,4 @@ public class DefaultMaskParser extends FaweParser { return null; } } - - } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java index 4242b4be0..e4e763f2b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/HashTagPatternParser.java @@ -1,6 +1,8 @@ package com.sk89q.worldedit.extension.factory; import com.boydti.fawe.command.FaweParser; +import com.boydti.fawe.command.SuggestInputParseException; +import com.boydti.fawe.config.BBC; import com.boydti.fawe.object.random.TrueRandom; import com.boydti.fawe.util.StringMan; import com.sk89q.minecraft.util.commands.CommandException; @@ -20,9 +22,13 @@ import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.SimpleDispatcher; import com.sk89q.worldedit.util.command.parametric.ParametricBuilder; +import com.sk89q.worldedit.world.block.BlockTypes; + import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class HashTagPatternParser extends FaweParser { private final Dispatcher dispatcher; @@ -47,7 +53,9 @@ public class HashTagPatternParser extends FaweParser { @Override public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { - if (input.isEmpty()) return null; + if (input.isEmpty()) { + throw new SuggestInputParseException("No input provided", "", () -> Stream.concat(Stream.of("#", ",", "&"), BlockTypes.getNameSpaces().stream().map(n -> n + ":")).collect(Collectors.toList())); + } List chances = new ArrayList<>(); List patterns = new ArrayList<>(); final CommandLocals locals = new CommandLocals(); @@ -58,7 +66,8 @@ public class HashTagPatternParser extends FaweParser { try { for (Map.Entry> entry : parse(input)) { ParseEntry pe = entry.getKey(); - String command = pe.input; + final String command = pe.input; + String full = pe.full; Pattern pattern = null; double chance = 1; if (command.isEmpty()) { @@ -70,11 +79,22 @@ public class HashTagPatternParser extends FaweParser { if (charMask && input.charAt(0) == '=') { return parseFromInput(char0 + "[" + input.substring(1) + "]", context); } + if (char0 == '#') { + throw new SuggestInputParseException(new NoMatchException("Unkown pattern: " + full + ", See: //patterns"), full, + () -> { + if (full.length() == 1) return new ArrayList<>(dispatcher.getPrimaryAliases()); + return dispatcher.getAliases().stream().filter( + s -> s.startsWith(command.toLowerCase()) + ).collect(Collectors.toList()); + } + ); + } + + if (charMask) { switch (char0) { case '$': { - command = command.substring(1); - String value = command + ((entry.getValue().isEmpty()) ? "" : "[" + StringMan.join(entry.getValue(), "][") + "]"); + String value = command.substring(1) + ((entry.getValue().isEmpty()) ? "" : "[" + StringMan.join(entry.getValue(), "][") + "]"); if (value.contains(":")) { if (value.charAt(0) == ':') value.replaceFirst(":", ""); value = value.replaceAll(":", "]["); @@ -92,12 +112,12 @@ public class HashTagPatternParser extends FaweParser { int percentIndex = command.indexOf('%'); if (percentIndex != -1) { // Legacy percent pattern chance = Expression.compile(command.substring(0, percentIndex)).evaluate(); - command = command.substring(percentIndex + 1); + String value = command.substring(percentIndex + 1); if (!entry.getValue().isEmpty()) { - if (!command.isEmpty()) command += " "; - command += StringMan.join(entry.getValue(), " "); + if (!value.isEmpty()) value += " "; + value += StringMan.join(entry.getValue(), " "); } - pattern = parseFromInput(command, context); + pattern = parseFromInput(value, context); } else { // legacy block pattern try { pattern = worldEdit.getBlockFactory().parseFromInput(pe.full, context); @@ -109,18 +129,45 @@ public class HashTagPatternParser extends FaweParser { } } else { List args = entry.getValue(); - if (!args.isEmpty()) { - command += " " + StringMan.join(args, " "); + String cmdArgs = ((args.isEmpty()) ? "" : " " + StringMan.join(args, " ")); + try { + pattern = (Pattern) dispatcher.call(command + cmdArgs, locals, new String[0]); + } catch (SuggestInputParseException rethrow) { + throw rethrow; + } catch (Throwable e) { + throw SuggestInputParseException.of(e, full, () -> { + try { + List suggestions = dispatcher.get(command).getCallable().getSuggestions(cmdArgs, locals); + if (suggestions.size() <= 2) { + for (int i = 0; i < suggestions.size(); i++) { + String suggestion = suggestions.get(i); + if (suggestion.indexOf(' ') != 0) { + String[] split = suggestion.split(" "); + suggestion = BBC.color("[" + StringMan.join(split, "][") + "]"); + suggestions.set(i, suggestion); + } + } + } + return suggestions; + } catch (CommandException e1) { + throw new InputParseException(e1.getMessage()); + } catch (Throwable e2) { + e2.printStackTrace(); + throw new InputParseException(e2.getMessage()); + } + }); } - pattern = (Pattern) dispatcher.call(command, locals, new String[0]); } if (pattern != null) { patterns.add(pattern); chances.add(chance); } } - } catch (CommandException | ExpressionException e) { - throw new RuntimeException(e); + } catch (InputParseException rethrow) { + throw rethrow; + } catch (Throwable e) { + e.printStackTrace(); + throw new InputParseException(e.getMessage(), e); } if (patterns.isEmpty()) { return null; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java index 01fef9803..9d826fb25 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java @@ -39,7 +39,6 @@ public final class MaskFactory extends AbstractFactory { */ public MaskFactory(WorldEdit worldEdit) { super(worldEdit); - parsers.add(new DefaultMaskParser(worldEdit)); } 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 1bf1b4785..08d453f66 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 @@ -39,9 +39,7 @@ public final class PatternFactory extends AbstractFactory { */ public PatternFactory(WorldEdit worldEdit) { super(worldEdit); - parsers.add(new HashTagPatternParser(worldEdit)); - parsers.add(new SingleBlockPatternParser(worldEdit)); } } 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 0134c7fbc..7d301642a 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 @@ -334,7 +334,8 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable { @Override public Location getBlockIn() { - return getLocation(); + Location loc = getLocation(); + return new Location(loc.getExtent(), loc.toBlockVector(), loc.getDirection()); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java index 4d1341c04..6b46a953a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/CommandManager.java @@ -48,8 +48,10 @@ import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; import com.sk89q.worldedit.function.factory.Deform; import com.sk89q.worldedit.function.factory.Deform.Mode; import com.sk89q.worldedit.internal.command.*; +import com.sk89q.worldedit.scripting.CommandScriptLoader; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.auth.AuthorizationException; +import com.sk89q.worldedit.util.command.CallableProcessor; import com.sk89q.worldedit.util.command.CommandCallable; import com.sk89q.worldedit.util.command.Dispatcher; import com.sk89q.worldedit.util.command.InvalidUsageException; @@ -184,13 +186,13 @@ public final class CommandManager { * @param clazz The class containing all the sub command methods * @param aliases The aliases to give the command */ - public void registerCommands(Object clazz, Object processor, String... aliases) { + public void registerCommands(Object clazz, CallableProcessor processor, String... aliases) { if (platform != null) { if (aliases.length == 0) { - builder.registerMethodsAsCommands(dispatcher, clazz); + builder.registerMethodsAsCommands(dispatcher, clazz, processor); } else { DispatcherNode graph = new CommandGraph().builder(builder).commands(); - graph = graph.registerMethods(clazz); + graph = graph.registerMethods(clazz, processor); dispatcher.registerCommand(graph.graph().getDispatcher(), aliases); } platform.registerCommands(dispatcher); @@ -298,6 +300,13 @@ public final class CommandManager { public void register(Platform platform) { log.log(Level.FINE, "Registering commands with " + platform.getClass().getCanonicalName()); + this.platform = null; + + try { + new CommandScriptLoader().load(); + } catch (Throwable e) { + e.printStackTrace(); + } LocalConfiguration config = platform.getConfiguration(); boolean logging = config.logCommands; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java index 6ec3c459b..179889559 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java @@ -1,6 +1,5 @@ package com.sk89q.worldedit.extent.transform; -import com.boydti.fawe.FaweCache; import com.boydti.fawe.object.extent.ResettableExtent; import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.jnbt.ByteTag; @@ -10,29 +9,23 @@ import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.Vector2D; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.helper.MCDirections; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Transform; import com.sk89q.worldedit.registry.state.AbstractProperty; import com.sk89q.worldedit.registry.state.DirectionalProperty; -import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.world.biome.BaseBiome; -import com.sk89q.worldedit.world.block.*; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.registry.BlockRegistry; -import jdk.nashorn.internal.ir.Block; +import com.sk89q.worldedit.world.block.BlockTypes; +import javax.annotation.Nullable; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; -import javax.annotation.Nullable; - - -import static com.google.common.base.Preconditions.checkNotNull; /** * Transforms blocks themselves (but not their position) according to a diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java index d606e267b..9398c4e0a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockMaskBuilder.java @@ -1,5 +1,6 @@ package com.sk89q.worldedit.function.mask; +import com.boydti.fawe.command.SuggestInputParseException; import com.boydti.fawe.object.collection.FastBitSet; import com.boydti.fawe.object.string.MutableCharSequence; import com.boydti.fawe.util.StringMan; @@ -15,7 +16,10 @@ import com.sk89q.worldedit.world.block.BlockTypes; import java.util.*; import java.util.function.BiPredicate; +import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; public class BlockMaskBuilder { private static final Operator GREATER = (a, b) -> a > b; @@ -30,41 +34,51 @@ public class BlockMaskBuilder { boolean test(int left, int right); } - public BlockMaskBuilder filterRegex(BlockType blockType, PropertyKey key, String regex) { + private boolean filterRegex(BlockType blockType, PropertyKey key, String regex) { Property property = blockType.getProperty(key); - if (property == null) return this; + if (property == null) return false; List values = property.getValues(); + boolean result = false; for (int i = 0; i < values.size(); i++) { Object value = values.get(i); - if (!value.toString().matches(regex)) { + if (!value.toString().matches(regex) && has(blockType, property, i)) { filter(blockType, property, i); + result = true; } } - return this; + return result; } - private void filterOperator(BlockType blockType, PropertyKey key, Operator operator, CharSequence value) { + private boolean filterOperator(BlockType blockType, PropertyKey key, Operator operator, CharSequence value) { Property property = blockType.getProperty(key); - if (property == null) return; + if (property == null) return false; int index = property.getIndexFor(value); List values = property.getValues(); + boolean result = false; for (int i = 0; i < values.size(); i++) { - if (!operator.test(index, i)) { + if (!operator.test(index, i) && has(blockType, property, i)) { filter(blockType, property, i); + result = true; } } + return result; } - private void filterRegexOrOperator(BlockType type, PropertyKey key, Operator operator, CharSequence value) { + private boolean filterRegexOrOperator(BlockType type, PropertyKey key, Operator operator, CharSequence value) { + boolean result = false; if (!type.hasProperty(key)) { - if (operator == EQUAL) remove(type); + if (operator == EQUAL) { + result = bitSets[type.getInternalId()] != null; + remove(type); + } } else if (value.length() == 0) { } else if ((operator == EQUAL || operator == EQUAL_OR_NULL) && !StringMan.isAlphanumericUnd(value)) { - filterRegex(type, key, value.toString()); + result = filterRegex(type, key, value.toString()); } else { - filterOperator(type, key, operator, value); + result = filterOperator(type, key, operator, value); } + return result; } public BlockMaskBuilder addRegex(String input) throws InputParseException { @@ -90,8 +104,13 @@ public class BlockMaskBuilder { add(myType); } } + if (blockTypeList.isEmpty()) { + throw new InputParseException("No block found for " + input); + } if (blockTypeList.size() == 1) type = blockTypeList.get(0); } + // Empty string + charSequence.setSubstring(0, 0); PropertyKey key = null; int length = input.length(); @@ -109,13 +128,40 @@ public class BlockMaskBuilder { case ']': case ',': { charSequence.setSubstring(last, i); - char firstChar = input.charAt(last + 1); - if (type != null) filterRegexOrOperator(type, key, operator, charSequence); + if (key == null && PropertyKey.get(charSequence) == null) suggest(input, charSequence.toString(), type != null ? Collections.singleton(type) : blockTypeList); + if (operator == null) throw new SuggestInputParseException("No operator for " + input, "", () -> Arrays.asList("=", "~", "!", "<", ">", "<=", ">=")); + boolean filtered = false; + if (type != null) { + filtered = filterRegexOrOperator(type, key, operator, charSequence); + } else { for (BlockTypes myType : blockTypeList) { - filterRegexOrOperator(myType, key, operator, charSequence); + filtered |= filterRegexOrOperator(myType, key, operator, charSequence); } } + if (!filtered) { + String value = charSequence.toString(); + final PropertyKey fKey = key; + Collection types = type != null ? Collections.singleton(type) : blockTypeList; + throw new SuggestInputParseException("No value for " + input, input, () -> { + HashSet values = new HashSet<>(); + types.forEach(t -> { + if (t.hasProperty(fKey)) { + Property p = t.getProperty(fKey); + for (int j = 0; j < p.getValues().size(); j++) { + if (has(t, p, j)) { + String o = p.getValues().get(j).toString(); + if (o.startsWith(value)) values.add(o); + } + } + } + }); + return new ArrayList<>(values); + }); + } + // Reset state + key = null; + operator = null; last = i + 1; break; } @@ -144,7 +190,11 @@ public class BlockMaskBuilder { operator = extra ? GREATER_EQUAL : GREATER; break; } - if (charSequence.length() > 0) key = PropertyKey.get(charSequence); + if (charSequence.length() > 0 || key == null) { + key = PropertyKey.get(charSequence); + if (key == null) + suggest(input, charSequence.toString(), type != null ? Collections.singleton(type) : blockTypeList); + } last = i + 1; break; } @@ -166,7 +216,24 @@ public class BlockMaskBuilder { return this; } - ///// end test ///// + private boolean has(BlockType type, Property property, int index) { + AbstractProperty prop = (AbstractProperty) property; + long[] states = bitSets[type.getInternalId()]; + if (states == null) return false; + List values = prop.getValues(); + int localI = index << prop.getBitOffset() >> BlockTypes.BIT_OFFSET; + return (states == BlockMask.ALL || FastBitSet.get(states, localI)); + } + + private void suggest(String input, String property, Collection finalTypes) throws InputParseException { + throw new SuggestInputParseException(input + " does not have: " + property, input, () -> { + Set keys = new HashSet<>(); + finalTypes.forEach(t -> t.getProperties().stream().forEach(p -> keys.add(p.getKey()))); + return keys.stream().map(p -> p.getId()).filter(p -> p.startsWith(property)).collect(Collectors.toList()); + }); + } + + ///// end internal ///// private long[][] bitSets; @@ -230,7 +297,9 @@ public class BlockMaskBuilder { public BlockMaskBuilder filter(BlockType type) { for (int i = 0; i < bitSets.length; i++) { - if (i != type.getInternalId()) bitSets[i] = null; + if (i != type.getInternalId()) { + bitSets[i] = null; + } } return this; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java index d2578fcf5..f8a4416e5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/WorldEditBinding.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.internal.command; import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.util.MathMan; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.LocalSession; @@ -355,18 +356,7 @@ public class WorldEditBinding extends BindingHelper { public BaseBiome getBiomeType(ArgumentStack context) throws ParameterException, WorldEditException { String input = context.next(); if (input != null) { - Actor actor = context.getContext().getLocals().get(Actor.class); - World world; - if (actor instanceof Entity) { - Extent extent = ((Entity) actor).getExtent(); - if (extent instanceof World) { - world = (World) extent; - } else { - throw new ParameterException("A world is required."); - } - } else { - throw new ParameterException("An entity is required."); - } + if (MathMan.isInteger(input)) return new BaseBiome(Integer.parseInt(input)); BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager() .queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/DirectionalProperty.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/DirectionalProperty.java index c0d4d1d2d..bada26598 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/DirectionalProperty.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/DirectionalProperty.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.registry.state; import com.sk89q.worldedit.util.Direction; +import java.util.Arrays; import java.util.List; import javax.annotation.Nullable; @@ -36,6 +37,7 @@ public class DirectionalProperty extends AbstractProperty { public DirectionalProperty(final String name, final List values, int bitOffset) { super(name, values, bitOffset); this.map = new int[Direction.values().length]; + Arrays.fill(this.map, -1); for (int i = 0; i < values.size(); i++) { this.map[values.get(i).ordinal()] = i; } @@ -53,7 +55,9 @@ public class DirectionalProperty extends AbstractProperty { @Override public int getIndexFor(CharSequence string) throws IllegalArgumentException { - return getIndex(Direction.get(string)); + Direction dir = Direction.get(string); + if (dir == null) return -1; + return getIndex(dir); } @Nullable diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/EnumProperty.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/EnumProperty.java index 4643552e6..be7b82ef3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/EnumProperty.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/state/EnumProperty.java @@ -51,7 +51,8 @@ public class EnumProperty extends AbstractProperty { @Override public int getIndexFor(CharSequence string) throws IllegalArgumentException { - return offsets.get(string); + Integer value = offsets.get(string); + return value == null ? -1 : value; } @Nullable diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CommandScriptLoader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CommandScriptLoader.java new file mode 100644 index 000000000..94d50fd2a --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/CommandScriptLoader.java @@ -0,0 +1,106 @@ +package com.sk89q.worldedit.scripting; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweAPI; +import com.boydti.fawe.command.FaweParser; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.RunnableVal2; +import com.boydti.fawe.util.MainUtil; +import com.google.common.base.Charsets; +import com.google.common.base.Function; +import com.google.common.io.CharStreams; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.BrushProcessor; +import com.sk89q.worldedit.extension.factory.DefaultMaskParser; +import com.sk89q.worldedit.extension.factory.HashTagPatternParser; +import com.sk89q.worldedit.extension.platform.CommandManager; +import com.sk89q.worldedit.util.command.CallableProcessor; +import com.sk89q.worldedit.util.command.ProcessedCallable; +import com.sk89q.worldedit.util.command.parametric.FunctionParametricCallable; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +public class CommandScriptLoader { + private final NashornCraftScriptEngine engine; + private final String loader; + + public CommandScriptLoader() throws IOException { + this.engine = new NashornCraftScriptEngine(); + + try (InputStream inputStream = WorldEdit.class.getResourceAsStream("/cs_adv.js")) { + this.loader = CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8)); + } + } + + /** + * Load all file commands + * @throws Throwable + */ + public void load() throws Throwable { + File commands = MainUtil.getFile(Fawe.imp().getDirectory(), Settings.IMP.PATHS.COMMANDS); + if (commands.exists()) { + for (File file : commands.listFiles()) add(new String[0], file); + } + } + + private void add(String[] aliases, File file) throws Throwable { + if (file.isDirectory()) { + if (aliases.length == 0) { + String[] newAliases = new String[] {file.getName()}; + for (File newFile : file.listFiles()) { + add(newAliases, newFile); + } + } else { + Fawe.debug("Ignoring nested directory: " + file); + } + } else { + String name = file.getName(); + if (name.endsWith(".js")) { + Fawe.debug("Loading script: " + name); + List cmds = getCommands(file, Collections.emptyMap()); + FaweParser parser = null; + if (aliases.length == 1) { + switch (aliases[0]) { + case "brush": + if (!cmds.isEmpty()) { + BrushProcessor processor = new BrushProcessor(WorldEdit.getInstance()); + for (FunctionParametricCallable cmd : cmds) { + ProcessedCallable processed = new ProcessedCallable(cmd, processor); + CommandManager.getInstance().registerCommand(aliases, cmd.getCommand(), processed); + } + } + return; + case "patterns": + parser = FaweAPI.getParser(HashTagPatternParser.class); + break; + case "masks": + parser = FaweAPI.getParser(DefaultMaskParser.class); + break; + } + if (parser != null) { + for (FunctionParametricCallable cmd : cmds) { + parser.getDispatcher().registerCommand(cmd, cmd.getCommand().aliases()); + } + return; + } + } + for (FunctionParametricCallable cmd : cmds) { + CommandManager.getInstance().registerCommand(aliases, cmd.getCommand(), cmd); + } + } + } + } + + private List getCommands(File file, Map vars) throws Throwable { + String script = new String(Files.readAllBytes(file.toPath())) + loader; + return (List) engine.evaluate(script, file.getPath(), vars); + } +} \ No newline at end of file diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/NashornCraftScriptEngine.java b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/NashornCraftScriptEngine.java new file mode 100644 index 000000000..02a377f22 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/scripting/NashornCraftScriptEngine.java @@ -0,0 +1,79 @@ +/* + * 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.scripting; + +import com.boydti.fawe.Fawe; +import com.sk89q.worldedit.WorldEditException; +import jdk.nashorn.api.scripting.NashornScriptEngineFactory; + +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import javax.script.SimpleBindings; +import java.util.Map; + +public class NashornCraftScriptEngine implements CraftScriptEngine { + private static NashornScriptEngineFactory FACTORY; + private int timeLimit; + + @Override + public void setTimeLimit(int milliseconds) { + timeLimit = milliseconds; + } + + @Override + public int getTimeLimit() { + return timeLimit; + } + + @Override + public Object evaluate(String script, String filename, Map args) throws Throwable { + ClassLoader cl = Fawe.get().getClass().getClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + synchronized (NashornCraftScriptEngine.class) { + if (FACTORY == null) FACTORY = new NashornScriptEngineFactory(); + } + ; + ScriptEngine engine = FACTORY.getScriptEngine("--language=es6"); + SimpleBindings bindings = new SimpleBindings(); + + for (Map.Entry entry : args.entrySet()) { + bindings.put(entry.getKey(), entry.getValue()); + } + + try { + Object result = engine.eval(script, bindings); + return result; + } catch (Error e) { + e.printStackTrace(); + throw new ScriptException(e.getMessage()); + } catch (Throwable e) { + e.printStackTrace(); + while (e.getCause() != null) { + e = e.getCause(); + } + if (e instanceof WorldEditException) { + throw e; + } + throw e; + } finally { + } + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java index 2ccd0c6ce..ca03998b0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/SimpleDispatcher.java @@ -72,6 +72,7 @@ public class SimpleDispatcher implements Dispatcher { continue; } else { Fawe.debug("Replacing commands is currently undefined behavior: " + StringMan.getString(alias)); + commands.put(lower, mapping); continue; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java index 2a5ee0ccf..a26a195d4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/binding/PrimitiveBindings.java @@ -86,8 +86,8 @@ public final class PrimitiveBindings extends BindingHelper { * @throws ParameterException on error */ @BindingMatch(type = { Boolean.class, boolean.class }, - behavior = BindingBehavior.CONSUMES, - consumedCount = 1) + behavior = BindingBehavior.CONSUMES, + consumedCount = 1) public Boolean getBoolean(ArgumentStack context) throws ParameterException { return context.nextBoolean(); } @@ -117,7 +117,6 @@ public final class PrimitiveBindings extends BindingHelper { throw new ParameterException(String.format( "Expected '%s' to be a number or valid math expression (error: %s)", input, e.getMessage())); } - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/AParametricCallable.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/AParametricCallable.java index 59e57d567..a7cd05137 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/AParametricCallable.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/AParametricCallable.java @@ -1,14 +1,14 @@ package com.sk89q.worldedit.util.command.parametric; import com.boydti.fawe.command.SuggestInputParseException; +import com.boydti.fawe.config.BBC; +import com.boydti.fawe.util.chat.UsageMessage; import com.sk89q.minecraft.util.commands.*; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.util.command.*; import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; public abstract class AParametricCallable implements CommandCallable { // private final ParametricBuilder builder; @@ -206,101 +206,95 @@ public abstract class AParametricCallable implements CommandCallable { @Override public List getSuggestions(String arguments, CommandLocals locals) throws CommandException { String[] split = ("ignored" + " " + arguments).split(" ", -1); + + // &a &f + // &cerrors + CommandContext context = new CommandContext(split, getValueFlags(), !arguments.endsWith(" "), locals); ContextArgumentStack scoped = new ContextArgumentStack(context); SuggestionContext suggestable = context.getSuggestionContext(); + List suggestions = new ArrayList<>(2); + ParameterData parameter = null; + ParameterData[] parameters = getParameters(); + String consumed = ""; - // For /command -f | - // For /command -f flag| - if (suggestable.forFlag()) { - for (int i = 0; i < getParameters().length; i++) { - ParameterData parameter = getParameters()[i]; + boolean hasSuggestion = false; + int maxConsumedI = 0; // The maximum argument index + int minConsumedI = 0; // The minimum argument that has been consumed + // Collect parameters + try { + for (;maxConsumedI < parameters.length; maxConsumedI++) { + parameter = parameters[maxConsumedI]; + if (parameter.getBinding().getBehavior(parameter) != BindingBehavior.PROVIDES) { + // Parse the user input into a method argument + ArgumentStack usedArguments = getScopedContext(parameter, scoped); - if (parameter.getFlag() == suggestable.getFlag()) { - String prefix = context.getFlag(parameter.getFlag()); - if (prefix == null) { - prefix = ""; - } - -// System.out.println("(0) Return get binding suggestions " + parameter + " | " + prefix); - return parameter.getBinding().getSuggestions(parameter, prefix); - } - } - - // This should not happen -// System.out.println("(1) This should not happen"); - return new ArrayList(); - } - - int consumerIndex = 0; - ParameterData lastConsumer = null; - String lastConsumed = null; - - for (int i = 0; i < getParameters().length; i++) { - ParameterData parameter = getParameters()[i]; - if (parameter.getFlag() != null) { - continue; // We already handled flags - } - try { - scoped.mark(); - parameter.getBinding().bind(parameter, scoped, true); - if (scoped.wasConsumed()) { - lastConsumer = parameter; - lastConsumed = context.getString(scoped.position() - 1); - consumerIndex++; - } - } catch (MissingParameterException e) { - // For /command value1 |value2 - // For /command |value1 value2 - if (suggestable.forHangingValue()) { -// System.out.println("(2) Return get binding dangling " + parameter + " | " + ""); - return parameter.getBinding().getSuggestions(parameter, ""); - } else { - // For /command value1| value2 - if (lastConsumer != null) { -// System.out.println("(3) Return get consumed " + lastConsumer + " | " + lastConsumed); - return lastConsumer.getBinding().getSuggestions(lastConsumer, lastConsumed); - // For /command| value1 value2 - // This should never occur - } else { -// System.out.println("(4) Invalid suggestion context"); - throw new RuntimeException("Invalid suggestion context"); + usedArguments.mark(); + try { + parameter.getBinding().bind(parameter, usedArguments, false); + minConsumedI = maxConsumedI + 1; + } catch (Throwable e) { + while (e.getCause() != null && !(e instanceof ParameterException || e instanceof InvocationTargetException)) e = e.getCause(); + consumed = usedArguments.reset(); + // Not optional? Then we can't execute this command + if (!parameter.isOptional()) { + if (!(e instanceof MissingParameterException)) minConsumedI = maxConsumedI; + throw e; + } } } - } catch (ParameterException | InvocationTargetException e) { - SuggestInputParseException suggestion = SuggestInputParseException.get(e); - if (suggestion != null) { -// System.out.println("(5) Has suggestion " + suggestion.getSuggestions()); - return suggestion.getSuggestions(); - } - if (suggestable.forHangingValue()) { - String name = getDescription().getParameters().get(consumerIndex).getName(); -// System.out.println("(6) Has dangling invalid " + name + " | " + e.getMessage()); - throw new InvalidUsageException("For parameter '" + name + "': " + e.getMessage(), this); - } else { -// System.out.println("(7) HGet binding suggestions " + parameter + " | " + lastConsumed); - return parameter.getBinding().getSuggestions(parameter, ""); - } } - } - // For /command value1 value2 | - if (suggestable.forHangingValue()) { - // There's nothing that we can suggest because there's no more parameters - // to add on, and we can't change the previous parameter -// System.out.println("(7.1) No more parameters"); - return new ArrayList(); - } else { - // For /command value1 value2| - if (lastConsumer != null) { -// System.out.println("(8) Get binding suggestions " + lastConsumer + " | " + lastConsumed); - return lastConsumer.getBinding().getSuggestions(lastConsumer, lastConsumed); - // This should never occur + if (minConsumedI >= maxConsumedI && (parameter == null || parameter.getType() == CommandContext.class)) checkUnconsumed(scoped); + } catch (MissingParameterException ignore) { + } catch (UnconsumedParameterException e) { + suggestions.add(BBC.color("&cToo many parameters! Unused parameters: " + e.getUnconsumed())); + } catch (ParameterException e) { + String name = parameter.getName(); + suggestions.add(BBC.color("&cFor parameter '" + name + "': " + e.getMessage())); + } catch (InvocationTargetException e) { + SuggestInputParseException suggestion = SuggestInputParseException.get(e); + if (suggestion != null && !suggestion.getSuggestions().isEmpty()) { + hasSuggestion = true; + suggestions.addAll(suggestion.getSuggestions()); } else { -// System.out.println("(9) Invalid suggestion context"); - throw new RuntimeException("Invalid suggestion context"); + Throwable t = e; + while (t.getCause() != null) t = t.getCause(); + String msg = t.getMessage(); + String name = parameter.getName(); + if (msg != null && !msg.isEmpty()) suggestions.add(BBC.color("&cFor parameter '" + name + "': " + msg)); } - + } catch (Throwable t) { + t.printStackTrace(); + throw new WrappedCommandException(t); } + // If there's 1 or less suggestions already, then show parameter suggestion + if (!hasSuggestion && suggestions.size() <= 1) { + StringBuilder suggestion = new StringBuilder(); + outer: + for (String prefix = ""; minConsumedI < parameters.length; minConsumedI++) { + parameter = parameters[minConsumedI]; + if (parameter.getBinding().getBehavior(parameter) != BindingBehavior.PROVIDES) { + suggestion.append(prefix); + List argSuggestions = parameter.getBinding().getSuggestions(parameter, consumed); + switch (argSuggestions.size()) { + case 0: + break; + case 1: + suggestion.append(argSuggestions.iterator().next()); + break; + default: + suggestion.setLength(0); + suggestions.addAll(argSuggestions); + break outer; + + } + consumed = ""; + prefix = " "; + } + } + if (suggestion.length() != 0) suggestions.add(suggestion.toString()); + } + return suggestions; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java index 34b1efda2..e5024f77b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/BindingHelper.java @@ -191,9 +191,11 @@ public class BindingHelper implements Binding { char bracket = parameter.isOptional() ? '[' : '<'; char endBracket = StringMan.getMatchingBracket(bracket); StringBuilder result = new StringBuilder(); + result.append("\u00A75"); result.append(bracket); + result.append("\u00A7r"); if (parameter.getFlag() != null) { - result.append('-').append(parameter.getFlag()).append(' '); + result.append('-').append(parameter.getFlag()).append("\u00A75 \u00A7r"); } result.append(parameter.getName()); if (parameter.getDefaultValue() != null) { @@ -203,7 +205,9 @@ public class BindingHelper implements Binding { if (range != null) { result.append('|').append(StringMan.prettyFormat(range.min())).append(",").append(StringMan.prettyFormat(range.max())); } + result.append("\u00A75"); result.append(endBracket); + result.append("\u00A7r"); return Collections.singletonList(result.toString()); } return new ArrayList<>(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ContextArgumentStack.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ContextArgumentStack.java index d971d6056..b963c6cfe 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ContextArgumentStack.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ContextArgumentStack.java @@ -139,7 +139,7 @@ public class ContextArgumentStack implements ArgumentStack { */ @Override public String reset() { - String value = context.getString(markedIndex, index - 1); + String value = (index - 1 > markedIndex) ? context.getString(markedIndex, index - 1) : ""; index = markedIndex; return value; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FaweParanamer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FaweParanamer.java index 8e2c0fbf8..4dbcd491a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FaweParanamer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FaweParanamer.java @@ -28,7 +28,9 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -package com.thoughtworks.paranamer; +package com.sk89q.worldedit.util.command.parametric; + +import com.thoughtworks.paranamer.CachingParanamer; import java.lang.reflect.Constructor; import java.lang.reflect.Method; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FunctionParametricCallable.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FunctionParametricCallable.java index 19f2bcaab..f6489b629 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FunctionParametricCallable.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/FunctionParametricCallable.java @@ -31,27 +31,8 @@ public class FunctionParametricCallable extends AParametricCallable { this.function = function; this.group = group; - List paramNames = new ArrayList<>(); - List typeStrings = new ArrayList<>(); - List types = new ArrayList<>(); + List paramParsables = new ArrayList<>(); { - boolean checkType = false; - for (String argument : arguments) { - if (checkType) { - typeStrings.set(typeStrings.size() - 1, argument); - } else { - checkType = false; - if (argument.equals("=")) { - checkType = true; - } else if (argument.length() == 1 && command.flags().contains(argument)) { - typeStrings.add("java.lang.Boolean"); - paramNames.add(argument); - } else { - typeStrings.add("java.lang.String"); - paramNames.add(argument); - } - } - } Map bindings = builder.getBindings(); Map unqualified = new HashMap<>(); for (Map.Entry entry : bindings.entrySet()) { @@ -60,14 +41,46 @@ public class FunctionParametricCallable extends AParametricCallable { unqualified.put(typeStr, type); unqualified.put(typeStr.substring(typeStr.lastIndexOf('.') + 1), type); } - for (String typeStr : typeStrings) { - Type type = unqualified.get(typeStr); - if (type == null) type = unqualified.get("java.lang.String"); - types.add(type); + { + Object[] param = null; // name | type | optional value + boolean checkEq = false; + int checkEqI = 0; + for (int i = 0; i < arguments.size(); i++) { + String arg = arguments.get(i); + if (arg.equals("=")) { + checkEqI++; + checkEq = true; + } else if (param == null || !checkEq) { + if (param != null) paramParsables.add(param); + param = new Object[3]; + param[0] = arg; + if (arg.length() == 1 && command.flags().contains(arg)) { + param[1] = Boolean.class; + } else { + param[1] = String.class; + } + param[2] = null; + checkEqI = 0; + checkEq = false; + } else { + if (checkEqI == 1) { + param[1] = unqualified.getOrDefault(arg, String.class); + checkEq = false; + } + else if (checkEqI == 2) { + char c = arg.charAt(0); + if (c == '\'' || c == '"') arg = arg.substring(1, arg.length() - 1); + param[2] = arg; + checkEqI = 0; + checkEq = false; + } + } + } + if (param != null) paramParsables.add(param); } } - parameters = new ParameterData[paramNames.size()]; + parameters = new ParameterData[paramParsables.size()]; List userParameters = new ArrayList(); // This helps keep tracks of @Nullables that appear in the middle of a list @@ -75,20 +88,25 @@ public class FunctionParametricCallable extends AParametricCallable { int numOptional = 0; // // Go through each parameter - for (int i = 0; i < types.size(); i++) { - Type type = types.get(i); + for (int i = 0; i < paramParsables.size(); i++) { + Object[] parsable = paramParsables.get(i); + String paramName = (String) parsable[0]; + Type type = (Type) parsable[1]; + String optional = (String) parsable[2]; ParameterData parameter = new ParameterData(); parameter.setType(type); parameter.setModifiers(new Annotation[0]); - String paramName = paramNames.get(i); boolean flag = paramName.length() == 1 && command.flags().contains(paramName); if (flag) { parameter.setFlag(paramName.charAt(0), type != boolean.class && type != Boolean.class); } - // TODO switch / Optional / Search for annotations / + if (optional != null) { + parameter.setOptional(true); + if (!optional.equalsIgnoreCase("null")) parameter.setDefaultValue(new String[]{optional}); + } parameter.setName(paramName); @@ -256,7 +274,6 @@ public class FunctionParametricCallable extends AParametricCallable { // postInvoke handlers { - } return result; } catch (MissingParameterException e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java index 7293fa715..918bff40d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/ParametricBuilder.java @@ -45,7 +45,6 @@ import com.sk89q.worldedit.util.command.ProcessedCallable; import com.sk89q.worldedit.util.command.binding.PrimitiveBindings; import com.sk89q.worldedit.util.command.binding.StandardBindings; import com.sk89q.worldedit.util.command.binding.Switch; -import com.thoughtworks.paranamer.FaweParanamer; import com.thoughtworks.paranamer.Paranamer; import java.lang.reflect.Method; import java.lang.reflect.Type; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/StringArgumentStack.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/StringArgumentStack.java index 7ecaeaeb8..0b6524aa3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/StringArgumentStack.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/command/parametric/StringArgumentStack.java @@ -146,7 +146,7 @@ public class StringArgumentStack implements ArgumentStack { */ @Override public String reset() { - String value = context.getString(markedIndex, index - 1); + String value = (index - 1 > markedIndex) ? context.getString(markedIndex, index - 1) : ""; index = markedIndex; return value; } 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 f60796f26..1a7322756 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 @@ -20,25 +20,27 @@ package com.sk89q.worldedit.world.block; import com.boydti.fawe.Fawe; +import com.boydti.fawe.command.SuggestInputParseException; import com.boydti.fawe.object.string.MutableCharSequence; import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; import com.google.common.collect.Maps; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.blocks.TileEntityBlock; +import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.SingleBlockStateMask; -import com.sk89q.worldedit.function.pattern.FawePattern; import com.sk89q.worldedit.registry.state.AbstractProperty; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.PropertyKey; import javax.annotation.Nullable; import java.util.*; - -import static com.google.common.base.Preconditions.checkNotNull; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * An immutable class that represents the state a block can be in. @@ -60,7 +62,7 @@ public class BlockState implements BlockStateHolder { * @return BlockState */ @Deprecated - public static BlockState get(int combinedId) { + public static BlockState get(int combinedId) throws InputParseException { return BlockTypes.getFromStateId(combinedId).withStateId(combinedId); } @@ -69,7 +71,7 @@ public class BlockState implements BlockStateHolder { * @param state String e.g. minecraft:water[level=4] * @return BlockState */ - public static BlockState get(String state) { + public static BlockState get(String state) throws InputParseException { return get(null, state); } @@ -80,7 +82,7 @@ public class BlockState implements BlockStateHolder { * @param state String e.g. minecraft:water[level=4] * @return BlockState */ - public static BlockState get(@Nullable BlockType type, String state) { + public static BlockState get(@Nullable BlockType type, String state) throws InputParseException { return get(type, state, 0); } @@ -91,7 +93,7 @@ public class BlockState implements BlockStateHolder { * @param state String e.g. minecraft:water[level=4] * @return BlockState */ - public static BlockState get(@Nullable BlockType type, String state, int propId) { + public static BlockState get(@Nullable BlockType type, String state, int propId) throws InputParseException { int propStrStart = state.indexOf('['); if (type == null) { CharSequence key; @@ -104,6 +106,14 @@ public class BlockState implements BlockStateHolder { key = charSequence; } type = BlockTypes.get(key); + if (type == null) { + String input = key.toString(); + throw new SuggestInputParseException("Unkown block for " + input, input, () -> Stream.of(BlockTypes.values) + .filter(b -> b.getId().contains(input)) + .map(e1 -> e1.getId()) + .collect(Collectors.toList()) + ); + } } if (propStrStart == -1) { return type.getDefaultState(); @@ -111,6 +121,7 @@ public class BlockState implements BlockStateHolder { List propList = type.getProperties(); + if (state.charAt(state.length() - 1) != ']') state = state + "]"; MutableCharSequence charSequence = MutableCharSequence.getTemporal(); charSequence.setString(state); @@ -133,13 +144,36 @@ public class BlockState implements BlockStateHolder { switch (c) { case ']': case ',': { + charSequence.setSubstring(last, i); if (property != null) { - charSequence.setSubstring(last, i); int index = property.getIndexFor(charSequence); + if (index == -1) { + String input = charSequence.toString(); + List values = property.getValues(); + throw new SuggestInputParseException("No value: " + input + " for " + type, input, () -> + values.stream() + .map(v -> v.toString()) + .filter(v -> v.startsWith(input)) + .collect(Collectors.toList())); + } stateId = property.modifyIndex(stateId, index); } else { - Fawe.debug("Invalid property " + type + " | " + charSequence); + // suggest + PropertyKey key = PropertyKey.get(charSequence); + if (key == null || !type.hasProperty(key)) { + // Suggest property + String input = charSequence.toString(); + BlockType finalType = type; + throw new SuggestInputParseException("Invalid property " + type + " | " + input, input, () -> + finalType.getProperties().stream() + .map(p -> p.getName()) + .filter(p -> p.startsWith(input)) + .collect(Collectors.toList())); + } else { + throw new SuggestInputParseException("No operator for " + state, "", () -> Arrays.asList("=")); + } } + property = null; last = i + 1; break; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java index a9cd5a1a0..b58878bf9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.world.block; +import com.boydti.fawe.command.SuggestInputParseException; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.worldedit.Vector; @@ -41,6 +42,7 @@ import com.sk89q.worldedit.world.registry.LegacyMapper; import javax.annotation.Nullable; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Stores a list of common Block String IDs. @@ -946,6 +948,7 @@ public enum BlockTypes implements BlockType { private static final Map $REGISTRY = new HashMap<>(); private static int $LENGTH; public static final BlockTypes[] values; + private static final Set $NAMESPACES = new LinkedHashSet(); static { try { @@ -979,8 +982,9 @@ public enum BlockTypes implements BlockType { } } - public static BlockTypes parse(String input) throws InputParseException { - input = input.toLowerCase(); + public static BlockTypes parse(final String type) throws InputParseException { + final String inputLower = type.toLowerCase(); + String input = inputLower; if (!input.split("\\[", 2)[0].contains(":")) input = "minecraft:" + input; BlockTypes result = $REGISTRY.get(input); @@ -991,7 +995,12 @@ public enum BlockTypes implements BlockType { if (block != null) return block.getBlockType(); } catch (NumberFormatException e) { } catch (IndexOutOfBoundsException e) {} - throw new InputParseException("Unkown block for " + input); + + throw new SuggestInputParseException("Unkown block for " + inputLower, inputLower, () -> Stream.of(BlockTypes.values) + .filter(b -> b.getId().contains(inputLower)) + .map(e1 -> e1.getId()) + .collect(Collectors.toList()) + ); } private static BlockTypes register(final String id) { @@ -1014,9 +1023,15 @@ public enum BlockTypes implements BlockType { existing.init(id, internalId); if (typeName.startsWith("minecraft:")) $REGISTRY.put(typeName.substring(10), existing); $REGISTRY.put(typeName, existing); + String nameSpace = typeName.substring(0, typeName.indexOf(':')); + $NAMESPACES.add(nameSpace); return existing; } + public static Set getNameSpaces() { + return $NAMESPACES; + } + public static final @Nullable BlockTypes get(final String id) { return $REGISTRY.get(id); } diff --git a/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/legacy.json b/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/legacy.json index 7fc9510b3..d56e52751 100644 --- a/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/legacy.json +++ b/worldedit-core/src/main/resources/com/sk89q/worldedit/world/registry/legacy.json @@ -311,8 +311,8 @@ "44:14": "minecraft:nether_brick_slab[type=top]", "44:15": "minecraft:quartz_slab[type=top]", "45:0": "minecraft:bricks", - "46:0": "minecraft:tnt[unstable=false]", - "46:1": "minecraft:tnt[unstable=true]", + "46:0": "minecraft:tnt", + "46:1": "minecraft:tnt", "47:0": "minecraft:bookshelf", "48:0": "minecraft:mossy_cobblestone", "49:0": "minecraft:obsidian", @@ -321,11 +321,11 @@ "50:2": "minecraft:wall_torch[facing=west]", "50:3": "minecraft:wall_torch[facing=south]", "50:4": "minecraft:wall_torch[facing=north]", - "50:9": "minecraft:torch[facing=east]", - "50:10": "minecraft:torch[facing=west]", - "50:11": "minecraft:torch[facing=south]", - "50:12": "minecraft:torch[facing=north]", - "50:13": "minecraft:torch[facing=up]", + "50:9": "minecraft:wall_torch[facing=east]", + "50:10": "minecraft:wall_torch[facing=west]", + "50:11": "minecraft:wall_torch[facing=south]", + "50:12": "minecraft:wall_torch[facing=north]", + "50:13": "minecraft:torch", "51:0": "minecraft:fire[east=false,south=false,north=false,west=false,up=false,age=0]", "51:1": "minecraft:fire[east=false,south=false,north=false,west=false,up=false,age=1]", "51:2": "minecraft:fire[east=false,south=false,north=false,west=false,up=false,age=2]", diff --git a/worldedit-core/src/main/resources/cs_adv.js b/worldedit-core/src/main/resources/cs_adv.js new file mode 100644 index 000000000..1ca0de167 --- /dev/null +++ b/worldedit-core/src/main/resources/cs_adv.js @@ -0,0 +1,49 @@ +{ + var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; + var ARGUMENT_NAMES = /([^\s,]+)/g; + function getParamNames(func) { + var a = func.toString().replace(STRIP_COMMENTS, ''); + var r = a.slice(a.indexOf('(')+1, a.indexOf(')')).match(ARGUMENT_NAMES); + var l = new java.util.ArrayList(); + if(r !== null) { + for (var i = 0; i < r.length; i++) { + l.add(r[i]); + } + } + return l; + } + + function getAllFunctions(){ + var a = new java.util.ArrayList(); + for (var f in this){ + if (this.hasOwnProperty(f) && this[f] instanceof Function && !/a/i.test(f)){ + a.add(this[f]); + } + } + return a; + } + + var functions = getAllFunctions(); + var commands = new java.util.ArrayList() + for (var i = 0; i < functions.length; i++) { + var f = functions[i]; + if (f.hasOwnProperty('desc')) + { + if (!f.hasOwnProperty('aliases')) f.aliases = [f.name]; + var cmd = com.boydti.fawe.config.Commands.fromArgs(f.aliases, f.usage, f.desc, f.min, f.max, f.flags, f.help); + var man = com.sk89q.worldedit.extension.platform.CommandManager.getInstance(); + var builder = man.getBuilder(); + var args = getParamNames(f); + + var wrap = Java.extend(java.util.function.Function, { + apply: function(a) { + return f.apply(null, a); + } + }); + var w2 = new wrap(); + var callable = new com.sk89q.worldedit.util.command.parametric.FunctionParametricCallable(builder, "", cmd, "fawe.use", args, w2); + commands.add(callable); + } + } + commands; +} \ No newline at end of file diff --git a/worldedit-core/src/main/resources/fr/message.yml b/worldedit-core/src/main/resources/fr/message.yml new file mode 100644 index 000000000..7a84641c3 --- /dev/null +++ b/worldedit-core/src/main/resources/fr/message.yml @@ -0,0 +1,356 @@ +info: + prefix: '&8(&4&lFAWE&8)&r&7' + file_deleted: '%s0 a été supprimé.' + schematic_pasting: '&7La schematic est en train d''être collée. Cette action ne + peut pas être annulée.' + lighting_propogate_selection: '&7L''éclairage a été propagé dans %s0 chunks. (Info + : Pour supprimer l''éclairage, faites //removelight)' + updated_lighting_selection: '&7L''éclairage a été mis à jour dans %s0 chunks. (Cela + devraît prendre quelques secondes pour que ce soit à jour)' + set_region: '&7La sélection a été mise pour votre région actuellement autorisée' + worldedit_command_limit: '&7Veuillez patienter le temps que vos actions en cours + soient terminées' + worldedit_delayed: '&7Veuillez patienter pendant que votre action soit terminée.' + worldedit_run: '&7Désolé du délai. Exécution en cours: %s' + worldedit_complete: '&7Modification terminée.' + require_selection_in_mask: '&7%s de vos sélections ne sont pas dans votre masque. + Vous ne pouvez effectuer des modifications que dans vos régions autorisées.' + worldedit_volume: '&7Vous ne pouvez pas sélectionner un volume de %current% blocs. + Le volume maximal que vous pouvez éditer est de %max% blocs.' + worldedit_iterations: '&7Vous ne pouvez pas faire %current% itérations. Votre nombre + maximal d''itérations est de %max%.' + worldedit_unsafe: '&7L''accès à cette commande est restreint.' + worldedit_dangerous_worldedit: '&cModification non sécurisée complétée à %s0 par + %s1' + worldedit_toggle_tips_on: '&7Conseils désactivés.' + worldedit_toggle_tips_off: '&7Conseils activés.' + worldedit_bypassed: '&7Bypass de la restriction du plugin.' + worldedit_unmasked: '&6Vos éditions ne sont désormais plus restreintes.' + worldedit_restricted: '&6Vos éditions sont désormais restreintes.' + worldedit_oom_admin: |- + &cOptions possibles: + &8 - &7//fast + &8 - &7Faites de petites modifications + &8 - &7Allouez plus de mémoire + &8 - &7Désactivez `max-memory-percent` + compressed: Historique compressé. Sauvegardé ~ %s0b (%s1x plus petit) + action_complete: Action complétée en %s0 secondes. +error: + worldedit_extend: '&cVotre modification a peut-être été étendue en dehors de votre + région autorisée.' + web_unauthorized: 'Seuls les liens suivants sont autorisés : %s0' + command_syntax: '&cUtilisation : &7%s0' + no_perm: '&cIl vous manque la permission %s0' + setting_disable: '&cOption manquante : %s0' + brush_not_found: '&cBrosses disponibles : %s0' + brush_incompatible: '&cBrosse non compatible avec cette version' + schematic_not_found: '&cSchematic non trouvée : &7%s0' + no_region: '&cVous n''avez pas de région actuellement autorisée' + no_mask: '&cVous n''avez pas de masque actuel' + not_player: '&cVous devez être un joueur pour effectuer cette action !' + player_not_found: '&cJoueur non trouvé :&7 %s0' + oom: |- + &8[&cAttention&8] &cMémoire faible détectée (< 1%). Nous allons : + &8 - &7Terminer le placement des blocs + &8 - &7Supprimer les historiques de blocs + &8 - &7Décharger les chunks non essentiels + &8 - &7Tuer les entités + &8 - &7Vider la mémoire + &cIgnorez ceci si vous essayez de faire crash le serveur. + &7Info : La mémoire faible est causé par WE (mais pas nécessairement) + worldedit_some_fails: '&c%s0 blocs ne peuvent pas être posés car ils sont en dehors + de votre région autorisée.' + worldedit_some_fails_blockbag: '&cBlocs manquants : %s0' +web: + generating_link: Téléversement de %s, veuillez patienter... + generating_link_failed: '&cImpossible de générer le lien.' + download_link: '%s' +worldedit: + general: + mask_disabled: Masque global désactivé + mask: Masque global défini + texture_disabled: Remise à zéro de la texture + texture_set: Texture mise à %s1 + source_mask_disabled: Masque source global désactivé + source_mask: Masque source global défini + transform_disabled: Transformation globale désactivée + transform: Transformation globale définie + fast_enabled: Mode rapide activé. Les modifications et les historiques vont être + bypass. + fast_disabled: Mode rapide désactivé. + place_enabled: 'Pose de blocs en #1.' + place_disabled: Pose de blocs à l'endroit où vous vous situez. + copy: + command_copy: '%s0 blocs copiés.' + cut: + command_cut_slow: '%s0 blocs supprimés.' + command_cut_lazy: '%s0 blocs vont être supprimés lors du collage.' + paste: + command_paste: Blocs dans le presse-papier collés à %s0 + rotate: + command_rotate: Le presse-papier a subit une rotation + flip: + command_flipped: Le presse-papier a été retourné + regen: + command_regen_0: Région regénérée. + command_regen_1: Région regénérée. + command_regen_2: Région regénérée. + tree: + command_tree: '%s0 arbres créés.' + command_pumpkin: '%s0 patchs de citrouilles créés.' + flora: + command_flora: '%s0 végétations créés.' + history: + command_history_clear: Historique supprimé + command_redo_error: Rien à refaire. (Tapez `/inspect` et `/frb`) + command_history_other_error: Impossible de trouver une session pour %s0. + command_redo_success: Opération refaite. + command_undo_error: Rien à défaire. (Tapez `/inspect` et `/frb`) + command_undo_success: Annulation réussie. + operation: + operation: Opération en attente (%s0) + selection: + selection_wand: 'Clic gauche: sélection position #1; Clic droit: sélection position + #2' + selection_wand_disable: Baguette d'édition désactivée. + selection_wand_enable: Baguette d'édition activée. + selection_chunk: Chunk sélectionné (%s0) + selection_chunks: Chunks sélectionnés (%s0) - (%s1) + selection_contract: blocs (%s0) contractés dans la région. + selection_count: blocs (%s0) dans le compte. + selection_distr: '# blocs au total : %s0' + selection_expand: Sélection étendue de %s0 blocs + selection_expand_vert: Sélection étendue de %s0 blocs (de bas en haut) + selection_inset: Région en médaillon + selection_outset: Début de la région + selection_shift: Région déplacée + selection_cleared: Sélection effacée + navigation: + navigation_wand_error: '&cIl n''y a rien à traverser.' + anvil: + world_is_loaded: Le monde ne devrait pas être utilisé lors de l'exécution. Décharger + le monde, ou utilisez -f pour passer outre (sauvegarder d'abord) + brush: + brush_reset: Réinitialisez votre brosse. (SHIFT + Clic) + brush_none: Tu ne tiens pas une brosse ! + brush_scroll_action_set: Régler l'action de défilement sur %s0 + brush_scroll_action_unset: Suppression de l'action de défilement + brush_visual_mode_set: Mode visuel mis sur %s0 + brush_target_mode_set: Mode cible mis sur %s0 + brush_target_mask_set: Masque cible mis sur %s0 + brush_target_offset_set: Définir le décalage de la cible à%s0 + brush_equipped: Brosse équipée de %s0 + brush_try_other: |- + &cIl existe d'autres brosses plus appropriées, par ex. + &8 - &7//br height [radius=5] [#clipboard|file=null] [rotation=0] [yscale=1.00] + brush_copy: Clic gauche sur la base d'un objet à copier, clic droit pour coller. + Augmentez le rayon de la brosse si nécessaire. + brush_height_invalid: Hauteur de la carte invalide (%s0) + brush_smooth: 'Note : Utilisez la brosse de mélange si vous voulez lisser les + porte-à-faux ou les grottes.' + brush_spline: Cliquez pour ajouter un point, cliquez sur le même point pour terminer. + brush_line_primary: Ajout du point %s0, cliquez sur une autre position pour créer + la ligne. + brush_catenary_direction: Ajout du point %s0, cliquez à la direction que vous + souhaitez pour créer la languette. + brush_line_secondary: Languette créée + brush_spline_primary_2: Position ajoutée, cliquez sur le même point pour les rejoindre + ! + brush_spline_secondary_error: Pas assez de positions insérées ! + brush_spline_secondary: Languette créée + brush_size: Taille de brosse définie + brush_range: Taille de brosse dfinie + brush_mask_disabled: Masque de la brosse désactivé + brush_mask: Masque de la brosse défini + brush_source_mask_disabled: Masque source de la brosse désactivé + brush_source_mask: Masque de la brosse source défini + brush_transform_disabled: Transformation de brosse désactivée + brush_transform: Transformation de brosse défini + brush_material: Type de bloc de la brosse défini + rollback: + rollback_element: Rollback de %s0 + tool: + tool_inspect: Outil d'inspection lié à %s0. + tool_inspect_info: '&7%s0 a changé %s1 à %s2 il y a %s3' + tool_inspect_info_footer: '&6Total : &7%s0 modifications' + tool_none: L'outil plus reliée à l'élément courant. + tool_info: Outil d'information lié à %s0. + tool_tree: Outil d'arbres lié à %s0. + tool_tree_error: Type de l'arbre %s0 inconnu. + tool_repl: Outil de remplacement des blocs lié à %s0. + tool_cycler: Outil de type de cycleur de bloc lié à %s0. + tool_flood_fill: Outil de remplissage des blocs lié à %s0. + tool_range_error: 'Portée maximale : %s0.' + tool_radius_error: 'Rayon de brosse maximum autorisé : %s0.' + tool_deltree: Outil flottant d'abattage d'arbres lié à %s0. + tool_farwand: Outil de baguette lointain lié à%s0. + tool_lrbuild_bound: Outil de construction à longue portée lié à %s0. + tool_lrbuild_info: Clic gauche défini sur %s0; Clic droit défini sur %s1. + superpickaxe_enabled: Super pioche activée. + superpickaxe_disabled: Super pioche désactivée. + superpickaxe_area_enabled: Mode modifié. Clique gauche avec la pioche. // pour + désactiver. + snapshot: + snapshot_loaded: Instantané '%s0' chargé; restauration en cours... + snapshot_set: 'Instantané mis sur : %s0' + snapshot_newest: Utilisation du dernier instantané désormais. + snapshot_list_header: 'Instantanés du monde (%s0):' + snapshot_list_footer: Utilisez /snap use [instantané] our /snap use latest. + biome: + biome_list_header: 'Biomes (page %s0/%s1):' + biome_changed: Les biomes ont été modifiés dans %s0 colonnes. + utility: + kill_success: entitées (%s0) tuées dans une portée de %s1 blocs. + nothing_confirmed: Vous n'avez aucune action en attente de confirmation. + page_footer: Utilisez %s0 pour aller à la page suivante. + schematic: + schematic_prompt_clear: '&7Vous voudriez peut-être utiliser &c%s0 &7pour effacer + votre actuel presse-papier' + schematic_show: |- + &7Affichage de &a%s0&7 schématiques depuis &a%s1&7: + &8 - &aClic gauche &7> une structure à mettre dans le presse-papier + &8 - &aClic droit &7> pour mettre une structure dans le presse-papier multiple + &8 - &7Utilisez &a%s2&7 pour revenir au monde + schematic_format: 'Formats disponibles (nom: Noms de recherche)' + schematic_move_exists: '&c%s0 existe déjà' + schematic_move_success: '&a%s0 -> %s1' + schematic_move_failed: '&a%s0 non déplacé : %s1' + schematic_loaded: '%s0 chargé. Collez-le avec //paste' + schematic_saved: '%s0 sauvegardé.' + schematic_page: La page doit-être %s + schematic_none: Aucun fichier trouvé. + schematic_list: 'Fichiers disponibles (nom du fichier: Format) [%s0/%s1]:' + schematic_list_elem: '&8 - &a%s0 &8- &7%s1' + clipboard: + clipboard_uri_not_found: Vous n'avez pas %s0 chargé. + clipboard_cleared: Presse-papier effacé + clipboard_invalid_format: 'Format de presse-papier inconnu : %s0' + visitor: + visitor_block: '%s0 blocs affectés' + visitor_entity: '%s0 entitées affectés' + visitor_flat: '%s0 colonnes affectées' + selector: + selector_fuzzy_pos1: Région définie et étendue depuis %s0 %s1. + selector_fuzzy_pos2: Ajout de l'étendue de %s0 %s1. + selector_pos: position %s0 définie sur %s1 (%s2). + selector_center: Milieu définie sur %s0 (%s1). + selector_radius: Rayon défini sur %s0 (%s1). + selector_expanded: Région étendue sur %s0 (%s1) + selector_invalid_coordinates: Cordonnées invalides ! + selector_already_set: Position déjà définie. + selector_set_default: Votre sélecteur de région par défaut est désormais %s0. + timezone: + timezone_set: Fuseau horaire fixé pour cette session sur %s0 + timezone_display: 'Le temps actuel pour ce fuseau horaire est : %s0' + command: + command_invalid_syntax: La commande n'a pas été utilisée correctement. (aucune + aide disponible). + help: + command_clarifying_bracket: '&7Ajout d''un crochet de clarification pour &c%s0' + help_suggest: '&7Impossible de trouver %s0. Essayez une des &c%s1 &7?' + help_header_categories: Types de commandes + help_header_subcommands: Sous commandes + help_header_command: '&cAide pour : &7%s0' + help_item_allowed: '&a%s0&8 - &7%s1' + help_item_denied: '&c%s0&8 - &7%s1' + help_header: 'Aide : page %s0/%s1' + help_footer: '&7Wiki anglais : https://git.io/vSKE5' +progress: + progress_message: '%s1/%s0 (%s2%) @%s3bps %s4s restants' + progress_finished: '[ Terminé ! ]' +cancel: + worldedit_cancel_count: '&cModifications %s0 annulées.' + worldedit_cancel_reason_confirm: '&7Votre sélection est large (&c%s0 &7-> &c%s1&7, + contenant &c%s3&7 blocs). Faites &c//confirm &7pour exécuter &c%s2' + worldedit_cancel_reason: '&cVotre action a été annulée :&7 %s0&c.' + worldedit_cancel_reason_manual: Annulation humaine + worldedit_cancel_reason_low_memory: Mémoire trop faible + worldedit_cancel_reason_max_changes: Trop de blocs modifiés + worldedit_cancel_reason_max_checks: Trop de vérifications + worldedit_cancel_reason_max_tiles: Trop de types de blocs + worldedit_cancel_reason_max_entities: Entités trop élevées + worldedit_cancel_reason_max_iterations: Itérations trop élevées + worldedit_cancel_reason_outside_level: En dehors du monde + worldedit_cancel_reason_outside_region: En dehors de la région autorisée (bypass + avec /wea, ou désactivez `region-restrictions` dans la configuration) + worldedit_cancel_reason_no_region: Région non autorisée (bypass avec /wea, ou désactivez + `region-restrictions` dans la configuration) + worldedit_failed_load_chunk: '&cChargement du chunk ignoré : &7%s0;%s1&c. Essayez + d''augmenter chunk-wait.' +navigation: + ascend_fail: Aucune place libre au-dessus de vous n'a été trouvée + ascended_plural: Montée de %s0 couche. + ascended_singular: Montée de 1 couche. + unstuck: Et voilà ! + descend_fail: Aucune place libre en dessous de vous n'a été trouvée. + descend_plural: Descendu de %s0 couches. + descend_singular: Descendu de 1 couche. + whoosh: Waouh ! + poof: Pouf ! + thru_fail: Aucune place libre devant vous n'a été trouvée. + no_block: Pas de bloc en vue ! (ou trop loin) + up_fail: ' Tu frapperais quelque chose au-dessus de toi.' +selection: + sel_cuboid: 'Cuboïde : clic gauche pour point 1, clic droit pour point 2' + sel_cuboid_extend: 'Cuboïde : clic gauche pour le point de départ, clic droit pour + étendre' + sel_2d_polygon: 'Sélecteur de polygone 2D : Clic gauche/droit pour ajouter un point.' + sel_ellipsiod: 'Sélecteur ellipsoïde : clic gauche=centre, clic droit pour agrandir.' + sel_sphere: 'Sélecteur de sphère : clic gauche=centre, clic droit pour définir le + rayon.' + sel_cylindrical: 'Sélecteur cylindrique : Clic gauche=centre, clic droit pour étendre.' + sel_max: '%s0 points maximum.' + sel_fuzzy: 'Sélecteur flou : Clic gauche pour sélectionner tous les blocs contingents, + clic droit. à ajouter. Pour sélectionner une cavité d''air, utilisez //pos1.' + sel_convex_polyhedral: 'Sélecteur polyédrique convexe : Clic gauche=Premier sommet, + droite, droite cliquez pour en ajouter d''autres.' + sel_list: Pour une liste de types de sélection, faites &c //sel list + sel_modes: 'Sélectionnez un des modes en dessous :' +tips: + tip_sel_list: 'Astuce : Voir les différents modes de sélection avec &c//sel list' + tip_select_connected: Astuce, Sélectionner tous les blocs connectés avec //sel fuzzy' + tip_set_pos1: Astuce, Utilisez pos1 comme motif avec &c//set pos1 + tip_farwand: Astuce, Sélectionnez les points distants avec &c//farwand.' + tip_discord: 'Besoin d''aide ? Aide en anglais disponible : https://discord.gg/ngZCzbU' + tip_lazycut: '&7Conseil, Il est plus sûr d''utiliser &c//lazycut' + tip_fast: '&7Astuce, Définir rapidement et sans défaire en utilisant &c//fas' + tip_cancel: '&7Astuce, Vous pouvez &c//cancel &7et éditer en cours de traitement.' + tip_mask: '&7Astuce, Définir un masque de destination globale avec &c/gmask' + tip_mask_angle: '&7Astuce, Remplacer les pentes ascendantes de 3-20 blocs en faisant&c + //replace /[-20][-3] bedrock' + tip_set_linear: '&7Conseil, Définissez les blocs de manière linéaire avec&c //set + #l3d[wood,bedrock]' + tip_surface_spread: '&7Conseil, Étendez une surface plane à l''aide de&c //set #surfacespread[5][0][5][#existing]' + tip_set_hand: '&7Astuce, Utilisez votre main actuelle avec &c//set hand' + tip_replace_regex: '&7Tip, Remplacer à l''aide de regex :&c //replace .*_log ' + tip_replace_regex_2: '&7Tip, Remplacer à l''aide de regex :&c //replace .*stairs[facing=(north|south)] + ' + tip_replace_regex_3: '&7Tip, Remplacer à l''aide des opérateurs :&c //replace water[level>2] + sand' + tip_replace_regex_4: '&7Tip, Remplacer à l''aide des opérateurs :&c //replace true + *[waterlogged=false]' + tip_replace_regex_5: '&7Tip, Remplacer à l''aide des opérateurs :&c //replace true + *[level-=1]' + tip_replace_id: '&7Tip, Remplacez seulement l''ID du bloc :&c //replace woodenstair + #id[cobblestair]' + tip_replace_light: 'Tip, Remplacez seulement les sources de lumière :&c //replace + #brightness[1][15] 0' + tip_tab_complete: Tip, La commande de remplacement prend en charge la complétion + du tab. + tip_flip: Tip, Flip avec &c//flip + tip_deform: Tip, Remodeler avec &c//deform + tip_transform: Tip, Sélectionnez une transformation avec &c//gtransform + tip_copypaste: Tip, Collez en cliquant avec &c//br copypaste + tip_source_mask: Tip, Sélectionnez un masque source avec &c/gsmask &7 + tip_replace_marker: 'Tip, Remplacez un bloc avec votre presse-papier entier avec + &c//replace wool #fullcopy' + tip_paste: Tip, Collez avec &c//paste + tip_lazycopy: Tip, LazyCopy est plus rapide + tip_download: Tip, Essayez &c//download + tip_rotate: Tip, Orientez vos sélections avec &c//rotate + tip_copy_pattern: Tip, Utilisez un type de pattern avec &c#copy + tip_regen_0: Tip, Utilisez un biome avec /regen [biome] + tip_regen_1: Tip, Utilisez un seed avec /regen [biome] [seed] + tip_biome_pattern: Tip, Le pattern &c#biome[forest]&7 peut être utilisé dans une + quelconque commande + tip_biome_mask: Tip, Mettez une restriction de biome en utilisant le masque `$jungle` diff --git a/worldedit-core/src/main/resources/nl/message.yml b/worldedit-core/src/main/resources/nl/message.yml new file mode 100644 index 000000000..68bea5edc --- /dev/null +++ b/worldedit-core/src/main/resources/nl/message.yml @@ -0,0 +1,331 @@ +#Published by NotMyFault +info: + prefix: '&8(&4&lFAWE&8)&r&7' + file_deleted: '%s0 Is verwijderd.' + schematic_pasting: '&7Het schematic wordt geplaatst. dit kan niet veranderd worden.' + lighting_propogate_selection: '&7Het licht heeft zich verspreid in %s0 chunks. (Note: + om het licht te verwijderen, doe //removelight)' + updated_lighting_selection: '&7Lighting is geüpdatet in %s0 chunks. + (het kan even duren voordat de packets worden verstuurd)' + set_region: '&7Toegestaande regio geselecteerd' + worldedit_command_limit: '&7U kunt weer verder gaan wanneer uw vorige actie voltooid is' + worldedit_delayed: '&7U kunt weer verder gaan wanneer wij uw FAWE action voltooien...' + worldedit_run: '&7Sorry voor het ongemak. huidige uitvoerende actie: %s' + worldedit_complete: '&7Verandering voldaan.' + require_selection_in_mask: '&7%s Van je selectie is niet binnen je mask. + Je kan alleen veranderingen uitvoeren in de toegestaande regio.' + worldedit_volume: '&7Je kan niet een volume van %current% toepassen. + Het maximum volume dat je kan toepassen is %max%.' + worldedit_iterations: '&7Je kan %current% niet zoveel keer herhalen. het maximale + wat toegestaan is, is %max%.' + worldedit_unsafe: '&7Toegang tot deze command is geblokkeerd' + worldedit_dangerous_worldedit: '&cNnveilige edits zijn verwerkt op %s0 by %s1' + worldedit_toggle_tips_on: '&7Disabled FAWE tips.' + worldedit_toggle_tips_off: '&7De FAWE tips staan nu aan.' + worldedit_bypassed: '&7De restrictie of FAWE wordt nu overscheiden.' + worldedit_unmasked: '&6Je FAWE edits hebben nu geen restrictie meer.' + worldedit_restricted: '&6Je FAWE edits hebben nu een restrictie.' + worldedit_oom_admin: |- + &cMogelijke opties: + &8 - &7//fast + &8 - &7Voer kleinere bewerkingen uit + &8 - &7Wijs meer geheugen toe + &8 - &7Disable `max-memory-percent` + compressed: Geschiedenis gecomprimeerd. Saved ~ %s0b (%s1x kleiner) + action_complete: Action completed in %s0 seconds +error: + worldedit_extend: '&cje edit is waarschijnlijk buiten het toegestaande gebied gekomen.' + web_unauthorized: 'alleen links van de configureerde webhost zijn toegestaan: %s0' + command_syntax: '&cGebruik: &7%s0' + no_perm: '&cje hebt hier geen permission node voor: %s0' + setting_disable: '&cSetting ontbreekt: %s0' + brush_not_found: '&cBruikbare brushes: %s0' + brush_incompatible: '&cDeze brush kan niet in deze versie gebruikt worden' + schematic_not_found: '&cSchematic niet gevonden: &7%s0' + no_region: '&cJe hebt momenteel geen toegestaand gebied' + no_mask: '&cJe hebt momenteel geen brush geselecteerd' + not_player: '&cJe moet een speler zijn om dit te doen!' + player_not_found: '&cSpeler niet gevonden:&7 %s0' + oom: |- + &8[&cCritical&8] &cGedetecteerd laag geheugen i.e. < 1%. We zullen de volgende acties ondernemen: + &8 - &7Verwijder de huidige WE blok plaatsing + &8 - &7Clear WE history + &8 - &7Unload non essential chunks + &8 - &7Dood de entities + &8 - &7Onnodige data collecteren + &cNegeer dit als je de server wilt laten crashen. + &7Note: Een laag opslagvermogen kan door WE veroorzaakt worden (dit hoeft niet) + worldedit_some_fails: '&c%s0 Blokken zijn niet geplaats omdat ze uit je + toegestaande gebied werden geplaatst.' + worldedit_some_fails_blockbag: '&coOntbrekende blokken: %s0' +web: + generating_link: Uploading %s, even geduld A.U.B... + generating_link_failed: '&cDe donwload link kon niet worden gemaakt!' + download_link: '%s' +worldedit: + general: + mask_disabled: Global mask uitgeschakeld + mask: Global mask is gezet + texture_disabled: Texturing reset + texture_set: Texturing tot %s1 gezet + source_mask_disabled: Global source mask uitgeschakeld + source_mask: Global source mask geszet + transform_disabled: Global transform uitgeschakeld + transform: Global transform gezet + fast_enabled: Fast mode aangezet. History en edit restrictions zullen worden negeerd. + fast_disabled: Fast mode uitgeschakeld + place_enabled: 'Wordt nu geplaats op locatie #1.' + place_disabled: Het wordt nu geplaatst op het blok waar je op staat. + copy: + command_copy: '%s0 blokken zijn gekopieerd.' + cut: + command_cut_slow: '%s0 Blocks were cut.' + command_cut_lazy: '%s0 Blokken zullen verwijderd worden bij plaatsing' + paste: + command_paste: Het klipbord is geplaatst op %s0 + rotate: + command_rotate: Het klipbord is omgedraaid + flip: + command_flipped: Het klipbord is omgedraaid + regen: + command_regen_0: Regio gegenereerd. + command_regen_1: Regio gegenereerd. + command_regen_2: Regio gegenereerd. + tree: + command_tree: '%s0 Bomen gecreëerd.' + command_pumpkin: '%s0 Pompoenen gecreëerd.' + flora: + command_flora: '%s0 Flora gecreëerd.' + history: + command_history_clear: Geschiedenis verwijderd + command_redo_error: Er is niks om te ontdaan. (See also `/inspect` and `/frb`) + command_history_other_error: We kunnen geen sessie voor %s0 vinden. + command_redo_success: Herdaan succesvol. + command_undo_error: Er is niet om te ontdaan. (See also `/inspect` and `/frb`) + command_undo_success: Herdaan succesvol. + operation: + operation: Operaties die in de wacht staan (%s0) + selection: + selection_wand: 'Links klikken: selecteer pos #1; Rechts klikken: selecteer pos #2' + selection_wand_disable: Edit wand uitgeschakeld. + selection_wand_enable: Edit wand ingeschakeld. + selection_chunk: Chunk geselecteerd (%s0) + selection_chunks: Chunks geselecteerd (%s0) - (%s1) + selection_contract: Region contracted %s0 blokken. + selection_count: %s0 Blokken geteld. + selection_distr: '# Totaal aantal blokken: %s0' + selection_expand: Regio vergroot met %s0 blokken. + selection_expand_vert: Regio vergroot met %s0 blokken (van boven naar beneden) + selection_inset: Region inset + selection_outset: Region outset + selection_shift: Region verzet + selection_cleared: Selectie verwijderd + navigation: + navigation_wand_error: '&cNergens om door heen te gaan' + anvil: + world_is_loaded: de wereld zou niet in gebruik moeten zijn wanneer er een uitvoering wordt gedaan. onlaad de wereld, + of gebruik -f om het te overschrijden (save first) + brush: + brush_reset: Reset je brush. (SHIFT + Click) + brush_none: Je houdt hebt geen brush vast! + brush_scroll_action_set: Scroll action gezet naar %s0 + brush_scroll_action_unset: Scroll action uitgeschakeld. + brush_visual_mode_set: Visuele mode gezet naar %s0 + brush_target_mode_set: Target mode gezet naar %s0 + brush_target_mask_set: Target mode gezet naar %s0 + brush_target_offset_set: Uitschietting van de target gezet tot %s0 + brush_equipped: Brush in gebruik: %s0 + brush_try_other: |- + &cEr zijn betere brushes hier voor te gebruiken e.g. + &8 - &7//br height [radius=5] [#clipboard|file=null] [rotation=0] [yscale=1.00] + brush_copy: Links klik de fundering van de constructie om te kopieëren, rechts klik om te plakken. verhoog + de brush Settings als dat mogelijk is. + brush_height_invalid: Onvalide hoogte map file (%s0) + brush_smooth: 'Note: Gebruik de blend brush om caves en hangende dingen glad te krijgen.' + brush_spline: Klik Om een bevestigingspunt te zetten, klik hetzelfde punt om het af te ronden. + brush_line_primary: Punt %s0 toegevoegt, klik op een andere locatie om de lijn te creëeren. + brush_catenary_direction: Punt %s0 toegevoegt, klik naar de directie waar je + krommen wilt. + brush_line_secondary: Kromme lijn gecreëerd + brush_spline_primary_2: Positie toegevoegt, klik hetzelfde punt om mee te doen! + brush_spline_secondary_error: Niet genoeg posities gezet! + brush_spline_secondary: Kromme lijn gecreërd + brush_size: Brush grootte gezet + brush_range: Brush bereik gezet + brush_mask_disabled: Brush mask disabled + brush_mask: Brush mask gezet + brush_source_mask_disabled: Brush source mask uitgeschakeld + brush_source_mask: Brush source mask gezet + brush_transform_disabled: Brush transform uitgeschakeld + brush_transform: Brush transform gezet + brush_material: Brush material gezet + rollback: + rollback_element: %s0 aan het ontdaan maken. + tool: + tool_inspect: Inspect tool gebonden aan %s0. + tool_inspect_info: '&7%s0 veranderd %s1 to %s2 %s3 geleden' + tool_inspect_info_footer: '&6Totaal: &7%s0 veranderingen' + tool_none: De tool is niet meer gebonden aan je item. + tool_info: Info tool gebonden aan %s0. + tool_tree: Tree tool gebonden aan %s0. + tool_tree_error: Tree type %s0 is niet gevonden. + tool_repl: Block replacer tool gebonden aan %s0. + tool_cycler: Block data cycler tool gebonden aan %s0. + tool_flood_fill: Block flood fill tool gebonden aan %s0. + tool_range_error: 'Maximale bereik: %s0.' + tool_radius_error: 'Maximum toegestaande brush radius: %s0.' + tool_deltree: Floating tree remover tool gebonden aan %s0. + tool_farwand: Far wand tool gebonden aan %s0. + tool_lrbuild_bound: Long-range building tool gebonden aan %s0. + tool_lrbuild_info: links-klik gebonden aan %s0; rechts-klik gebonden aan %s1. + superpickaxe_enabled: Super Pickaxe ingeschakeld. + superpickaxe_disabled: Super Pickaxe uitgeschakeld. + superpickaxe_area_enabled: Mode veranderd. Links klik met een pickaxe. // om uit te schakelen. + snapshot: + snapshot_loaded: Snapshot '%s0' geladen; nu aan het herstellen... + snapshot_set: 'Snapshot gezet tot: %s0' + snapshot_newest: Je gebruikt de nieuwste snapshot. + snapshot_list_header: 'Snapshots voor wereld (%s0):' + snapshot_list_footer: Gebruik /snap use [snapshot] of /snap use latest. + biome: + biome_list_header: 'Biomes (page %s0/%s1):' + biome_changed: Biotopen zijn veranderd in %s0 columns. + utility: + kill_success: %s0 entities zijn vermoord in een radius van %s1. + nothing_confirmed: Je hebt geen acties die bevestigt moeten worden. + page_footer: Gebruik %s0 om naar de volgende pagina te gaan + schematic: + schematic_prompt_clear: '&7we raden je aan &c%s0 &7 te gebruiken om je klipbord te verwijderen + first' + schematic_show: |- + &7scematics &a%s0&7 aan het wergeven &a%s1&7: + &8 - &aLinks klik &7a het gebouw om er een klipbord van de maken + &8 - &aRechts klik &7a om een gebouw toe te voegen aan je multi-klipbord + &8 - &7Gebruik &a%s2&7 om terug naar de wereld te gaan + schematic_format: 'Beschikbare formats (Name: Lookup names)' + schematic_move_exists: '&c%s0 bestaat al' + schematic_move_success: '&a%s0 -> %s1' + schematic_move_failed: '&a%s0 aantal verplaatst: %s1' + schematic_loaded: '%s0 gelanden. plak met //paste' + schematic_saved: '%s0 opgeslagen.' + schematic_page: Pagina moet %s zijn + schematic_none: Niks gevonden. + schematic_list: 'Beschikbare files (Filename: Format) [%s0/%s1]:' + schematic_list_elem: '&8 - &a%s0 &8- &7%s1' + clipboard: + clipboard_uri_not_found: Je hebt %s0 niet geladen + clipboard_cleared: Jlikbord verwijderd + clipboard_invalid_format: 'Niet bestaand clipboard format: %s0' + visitor: + visitor_block: '%s0 blocks aangetast' + visitor_entity: '%s0 entities aangetast' + visitor_flat: '%s0 columns aangetast' + selector: + selector_fuzzy_pos1: Regio gezet en verwijd van/naar %s0 %s1. + selector_fuzzy_pos2: Verwijding van %s0 %s1 toegevoegt. + selector_pos: pos%s0 gezet naar %s1 (%s2). + selector_center: Center gezet naar %s0 (%s1). + selector_radius: Radius gezet naar %s0 (%s1). + selector_expanded: Regio uitgebreid naar %s0 (%s1) + selector_invalid_coordinates: non-valid coördinaten %s0 + selector_already_set: Positie al gezet. + selector_set_default: Your normale regio selector is nu %s0. + timezone: + timezone_set: 'Timezone gezet voor deze sessie naar: %s0' + timezone_display: 'De actuele tijd in die tijdzone is: %s0' + command: + command_invalid_syntax: De command was niet goed gebruikt (no more help available). + help: + command_clarifying_bracket: '&7 clarifying bracket toegevoegt aan &c%s0' + help_suggest: '&7kon %s0 niet binden. misschien één van deze proberen &c%s1 &7?' + help_header_categories: Command Types + help_header_subcommands: Subcommands + help_header_command: '&cHelp voor: &7%s0' + help_item_allowed: '&a%s0&8 - &7%s1' + help_item_denied: '&c%s0&8 - &7%s1' + help_header: 'Help: pagina %s0/%s1' + help_footer: '&7Wiki: https://git.io/vSKE5' +progress: + progress_message: '%s1/%s0 (%s2%) @%s3cps %s4s left' + progress_finished: '[ Klaar! ]' +cancel: + worldedit_cancel_count: '&czoveel edits: %s0 ontdaan.' + worldedit_cancel_reason_confirm: '&7Het geselecteerde gebied is groot (&c%s0 &7-> &c%s1&7, + containing &c%s3&7 blocks). Gebruik &c//confirm &7om te bevestigen &c%s2' + worldedit_cancel_reason: '&cYour WorldEdit actie was opgeheven:&7 %s0&c.' + worldedit_cancel_reason_manual: manuele opheffing + worldedit_cancel_reason_low_memory: Lage memory + worldedit_cancel_reason_max_changes: Teveel blokken verandered + worldedit_cancel_reason_max_checks: Teveel block checks + worldedit_cancel_reason_max_tiles: Teveel blockstates + worldedit_cancel_reason_max_entities: Teveel entities + worldedit_cancel_reason_max_iterations: Maximum herhaling + worldedit_cancel_reason_outside_level: Buiten de bebouwde kom + worldedit_cancel_reason_outside_region: Buiten het toegestaande gebied (bypass with /wea, + or disable `region-restrictions` in config.yml) + worldedit_cancel_reason_no_region: geen toegestaand gebied (bypass with /wea, or disable + `region-restrictions` in config.yml) + worldedit_failed_load_chunk: '&cChunks laden overgeslaan: &7%s0;%s1&c. probeer het + chunk-wait op te krikken.' +navigation: + ascend_fail: Geen vrije plekken boven je gesignaleerd. + ascended_plural: %s0 levels verlaagt. + ascended_singular: Ascended a level. + unstuck: There you go! + descend_fail: geen vrije plekken onder je gesignaleerd. + descend_plural: %s0 levels gedaald. + descend_singular: Een level gedaald. + whoosh: Whoosh! + poof: Poof! + thru_fail: Geen vrije plekken voor je. + no_block: Geen blokken in zicht! (of te ver) + up_fail: Er hangt helaas iets boven je. +selection: + sel_cuboid: 'Cuboid: links klik voor punt 1, rechts klik voor punt 2' + sel_cuboid_extend: 'Cuboid: links klik voor een begin punt, rechts klik om te verbreden' + sel_2d_polygon: '2D polygon selector: links/rechts klik om een punt toe te voegen.' + sel_ellipsiod: 'Ellipsoid selector: links klik=center, rechts klik om te verbreden' + sel_sphere: 'Sphere selector: Links klik=center, rechts klik om de radius te zetten' + sel_cylindrical: 'Cylindrical selector: links klik+center, right click to extend.' + sel_max: '%s0 points maximum.' + sel_fuzzy: 'Fuzzy selector: links klik om alle bestaande blokken te plaatsen, rechts klik + om toe te voegen. om een lucht holte te selecteren, doe //pos1.' + sel_convex_polyhedral: 'Convex polyhedral selector: Links klik=First vertex, rechts + klik om meer toe te voegen.' + sel_list: Voor een lijst met selecties doe:&c //sel list + sel_modes: 'Selecteer één van de modes hier:' +tips: + tip_sel_list: 'Tip: Zie de verschillende selectie modes met &c//sel list' + tip_select_connected: 'Tip: Selecteer alle verbonden blokken met //sel fuzzy' + tip_set_pos1: 'Tip: Gebruik pos1 als een punt met &c//set pos1' + tip_farwand: 'Tip: Selecteer punten van ver weg met &c//farwand' + tip_lazycut: '&7Tip: Het is veiliger om &c//lazycut te gebruiken' + tip_fast: '&7Tip: Set fast and without undo using &c//fast' + tip_cancel: '&7Tip: gebruik &c//cancel &7 om een edit stop te zetten' + tip_mask: '&7Tip: Zet een globale bestemmings mask met &c/gmask' + tip_mask_angle: 'Tip: Vervang opwaartse hellingen van 3-20 blocks met&c //replace /[-20][-3] + bedrock' + tip_set_linear: '&7Tip: Zet een blok lineaire met &c//set #l3d[wood,bedrock]' + tip_surface_spread: '&7Tip: Verpsreid een oppervlakte met &c//set #surfacespread[5][0][5][#existing]' + tip_set_hand: '&7Tip: Gebruik je huide hand met &c//set hand' + tip_replace_id: '&7Tip: Vervang alleen het blok id:&c//replace woodenstair #id[cobblestair]' + tip_replace_light: 'Tip: Verwijder licht met with&c//replace #brightness[1][15] + 0 + tip_tab_complete: 'Tip: The replace command supports tab completion' + tip_flip: 'Tip: Mirror met &c//flip' + tip_deform: 'Tip: Verander het van vorm met &c//deform' + tip_transform: 'Tip: Zet een transform met &c//gtransform' + tip_copypaste: 'Tip: Plaats bij klikken met &c//br copypaste' + tip_source_mask: 'Tip: Zet een source mask met + &c/gsmask &7' + tip_replace_marker: 'Tip: verander een blok in je volle klipbord met &c//replace + wool #fullcopy' + tip_paste: 'Tip: Plaats met &c//paste' + tip_lazycopy: 'Tip: lazycopy is sneller' + tip_download: 'Tip: probeer &c//download' + tip_rotate: 'Tip: orïenteer met &c//rotate' + tip_copy_pattern: 'Tip: Om een pattern te gebruiken, probeer &c#copy' + tip_regen_0: 'Tip: Gebruik een biome met /regen [biome]' + tip_regen_1: 'Tip: gebruik een seed met /regen [biome] [seed]' + tip_biome_pattern: 'Tip: Dee &c#biome[forest]&7 pattern kan ik elke opdracht gebruikt worden' + tip_biome_mask: 'Tip: Er wordt een restrictie geplaatst op de biome met `$jungle` mask'