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;