Add /rg flags command.

Provides an overview of all flags set on a region, including inherited
values, and allows the user to set or unset flags with a single click.
This commit is contained in:
wizjany 2019-05-04 20:03:33 -04:00
parent fc4c67ff29
commit 7199b8e70f
9 changed files with 672 additions and 65 deletions

View File

@ -0,0 +1,502 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard 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 <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.commands.region;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import com.sk89q.worldedit.registry.Keyed;
import com.sk89q.worldedit.registry.Registry;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.component.PaginationBox;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
import com.sk89q.worldedit.util.formatting.text.format.TextDecoration;
import com.sk89q.worldedit.util.formatting.text.serializer.legacy.LegacyComponentSerializer;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.internal.permission.RegionPermissionModel;
import com.sk89q.worldguard.protection.flags.BooleanFlag;
import com.sk89q.worldguard.protection.flags.DoubleFlag;
import com.sk89q.worldguard.protection.flags.Flag;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.flags.IntegerFlag;
import com.sk89q.worldguard.protection.flags.LocationFlag;
import com.sk89q.worldguard.protection.flags.RegistryFlag;
import com.sk89q.worldguard.protection.flags.SetFlag;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.flags.StringFlag;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
class FlagHelperBox extends PaginationBox {
private static final List<Flag<?>> FLAGS = WorldGuard.getInstance().getFlagRegistry().getAll().stream()
.sorted((f1, f2) -> {
if (f1 == f2) return 0;
int idx1 = Flags.INBUILT_FLAGS.indexOf(f1.getName());
int idx2 = Flags.INBUILT_FLAGS.indexOf(f2.getName());
if (idx1 < 0 && idx2 >= 0) return 1;
if (idx2 < 0 && idx1 >= 0) return -1;
if (idx1 < 0) return f1.getName().compareTo(f2.getName());
return idx1 < idx2 ? -1 : 1;
})
.collect(Collectors.toList());
private static final int SIZE = FLAGS.size() == Flags.INBUILT_FLAGS.size() ? FLAGS.size() : FLAGS.size() + 1;
private static final int PAD_PX_SIZE = 180;
private final World world;
private final ProtectedRegion region;
private final RegionPermissionModel perms;
FlagHelperBox(World world, ProtectedRegion region, RegionPermissionModel perms) {
super("Flags for " + region.getId(), "/rg flags -w " + world.getName() + " " + region.getId() + " %page%");
this.world = world;
this.region = region;
this.perms = perms;
}
@Override
public Component getComponent(int number) {
if (number == Flags.INBUILT_FLAGS.size()) {
return centerAndBorder(TextComponent.of("Third-Party Flags", TextColor.AQUA));
} else if (number > Flags.INBUILT_FLAGS.size()) {
number -= 1;
}
Flag<?> flag = FLAGS.get(number);
return createLine(flag, number >= Flags.INBUILT_FLAGS.size()); }
@Override
public int getComponentsSize() {
return SIZE;
}
private Component createLine(Flag<?> flag, boolean thirdParty) {
final TextComponent.Builder builder = TextComponent.builder("");
appendFlagName(builder, flag, thirdParty ? TextColor.LIGHT_PURPLE : TextColor.GOLD);
appendFlagValue(builder, flag);
return builder.build();
}
private void appendFlagName(TextComponent.Builder builder, Flag<?> flag, TextColor color) {
final String name = flag.getName();
int length = FlagFontInfo.getPxLength(name);
builder.append(TextComponent.of(name, color));
if (flag.usesMembershipAsDefault()) {
builder.append(Component.empty().append(TextComponent.of("*", TextColor.AQUA))
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
TextComponent.of("This is a special flag which defaults to allow for members, and deny for non-members"))));
length += FlagFontInfo.getPxLength('*');
}
if (flag == Flags.PASSTHROUGH) {
builder.append(Component.empty().append(TextComponent.of("*", TextColor.AQUA))
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
TextComponent.of("This is a special flag which overrides build checks. (Not movement related!)"))));
length += FlagFontInfo.getPxLength('*');
}
int leftover = PAD_PX_SIZE - length;
builder.append(Component.space());
leftover -= 4;
if (leftover > 0) {
builder.append(TextComponent.of(Strings.repeat(".", leftover / 2), TextColor.DARK_GRAY));
}
}
private void appendFlagValue(TextComponent.Builder builder, Flag<?> flag) {
if (flag instanceof StateFlag) {
appendStateFlagValue(builder, (StateFlag) flag);
} else if (flag instanceof BooleanFlag) {
appendBoolFlagValue(builder, ((BooleanFlag) flag));
} else if (flag instanceof SetFlag) {
appendSetFlagValue(builder, ((SetFlag<?>) flag));
} else if (flag instanceof RegistryFlag) {
appendRegistryFlagValue(builder, ((RegistryFlag<?>) flag));
} else if (flag instanceof StringFlag) {
appendStringFlagValue(builder, ((StringFlag) flag));
} else if (flag instanceof LocationFlag) {
appendLocationFlagValue(builder, ((LocationFlag) flag));
} else if (flag instanceof IntegerFlag) {
appendNumericFlagValue(builder, (IntegerFlag) flag);
} else if (flag instanceof DoubleFlag) {
appendNumericFlagValue(builder, (DoubleFlag) flag);
} else {
String display = String.valueOf(region.getFlag(flag));
if (display.length() > 23) {
display = display.substring(0, 20) + "...";
}
appendValueText(builder, flag, display, null);
}
}
private <T> T getInheritedValue(ProtectedRegion region, Flag<T> flag) {
ProtectedRegion parent = region.getParent();
T val;
while (parent != null) {
val = parent.getFlag(flag);
if (val != null) {
return val;
}
parent = parent.getParent();
}
return null;
}
private <V> void appendValueChoices(TextComponent.Builder builder, Flag<V> flag, Iterator<V> choices, @Nullable String suggestChoice) {
V defVal = flag.getDefault();
V currVal = region.getFlag(flag);
boolean inherited = false;
if (currVal == null) {
currVal = getInheritedValue(region, flag);
if (currVal != null) {
inherited = true;
}
}
while (choices.hasNext()) {
V choice = choices.next();
boolean isExplicitSet = currVal == choice && !inherited;
boolean maySet = perms.maySetFlag(region, flag, isExplicitSet ? null : String.valueOf(choice));
TextColor col = isExplicitSet ? TextColor.WHITE : inherited && currVal == choice ? TextColor.GRAY : TextColor.DARK_GRAY;
Set<TextDecoration> styles = choice == defVal ? ImmutableSet.of(TextDecoration.UNDERLINED) : Collections.emptySet();
Component choiceComponent = Component.empty().append(TextComponent.of(capitalize(String.valueOf(choice)), col, styles));
List<Component> hoverTexts = new ArrayList<>();
if (maySet) {
if (isExplicitSet) {
hoverTexts.add(TextComponent.of("Click to unset", TextColor.GOLD));
} else {
hoverTexts.add(TextComponent.of("Click to set", TextColor.GOLD));
}
}
Component valType = getToolTipHint(defVal, choice, inherited);
if (valType != null) {
hoverTexts.add(valType);
}
if (!hoverTexts.isEmpty()) {
TextComponent.Builder hoverBuilder = TextComponent.builder("");
for (Iterator<Component> hovIt = hoverTexts.iterator(); hovIt.hasNext(); ) {
hoverBuilder.append(hovIt.next());
if (hovIt.hasNext()) {
hoverBuilder.append(Component.newline());
}
}
choiceComponent = choiceComponent.hoverEvent(
new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverBuilder.build()));
}
if (maySet) {
builder.append(choiceComponent.clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND,
makeCommand(flag, isExplicitSet ? "" : choice))));
} else {
builder.append(choiceComponent);
}
builder.append(Component.space());
}
if (suggestChoice != null && perms.maySetFlag(region, flag)) {
builder.append(TextComponent.of(suggestChoice, TextColor.DARK_GRAY)
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
TextComponent.of("Click to set custom value", TextColor.GOLD)))
.clickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, makeCommand(flag, ""))));
}
}
private <V> void appendValueText(TextComponent.Builder builder, Flag<V> flag, String display, @Nullable Component hover) {
V defVal = flag.getDefault();
V currVal = region.getFlag(flag);
boolean inherited = false;
if (currVal == null) {
currVal = getInheritedValue(region, flag);
if (currVal != null) {
inherited = true;
}
}
boolean isExplicitSet = currVal != null && !inherited;
boolean maySet = perms.maySetFlag(region, flag);
TextColor col = isExplicitSet ? TextColor.WHITE : inherited ? TextColor.GRAY : TextColor.DARK_GRAY;
Set<TextDecoration> styles = currVal == defVal ? ImmutableSet.of(TextDecoration.UNDERLINED) : Collections.emptySet();
Component displayComponent = Component.empty().append(TextComponent.of(display, col, styles));
List<Component> hoverTexts = new ArrayList<>();
if (maySet) {
if (isExplicitSet) {
hoverTexts.add(TextComponent.of("Click to change", TextColor.GOLD));
} else {
hoverTexts.add(TextComponent.of("Click to set", TextColor.GOLD));
}
}
Component valType = getToolTipHint(defVal, currVal, inherited);
if (valType != null) {
hoverTexts.add(valType);
}
if (!hoverTexts.isEmpty()) {
TextComponent.Builder hoverBuilder = TextComponent.builder("");
for (Iterator<Component> hovIt = hoverTexts.iterator(); hovIt.hasNext(); ) {
hoverBuilder.append(hovIt.next());
if (hovIt.hasNext()) {
hoverBuilder.append(Component.newline());
}
}
if (hover != null) {
hoverBuilder.append(Component.newline());
hoverBuilder.append(hover);
}
displayComponent = displayComponent.hoverEvent(
new HoverEvent(HoverEvent.Action.SHOW_TEXT, hoverBuilder.build()));
}
if (maySet) {
builder.append(displayComponent.clickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND,
makeCommand(flag, ""))));
} else {
builder.append(displayComponent);
}
builder.append(Component.space());
}
private String makeCommand(Flag<?> flag, Object choice) {
return "/rg flag -w " + world.getName() + " -h " + getCurrentPage()
+ " " + region.getId() + " " + flag.getName() + " " + choice;
}
private String capitalize(String value) {
if (value.isEmpty()) return value;
value = value.toLowerCase();
return value.length() > 1
? Character.toUpperCase(value.charAt(0)) + value.substring(1)
: String.valueOf(Character.toUpperCase(value.charAt(0)));
}
@Nullable
private <V> Component getToolTipHint(V defVal, V currVal, boolean inherited) {
Component valType;
if (inherited) {
if (currVal == defVal) {
valType = TextComponent.of("Inherited & ")
.append(TextComponent.of("default")
.decoration(TextDecoration.UNDERLINED, true))
.append(TextComponent.of(" value"));
} else {
valType = TextComponent.of("Inherited value");
}
} else {
if (currVal == defVal) {
valType = Component.empty()
.append(TextComponent.of("Default")
.decoration(TextDecoration.UNDERLINED, true))
.append(TextComponent.of(" value"));
} else {
valType = null;
}
}
return valType;
}
private void appendStateFlagValue(TextComponent.Builder builder, StateFlag flag) {
final Iterator<StateFlag.State> choices = Iterators.forArray(StateFlag.State.values());
appendValueChoices(builder, flag, choices, null);
}
private void appendBoolFlagValue(TextComponent.Builder builder, BooleanFlag flag) {
final Iterator<Boolean> choices = Iterators.forArray(Boolean.TRUE, Boolean.FALSE);
appendValueChoices(builder, flag, choices, null);
}
private <V> void appendSetFlagValue(TextComponent.Builder builder, SetFlag<V> flag) {
Flag<V> subType = flag.getType();
Class<?> clazz = subType.getClass();
String subName;
subName = clazz.isAssignableFrom(RegistryFlag.class)
? ((RegistryFlag<?>) subType).getRegistry().getName()
: subType.getClass().getSimpleName().replace("Flag", "");
Set<V> currVal = region.getFlag(flag);
if (currVal == null) {
currVal = getInheritedValue(region, flag);
}
@SuppressWarnings("unchecked")
List<V> values = currVal == null ? Collections.emptyList() : (List<V>) flag.marshal(currVal);
String display = (currVal == null ? "" : currVal.size() + "x ") + subName;
final String stringValue = currVal == null ? ""
: values.stream().map(String::valueOf).collect(Collectors.joining(","));
TextComponent hoverComp = TextComponent.of("");
if (currVal != null) {
hoverComp = hoverComp.append(TextComponent.of("Current values:"))
.append(Component.newline()).append(TextComponent.of(stringValue));
}
appendValueText(builder, flag, display, hoverComp);
}
private void appendRegistryFlagValue(TextComponent.Builder builder, RegistryFlag<?> flag) {
final Registry<?> registry = flag.getRegistry();
String regName = registry.getName();
Keyed currVal = region.getFlag(flag);
if (currVal == null) {
currVal = getInheritedValue(region, flag);
}
String display = currVal == null ? regName : currVal.getId();
appendValueText(builder, flag, display, null);
}
private void appendLocationFlagValue(TextComponent.Builder builder, LocationFlag flag) {
Location currVal = region.getFlag(flag);
if (currVal == null) {
currVal = getInheritedValue(region, flag);
}
if (currVal == null) {
final Location defVal = flag.getDefault();
if (defVal == null) {
appendValueText(builder, flag, "unset location", null);
} else {
appendValueText(builder, flag, defVal.toString(), TextComponent.of("Default value:")
.append(Component.newline()).append(TextComponent.of(defVal.toString())));
}
} else {
appendValueText(builder, flag, currVal.toString(), TextComponent.of("Current value:")
.append(Component.newline()).append(TextComponent.of(currVal.toString())));
}
}
private <V extends Number> void appendNumericFlagValue(TextComponent.Builder builder, Flag<V> flag) {
Number currVal = region.getFlag(flag);
if (currVal == null) {
currVal = getInheritedValue(region, flag);
}
Number defVal = flag.getDefault();
Number[] suggested = getSuggestedNumbers(flag);
SortedSet<Number> choices = new TreeSet<>(Comparator.comparing(Number::doubleValue));
if (currVal != null) {
choices.add(currVal);
}
if (defVal != null) {
choices.add(defVal);
}
if (suggested.length > 0) {
choices.addAll(Arrays.asList(suggested));
}
//noinspection unchecked
appendValueChoices(builder, flag, (Iterator<V>) choices.iterator(), choices.isEmpty() ? "unset number" : "[custom]");
}
private Number[] getSuggestedNumbers(Flag<? extends Number> flag) {
if (flag == Flags.HEAL_AMOUNT || flag == Flags.FEED_AMOUNT) {
return new Number[]{0, 5, 10, 20};
} else if (flag == Flags.MIN_FOOD || flag == Flags.MIN_HEAL) {
return new Number[]{0, 10};
} else if (flag == Flags.MAX_FOOD || flag == Flags.MAX_HEAL) {
return new Number[]{10, 20};
} else if (flag == Flags.HEAL_DELAY || flag == Flags.FEED_DELAY) {
return new Number[]{0, 1, 5};
}
return new Number[0];
}
private void appendStringFlagValue(TextComponent.Builder builder, StringFlag flag) {
String currVal = region.getFlag(flag);
if (currVal == null) {
currVal = getInheritedValue(region, flag);
}
if (currVal == null) {
final String defVal = flag.getDefault();
if (defVal == null) {
appendValueText(builder, flag, "unset string", null);
} else {
final TextComponent defComp = LegacyComponentSerializer.INSTANCE.deserialize(defVal);
String display = reduceToText(defComp);
if (display.length() > 23) {
display = display.substring(0, 20) + "...";
}
appendValueText(builder, flag, display, TextComponent.of("Default value:")
.append(Component.newline()).append(defComp));
}
} else {
TextComponent currComp = LegacyComponentSerializer.INSTANCE.deserialize(currVal);
String display = reduceToText(currComp);
if (display.length() > 23) {
display = display.substring(0, 20) + "...";
}
appendValueText(builder, flag, display, TextComponent.of("Current value:")
.append(Component.newline()).append(currComp));
}
}
private static String reduceToText(Component component) {
StringBuilder text = new StringBuilder();
appendTextTo(text, component);
return text.toString();
}
private static void appendTextTo(StringBuilder builder, Component component) {
if (component instanceof TextComponent) {
builder.append(((TextComponent) component).content());
} else if (component instanceof TranslatableComponent) {
builder.append(((TranslatableComponent) component).key());
}
for (Component child : component.children()) {
appendTextTo(builder, child);
}
}
private static final class FlagFontInfo {
static int getPxLength(char c) {
switch (c) {
case 'i':
case ':':
return 1;
case 'l':
return 2;
case '*':
case 't':
return 3;
case 'f':
case 'k':
return 4;
default:
return 5;
}
}
static int getPxLength(String string) {
return string.chars().reduce(0, (p, i) -> p + getPxLength((char) i) + 1);
}
}
}

