diff --git a/config/checkstyle/import-control.xml b/config/checkstyle/import-control.xml
index 5bcfffeb3..36ccf3d0b 100644
--- a/config/checkstyle/import-control.xml
+++ b/config/checkstyle/import-control.xml
@@ -17,7 +17,6 @@
-
@@ -40,6 +39,7 @@
+
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java
index d889903be..8944cb586 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandInspector.java
@@ -27,7 +27,10 @@ import org.bukkit.command.CommandSender;
import org.enginehub.piston.CommandManager;
import org.enginehub.piston.CommandParameters;
import org.enginehub.piston.NoInputCommandParameters;
+import org.enginehub.piston.inject.InjectedValueStore;
import org.enginehub.piston.inject.Key;
+import org.enginehub.piston.inject.MapBackedValueStore;
+import org.enginehub.piston.inject.MemoizingValueAccess;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -74,10 +77,11 @@ class BukkitCommandInspector implements CommandInspector {
public boolean testPermission(CommandSender sender, Command command) {
Optional mapping = dispatcher.getCommand(command.getName());
if (mapping.isPresent()) {
+ InjectedValueStore store = MapBackedValueStore.create();
+ store.injectValue(Key.of(Actor.class), context ->
+ Optional.of(plugin.wrapCommandSender(sender)));
CommandParameters parameters = NoInputCommandParameters.builder()
- .injectedValues(ImmutableMap.of(
- Key.of(Actor.class), plugin.wrapCommandSender(sender)
- ))
+ .injectedValues(MemoizingValueAccess.wrap(store))
.build();
return mapping.get().getCondition().satisfied(parameters);
} else {
diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java
index 9d335bb24..699263d50 100644
--- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java
+++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditListener.java
@@ -21,7 +21,6 @@
package com.sk89q.worldedit.bukkit;
-import com.google.common.collect.ImmutableMap;
import com.sk89q.util.StringUtil;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player;
@@ -42,7 +41,12 @@ import org.bukkit.inventory.EquipmentSlot;
import org.enginehub.piston.CommandManager;
import org.enginehub.piston.CommandParameters;
import org.enginehub.piston.NoInputCommandParameters;
+import org.enginehub.piston.inject.InjectedValueStore;
import org.enginehub.piston.inject.Key;
+import org.enginehub.piston.inject.MapBackedValueStore;
+import org.enginehub.piston.inject.MemoizingValueAccess;
+
+import java.util.Optional;
/**
* Handles all events thrown in relation to a Player
@@ -108,17 +112,18 @@ public class WorldEditListener implements Listener {
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPlayerCommand(PlayerCommandSendEvent event) {
+ InjectedValueStore store = MapBackedValueStore.create();
+ store.injectValue(Key.of(Actor.class), context ->
+ Optional.of(plugin.wrapCommandSender(event.getPlayer())));
CommandParameters parameters = NoInputCommandParameters.builder()
- .injectedValues(ImmutableMap.of(
- Key.of(Actor.class), plugin.wrapCommandSender(event.getPlayer())
- ))
+ .injectedValues(MemoizingValueAccess.wrap(store))
.build();
CommandManager commandManager = plugin.getWorldEdit().getPlatformManager().getPlatformCommandMananger().getCommandManager();
event.getCommands().removeIf(name ->
// remove if in the manager and not satisfied
commandManager.getCommand(name)
- .filter(command -> !command.getCondition().satisfied(parameters))
- .isPresent()
+ .filter(command -> !command.getCondition().satisfied(parameters))
+ .isPresent()
);
}
diff --git a/worldedit-core/build.gradle b/worldedit-core/build.gradle
index 23f96a592..efbf0aed3 100644
--- a/worldedit-core/build.gradle
+++ b/worldedit-core/build.gradle
@@ -33,6 +33,9 @@ dependencies {
implementation "org.enginehub.piston.core-ap:runtime:$pistonVersion"
annotationProcessor "org.enginehub.piston.core-ap:processor:$pistonVersion"
api "org.enginehub.piston:default-impl:$pistonVersion"
+ def avVersion = "1.6.5"
+ compileOnly "com.google.auto.value:auto-value-annotations:$avVersion"
+ annotationProcessor "com.google.auto.value:auto-value:$avVersion"
//compile 'net.sf.trove4j:trove4j:3.0.3'
testCompile 'org.mockito:mockito-core:1.9.0-rc1'
}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionConverter.java
new file mode 100644
index 000000000..f454191da
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/DirectionConverter.java
@@ -0,0 +1,109 @@
+/*
+ * 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.command.argument;
+
+import com.google.auto.value.AutoAnnotation;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.sk89q.worldedit.UnknownDirectionException;
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.entity.Player;
+import com.sk89q.worldedit.internal.annotation.Direction;
+import com.sk89q.worldedit.math.BlockVector3;
+import org.enginehub.piston.CommandManager;
+import org.enginehub.piston.converter.ArgumentConverter;
+import org.enginehub.piston.converter.ConversionResult;
+import org.enginehub.piston.converter.FailedConversion;
+import org.enginehub.piston.converter.SuccessfulConversion;
+import org.enginehub.piston.inject.InjectedValueAccess;
+import org.enginehub.piston.inject.Key;
+
+import java.util.List;
+
+import static java.util.stream.Collectors.toList;
+
+public class DirectionConverter implements ArgumentConverter {
+
+ @AutoAnnotation
+ private static Direction direction(boolean includeDiagonals) {
+ return new AutoAnnotation_DirectionConverter_direction(includeDiagonals);
+ }
+
+ public static void register(WorldEdit worldEdit, CommandManager commandManager) {
+ commandManager.registerConverter(
+ Key.of(BlockVector3.class, direction(false)),
+ new DirectionConverter(worldEdit, false)
+ );
+ commandManager.registerConverter(
+ Key.of(BlockVector3.class, direction(true)),
+ new DirectionConverter(worldEdit, true)
+ );
+ }
+
+ private static final ImmutableSet NON_DIAGONALS = ImmutableSet.of(
+ "north", "south", "east", "west", "up", "down"
+ );
+ private static final ImmutableSet RELATIVE = ImmutableSet.of(
+ "me", "forward", "back", "left", "right"
+ );
+ private static final ImmutableSet DIAGONALS = ImmutableSet.of(
+ "northeast", "northwest", "southeast", "southwest"
+ );
+
+ private final WorldEdit worldEdit;
+ private final boolean includeDiagonals;
+ private final ImmutableList suggestions;
+
+ private DirectionConverter(WorldEdit worldEdit, boolean includeDiagonals) {
+ this.worldEdit = worldEdit;
+ this.includeDiagonals = includeDiagonals;
+ suggestions = ImmutableList.builder()
+ .addAll(NON_DIAGONALS)
+ .addAll(RELATIVE)
+ .addAll(includeDiagonals ? DIAGONALS : ImmutableList.of())
+ .build();
+ }
+
+ @Override
+ public ConversionResult convert(String argument, InjectedValueAccess context) {
+ Player player = context.injectedValue(Key.of(Player.class))
+ .orElseThrow(() -> new IllegalStateException("No player available"));
+ try {
+ return SuccessfulConversion.fromSingle(includeDiagonals
+ ? worldEdit.getDiagonalDirection(player, argument)
+ : worldEdit.getDirection(player, argument));
+ } catch (UnknownDirectionException e) {
+ return FailedConversion.from(e);
+ }
+ }
+
+ @Override
+ public String describeAcceptableArguments() {
+ return "`me` to use facing direction, or any "
+ + (includeDiagonals ? "direction" : "non-diagonal direction");
+ }
+
+ @Override
+ public List getSuggestions(String input) {
+ return suggestions.stream()
+ .filter(s -> s.startsWith(input))
+ .collect(toList());
+ }
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/MaskConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/MaskConverter.java
new file mode 100644
index 000000000..cabe53ec5
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/MaskConverter.java
@@ -0,0 +1,58 @@
+package com.sk89q.worldedit.command.argument;
+
+import com.sk89q.worldedit.WorldEdit;
+import com.sk89q.worldedit.entity.Entity;
+import com.sk89q.worldedit.extension.input.InputParseException;
+import com.sk89q.worldedit.extension.input.NoMatchException;
+import com.sk89q.worldedit.extension.input.ParserContext;
+import com.sk89q.worldedit.extension.platform.Actor;
+import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.function.mask.Mask;
+import com.sk89q.worldedit.world.World;
+import org.enginehub.piston.CommandManager;
+import org.enginehub.piston.converter.ArgumentConverter;
+import org.enginehub.piston.converter.ConversionResult;
+import org.enginehub.piston.converter.FailedConversion;
+import org.enginehub.piston.converter.SuccessfulConversion;
+import org.enginehub.piston.inject.InjectedValueAccess;
+import org.enginehub.piston.inject.Key;
+
+public class MaskConverter implements ArgumentConverter {
+
+ public static void register(WorldEdit worldEdit, CommandManager commandManager) {
+ commandManager.registerConverter(Key.of(Mask.class), new MaskConverter(worldEdit));
+ }
+
+ private final WorldEdit worldEdit;
+
+ private MaskConverter(WorldEdit worldEdit) {
+ this.worldEdit = worldEdit;
+ }
+
+ @Override
+ public ConversionResult convert(String argument, InjectedValueAccess context) {
+ Actor actor = context.injectedValue(Key.of(Actor.class))
+ .orElseThrow(() -> new IllegalStateException("No actor"));
+ ParserContext parserContext = new ParserContext();
+ parserContext.setActor(actor);
+ if (actor instanceof Entity) {
+ Extent extent = ((Entity) actor).getExtent();
+ if (extent instanceof World) {
+ parserContext.setWorld((World) extent);
+ }
+ }
+ parserContext.setSession(worldEdit.getSessionManager().get(actor));
+ try {
+ return SuccessfulConversion.fromSingle(
+ worldEdit.getMaskFactory().parseFromInput(argument, parserContext)
+ );
+ } catch (InputParseException e) {
+ return FailedConversion.from(e);
+ }
+ }
+
+ @Override
+ public String describeAcceptableArguments() {
+ return "any mask";
+ }
+}
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/package-info.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/package-info.java
new file mode 100644
index 000000000..e1d68643b
--- /dev/null
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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 .
+ */
+
+@org.enginehub.piston.util.NonnullByDefault
+package com.sk89q.worldedit.command.argument;
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandMananger.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandMananger.java
index aefcb4323..7bbab6a16 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandMananger.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandMananger.java
@@ -21,6 +21,7 @@ package com.sk89q.worldedit.extension.platform;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalConfiguration;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
@@ -35,6 +36,8 @@ import com.sk89q.worldedit.command.ClipboardCommandsRegistration;
import com.sk89q.worldedit.command.SchematicCommands;
import com.sk89q.worldedit.command.SchematicCommandsRegistration;
import com.sk89q.worldedit.command.argument.Arguments;
+import com.sk89q.worldedit.command.argument.DirectionConverter;
+import com.sk89q.worldedit.command.argument.MaskConverter;
import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator;
import com.sk89q.worldedit.command.util.PermissionCondition;
import com.sk89q.worldedit.entity.Entity;
@@ -42,11 +45,13 @@ import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.platform.CommandEvent;
import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
import com.sk89q.worldedit.extent.Extent;
+import com.sk89q.worldedit.internal.annotation.Selection;
import com.sk89q.worldedit.internal.command.ActorAuthorizer;
import com.sk89q.worldedit.internal.command.CommandLoggingHandler;
import com.sk89q.worldedit.internal.command.UserCommandCompleter;
import com.sk89q.worldedit.internal.command.WorldEditBinding;
import com.sk89q.worldedit.internal.command.WorldEditExceptionConverter;
+import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.request.Request;
import com.sk89q.worldedit.util.command.parametric.ExceptionConverter;
import com.sk89q.worldedit.util.command.parametric.LegacyCommandsHandler;
@@ -103,7 +108,7 @@ public final class PlatformCommandMananger {
private final PlatformManager platformManager;
private final CommandManager commandManager;
private final DynamicStreamHandler dynamicHandler = new DynamicStreamHandler();
- private final ExceptionConverter exceptionConverter;
+ private final WorldEditExceptionConverter exceptionConverter;
private final List callListeners;
/**
@@ -153,6 +158,45 @@ public final class PlatformCommandMananger {
builder.addBinding(new WorldEditBinding(worldEdit));
builder.addInvokeListener(new LegacyCommandsHandler());
+ registerAlwaysInjectedValues();
+ registerArgumentConverters();
+ registerAllCommands();
+ }
+
+ private void registerArgumentConverters() {
+ DirectionConverter.register(worldEdit, commandManager);
+ MaskConverter.register(worldEdit, commandManager);
+ }
+
+ private void registerAlwaysInjectedValues() {
+ commandManager.injectValue(Key.of(Region.class, Selection.class),
+ context -> {
+ LocalSession localSession = context.injectedValue(Key.of(LocalSession.class))
+ .orElseThrow(() -> new IllegalStateException("No LocalSession"));
+ return context.injectedValue(Key.of(Player.class))
+ .map(player -> {
+ try {
+ return localSession.getSelection(player.getWorld());
+ } catch (IncompleteRegionException e) {
+ exceptionConverter.convert(e);
+ throw new AssertionError("Should have thrown a new exception.");
+ }
+ });
+ });
+ commandManager.injectValue(Key.of(EditSession.class),
+ context -> {
+ LocalSession localSession = context.injectedValue(Key.of(LocalSession.class))
+ .orElseThrow(() -> new IllegalStateException("No LocalSession"));
+ return context.injectedValue(Key.of(Player.class))
+ .map(player -> {
+ EditSession editSession = localSession.createEditSession(player);
+ editSession.enableStandardMode();
+ return editSession;
+ });
+ });
+ }
+
+ private void registerAllCommands() {
commandManager.register("schematic", cmd -> {
cmd.aliases(ImmutableList.of("schem", "/schematic", "/schem"));
cmd.description("Schematic commands for saving/loading areas");
@@ -348,17 +392,6 @@ public final class PlatformCommandMananger {
localSession.tellVersion(actor);
return Optional.of(localSession);
});
- store.injectValue(Key.of(EditSession.class),
- context -> {
- LocalSession localSession = context.injectedValue(Key.of(LocalSession.class))
- .orElseThrow(() -> new IllegalStateException("No LocalSession"));
- return context.injectedValue(Key.of(Player.class))
- .map(player -> {
- EditSession editSession = localSession.createEditSession(player);
- editSession.enableStandardMode();
- return editSession;
- });
- });
MemoizingValueAccess context = MemoizingValueAccess.wrap(store);
diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Direction.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Direction.java
index dbc7d3b29..bd2b55b4f 100644
--- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Direction.java
+++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/annotation/Direction.java
@@ -19,7 +19,8 @@
package com.sk89q.worldedit.internal.annotation;
-import com.sk89q.worldedit.math.Vector3;
+import com.sk89q.worldedit.math.BlockVector3;
+import org.enginehub.piston.inject.InjectAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -27,12 +28,13 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Annotates a {@link Vector3} parameter to inject a direction.
+ * Annotates a {@link BlockVector3} parameter to inject a direction.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
+@InjectAnnotation
public @interface Direction {
-
+
String AIM = "me";
boolean includeDiagonals() default false;