From 8d5953a55009d500551771641db17eb0b6bb5abe Mon Sep 17 00:00:00 2001 From: JOO200 Date: Wed, 29 Dec 2021 21:20:54 +0100 Subject: [PATCH] apidomains: Added custom domains to WorldGuard This change allows third party plugins to dynamically add custom domains to WorldGuard. --- .../worldguard/bukkit/WorldGuardPlugin.java | 2 + .../java/com/sk89q/worldguard/WorldGuard.java | 13 ++ .../commands/CommandInputContext.java | 112 ++++++++++++ .../commands/region/MemberCommands.java | 22 ++- .../commands/region/RegionCommands.java | 4 +- .../commands/region/RegionCommandsBase.java | 7 +- .../worldguard/commands/task/RegionAdder.java | 18 +- .../worldguard/domains/CustomDomain.java | 108 +++++++++++ .../worldguard/domains/DefaultDomain.java | 147 +++++++++++---- .../domains/registry/CustomDomainContext.java | 98 ++++++++++ .../registry/DomainConflictException.java | 26 +++ .../domains/registry/DomainFactory.java | 28 +++ .../domains/registry/DomainRegistry.java | 94 ++++++++++ .../InvalidDomainFormatException.java | 28 +++ .../registry/SimpleDomainRegistry.java | 167 ++++++++++++++++++ .../domains/registry/UnknownDomain.java | 88 +++++++++ .../permission/RegionPermissionModel.java | 3 +- .../protection/flags/FlagContext.java | 97 ++-------- .../managers/storage/file/YamlRegionFile.java | 16 ++ .../protection/util/DomainInputResolver.java | 53 +++++- .../util/WorldGuardExceptionConverter.java | 6 + .../worldguard/domains/CustomUUIDDomain.java | 64 +++++++ .../worldguard/domains/DefaultDomainTest.java | 6 + 23 files changed, 1078 insertions(+), 129 deletions(-) create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/CustomDomain.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/CustomDomainContext.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainConflictException.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainFactory.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainRegistry.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/InvalidDomainFormatException.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/SimpleDomainRegistry.java create mode 100644 worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/UnknownDomain.java create mode 100644 worldguard-core/src/test/java/com/sk89q/worldguard/domains/CustomUUIDDomain.java diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 86aefb55..3ba99792 100644 --- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -65,6 +65,7 @@ import com.sk89q.worldguard.bukkit.util.Events; import com.sk89q.worldguard.commands.GeneralCommands; import com.sk89q.worldguard.commands.ProtectionCommands; import com.sk89q.worldguard.commands.ToggleCommands; +import com.sk89q.worldguard.domains.registry.SimpleDomainRegistry; import com.sk89q.worldguard.protection.flags.Flag; import com.sk89q.worldguard.protection.flags.Flags; import com.sk89q.worldguard.protection.flags.registry.SimpleFlagRegistry; @@ -212,6 +213,7 @@ public class WorldGuardPlugin extends JavaPlugin { }); ((SimpleFlagRegistry) WorldGuard.getInstance().getFlagRegistry()).setInitialized(true); + ((SimpleDomainRegistry) WorldGuard.getInstance().getDomainRegistry()).setInitialized(true); // Enable metrics final Metrics metrics = new Metrics(this, BSTATS_PLUGIN_ID); // bStats plugin id diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java b/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java index 637eee7b..cc287c6c 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/WorldGuard.java @@ -24,6 +24,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.worldguard.domains.registry.DomainRegistry; +import com.sk89q.worldguard.domains.registry.SimpleDomainRegistry; import com.sk89q.worldguard.util.profile.cache.HashMapCache; import com.sk89q.worldguard.util.profile.cache.ProfileCache; import com.sk89q.worldguard.util.profile.cache.SQLiteCache; @@ -55,6 +57,7 @@ public final class WorldGuard { private WorldGuardPlatform platform; private final SimpleFlagRegistry flagRegistry = new SimpleFlagRegistry(); + private final SimpleDomainRegistry domainRegistry = new SimpleDomainRegistry(); private final Supervisor supervisor = new SimpleSupervisor(); private ProfileCache profileCache; private ProfileService profileService; @@ -116,6 +119,16 @@ public final class WorldGuard { return this.flagRegistry; } + + /** + * Get the domain registry. + * + * @return the domain registry + */ + public DomainRegistry getDomainRegistry() { + return this.domainRegistry; + } + /** * Get the supervisor. * diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java new file mode 100644 index 00000000..7c6de508 --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/CommandInputContext.java @@ -0,0 +1,112 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldguard.commands; + +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.protection.flags.InvalidFlagFormat; + +import javax.annotation.Nullable; +import java.util.Map; + +public abstract class CommandInputContext { + protected final Actor sender; + protected final String input; + + protected Map context; + + protected CommandInputContext(Actor sender, String input, Map values) { + this.sender = sender; + this.input = input; + this.context = values; + } + + public void put(String name, Object value) { + context.put(name, value); + } + + public Actor getSender() { + return sender; + } + + public String getUserInput() { + return input; + } + + /** + * Gets the CommandSender as a player. + * + * @return Player + * @throws T if the sender is not a player + */ + public LocalPlayer getPlayerSender() throws T { + if (sender.isPlayer() && sender instanceof LocalPlayer) { + return (LocalPlayer) sender; + } else { + throw createException("Not a player"); + } + } + + public Integer getUserInputAsInt() throws T { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw createException("Not a number: " + input); + } + } + + public Double getUserInputAsDouble() throws T { + try { + return Double.parseDouble(input); + } catch (NumberFormatException e) { + throw createException("Not a number: " + input); + } + } + + protected abstract T createException(String str); + + /** + * Get an object from the context by key name. + * May return null if the object does not exist in the context. + * + * @param name key name of the object + * @return the object matching the key, or null + */ + @Nullable + public Object get(String name) { + return get(name, null); + } + + /** + * Get an object from the context by key name. + * Will only return null if + * a) you provide null as the default + * b) the key has explicity been set to null + * + * @param name key name of the object + * @return the object matching the key + */ + @Nullable + public Object get(String name, Object defaultValue) { + Object obj; + return (((obj = context.get(name)) != null) || context.containsKey(name) + ? obj : defaultValue); + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/MemberCommands.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/MemberCommands.java index e41eb1b9..45bb9f1b 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/MemberCommands.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/MemberCommands.java @@ -25,16 +25,27 @@ import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissionsException; import com.sk89q.worldedit.command.util.AsyncCommandBuilder; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldedit.util.auth.AuthorizationException; +import com.sk89q.worldedit.util.formatting.component.ErrorFormat; +import com.sk89q.worldedit.util.formatting.text.Component; +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.world.World; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.domains.registry.DomainFactory; +import com.sk89q.worldguard.domains.registry.DomainRegistry; +import com.sk89q.worldguard.internal.permission.RegionPermissionModel; import com.sk89q.worldguard.protection.managers.RegionManager; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.util.DomainInputResolver; import com.sk89q.worldguard.protection.util.DomainInputResolver.UserLocatorPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; import java.util.concurrent.Callable; public class MemberCommands extends RegionCommandsBase { @@ -67,6 +78,8 @@ public class MemberCommands extends RegionCommandsBase { DomainInputResolver resolver = new DomainInputResolver( WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0)); resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); + resolver.setActor(sender); + resolver.setRegion(region); final String description = String.format("Adding members to the region '%s' on '%s'", region.getId(), world.getName()); @@ -101,7 +114,8 @@ public class MemberCommands extends RegionCommandsBase { DomainInputResolver resolver = new DomainInputResolver( WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0)); resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_ONLY); - + resolver.setActor(sender); + resolver.setRegion(region); final String description = String.format("Adding owners to the region '%s' on '%s'", region.getId(), world.getName()); AsyncCommandBuilder.wrap(checkedAddOwners(sender, manager, region, world, resolver), sender) @@ -174,6 +188,8 @@ public class MemberCommands extends RegionCommandsBase { DomainInputResolver resolver = new DomainInputResolver( WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0)); resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME); + resolver.setActor(sender); + resolver.setRegion(region); callable = resolver; } @@ -217,6 +233,8 @@ public class MemberCommands extends RegionCommandsBase { DomainInputResolver resolver = new DomainInputResolver( WorldGuard.getInstance().getProfileService(), args.getParsedPaddedSlice(1, 0)); resolver.setLocatorPolicy(args.hasFlag('n') ? UserLocatorPolicy.NAME_ONLY : UserLocatorPolicy.UUID_AND_NAME); + resolver.setActor(sender); + resolver.setRegion(region); callable = resolver; } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java index 9299ad02..10418495 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommands.java @@ -160,7 +160,7 @@ public final class RegionCommands extends RegionCommandsBase { region = checkRegionFromSelection(sender, id); } - RegionAdder task = new RegionAdder(manager, region); + RegionAdder task = new RegionAdder(manager, region, sender); task.addOwnersFromCommand(args, 2); final String description = String.format("Adding region '%s'", region.getId()); @@ -214,7 +214,7 @@ public final class RegionCommands extends RegionCommandsBase { region.copyFrom(existing); - RegionAdder task = new RegionAdder(manager, region); + RegionAdder task = new RegionAdder(manager, region, sender); final String description = String.format("Updating region '%s'", region.getId()); AsyncCommandBuilder.wrap(task, sender) diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java index 7d2ca5a8..16304896 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/region/RegionCommandsBase.java @@ -32,18 +32,19 @@ import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionSelector; -import com.sk89q.worldedit.regions.selector.CuboidRegionSelector; -import com.sk89q.worldedit.regions.selector.Polygonal2DRegionSelector; import com.sk89q.worldedit.util.formatting.component.ErrorFormat; import com.sk89q.worldedit.util.formatting.component.SubtleFormat; 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.TextDecoration; import com.sk89q.worldedit.world.World; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.CustomDomain; +import com.sk89q.worldguard.domains.DefaultDomain; +import com.sk89q.worldguard.domains.registry.CustomDomainContext; +import com.sk89q.worldguard.domains.registry.InvalidDomainFormat; import com.sk89q.worldguard.internal.permission.RegionPermissionModel; import com.sk89q.worldguard.protection.ApplicableRegionSet; import com.sk89q.worldguard.protection.flags.Flag; diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/task/RegionAdder.java b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/task/RegionAdder.java index 932b8582..dc7a957d 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/commands/task/RegionAdder.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/commands/task/RegionAdder.java @@ -20,6 +20,7 @@ package com.sk89q.worldguard.commands.task; import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldguard.WorldGuard; import com.sk89q.worldguard.domains.DefaultDomain; import com.sk89q.worldguard.protection.managers.RegionManager; @@ -39,6 +40,7 @@ public class RegionAdder implements Callable { private final RegionManager manager; private final ProtectedRegion region; + private final Actor actor; @Nullable private String[] ownersInput; private UserLocatorPolicy locatorPolicy = UserLocatorPolicy.UUID_ONLY; @@ -46,15 +48,26 @@ public class RegionAdder implements Callable { /** * Create a new instance. * - * @param manager the manage + * @param manager the manager * @param region the region */ public RegionAdder(RegionManager manager, ProtectedRegion region) { + this(manager, region, null); + } + + /** + * Create a new instance. + * @param manager the manager + * @param region the region + * @param actor the actor + */ + public RegionAdder(RegionManager manager, ProtectedRegion region, Actor actor) { checkNotNull(manager); checkNotNull(region); this.manager = manager; this.region = region; + this.actor = actor; } /** @@ -75,6 +88,9 @@ public class RegionAdder implements Callable { if (ownersInput != null) { DomainInputResolver resolver = new DomainInputResolver(WorldGuard.getInstance().getProfileService(), ownersInput); resolver.setLocatorPolicy(locatorPolicy); + resolver.setActor(actor); + resolver.setRegion(region); + DefaultDomain domain = resolver.call(); region.getOwners().addAll(domain); } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/CustomDomain.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/CustomDomain.java new file mode 100644 index 00000000..1a410bee --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/CustomDomain.java @@ -0,0 +1,108 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldguard.domains; + +import com.sk89q.worldguard.LocalPlayer; +import com.sk89q.worldguard.domains.registry.CustomDomainContext; +import com.sk89q.worldguard.domains.registry.InvalidDomainFormatException; +import com.sk89q.worldguard.util.ChangeTracked; + +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkNotNull; + +public abstract class CustomDomain implements Domain, ChangeTracked { + private static final Pattern VALID_NAME = Pattern.compile("^[a-z0-9\\-]{1,40}$"); + + private final String name; + private boolean dirty; + + public CustomDomain(String name) { + if (name == null ||!isValidName(name)) { + throw new IllegalArgumentException("Invalid Domain name used."); + } + this.name = name; + } + + /** + * Get the name of the domain resolver. + * + * @return The name of the domain + */ + public String getName() { + return name; + } + + /** + * Parse a given input to fill the context of the CustomDomain. + * + * @param context the {@link CustomDomainContext} + * @throws InvalidDomainFormatException Raised if the input is invalid + */ + public abstract void parseInput(CustomDomainContext context) throws InvalidDomainFormatException; + + /** + * Convert a raw type that was loaded (from a YAML file, for example) + * into the custom domain. + * + * @param o The object + */ + public abstract void unmarshal(Object o); + + /** + * Convert the current Domain to a storable foramt + * + * @return The marshalled type + */ + public abstract Object marshal(); + + /** + * Test whether a flag name is valid. + * + * @param name The flag name + * @return Whether the name is valid + */ + public static boolean isValidName(String name) { + checkNotNull(name, "name"); + // g is already reserved by the group domain + return VALID_NAME.matcher(name).matches() && !name.equalsIgnoreCase("g"); + } + + + @Override + public boolean contains(LocalPlayer player) { + return contains(player.getUniqueId()); + } + + @Override + public int size() { + return 1; + } + + @Override + public boolean isDirty() { + return dirty; + } + + @Override + public void setDirty(boolean dirty) { + this.dirty = dirty; + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java index 3030a9c1..975d1a58 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/DefaultDomain.java @@ -22,8 +22,6 @@ package com.sk89q.worldguard.domains; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.sk89q.worldguard.util.profile.Profile; -import com.sk89q.worldguard.util.profile.cache.ProfileCache; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; @@ -31,9 +29,14 @@ import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; import com.sk89q.worldedit.util.formatting.text.format.TextColor; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.util.ChangeTracked; +import com.sk89q.worldguard.util.profile.Profile; +import com.sk89q.worldguard.util.profile.cache.ProfileCache; import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -50,6 +53,9 @@ public class DefaultDomain implements Domain, ChangeTracked { private PlayerDomain playerDomain = new PlayerDomain(); private GroupDomain groupDomain = new GroupDomain(); + private Set customDomains = new HashSet<>(); + private boolean customDomainsChanged = false; + /** * Create a new domain. */ @@ -64,6 +70,7 @@ public class DefaultDomain implements Domain, ChangeTracked { public DefaultDomain(DefaultDomain existing) { setPlayerDomain(existing.getPlayerDomain()); setGroupDomain(existing.getGroupDomain()); + setCustomDomains(existing.getCustomDomains()); } /** @@ -104,6 +111,62 @@ public class DefaultDomain implements Domain, ChangeTracked { this.groupDomain = new GroupDomain(groupDomain); } + /** + * Add new custom domains + * + * @param customDomain a domain + */ + public void addCustomDomain(CustomDomain customDomain) { + checkNotNull(customDomain); + removeCustomDomain(customDomain.getName()); + this.customDomains.add(customDomain); + customDomainsChanged = true; + } + + /** + * Remove a custom domain matched by the name + * + * @param name the name + */ + public void removeCustomDomain(String name) { + checkNotNull(name); + if (this.customDomains.removeIf(d -> d.getName().equalsIgnoreCase(name))) { + customDomainsChanged = true; + } + } + + /** + * Remove a custom domain + * + * @param customDomain a domain + */ + public void removeCustomDomain(CustomDomain customDomain) { + checkNotNull(customDomain); + if (this.customDomains.remove(customDomain)) { + customDomainsChanged = true; + } + } + + /** + * Set the api domains to a specified value + * + * @param customDomains the domains + */ + public void setCustomDomains(Collection customDomains) { + checkNotNull(customDomains); + this.customDomains = new HashSet<>(customDomains); + customDomainsChanged = true; + } + + /** + * Get all api domains + * + * @return a unmodifiable copy of the domains + */ + public Set getCustomDomains() { + return Collections.unmodifiableSet(this.customDomains); + } + /** * Add the given player to the domain, identified by the player's name. * @@ -175,6 +238,9 @@ public class DefaultDomain implements Domain, ChangeTracked { for (String group : other.getGroups()) { addGroup(group); } + for (CustomDomain domain : other.getCustomDomains()) { + addCustomDomain(domain); + } } /** @@ -193,6 +259,9 @@ public class DefaultDomain implements Domain, ChangeTracked { for (String group : other.getGroups()) { removeGroup(group); } + for (CustomDomain domain : other.getCustomDomains()) { + removeCustomDomain(domain.getName()); + } } /** @@ -242,12 +311,12 @@ public class DefaultDomain implements Domain, ChangeTracked { @Override public boolean contains(LocalPlayer player) { - return playerDomain.contains(player) || groupDomain.contains(player); + return playerDomain.contains(player) || groupDomain.contains(player) || customDomains.stream().anyMatch(d -> d.contains(player)); } @Override public boolean contains(UUID uniqueId) { - return playerDomain.contains(uniqueId); + return playerDomain.contains(uniqueId) || customDomains.stream().anyMatch(d -> d.contains(uniqueId)); } @Override @@ -257,7 +326,7 @@ public class DefaultDomain implements Domain, ChangeTracked { @Override public int size() { - return groupDomain.size() + playerDomain.size(); + return groupDomain.size() + playerDomain.size() + customDomains.size(); } @Override @@ -275,7 +344,6 @@ public class DefaultDomain implements Domain, ChangeTracked { } public String toPlayersString(@Nullable ProfileCache cache) { - StringBuilder str = new StringBuilder(); List output = new ArrayList<>(); for (String name : playerDomain.getPlayers()) { @@ -299,13 +367,7 @@ public class DefaultDomain implements Domain, ChangeTracked { } output.sort(String.CASE_INSENSITIVE_ORDER); - for (Iterator it = output.iterator(); it.hasNext();) { - str.append(it.next()); - if (it.hasNext()) { - str.append(", "); - } - } - return str.toString(); + return String.join(", ", output); } public String toGroupsString() { @@ -320,25 +382,20 @@ public class DefaultDomain implements Domain, ChangeTracked { return str.toString(); } - public String toUserFriendlyString() { - StringBuilder str = new StringBuilder(); - - if (playerDomain.size() > 0) { - str.append(toPlayersString()); + public String toCustomDomainsString() { + List output = new ArrayList<>(); + for (CustomDomain customDomain : customDomains) { + output.add(customDomain.getName() + ":" + customDomain.toString()); } - - if (groupDomain.size() > 0) { - if (str.length() > 0) { - str.append("; "); - } - - str.append(toGroupsString()); - } - - return str.toString(); + output.sort(String.CASE_INSENSITIVE_ORDER); + return String.join(", ", output); } - public String toUserFriendlyString(ProfileCache cache) { + public String toUserFriendlyString() { + return toUserFriendlyString(null); + } + + public String toUserFriendlyString(@Nullable ProfileCache cache) { StringBuilder str = new StringBuilder(); if (playerDomain.size() > 0) { @@ -352,6 +409,12 @@ public class DefaultDomain implements Domain, ChangeTracked { str.append(toGroupsString()); } + if (!customDomains.isEmpty()) { + if (str.length() > 0) { + str.append("; "); + } + str.append(toCustomDomainsString()); + } return str.toString(); } @@ -367,6 +430,12 @@ public class DefaultDomain implements Domain, ChangeTracked { } builder.append(toGroupsComponent()); } + if (!customDomains.isEmpty()) { + if (playerDomain.size() > 0 || groupDomain.size() > 0) { + builder.append(TextComponent.of("; ")); + } + builder.append(toCustomDomainsComponent()); + } return builder.build(); } @@ -442,21 +511,39 @@ public class DefaultDomain implements Domain, ChangeTracked { return builder.build(); } + private Component toCustomDomainsComponent() { + final TextComponent.Builder builder = TextComponent.builder(""); + for (Iterator it = customDomains.iterator(); it.hasNext(); ) { + CustomDomain domain = it.next(); + builder.append(TextComponent.of(domain.getName() + ":", TextColor.LIGHT_PURPLE)) + .append(TextComponent.of(domain.toString(), TextColor.GOLD)); + if (it.hasNext()) { + builder.append(TextComponent.of(", ")); + } + } + return builder.build().hoverEvent(HoverEvent.of(HoverEvent.Action.SHOW_TEXT, TextComponent.of("CustomDomain"))); + } + + @Override public boolean isDirty() { - return playerDomain.isDirty() || groupDomain.isDirty(); + return playerDomain.isDirty() || groupDomain.isDirty() || + customDomainsChanged || customDomains.stream().anyMatch(ChangeTracked::isDirty); } @Override public void setDirty(boolean dirty) { playerDomain.setDirty(dirty); groupDomain.setDirty(dirty); + customDomainsChanged = dirty; + customDomains.forEach(d -> d.setDirty(dirty)); } @Override public String toString() { return "{players=" + playerDomain + ", groups=" + groupDomain + + ", custom=" + customDomains + '}'; } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/CustomDomainContext.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/CustomDomainContext.java new file mode 100644 index 00000000..cdcfd461 --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/CustomDomainContext.java @@ -0,0 +1,98 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldguard.domains.registry; + +import com.google.common.collect.Maps; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldguard.commands.CommandInputContext; + +import javax.annotation.Nullable; +import java.util.Map; + +public final class CustomDomainContext extends CommandInputContext { + + private CustomDomainContext(Actor sender, String input, Map values) { + super(sender, input, values); + } + + + public static CustomDomainContext.CustomDomainContextBuilder create() { + return new CustomDomainContext.CustomDomainContextBuilder(); + } + + /** + * Create a copy of this CustomDomainContext, with optional substitutions for values + * + *

