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

* :(

* :(
This commit is contained in:
Matthew Miller 2021-01-05 16:19:45 +10:00 committed by GitHub
parent 2c172197ac
commit df71f3ae7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 191 additions and 4 deletions

View File

@ -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"

View File

@ -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<T> implements ArgumentConverter<T> {
return wrapAndLimit(delegate, -1);
}
public static <T> CommaSeparatedValuesConverter<T> wrapNoRepeats(ArgumentConverter<T> delegate) {
return wrapAndLimitNoRepeats(delegate, -1);
}
public static <T> CommaSeparatedValuesConverter<T> wrapAndLimit(ArgumentConverter<T> delegate, int maximum) {
return new CommaSeparatedValuesConverter<>(delegate, maximum);
return new CommaSeparatedValuesConverter<>(delegate, maximum, true);
}
public static <T> CommaSeparatedValuesConverter<T> wrapAndLimitNoRepeats(ArgumentConverter<T> delegate, int maximum) {
return new CommaSeparatedValuesConverter<>(delegate, maximum, false);
}
private static final Splitter COMMA = Splitter.on(',');
private final ArgumentConverter<T> delegate;
private final int maximum;
private final boolean repeats;
private CommaSeparatedValuesConverter(ArgumentConverter<T> delegate, int maximum) {
private CommaSeparatedValuesConverter(ArgumentConverter<T> 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<T> implements ArgumentConverter<T> {
public List<String> getSuggestions(String input, InjectedValueAccess context) {
String lastInput = Iterables.getLast(COMMA.split(input), "");
assert lastInput != null;
return delegate.getSuggestions(lastInput, context);
List<String> suggestions = delegate.getSuggestions(lastInput, context);
if (input.contains(",")) {
String prefix = input.substring(0, input.length() - lastInput.length());
Set<String> 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

View File

@ -0,0 +1,81 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <https://www.gnu.org/licenses/>.
*/
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<SideEffectSet> {
public static void register(CommandManager commandManager) {
ArgumentConverter<SideEffect> 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<SideEffect> sideEffectConverter;
private SideEffectSetConverter(CommaSeparatedValuesConverter<SideEffect> sideEffectConverter) {
this.sideEffectConverter = sideEffectConverter;
}
@Override
public Component describeAcceptableArguments() {
return choices;
}
@Override
public List<String> getSuggestions(String input, InjectedValueAccess context) {
return sideEffectConverter.getSuggestions(input, context);
}
@Override
public ConversionResult<SideEffectSet> convert(String argument, InjectedValueAccess context) {
try {
ConversionResult<SideEffect> 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);
}
}
}

View File

@ -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);
}

View File

@ -0,0 +1,50 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <https://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -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() {

View File

@ -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.",