View File

@ -21,6 +21,7 @@
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.Command;
@ -35,6 +36,7 @@
import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.component.ErrorFormat; import com.sk89q.worldedit.util.formatting.component.ErrorFormat;
import com.sk89q.worldedit.util.formatting.component.InvalidComponentException;
import com.sk89q.worldedit.util.formatting.component.LabelFormat; import com.sk89q.worldedit.util.formatting.component.LabelFormat;
import com.sk89q.worldedit.util.formatting.component.SubtleFormat; import com.sk89q.worldedit.util.formatting.component.SubtleFormat;
import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.Component;
@ -319,7 +321,7 @@ public void select(CommandContext args, Actor sender) throws CommandException {
// If no arguments were given, get the region that the player is inside // If no arguments were given, get the region that the player is inside
if (args.argsLength() == 0) { if (args.argsLength() == 0) {
existing = checkRegionStandingIn(manager, player); existing = checkRegionStandingIn(manager, player, "/rg select %id%");
} else { } else {
existing = checkExistingRegion(manager, args.getString(0), false); existing = checkExistingRegion(manager, args.getString(0), false);
} }
@ -360,7 +362,8 @@ public void info(CommandContext args, Actor sender) throws CommandException {
throw new CommandException("Please specify the region with /region info -w world_name region_name."); throw new CommandException("Please specify the region with /region info -w world_name region_name.");
} }
existing = checkRegionStandingIn(manager, (LocalPlayer) sender, true); existing = checkRegionStandingIn(manager, (LocalPlayer) sender, true,
"/rg info -w " + world.getName() + " %id%" + (args.hasFlag('u') ? " -u" : "") + (args.hasFlag('s') ? " -s" : ""));
} else { // Get region from the ID } else { // Get region from the ID
existing = checkExistingRegion(manager, args.getString(0), true); existing = checkExistingRegion(manager, args.getString(0), true);
} }
@ -463,7 +466,7 @@ public void list(CommandContext args, Actor sender) throws CommandException {
*/ */
@Command(aliases = {"flag", "f"}, @Command(aliases = {"flag", "f"},
usage = "<id> <flag> [-w world] [-g group] [value]", usage = "<id> <flag> [-w world] [-g group] [value]",
flags = "g:w:e", flags = "g:w:eh:",
desc = "Set flags", desc = "Set flags",
min = 2) min = 2)
public void flag(CommandContext args, Actor sender) throws CommandException { public void flag(CommandContext args, Actor sender) throws CommandException {
@ -537,9 +540,9 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
sender.print(builder.build()); sender.print(builder.build());
return; return;
} else { } else if (value != null) {
if (foundFlag == Flags.BUILD || foundFlag == Flags.BLOCK_BREAK || foundFlag == Flags.BLOCK_PLACE) { if (foundFlag == Flags.BUILD || foundFlag == Flags.BLOCK_BREAK || foundFlag == Flags.BLOCK_PLACE) {
sender.print(Component.empty().append(TextComponent.of("WARNING:", TextColor.RED).decoration(TextDecoration.BOLD, true)) sender.print(Component.empty().append(TextComponent.of("WARNING:", TextColor.RED, Sets.newHashSet(TextDecoration.BOLD)))
.append(ErrorFormat.wrap(" Setting the " + foundFlag.getName() + " flag is not required for protection.")) .append(ErrorFormat.wrap(" Setting the " + foundFlag.getName() + " flag is not required for protection."))
.append(Component.newline()) .append(Component.newline())
.append(TextComponent.of("Setting this flag will completely override default protection, and apply" + .append(TextComponent.of("Setting this flag will completely override default protection, and apply" +
@ -554,7 +557,7 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
sender.printRaw("https://worldguard.readthedocs.io/en/latest/regions/flags/#protection-related"); sender.printRaw("https://worldguard.readthedocs.io/en/latest/regions/flags/#protection-related");
} }
} else if (foundFlag == Flags.PASSTHROUGH) { } else if (foundFlag == Flags.PASSTHROUGH) {
sender.print(Component.empty().append(TextComponent.of("WARNING:", TextColor.RED).decoration(TextDecoration.BOLD, true)) sender.print(Component.empty().append(TextComponent.of("WARNING:", TextColor.RED, Sets.newHashSet(TextDecoration.BOLD)))
.append(ErrorFormat.wrap(" This flag is unrelated to moving through regions.")) .append(ErrorFormat.wrap(" This flag is unrelated to moving through regions."))
.append(Component.newline()) .append(Component.newline())
.append(TextComponent.of("It overrides build checks. If you're unsure what this means, see ") .append(TextComponent.of("It overrides build checks. If you're unsure what this means, see ")
@ -604,7 +607,9 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
throw new CommandException(e.getMessage()); throw new CommandException(e.getMessage());
} }
if (!args.hasFlag('h')) {
sender.print("Region flag " + foundFlag.getName() + " set on '" + existing.getId() + "' to '" + value + "'."); sender.print("Region flag " + foundFlag.getName() + " set on '" + existing.getId() + "' to '" + value + "'.");
}
// No value? Clear the flag, if -g isn't specified // No value? Clear the flag, if -g isn't specified
} else if (!args.hasFlag('g')) { } else if (!args.hasFlag('g')) {
@ -617,8 +622,10 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
existing.setFlag(groupFlag, null); existing.setFlag(groupFlag, null);
} }
if (!args.hasFlag('h')) {
sender.print("Region flag " + foundFlag.getName() + " removed from '" + existing.getId() + "'. (Any -g(roups) were also removed.)"); sender.print("Region flag " + foundFlag.getName() + " removed from '" + existing.getId() + "'. (Any -g(roups) were also removed.)");
} }
}
// Now set the group // Now set the group
if (groupValue != null) { if (groupValue != null) {
@ -635,12 +642,49 @@ public void flag(CommandContext args, Actor sender) throws CommandException {
} }
// Print region information // Print region information
if (args.hasFlag('h')) {
int page = args.getFlagInteger('h');
try {
final FlagHelperBox flagHelperBox = new FlagHelperBox(world, existing, permModel);
flagHelperBox.setComponentsPerPage(18);
sender.print(flagHelperBox.create(page));
} catch (InvalidComponentException e) {
throw new CommandException("Error creating component.", e);
}
} else {
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(world.getName(), existing, null, sender); RegionPrintoutBuilder printout = new RegionPrintoutBuilder(world.getName(), existing, null, sender);
printout.append(SubtleFormat.wrap("(Current flags: ")); printout.append(SubtleFormat.wrap("(Current flags: "));
printout.appendFlagsList(false); printout.appendFlagsList(false);
printout.append(SubtleFormat.wrap(")")); printout.append(SubtleFormat.wrap(")"));
printout.send(sender); printout.send(sender);
} }
}
@Command(aliases = "flags",
usage = "<id> [page]",
flags = "w:",
desc = "View region flags",
min = 1, max = 2)
public void flagHelper(CommandContext args, Actor sender) throws CommandException {
World world = checkWorld(args, sender, 'w'); // Get the world
// Lookup the existing region
RegionManager manager = checkRegionManager(world);
ProtectedRegion region = checkExistingRegion(manager, args.getString(0), true);
final RegionPermissionModel perms = getPermissionModel(sender);
if (!perms.mayLookup(region)) {
throw new CommandPermissionsException();
}
int page = args.getInteger(1, 1);
try {
final FlagHelperBox flagHelperBox = new FlagHelperBox(world, region, perms);
flagHelperBox.setComponentsPerPage(18);
sender.print(flagHelperBox.create(page));
} catch (InvalidComponentException e) {
throw new CommandException("Error creating component.", e);
}
}
/** /**
* Set the priority of a region. * Set the priority of a region.

View File

@ -34,6 +34,8 @@
import com.sk89q.worldedit.regions.selector.Polygonal2DRegionSelector; import com.sk89q.worldedit.regions.selector.Polygonal2DRegionSelector;
import com.sk89q.worldedit.util.formatting.component.SubtleFormat; import com.sk89q.worldedit.util.formatting.component.SubtleFormat;
import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.event.ClickEvent;
import com.sk89q.worldedit.util.formatting.text.event.HoverEvent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor; import com.sk89q.worldedit.util.formatting.text.format.TextColor;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.LocalPlayer;
@ -157,8 +159,8 @@ protected static ProtectedRegion checkExistingRegion(RegionManager regionManager
* @return a region * @return a region
* @throws CommandException thrown if no region was found * @throws CommandException thrown if no region was found
*/ */
protected static ProtectedRegion checkRegionStandingIn(RegionManager regionManager, LocalPlayer player) throws CommandException { protected static ProtectedRegion checkRegionStandingIn(RegionManager regionManager, LocalPlayer player, String rgCmd) throws CommandException {
return checkRegionStandingIn(regionManager, player, false); return checkRegionStandingIn(regionManager, player, false, rgCmd);
} }
/** /**
@ -176,7 +178,7 @@ protected static ProtectedRegion checkRegionStandingIn(RegionManager regionManag
* @return a region * @return a region
* @throws CommandException thrown if no region was found * @throws CommandException thrown if no region was found
*/ */
protected static ProtectedRegion checkRegionStandingIn(RegionManager regionManager, LocalPlayer player, boolean allowGlobal) throws CommandException { protected static ProtectedRegion checkRegionStandingIn(RegionManager regionManager, LocalPlayer player, boolean allowGlobal, String rgCmd) throws CommandException {
ApplicableRegionSet set = regionManager.getApplicableRegions(player.getLocation().toVector().toBlockPoint()); ApplicableRegionSet set = regionManager.getApplicableRegions(player.getLocation().toVector().toBlockPoint());
if (set.size() == 0) { if (set.size() == 0) {
@ -190,19 +192,24 @@ protected static ProtectedRegion checkRegionStandingIn(RegionManager regionManag
"You're not standing in a region." + "You're not standing in a region." +
"Specify an ID if you want to select a specific region."); "Specify an ID if you want to select a specific region.");
} else if (set.size() > 1) { } else if (set.size() > 1) {
StringBuilder builder = new StringBuilder();
boolean first = true; boolean first = true;
final TextComponent.Builder builder = TextComponent.builder("");
builder.append(TextComponent.of("Current regions: ", TextColor.GOLD));
for (ProtectedRegion region : set) { for (ProtectedRegion region : set) {
if (!first) { if (!first) {
builder.append(", "); builder.append(TextComponent.of(", "));
} }
first = false; first = false;
builder.append(region.getId()); TextComponent regionComp = TextComponent.of(region.getId(), TextColor.AQUA);
if (rgCmd != null && rgCmd.contains("%id%")) {
regionComp = regionComp.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to pick this region")))
.clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, rgCmd.replace("%id%", region.getId())));
} }
builder.append(regionComp);
throw new CommandException( }
"You're standing in several regions (please pick one).\nYou're in: " + builder.toString()); player.print(builder.build());
throw new CommandException("You're standing in several regions (please pick one).");
} }
return set.iterator().next(); return set.iterator().next();

View File

@ -163,9 +163,9 @@ public void appendFlagsList(boolean useColors) {
if (perms != null && perms.maySetFlag(region)) { if (perms != null && perms.maySetFlag(region)) {
builder.append(Component.space()) builder.append(Component.space())
.append(TextComponent.of("[Add]", useColors ? TextColor.GREEN : TextColor.GRAY) .append(TextComponent.of("[Flags]", useColors ? TextColor.GREEN : TextColor.GRAY)
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to set a flag"))) .hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to set a flag")))
.clickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/rg flag -w " + world + " " + region.getId() + " "))); .clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/rg flags -w " + world + " " + region.getId())));
} }
} }