If any supplied variable is null, it will be ignored. + * If a map is supplied, it will override this CustomDomainContext's values of the same key, + * but unprovided keys will not be overriden and will be returned as shallow copies.

+ * + * @param commandSender CommandSender for the new CustomDomainContext to run under + * @param s String of the user input for the new CustomDomainContext + * @param values map of values to override from the current CustomDomainContext + * @return a copy of this CustomDomainContext + */ + public CustomDomainContext copyWith(@Nullable Actor commandSender, @Nullable String s, @Nullable Map values) { + Map map = Maps.newHashMap(); + map.putAll(context); + if (values != null) { + map.putAll(values); + } + return new CustomDomainContext(commandSender == null ? this.sender : commandSender, s == null ? this.input : s, map); + } + + @Override + protected InvalidDomainFormatException createException(String str) { + return new InvalidDomainFormatException(str); + } + + public static class CustomDomainContextBuilder { + private Actor sender; + private String input; + private Map map = Maps.newHashMap(); + + public CustomDomainContextBuilder setSender(Actor sender) { + this.sender = sender; + return this; + } + + public CustomDomainContextBuilder setInput(String input) { + this.input = input; + return this; + } + + public CustomDomainContextBuilder setObject(String key, Object value) { + this.map.put(key, value); + return this; + } + + public boolean tryAddToMap(String key, Object value) { + if (map.containsKey(key)) return false; + this.map.put(key, value); + return true; + } + + public CustomDomainContext build() { + return new CustomDomainContext(sender, input, map); + } + } + +} + diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainConflictException.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainConflictException.java new file mode 100644 index 00000000..4ab7e933 --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainConflictException.java @@ -0,0 +1,26 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldguard.domains.registry; + +public class DomainConflictException extends RuntimeException { + public DomainConflictException(String message) { + super(message); + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainFactory.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainFactory.java new file mode 100644 index 00000000..5d901bd3 --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainFactory.java @@ -0,0 +1,28 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * 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 . + */ + + +package com.sk89q.worldguard.domains.registry; + +import com.sk89q.worldguard.domains.CustomDomain; + +@FunctionalInterface +public interface DomainFactory { + T create(String name); +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainRegistry.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainRegistry.java new file mode 100644 index 00000000..5fd8ac46 --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/DomainRegistry.java @@ -0,0 +1,94 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldguard.domains.registry; + +import com.sk89q.worldguard.domains.CustomDomain; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; + +public interface DomainRegistry extends Iterable> { + + /** + * Register a new Domain + * + *

There may be an appropiate time to register domains. if domains are + * registered outside this time, then an exception may be thrown.

+ * + * @param domain The domain + * @throws DomainConflictException Thrown when already an existing domain exists with the same name + * @throws IllegalStateException If it is not the right time to register new domains + */ + void register(String name, DomainFactory domain) throws DomainConflictException; + + /** + * Register a collection of domains. + * + *

There may be an appropriate time to register domains. If domains are + * registered outside this time, then an exception may be thrown.

+ * + *

If there is a domain conflict, then an error will be logged but + * no exception will be thrown.

+ * + * @param domains a collection of domain factories + * @throws IllegalStateException If it is not the right time to register new domains + */ + void registerAll(Map> domains); + + /** + * Get the domain by its name. + * + * @param name The name + * @return The domain, if it has been registered + */ + @Nullable + DomainFactory get(String name); + + /** + * Try to get a domain by its name + */ + @Nullable + CustomDomain createDomain(String name); + + /** + * Get all domains keyed by the registered name + * + * @return All domains + */ + Map> getAll(); + + /** + * Unmarshal a raw map of values into a list of domains with their + * unmarshalled values. + * + * @param rawValues The raw values map + * @param createUnknown Whether "just in time" domains should be created for unknown domains + * @return The unmarshalled domain list + */ + List unmarshal(Map rawValues, boolean createUnknown); + + /** + * Get the number of registered domains. + * + * @return The number of registered domains + */ + int size(); +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/InvalidDomainFormatException.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/InvalidDomainFormatException.java new file mode 100644 index 00000000..1d6397ef --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/InvalidDomainFormatException.java @@ -0,0 +1,28 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldguard.domains.registry; + +public class InvalidDomainFormatException extends Exception { + private static final long serialVersionUID = 8101615074524004172L; + + public InvalidDomainFormatException(String msg) { + super(msg); + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/SimpleDomainRegistry.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/SimpleDomainRegistry.java new file mode 100644 index 00000000..2d4330a2 --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/SimpleDomainRegistry.java @@ -0,0 +1,167 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldguard.domains.registry; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterators; +import com.google.common.collect.Maps; +import com.sk89q.worldguard.domains.CustomDomain; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class SimpleDomainRegistry implements DomainRegistry { + private static final Logger log = Logger.getLogger(SimpleDomainRegistry.class.getCanonicalName()); + + private final Object lock = new Object(); + private final ConcurrentMap> domains = Maps.newConcurrentMap(); + private boolean initialized = false; + + public boolean isInitialized() { + return initialized; + } + + public void setInitialized(boolean initialized) { + this.initialized = initialized; + } + + @Override + public void register(String name, DomainFactory domain) throws DomainConflictException { + synchronized (lock) { + if (initialized) { + throw new IllegalStateException("New domains cannot be registered at this time"); + } + + forceRegister(name, domain); + } + } + + @Override + public void registerAll(Map> domains) { + synchronized (lock) { + for (Map.Entry> entry : domains.entrySet()) { + try { + register(entry.getKey(), entry.getValue()); + } catch (DomainConflictException e) { + log.log(Level.WARNING, e.getMessage()); + } + } + } + } + + private > T forceRegister(String name, T domain) throws DomainConflictException { + checkNotNull(domain, "domain"); + checkNotNull(name, "name"); + + if (!CustomDomain.isValidName(name)) { + throw new IllegalArgumentException("Invalid Domain name used."); + } + + synchronized (lock) { + if (domains.containsKey(name)) { + throw new DomainConflictException("A domain already exists by the name " + name); + } + + domains.put(name, domain); + } + + return domain; + } + + @Nullable + @Override + public DomainFactory get(String name) { + checkNotNull(name, "name"); + return domains.get(name.toLowerCase()); + } + + @Nullable + @Override + public CustomDomain createDomain(String name) { + DomainFactory factory = get(name); + if (factory == null) return null; + return factory.create(name); + } + + @Override + public Map> getAll() { + return ImmutableMap.copyOf(domains); + } + + private CustomDomain getOrCreate(String name, Object value, boolean createUnknown) { + CustomDomain customDomain = createDomain(name); + + if (customDomain != null) { + customDomain.unmarshal(value); + return customDomain; + } + + synchronized (lock) { + customDomain = createDomain(name); // Load again because the previous load was not synchronized + if (customDomain != null) { + customDomain.unmarshal(value); + return customDomain; + } + if (createUnknown) { + DomainFactory unknownFactory = forceRegister(name, UnknownDomain.FACTORY); + if (unknownFactory != null) { + customDomain = unknownFactory.create(name); + if (customDomain != null) customDomain.unmarshal(value); + return customDomain; + } + } + } + return null; + } + + public List unmarshal(Map rawValues, boolean createUnknown) { + checkNotNull(rawValues, "rawValues"); + + List domainList = new ArrayList<>(); + + for (Map.Entry entry : rawValues.entrySet()) { + try { + CustomDomain domain = getOrCreate(entry.getKey(), entry.getValue(), createUnknown); + domainList.add(domain); + } catch (Throwable e) { + log.log(Level.WARNING, "Failed to unmarshal domain for " + entry.getKey(), e); + } + } + return domainList; + } + + @Override + public int size() { + return domains.size(); + } + + @Override + public Iterator> iterator() { + return Iterators.unmodifiableIterator(domains.values().iterator()); + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/UnknownDomain.java b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/UnknownDomain.java new file mode 100644 index 00000000..2ff9d1ac --- /dev/null +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/domains/registry/UnknownDomain.java @@ -0,0 +1,88 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldguard.domains.registry; + +import com.sk89q.worldguard.domains.CustomDomain; + +import java.util.UUID; + +public class UnknownDomain extends CustomDomain { + public static DomainFactory FACTORY = UnknownDomain::new; + + private boolean isDirty = false; + private Object o; + + public UnknownDomain(String name) { + super(name); + } + + @Override + public void parseInput(CustomDomainContext context) throws InvalidDomainFormatException { + throw new InvalidDomainFormatException("The plugin that registered this domain is not currently installed"); + } + + @Override + public void unmarshal(Object o) { + this.o = o; + } + + @Override + public Object marshal() { + return o; + } + + @Override + public boolean contains(UUID uniqueId) { + return false; + } + + @Override + public boolean contains(String playerName) { + return false; + } + + @Override + public int size() { + return 0; + } + + @Override + public void clear() { + isDirty = true; + o = null; + } + + @Override + public void setDirty(boolean dirty) { + isDirty = dirty; + } + + @Override + public boolean isDirty() { + return isDirty; + } + + @Override + public String toString() { + return "UnknownDomain{" + + "o=" + o + + '}'; + } +} diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/internal/permission/RegionPermissionModel.java b/worldguard-core/src/main/java/com/sk89q/worldguard/internal/permission/RegionPermissionModel.java index 287ed972..c3da47fb 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/internal/permission/RegionPermissionModel.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/internal/permission/RegionPermissionModel.java @@ -176,13 +176,14 @@ public class RegionPermissionModel extends AbstractPermissionModel { public boolean mayRemoveOwners(ProtectedRegion region) { return hasPatternPermission("removeowner", region); } - + /** * Checks to see if the given sender has permission to modify the given region * using the region permission pattern. * * @param perm the name of the node * @param region the region + * @return whether the actor has the permission */ private boolean hasPatternPermission(String perm, ProtectedRegion region) { if (!(getSender() instanceof Player)) { diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java index a394a18f..3283de3a 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/flags/FlagContext.java @@ -21,106 +21,28 @@ package com.sk89q.worldguard.protection.flags; import com.google.common.collect.Maps; import com.sk89q.worldedit.extension.platform.Actor; -import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; - -import java.util.Map; +import com.sk89q.worldguard.commands.CommandInputContext; import javax.annotation.Nullable; +import java.util.Map; -public final class FlagContext { - - private final Actor sender; - private final String input; - - private Map context; +public final class FlagContext extends CommandInputContext { private FlagContext(Actor sender, String input, Map values) { - this.sender = sender; - this.input = input; - this.context = values; + super(sender, input, values); } - public static FlagContextBuilder create() { + public static FlagContext.FlagContextBuilder create() { return new FlagContextBuilder(); } - public void put(String name, Object value) { - context.put(name, value); - } - - public Actor getSender() { - return sender; - } - - public String getUserInput() { - return input; - } - - /** - * Gets the CommandSender as a player. - * - * @return Player - * @throws InvalidFlagFormat if the sender is not a player - */ - public LocalPlayer getPlayerSender() throws InvalidFlagFormat { - if (sender.isPlayer() && sender instanceof LocalPlayer) { - return (LocalPlayer) sender; - } else { - throw new InvalidFlagFormat("Not a player"); - } - } - - public Integer getUserInputAsInt() throws InvalidFlagFormat { - try { - return Integer.parseInt(input); - } catch (NumberFormatException e) { - throw new InvalidFlagFormat("Not a number: " + input); - } - } - - public Double getUserInputAsDouble() throws InvalidFlagFormat { - try { - return Double.parseDouble(input); - } catch (NumberFormatException e) { - throw new InvalidFlagFormat("Not a number: " + input); - } - } - - /** - * Get an object from the context by key name. - * May return null if the object does not exist in the context. - * - * @param name key name of the object - * @return the object matching the key, or null - */ - @Nullable - public Object get(String name) { - return get(name, null); - } - - /** - * Get an object from the context by key name. - * Will only return null if - * a) you provide null as the default - * b) the key has explicity been set to null - * - * @param name key name of the object - * @return the object matching the key - */ - @Nullable - public Object get(String name, Object defaultValue) { - Object obj; - return (((obj = context.get(name)) != null) || context.containsKey(name) - ? obj : defaultValue); - } - /** * Create a copy of this FlagContext, with optional substitutions for values * - * If any supplied variable is null, it will be ignored. + *

If any supplied variable is null, it will be ignored. * If a map is supplied, it will override this FlagContext's values of the same key, - * but unprovided keys will not be overriden and will be returned as shallow copies. + * but unprovided keys will not be overriden and will be returned as shallow copies.

* * @param commandSender CommandSender for the new FlagContext to run under * @param s String of the user input for the new FlagContext @@ -136,6 +58,11 @@ public final class FlagContext { return new FlagContext(commandSender == null ? this.sender : commandSender, s == null ? this.input : s, map); } + @Override + protected InvalidFlagFormat createException(String str) { + return new InvalidFlagFormat(str); + } + public static class FlagContextBuilder { private Actor sender; private String input; diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java index 9aebc5ac..531b499f 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/managers/storage/file/YamlRegionFile.java @@ -27,6 +27,8 @@ import com.sk89q.util.yaml.YAMLProcessor; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.CustomDomain; import com.sk89q.worldguard.domains.DefaultDomain; import com.sk89q.worldguard.protection.flags.FlagUtil; import com.sk89q.worldguard.protection.flags.registry.FlagRegistry; @@ -284,6 +286,12 @@ public class YamlRegionFile implements RegionDatabase { } } + YAMLNode apiDomains = node.getNode("custom"); + if (apiDomains != null) { + List parsedDomains = WorldGuard.getInstance().getDomainRegistry().unmarshal(apiDomains.getMap(), true); + domain.setCustomDomains(parsedDomains); + } + return domain; } @@ -304,6 +312,14 @@ public class YamlRegionFile implements RegionDatabase { setDomainData(domainData, "unique-ids", domain.getUniqueIds()); setDomainData(domainData, "groups", domain.getGroups()); + if (!domain.getCustomDomains().isEmpty()) { + Map values = new HashMap<>(); + for (CustomDomain customDomain : domain.getCustomDomains()) { + values.put(customDomain.getName(), customDomain.marshal()); + } + domainData.put("custom", values); + } + return domainData; } diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java index 4c8a00ff..3a5fad75 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/protection/util/DomainInputResolver.java @@ -21,6 +21,12 @@ package com.sk89q.worldguard.protection.util; import com.google.common.base.Function; import com.google.common.base.Joiner; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.CustomDomain; +import com.sk89q.worldguard.domains.registry.CustomDomainContext; +import com.sk89q.worldguard.domains.registry.InvalidDomainFormatException; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.util.profile.Profile; import com.sk89q.worldguard.util.profile.resolver.ProfileService; import com.sk89q.worldguard.util.profile.util.UUIDs; @@ -43,6 +49,7 @@ import static com.google.common.base.Preconditions.checkNotNull; public class DomainInputResolver implements Callable { private static final Pattern GROUP_PATTERN = Pattern.compile("(?i)^[G]:(.+)$"); + private static final Pattern CUSTOM_PATTERN = Pattern.compile("(?i)^([A-Za-z0-9\\-]{1,40}):(.*)$"); /** * The policy for locating users. @@ -56,6 +63,8 @@ public class DomainInputResolver implements Callable { private final ProfileService profileService; private final String[] input; private UserLocatorPolicy locatorPolicy = UserLocatorPolicy.UUID_ONLY; + private ProtectedRegion region; + private Actor actor; /** * Create a new instance. @@ -89,20 +98,54 @@ public class DomainInputResolver implements Callable { this.locatorPolicy = locatorPolicy; } + /** + * Set the region for the Resolver + * @param region the region + */ + public void setRegion(ProtectedRegion region) { + this.region = region; + } + + /** + * Get the current region from the Resolver + * @return the region + */ + public @Nullable ProtectedRegion getRegion() { + return region; + } + + /** + * Set the actor of the Resolver + * @param actor the actor + */ + public void setActor(Actor actor) { + this.actor = actor; + } + @Override - public DefaultDomain call() throws UnresolvedNamesException { + public DefaultDomain call() throws UnresolvedNamesException, InvalidDomainFormatException { DefaultDomain domain = new DefaultDomain(); List namesToQuery = new ArrayList<>(); for (String s : input) { - Matcher m = GROUP_PATTERN.matcher(s); - if (m.matches()) { - domain.addGroup(m.group(1)); + Matcher groupMatcher = GROUP_PATTERN.matcher(s); + Matcher customMatcher = CUSTOM_PATTERN.matcher(s); + if (groupMatcher.matches()) { + domain.addGroup(groupMatcher.group(1)); + } else if (customMatcher.matches()) { + String domainName = customMatcher.group(1); + CustomDomain customDomain = WorldGuard.getInstance().getDomainRegistry().createDomain(domainName); + if (customDomain == null) { + throw new InvalidDomainFormatException("No domain named '" + domainName + "' found."); + } + customDomain.parseInput(CustomDomainContext.create() + .setSender(actor).setInput(customMatcher.group(2)).setObject("region", region).build()); + domain.addCustomDomain(customDomain); } else { UUID uuid = parseUUID(s); if (uuid != null) { // Try to add any UUIDs given - domain.addPlayer(UUID.fromString(UUIDs.addDashes(s.replaceAll("^uuid:", "")))); + domain.addPlayer(uuid); } else { switch (locatorPolicy) { case NAME_ONLY: diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/util/WorldGuardExceptionConverter.java b/worldguard-core/src/main/java/com/sk89q/worldguard/util/WorldGuardExceptionConverter.java index 89492df2..f508951c 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/util/WorldGuardExceptionConverter.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/util/WorldGuardExceptionConverter.java @@ -26,6 +26,7 @@ import com.sk89q.worldedit.internal.command.exception.ExceptionMatch; import com.sk89q.worldedit.util.auth.AuthorizationException; import com.sk89q.worldedit.util.formatting.component.InvalidComponentException; import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.domains.registry.InvalidDomainFormat; import com.sk89q.worldguard.protection.managers.storage.StorageException; import com.sk89q.worldguard.protection.util.UnresolvedNamesException; @@ -91,6 +92,11 @@ public class WorldGuardExceptionConverter extends ExceptionConverterHelper { throw newCommandException(e.getMessage(), e); } + @ExceptionMatch + public void convert(InvalidDomainFormat e) throws CommandException { + throw newCommandException(e.getMessage(), e); + } + @ExceptionMatch public void convert(AuthorizationException e) throws CommandException { throw newCommandException("You don't have permission to do that.", e); diff --git a/worldguard-core/src/test/java/com/sk89q/worldguard/domains/CustomUUIDDomain.java b/worldguard-core/src/test/java/com/sk89q/worldguard/domains/CustomUUIDDomain.java new file mode 100644 index 00000000..a70b874d --- /dev/null +++ b/worldguard-core/src/test/java/com/sk89q/worldguard/domains/CustomUUIDDomain.java @@ -0,0 +1,64 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldguard.domains; + +import com.sk89q.worldguard.domains.registry.CustomDomainContext; +import com.sk89q.worldguard.domains.registry.InvalidDomainFormatException; + +import java.util.Objects; +import java.util.UUID; + +public class CustomUUIDDomain extends CustomDomain { + private UUID test; + + public CustomUUIDDomain(String name, UUID test) { + super(name); + this.test = test; + } + + @Override + public void parseInput(CustomDomainContext context) throws InvalidDomainFormatException { + throw new InvalidDomainFormatException("not supported"); + } + + @Override + public void unmarshal(Object o) { + } + + @Override + public Object marshal() { + return null; + } + + @Override + public boolean contains(UUID uniqueId) { + return Objects.equals(test, uniqueId); + } + + @Override + public boolean contains(String playerName) { + return false; + } + + @Override + public void clear() { + test = null; + } +} diff --git a/worldguard-core/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java b/worldguard-core/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java index 86586657..fc112219 100644 --- a/worldguard-core/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java +++ b/worldguard-core/src/test/java/com/sk89q/worldguard/domains/DefaultDomainTest.java @@ -112,5 +112,11 @@ public class DefaultDomainTest { assertFalse(domain.contains(player1)); assertTrue(domain.contains(player2)); assertTrue(domain.contains(player3)); + + domain = new DefaultDomain(); + domain.addCustomDomain(new CustomUUIDDomain("test", player2.getUniqueId())); + assertTrue(domain.contains(player2)); + assertFalse(domain.contains(player2.getName())); + assertFalse(domain.contains(player3)); } } \ No newline at end of file