From df71f3ae7dacaa26d2187243bb029a5a9b72859c Mon Sep 17 00:00:00 2001 From: Matthew Miller Date: Tue, 5 Jan 2021 16:19:45 +1000 Subject: [PATCH] Add a command to apply side effects (#1623) * [WIP] Add a command to apply side effects * Fix checkstyle * Switch to the //update command, and add messages * The update command won't change in the future, remove that warning * Create a Function that applies side effects for easier API usage, and make gmasks apply to `//update` * Fixed `//update` not using the current side effect set * Fixed PR review notes * Fixed regression in last commit * smh why is it null * Update GeneralCommands.java * PR review * PR review * :( * :( --- .../worldedit/command/GeneralCommands.java | 30 ++++++- .../CommaSeparatedValuesConverter.java | 29 ++++++- .../argument/SideEffectSetConverter.java | 81 +++++++++++++++++++ .../platform/PlatformCommandManager.java | 2 + .../function/block/ApplySideEffect.java | 50 ++++++++++++ .../com/sk89q/worldedit/util/SideEffect.java | 2 + .../src/main/resources/lang/strings.json | 1 + 7 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectSetConverter.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/function/block/ApplySideEffect.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java index 2b558ee7d..ee340b42f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GeneralCommands.java @@ -32,7 +32,12 @@ import com.sk89q.worldedit.command.util.WorldEditAsyncCommandBuilder; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.function.RegionMaskingFilter; +import com.sk89q.worldedit.function.block.ApplySideEffect; import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.function.visitor.RegionVisitor; import com.sk89q.worldedit.internal.command.CommandRegistrationHandler; import com.sk89q.worldedit.internal.command.CommandUtil; import com.sk89q.worldedit.util.SideEffect; @@ -76,7 +81,7 @@ public class GeneralCommands { CommandManager commandManager, CommandManagerService commandManagerService, WorldEdit worldEdit) { - // Collect the tool commands + // Collect the commands CommandManager collect = commandManagerService.newCommandManager(); registration.register( @@ -282,6 +287,29 @@ public class GeneralCommands { } } + @Command( + name = "/update", + desc = "Apply side effects to your selection" + ) + @CommandPermissions("worldedit.update") + void update(Actor actor, LocalSession session, World injectedWorld, + @Arg(desc = "The side effects", def = "") + SideEffectSet sideEffectSet) throws WorldEditException { + if (sideEffectSet == null) { + // Use defaults if none supplied. + sideEffectSet = SideEffectSet.defaults(); + } + RegionFunction apply = new ApplySideEffect(injectedWorld, sideEffectSet); + if (session.getMask() != null) { + apply = new RegionMaskingFilter(session.getMask(), apply); + } + + RegionVisitor visitor = new RegionVisitor(session.getSelection(injectedWorld), apply); + Operations.complete(visitor); + + actor.printInfo(TranslatableComponent.of("worldedit.update")); + } + @Command( name = "/reorder", desc = "Sets the reorder mode of WorldEdit" diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java index ef819f376..d4f3c92d8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/CommaSeparatedValuesConverter.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.command.argument; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; @@ -30,6 +31,8 @@ import org.enginehub.piston.converter.SuccessfulConversion; import org.enginehub.piston.inject.InjectedValueAccess; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; import static com.sk89q.worldedit.util.formatting.text.TextComponent.space; @@ -40,20 +43,30 @@ public class CommaSeparatedValuesConverter implements ArgumentConverter { return wrapAndLimit(delegate, -1); } + public static CommaSeparatedValuesConverter wrapNoRepeats(ArgumentConverter delegate) { + return wrapAndLimitNoRepeats(delegate, -1); + } + public static CommaSeparatedValuesConverter wrapAndLimit(ArgumentConverter delegate, int maximum) { - return new CommaSeparatedValuesConverter<>(delegate, maximum); + return new CommaSeparatedValuesConverter<>(delegate, maximum, true); + } + + public static CommaSeparatedValuesConverter wrapAndLimitNoRepeats(ArgumentConverter delegate, int maximum) { + return new CommaSeparatedValuesConverter<>(delegate, maximum, false); } private static final Splitter COMMA = Splitter.on(','); private final ArgumentConverter delegate; private final int maximum; + private final boolean repeats; - private CommaSeparatedValuesConverter(ArgumentConverter delegate, int maximum) { + private CommaSeparatedValuesConverter(ArgumentConverter delegate, int maximum, boolean repeats) { checkArgument(maximum == -1 || maximum > 1, "Maximum must be bigger than 1, or exactly -1"); this.delegate = delegate; this.maximum = maximum; + this.repeats = repeats; } @Override @@ -73,7 +86,17 @@ public class CommaSeparatedValuesConverter implements ArgumentConverter { public List getSuggestions(String input, InjectedValueAccess context) { String lastInput = Iterables.getLast(COMMA.split(input), ""); assert lastInput != null; - return delegate.getSuggestions(lastInput, context); + List suggestions = delegate.getSuggestions(lastInput, context); + if (input.contains(",")) { + String prefix = input.substring(0, input.length() - lastInput.length()); + Set entries = ImmutableSet.copyOf(COMMA.split(input)); + suggestions = suggestions + .stream() + .filter(suggestion -> repeats || !entries.contains(suggestion)) + .map(suggestion -> prefix + suggestion) + .collect(Collectors.toList()); + } + return suggestions; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectSetConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectSetConverter.java new file mode 100644 index 000000000..509b99381 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/SideEffectSetConverter.java @@ -0,0 +1,81 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.argument; + +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +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; + +public class SideEffectSetConverter implements ArgumentConverter { + + public static void register(CommandManager commandManager) { + ArgumentConverter sideEffectConverter = commandManager.getConverter(Key.of(SideEffect.class)) + .orElseThrow(() -> new IllegalStateException("SideEffectSetConverter must be registered after SideEffectConverter")); + commandManager.registerConverter( + Key.of(SideEffectSet.class), + new SideEffectSetConverter(CommaSeparatedValuesConverter.wrapNoRepeats(sideEffectConverter)) + ); + } + + private final TextComponent choices = TextComponent.of("any side effects"); + private final CommaSeparatedValuesConverter sideEffectConverter; + + private SideEffectSetConverter(CommaSeparatedValuesConverter sideEffectConverter) { + this.sideEffectConverter = sideEffectConverter; + } + + @Override + public Component describeAcceptableArguments() { + return choices; + } + + @Override + public List getSuggestions(String input, InjectedValueAccess context) { + return sideEffectConverter.getSuggestions(input, context); + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + try { + ConversionResult result = sideEffectConverter.convert(argument, context); + if (result.isSuccessful()) { + SideEffectSet set = SideEffectSet.none(); + for (SideEffect sideEffect : result.get()) { + set = set.with(sideEffect, SideEffect.State.ON); + } + return SuccessfulConversion.fromSingle(set); + } else { + return result.failureAsAny(); + } + } catch (Exception e) { + return FailedConversion.from(e); + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index b2dc9d429..f8478d21c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -81,6 +81,7 @@ import com.sk89q.worldedit.command.argument.OffsetConverter; import com.sk89q.worldedit.command.argument.RegionFactoryConverter; import com.sk89q.worldedit.command.argument.RegistryConverter; import com.sk89q.worldedit.command.argument.SideEffectConverter; +import com.sk89q.worldedit.command.argument.SideEffectSetConverter; import com.sk89q.worldedit.command.argument.VectorConverter; import com.sk89q.worldedit.command.argument.WorldConverter; import com.sk89q.worldedit.command.argument.ZonedDateTimeConverter; @@ -221,6 +222,7 @@ public final class PlatformCommandManager { RegionFactoryConverter.register(commandManager); WorldConverter.register(commandManager); SideEffectConverter.register(commandManager); + SideEffectSetConverter.register(commandManager); HeightConverter.register(commandManager); OffsetConverter.register(worldEdit, commandManager); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/ApplySideEffect.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/ApplySideEffect.java new file mode 100644 index 000000000..f0b59a99f --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/block/ApplySideEffect.java @@ -0,0 +1,50 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.function.block; + +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.function.RegionFunction; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.world.World; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Applies the given side effect within a region. + */ +public class ApplySideEffect implements RegionFunction { + + private final World world; + private final SideEffectSet sideEffectSet; + + public ApplySideEffect(World world, SideEffectSet sideEffectSet) { + checkNotNull(world); + checkNotNull(sideEffectSet); + this.world = world; + this.sideEffectSet = sideEffectSet; + } + + @Override + public boolean apply(BlockVector3 position) throws WorldEditException { + world.applySideEffects(position, world.getBlock(position), sideEffectSet); + return true; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java index e2793cd9f..3ec36e348 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/SideEffect.java @@ -29,6 +29,7 @@ public enum SideEffect { ENTITY_AI(State.OFF), EVENTS(State.OFF); + // TODO Make these components in WE8 private final String displayName; private final String description; private final State defaultValue; @@ -56,6 +57,7 @@ public enum SideEffect { ON, DELAYED; + // TODO Make this a component in WE8 private final String displayName; State() { diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 2485f2935..2ed98270c 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -52,6 +52,7 @@ "worldedit.perf.sideeffect.get": "Side effect \"{0}\" is set to {1}", "worldedit.perf.sideeffect.already-set": "Side effect \"{0}\" is already {1}", "worldedit.perf.sideeffect.set-all": "All side effects set to {0}", + "worldedit.update": "Applied side effects to the selection.", "worldedit.reorder.current": "The reorder mode is {0}", "worldedit.reorder.set": "The reorder mode is now {0}", "worldedit.gmask.disabled": "Global mask disabled.",