View File

@ -33,7 +33,15 @@
import com.sk89q.worldguard.protection.util.NormativeOrders; import com.sk89q.worldguard.protection.util.NormativeOrders;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -49,7 +57,6 @@
*/ */
public class FlagValueCalculator { public class FlagValueCalculator {
private final List<ProtectedRegion> regions;
@Nullable @Nullable
private final ProtectedRegion globalRegion; private final ProtectedRegion globalRegion;
private final Iterable<ProtectedRegion> applicable; private final Iterable<ProtectedRegion> applicable;
@ -63,14 +70,10 @@ public class FlagValueCalculator {
public FlagValueCalculator(List<ProtectedRegion> regions, @Nullable ProtectedRegion globalRegion) { public FlagValueCalculator(List<ProtectedRegion> regions, @Nullable ProtectedRegion globalRegion) {
checkNotNull(regions); checkNotNull(regions);
this.regions = regions;
this.globalRegion = globalRegion; this.globalRegion = globalRegion;
if (globalRegion != null) { applicable = globalRegion == null ? regions
applicable = Iterables.concat(regions, Arrays.asList(globalRegion)); : Iterables.concat(regions, Collections.singletonList(globalRegion));
} else {
applicable = regions;
}
} }
/** /**
@ -128,7 +131,7 @@ public Result getMembership(RegionAssociable subject) {
minimumPriority = getPriority(region); minimumPriority = getPriority(region);
boolean member = RegionGroup.MEMBERS.contains(subject.getAssociation(Arrays.asList(region))); boolean member = RegionGroup.MEMBERS.contains(subject.getAssociation(Collections.singletonList(region)));
if (member) { if (member) {
result = Result.SUCCESS; result = Result.SUCCESS;

View File

@ -32,12 +32,18 @@
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry; import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** /**
* The flags that are used in WorldGuard. * The flags that are used in WorldGuard.
*/ */
public final class Flags { public final class Flags {
private static final List<String> INBUILT_FLAGS_LIST = new ArrayList<>();
public static final List<String> INBUILT_FLAGS = Collections.unmodifiableList(INBUILT_FLAGS_LIST);
// Overrides membership check // Overrides membership check
public static final StateFlag PASSTHROUGH = register(new StateFlag("passthrough", false)); public static final StateFlag PASSTHROUGH = register(new StateFlag("passthrough", false));
@ -91,7 +97,7 @@ public final class Flags {
// mob spawning related // mob spawning related
public static final StateFlag MOB_SPAWNING = register(new StateFlag("mob-spawning", true)); public static final StateFlag MOB_SPAWNING = register(new StateFlag("mob-spawning", true));
public static final SetFlag<EntityType> DENY_SPAWN = register(new SetFlag<>("deny-spawn", new EntityTypeFlag(null))); public static final SetFlag<EntityType> DENY_SPAWN = register(new SetFlag<>("deny-spawn", new RegistryFlag<>(null, EntityType.REGISTRY)));
// block dynamics // block dynamics
public static final StateFlag PISTONS = register(new StateFlag("pistons", true)); public static final StateFlag PISTONS = register(new StateFlag("pistons", true));
@ -111,6 +117,9 @@ public final class Flags {
public static final StateFlag WATER_FLOW = register(new StateFlag("water-flow", true)); public static final StateFlag WATER_FLOW = register(new StateFlag("water-flow", true));
public static final StateFlag LAVA_FLOW = register(new StateFlag("lava-flow", true)); public static final StateFlag LAVA_FLOW = register(new StateFlag("lava-flow", true));
public static final RegistryFlag<WeatherType> WEATHER_LOCK = register(new RegistryFlag<>("weather-lock", WeatherType.REGISTRY));
public static final StringFlag TIME_LOCK = register(new StringFlag("time-lock"));
// chat related flags // chat related flags
public static final StateFlag SEND_CHAT = register(new StateFlag("send-chat", true)); public static final StateFlag SEND_CHAT = register(new StateFlag("send-chat", true));
public static final StateFlag RECEIVE_CHAT = register(new StateFlag("receive-chat", true)); public static final StateFlag RECEIVE_CHAT = register(new StateFlag("receive-chat", true));
@ -140,9 +149,7 @@ public final class Flags {
public static final BooleanFlag NOTIFY_ENTER = register(new BooleanFlag("notify-enter")); public static final BooleanFlag NOTIFY_ENTER = register(new BooleanFlag("notify-enter"));
public static final BooleanFlag NOTIFY_LEAVE = register(new BooleanFlag("notify-leave")); public static final BooleanFlag NOTIFY_LEAVE = register(new BooleanFlag("notify-leave"));
public static final Flag<GameMode> GAME_MODE = register(new GameModeTypeFlag("game-mode")); public static final RegistryFlag<GameMode> GAME_MODE = register(new RegistryFlag<>("game-mode", GameMode.REGISTRY));
public static final StringFlag TIME_LOCK = register(new StringFlag("time-lock"));
public static final Flag<WeatherType> WEATHER_LOCK = register(new WeatherTypeFlag("weather-lock"));
public static final IntegerFlag HEAL_DELAY = register(new IntegerFlag("heal-delay")); public static final IntegerFlag HEAL_DELAY = register(new IntegerFlag("heal-delay"));
public static final IntegerFlag HEAL_AMOUNT = register(new IntegerFlag("heal-amount")); public static final IntegerFlag HEAL_AMOUNT = register(new IntegerFlag("heal-amount"));
@ -173,6 +180,7 @@ private Flags() {
private static <T extends Flag<?>> T register(final T flag) throws FlagConflictException { private static <T extends Flag<?>> T register(final T flag) throws FlagConflictException {
WorldGuard.getInstance().getFlagRegistry().register(flag); WorldGuard.getInstance().getFlagRegistry().register(flag);
INBUILT_FLAGS_LIST.add(flag.getName());
return flag; return flag;
} }

View File

@ -67,23 +67,14 @@ public static boolean isMember(ProtectedRegion region, RegionGroup group, @Nulla
if (group == null || group == RegionGroup.ALL) { if (group == null || group == RegionGroup.ALL) {
return true; return true;
} else if (group == RegionGroup.OWNERS) { } else if (group == RegionGroup.OWNERS) {
if (player != null && region.isOwner(player)) { return player != null && region.isOwner(player);
return true;
}
} else if (group == RegionGroup.MEMBERS) { } else if (group == RegionGroup.MEMBERS) {
if (player != null && region.isMember(player)) { return player != null && region.isMember(player);
return true;
}
} else if (group == RegionGroup.NON_OWNERS) { } else if (group == RegionGroup.NON_OWNERS) {
if (player == null || !region.isOwner(player)) { return player == null || !region.isOwner(player);
return true;
}
} else if (group == RegionGroup.NON_MEMBERS) { } else if (group == RegionGroup.NON_MEMBERS) {
if (player == null || !region.isMember(player)) { return player == null || !region.isMember(player);
return true;
} }
}
return false; return false;
} }
@ -91,23 +82,14 @@ public static boolean isMember(ApplicableRegionSet set, @Nullable RegionGroup gr
if (group == null || group == RegionGroup.ALL) { if (group == null || group == RegionGroup.ALL) {
return true; return true;
} else if (group == RegionGroup.OWNERS) { } else if (group == RegionGroup.OWNERS) {
if (set.isOwnerOfAll(player)) { return set.isOwnerOfAll(player);
return true;
}
} else if (group == RegionGroup.MEMBERS) { } else if (group == RegionGroup.MEMBERS) {
if (set.isMemberOfAll(player)) { return set.isMemberOfAll(player);
return true;
}
} else if (group == RegionGroup.NON_OWNERS) { } else if (group == RegionGroup.NON_OWNERS) {
if (!set.isOwnerOfAll(player)) { return !set.isOwnerOfAll(player);
return true;
}
} else if (group == RegionGroup.NON_MEMBERS) { } else if (group == RegionGroup.NON_MEMBERS) {
if (!set.isMemberOfAll(player)) { return !set.isMemberOfAll(player);
return true;
} }
}
return false; return false;
} }

View File

@ -0,0 +1,62 @@
/*
* WorldGuard, a suite of tools for Minecraft
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldGuard 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 <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldguard.protection.flags;
import com.sk89q.worldedit.registry.Keyed;
import com.sk89q.worldedit.registry.Registry;
import javax.annotation.Nullable;
import java.util.Locale;
import java.util.Optional;
public class RegistryFlag<T extends Keyed> extends Flag<T> {
private final Registry<T> registry;
public RegistryFlag(String name, Registry<T> registry) {
super(name);
this.registry = registry;
}
public RegistryFlag(String name, @Nullable RegionGroup defaultGroup, Registry<T> registry) {
super(name, defaultGroup);
this.registry = registry;
}
@Override
public T parseInput(FlagContext context) throws InvalidFlagFormat {
final String key = context.getUserInput().trim().toLowerCase(Locale.ENGLISH);
return Optional.ofNullable(registry.get(key))
.orElseThrow(() -> new InvalidFlagFormat("Unknown " + registry.getName() + ": " + key));
}
public Registry<T> getRegistry() {
return registry;
}
@Override
public T unmarshal(@Nullable Object o) {
return registry.get(String.valueOf(o));
}
@Override
public Object marshal(T o) {
return o.getId();
}
}

View File

@ -19,7 +19,6 @@
package com.sk89q.worldguard.protection.flags.registry; package com.sk89q.worldguard.protection.flags.registry;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterators; import com.google.common.collect.Iterators;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -80,7 +79,7 @@ public void registerAll(Collection<Flag<?>> flags) {
private Flag<?> forceRegister(Flag<?> flag) throws FlagConflictException { private Flag<?> forceRegister(Flag<?> flag) throws FlagConflictException {
checkNotNull(flag, "flag"); checkNotNull(flag, "flag");
Preconditions.checkNotNull(flag.getName(), "flag.getName()"); checkNotNull(flag.getName(), "flag.getName()");
synchronized (lock) { synchronized (lock) {
String name = flag.getName().toLowerCase(); String name = flag.getName().toLowerCase();