Add support for UUIDs in region commands.

This commit is contained in:
sk89q 2014-08-02 09:32:30 -07:00
parent 189fd15498
commit 81a727fe7f
13 changed files with 721 additions and 142 deletions

View File

@ -184,6 +184,14 @@
<type>jar</type>
</dependency>
<dependency>
<groupId>com.sk89q</groupId>
<artifactId>squirrelid</artifactId>
<version>0.1.0-SNAPSHOT</version>
<scope>compile</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>

View File

@ -19,6 +19,8 @@
package com.sk89q.worldguard.bukkit;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.sk89q.bukkit.util.CommandsManagerRegistration;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.minecraft.util.commands.CommandPermissionsException;
@ -27,6 +29,14 @@
import com.sk89q.minecraft.util.commands.MissingNestedCommandException;
import com.sk89q.minecraft.util.commands.SimpleInjector;
import com.sk89q.minecraft.util.commands.WrappedCommandException;
import com.sk89q.squirrelid.cache.HashMapCache;
import com.sk89q.squirrelid.cache.ProfileCache;
import com.sk89q.squirrelid.cache.SQLiteCache;
import com.sk89q.squirrelid.resolver.BukkitPlayerService;
import com.sk89q.squirrelid.resolver.CacheForwardingService;
import com.sk89q.squirrelid.resolver.CombinedProfileService;
import com.sk89q.squirrelid.resolver.HttpRepositoryService;
import com.sk89q.squirrelid.resolver.ProfileService;
import com.sk89q.wepif.PermissionsResolverManager;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldguard.LocalPlayer;
@ -40,6 +50,7 @@
import com.sk89q.worldguard.protection.GlobalRegionManager;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.util.FatalConfigurationLoadingException;
import com.sk89q.worldguard.util.concurrency.EvenMoreExecutors;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.World;
@ -64,41 +75,23 @@
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
/**
* The main class for WorldGuard as a Bukkit plugin.
*
* @author sk89q
*/
public class WorldGuardPlugin extends JavaPlugin {
/**
* Current instance of this plugin.
*/
private static WorldGuardPlugin inst;
/**
* Manager for commands. This automatically handles nested commands,
* permissions checking, and a number of other fancy command things.
* We just set it up and register commands against it.
*/
private final CommandsManager<CommandSender> commands;
/**
* Handles the region databases for all worlds.
*/
private final GlobalRegionManager globalRegionManager;
/**
* Handles all configuration.
*/
private final ConfigurationManager configuration;
/**
* Used for scheduling flags.
*/
private FlagStateManager flagStateManager;
private final ListeningExecutorService executorService = MoreExecutors.listeningDecorator(
EvenMoreExecutors.newBoundedCachedThreadPool(0, 4, 20));
private ProfileService profileService;
private ProfileCache profileCache;
/**
* Construct objects. Actual loading occurs when the plugin is enabled, so
@ -205,6 +198,21 @@ public void run() {
}
worldListener.registerEvents();
File cacheDir = new File(getDataFolder(), "cache");
cacheDir.mkdirs();
try {
profileCache = new SQLiteCache(new File(cacheDir, "profiles.sqlite"));
} catch (IOException e) {
getLogger().log(Level.WARNING, "Failed to initialize SQLite profile cache");
profileCache = new HashMapCache();
}
profileService = new CacheForwardingService(
new CombinedProfileService(
BukkitPlayerService.getInstance(),
HttpRepositoryService.forMinecraft()),
profileCache);
if (!configuration.hasCommandBookGodMode()) {
// Check god mode for existing players, if any
for (Player player : getServer().getOnlinePlayers()) {
@ -216,9 +224,6 @@ public void run() {
}
}
/**
* Called on plugin disable.
*/
@Override
public void onDisable() {
globalRegionManager.unload();
@ -226,9 +231,6 @@ public void onDisable() {
this.getServer().getScheduler().cancelTasks(this);
}
/**
* Handle a command.
*/
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label,
String[] args) {
@ -293,6 +295,34 @@ public ConfigurationManager getGlobalStateManager() {
return configuration;
}
/**
* Get the global executor service for internal usage (please use your
* own executor service).
*
* @return the global executor service
*/
public ListeningExecutorService getExecutorService() {
return executorService;
}
/**
* Get the profile lookup service.
*
* @return the profile lookup service
*/
public ProfileService getProfileService() {
return profileService;
}
/**
* Get the profile cache.
*
* @return the profile cache
*/
public ProfileCache getProfileCache() {
return profileCache;
}
/**
* Check whether a player is in a group.
* This calls the corresponding method in PermissionsResolverManager

View File

@ -0,0 +1,188 @@
/*
* 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.bukkit.commands;
import com.google.common.base.Function;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.sk89q.worldguard.protection.databases.util.UnresolvedNamesException;
import com.sk89q.worldguard.protection.managers.RegionManager;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import javax.annotation.Nullable;
import java.util.Timer;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Command-related utility methods.
*/
final class CommandUtils {
private static final Logger log = Logger.getLogger(CommandUtils.class.getCanonicalName());
private static final Timer timer = new Timer();
private static final int MESSAGE_DELAY = 1000;
private CommandUtils() {
}
/**
* Add a message that gets shown after a delay if the future has not
* completed.
*
* @param future the future
* @param sender the sender to send a message to
* @param message the message to send (will be grey)
*/
static void progressCallback(ListenableFuture<?> future, CommandSender sender, String message) {
checkNotNull(future);
checkNotNull(sender);
checkNotNull(message);
final MessageTimerTask task = new MessageTimerTask(sender, ChatColor.GRAY + message);
timer.schedule(task, MESSAGE_DELAY);
future.addListener(new Runnable() {
@Override
public void run() {
task.cancel();
}
}, MoreExecutors.sameThreadExecutor());
}
/**
* Create a callback to print a message to the user.
*
* @param sender the sender
* @param successMessage a success message or {@code null} to print nothing
* @param errorMessage an error message
* @param <T> ignored type
* @return a callback
*/
static <T> FutureCallback<T> messageCallback(final CommandSender sender, @Nullable final String successMessage, final String errorMessage) {
checkNotNull(sender);
checkNotNull(errorMessage);
return new FutureCallback<T>() {
@Override
public void onSuccess(@Nullable T o) {
if (successMessage != null) {
sender.sendMessage(ChatColor.YELLOW + successMessage);
}
}
@Override
public void onFailure(@Nullable Throwable throwable) {
sender.sendMessage(ChatColor.RED + errorMessage + ": " + convertThrowable(throwable));
}
};
}
/**
* Return a function that accepts a string to send a message to the
* given sender.
*
* @param sender the sender
* @return a function
*/
static Function<String, ?> messageFunction(final CommandSender sender) {
return new Function<String, Object>() {
@Override
public Object apply(@Nullable String s) {
sender.sendMessage(s);
return null;
}
};
}
/**
* Return a function to add a prefix and suffix to the input string.
*
* @param prefix the prefix
* @param suffix the suffix
* @return a function
*/
static Function<String, String> messageAppender(final String prefix, final String suffix) {
return new Function<String, String>() {
@Override
public String apply(@Nullable String s) {
return prefix + s + suffix;
}
};
}
/**
* Create a callback to save a region manager on success.
*
* @param sender the sender
* @param manager the region manager
* @param world the world
* @param silent true to not print a success message
* @param <T> an ignored type
* @return a callback
*/
static <T> FutureCallback<T> saveRegionsCallback(final CommandSender sender, final RegionManager manager, final World world, final boolean silent) {
checkNotNull(sender);
checkNotNull(manager);
checkNotNull(world);
return new FutureCallback<T>() {
@Override
public void onSuccess(@Nullable T o) {
ListenableFuture<?> future = manager.save(true);
String successMessage = silent ? null : "Successfully saved the region data for '" + world.getName() + "'.";
String failureMessage = "Failed to save the region data for '" + world.getName() + "'";
Futures.addCallback(future, messageCallback(sender, successMessage, failureMessage));
}
@Override
public void onFailure(@Nullable Throwable throwable) {
}
};
}
/**
* Convert the throwable into a somewhat friendly message.
*
* @param throwable the throwable
* @return a message
*/
private static String convertThrowable(@Nullable Throwable throwable) {
if (throwable instanceof CancellationException) {
return "Task was cancelled";
} else if (throwable instanceof InterruptedException) {
return "Task was interrupted";
} else if (throwable instanceof UnresolvedNamesException) {
return throwable.getMessage();
} else if (throwable instanceof Exception) {
log.log(Level.WARNING, "WorldGuard encountered an unexpected error", throwable);
return "Unexpected error occurred: " + ((Exception) throwable).getMessage();
} else {
return "Unknown error";
}
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.bukkit.commands;
import org.bukkit.command.CommandSender;
import java.util.TimerTask;
import static com.google.common.base.Preconditions.checkNotNull;
class MessageTimerTask extends TimerTask {
private final CommandSender sender;
private final String message;
MessageTimerTask(CommandSender sender, String message) {
checkNotNull(sender);
checkNotNull(message);
this.sender = sender;
this.message = message;
}
@Override
public void run() {
sender.sendMessage(message);
}
}

View File

@ -706,7 +706,7 @@ public void select(CommandContext args, CommandSender sender) throws CommandExce
*/
@Command(aliases = {"info", "i"},
usage = "[id]",
flags = "sw:",
flags = "usw:",
desc = "Get information about a region",
min = 0, max = 1)
public void info(CommandContext args, CommandSender sender) throws CommandException {
@ -734,9 +734,11 @@ public void info(CommandContext args, CommandSender sender) throws CommandExcept
}
// Print region information
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing);
printout.appendRegionInfo();
printout.send(sender);
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, args.hasFlag('u') ? null : plugin.getProfileCache());
ListenableFuture<?> future = Futures.transform(
plugin.getExecutorService().submit(printout),
CommandUtils.messageFunction(sender));
Futures.addCallback(future, CommandUtils.messageCallback(sender, null, "Failed to retrieve region info"));
// Let the player also select the region
if (args.hasFlag('s')) {
@ -964,7 +966,7 @@ public void flag(CommandContext args, CommandSender sender) throws CommandExcept
commitChanges(sender, regionManager, world, true); // Save to disk
// Print region information
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing);
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(existing, null);
printout.append(ChatColor.GRAY);
printout.append("(Current flags: ");
printout.appendFlagsList(false);
@ -1044,7 +1046,7 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE
child.setParent(parent);
} catch (CircularInheritanceException e) {
// Tell the user what's wrong
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(parent);
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(parent, null);
printout.append(ChatColor.RED);
printout.append("Uh oh! Setting '" + parent.getId() + "' to be the parent " +
"of '" + child.getId() + "' would cause circular inheritance.\n");
@ -1060,7 +1062,7 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE
commitChanges(sender, regionManager, world, true); // Save to disk
// Tell the user the current inheritance
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(child);
RegionPrintoutBuilder printout = new RegionPrintoutBuilder(child, null);
printout.append(ChatColor.YELLOW);
printout.append("Inheritance set for region '" + child.getId() + "'.\n");
if (parent != null) {
@ -1071,7 +1073,6 @@ public void setParent(CommandContext args, CommandSender sender) throws CommandE
printout.append(")");
}
printout.send(sender);
return;
}
/**

View File

@ -19,26 +19,29 @@
package com.sk89q.worldguard.bukkit.commands;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.sk89q.minecraft.util.commands.Command;
import com.sk89q.minecraft.util.commands.CommandContext;
import com.sk89q.minecraft.util.commands.CommandException;
import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.domains.DefaultDomain;
import com.sk89q.worldguard.protection.databases.ProtectionDatabaseException;
import com.sk89q.worldguard.protection.databases.RegionDBUtil;
import com.sk89q.worldguard.protection.databases.util.DomainInputResolver;
import com.sk89q.worldguard.protection.flags.DefaultFlag;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import static com.google.common.util.concurrent.Futures.addCallback;
import static com.sk89q.worldguard.bukkit.commands.CommandUtils.*;
// @TODO: A lot of code duplication here! Need to fix.
public class RegionMemberCommands {
private final WorldGuardPlugin plugin;
public RegionMemberCommands(WorldGuardPlugin plugin) {
@ -47,7 +50,7 @@ public RegionMemberCommands(WorldGuardPlugin plugin) {
@Command(aliases = {"addmember", "addmember", "addmem", "am"},
usage = "<id> <members...>",
flags = "w:",
flags = "nw:",
desc = "Add a member to a region",
min = 2)
public void addMember(CommandContext args, CommandSender sender) throws CommandException {
@ -89,22 +92,24 @@ public void addMember(CommandContext args, CommandSender sender) throws CommandE
}
}
RegionDBUtil.addToDomain(region.getMembers(), args.getParsedPaddedSlice(1, 0), 0);
// Resolve members asynchronously
DomainInputResolver resolver = new DomainInputResolver(
plugin.getProfileService(), args.getParsedPaddedSlice(1, 0));
resolver.setUseNames(args.hasFlag('n'));
sender.sendMessage(ChatColor.YELLOW
+ "Region '" + id + "' updated.");
// Then add it to the members
ListenableFuture<DefaultDomain> future = Futures.transform(
plugin.getExecutorService().submit(resolver),
resolver.createAddAllFunction(region.getMembers()));
try {
mgr.save();
} catch (ProtectionDatabaseException e) {
throw new CommandException("Failed to write regions: "
+ e.getMessage());
}
progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)");
addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to add members"));
addCallback(future, saveRegionsCallback(sender, mgr, world, true));
}
@Command(aliases = {"addowner", "addowner", "ao"},
usage = "<id> <owners...>",
flags = "w:",
flags = "nw:",
desc = "Add an owner to a region",
min = 2)
public void addOwner(CommandContext args, CommandSender sender) throws CommandException {
@ -159,22 +164,24 @@ public void addOwner(CommandContext args, CommandSender sender) throws CommandEx
}
}
RegionDBUtil.addToDomain(region.getOwners(), args.getParsedPaddedSlice(1, 0), 0);
// Resolve owners asynchronously
DomainInputResolver resolver = new DomainInputResolver(
plugin.getProfileService(), args.getParsedPaddedSlice(1, 0));
resolver.setUseNames(args.hasFlag('n'));
sender.sendMessage(ChatColor.YELLOW
+ "Region '" + id + "' updated.");
// Then add it to the owners
ListenableFuture<DefaultDomain> future = Futures.transform(
plugin.getExecutorService().submit(resolver),
resolver.createAddAllFunction(region.getOwners()));
try {
mgr.save();
} catch (ProtectionDatabaseException e) {
throw new CommandException("Failed to write regions: "
+ e.getMessage());
}
progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)");
addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to add owners"));
addCallback(future, saveRegionsCallback(sender, mgr, world, true));
}
@Command(aliases = {"removemember", "remmember", "removemem", "remmem", "rm"},
usage = "<id> <owners...>",
flags = "aw:",
flags = "naw:",
desc = "Remove an owner to a region",
min = 1)
public void removeMember(CommandContext args, CommandSender sender) throws CommandException {
@ -216,29 +223,36 @@ public void removeMember(CommandContext args, CommandSender sender) throws Comma
}
}
ListenableFuture<?> future;
if (args.hasFlag('a')) {
region.getMembers().removeAll();
future = Futures.immediateFuture(null);
} else {
if (args.argsLength() < 2) {
throw new CommandException("List some names to remove, or use -a to remove all.");
}
RegionDBUtil.removeFromDomain(region.getMembers(), args.getParsedPaddedSlice(1, 0), 0);
// Resolve members asynchronously
DomainInputResolver resolver = new DomainInputResolver(
plugin.getProfileService(), args.getParsedPaddedSlice(1, 0));
resolver.setUseNames(args.hasFlag('n'));
// Then remove it from the members
future = Futures.transform(
plugin.getExecutorService().submit(resolver),
resolver.createRemoveAllFunction(region.getMembers()));
}
sender.sendMessage(ChatColor.YELLOW
+ "Region '" + id + "' updated.");
try {
mgr.save();
} catch (ProtectionDatabaseException e) {
throw new CommandException("Failed to write regions: "
+ e.getMessage());
}
progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)");
addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to remove members"));
addCallback(future, saveRegionsCallback(sender, mgr, world, true));
}
@Command(aliases = {"removeowner", "remowner", "ro"},
usage = "<id> <owners...>",
flags = "aw:",
flags = "naw:",
desc = "Remove an owner to a region",
min = 1)
public void removeOwner(CommandContext args,
@ -281,23 +295,30 @@ public void removeOwner(CommandContext args,
}
}
ListenableFuture<?> future;
if (args.hasFlag('a')) {
region.getOwners().removeAll();
future = Futures.immediateFuture(null);
} else {
if (args.argsLength() < 2) {
throw new CommandException("List some names to remove, or use -a to remove all.");
}
RegionDBUtil.removeFromDomain(region.getOwners(), args.getParsedPaddedSlice(1, 0), 0);
// Resolve owners asynchronously
DomainInputResolver resolver = new DomainInputResolver(
plugin.getProfileService(), args.getParsedPaddedSlice(1, 0));
resolver.setUseNames(args.hasFlag('n'));
// Then remove it from the owners
future = Futures.transform(
plugin.getExecutorService().submit(resolver),
resolver.createRemoveAllFunction(region.getOwners()));
}
sender.sendMessage(ChatColor.YELLOW
+ "Region '" + id + "' updated.");
try {
mgr.save();
} catch (ProtectionDatabaseException e) {
throw new CommandException("Failed to write regions: "
+ e.getMessage());
}
progressCallback(future, sender, "(Please wait... resolving names into UUIDs...)");
addCallback(future, messageCallback(sender, "Region '" + id + "' updated.", "Failed to remove owners"));
addCallback(future, saveRegionsCallback(sender, mgr, world, true));
}
}

View File

@ -19,36 +19,42 @@
package com.sk89q.worldguard.bukkit.commands;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import com.sk89q.squirrelid.cache.ProfileCache;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldguard.domains.DefaultDomain;
import com.sk89q.worldguard.protection.flags.DefaultFlag;
import com.sk89q.worldguard.protection.flags.Flag;
import com.sk89q.worldguard.protection.flags.RegionGroupFlag;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Callable;
/**
* Create a region printout, as used in /region info to show information about
* a region.
*/
public class RegionPrintoutBuilder {
class RegionPrintoutBuilder implements Callable<String> {
private final ProtectedRegion region;
@Nullable
private final ProfileCache cache;
private final StringBuilder builder = new StringBuilder();
/**
* Create a new instance with a region to report on.
*
*
* @param region the region
* @param cache a profile cache, or {@code null}
*/
public RegionPrintoutBuilder(ProtectedRegion region) {
RegionPrintoutBuilder(ProtectedRegion region, @Nullable ProfileCache cache) {
this.region = region;
this.cache = cache;
}
/**
@ -122,11 +128,11 @@ public void appendFlagsList(boolean useColors) {
if(group == null) {
builder.append(flag.getName()).append(": ")
.append(String.valueOf(val));
.append(val);
} else {
builder.append(flag.getName()).append(" -g ")
.append(String.valueOf(group)).append(": ")
.append(String.valueOf(val));
.append(group).append(": ")
.append(val);
}
hasFlags = true;
@ -192,7 +198,7 @@ public void appendParentTree(boolean useColors) {
if (useColors) {
builder.append(ChatColor.GRAY);
}
builder.append(" (parent, priority=" + cur.getPriority() + ")");
builder.append(" (parent, priority=").append(cur.getPriority()).append(")");
}
indent++;
@ -206,29 +212,23 @@ public void appendParentTree(boolean useColors) {
public void appendDomain() {
builder.append(ChatColor.BLUE);
builder.append("Owners: ");
DefaultDomain owners = region.getOwners();
if (owners.size() != 0) {
builder.append(ChatColor.YELLOW);
builder.append(owners.toUserFriendlyString());
} else {
builder.append(ChatColor.RED);
builder.append("(no owners)");
}
addDomainString(region.getOwners());
newLine();
builder.append(ChatColor.BLUE);
builder.append("Members: ");
DefaultDomain members = region.getMembers();
if (members.size() != 0) {
addDomainString(region.getMembers());
newLine();
}
private void addDomainString(DefaultDomain domain) {
if (domain.size() != 0) {
builder.append(ChatColor.YELLOW);
builder.append(members.toUserFriendlyString());
builder.append(domain.toUserFriendlyString(cache));
} else {
builder.append(ChatColor.RED);
builder.append("(no members)");
builder.append("(none)");
}
newLine();
}
/**
@ -240,16 +240,13 @@ public void appendBounds() {
builder.append(ChatColor.BLUE);
builder.append("Bounds:");
builder.append(ChatColor.YELLOW);
builder.append(" (" + min.getBlockX() + "," + min.getBlockY() + "," + min.getBlockZ() + ")");
builder.append(" -> (" + max.getBlockX() + "," + max.getBlockY() + "," + max.getBlockZ() + ")");
builder.append(" (").append(min.getBlockX()).append(",").append(min.getBlockY()).append(",").append(min.getBlockZ()).append(")");
builder.append(" -> (").append(max.getBlockX()).append(",").append(max.getBlockY()).append(",").append(max.getBlockZ()).append(")");
newLine();
}
/**
* Append all the default fields used for /rg info.
*/
public void appendRegionInfo() {
private void appendRegionInformation() {
builder.append(ChatColor.GRAY);
builder.append("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
builder.append(" Region Info ");
@ -260,17 +257,28 @@ public void appendRegionInfo() {
appendParents();
appendDomain();
appendBounds();
if (cache != null) {
builder.append(ChatColor.GRAY).append("Any names suffixed by * are 'last seen names' and may not be up to date.");
newLine();
}
}
@Override
public String call() throws Exception {
appendRegionInformation();
return builder.toString();
}
/**
* Send the report to a {@link CommandSender}.
*
* @param sender the recepient
*
* @param sender the recipient
*/
public void send(CommandSender sender) {
sender.sendMessage(toString());
}
public StringBuilder append(boolean b) {
return builder.append(b);
}

View File

@ -19,8 +19,12 @@
package com.sk89q.worldguard.domains;
import com.google.common.collect.ImmutableMap;
import com.sk89q.squirrelid.Profile;
import com.sk89q.squirrelid.cache.ProfileCache;
import com.sk89q.worldguard.LocalPlayer;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@ -28,6 +32,8 @@
import java.util.Set;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A combination of a {@link PlayerDomain} and a {@link GroupDomain}.
*/
@ -58,6 +64,15 @@ public void removePlayer(String name) {
playerDomain.removePlayer(name);
}
/**
* Remove the given player from the domain, identified by the player's UUID.
*
* @param uuid the UUID of the player
*/
public void removePlayer(UUID uuid) {
playerDomain.removePlayer(uuid);
}
/**
* Add the given player to the domain, identified by the player's UUID.
*
@ -86,6 +101,42 @@ public void addPlayer(LocalPlayer player) {
playerDomain.addPlayer(player);
}
/**
* Add all the entries from another domain.
*
* @param other the other domain
*/
public void addAll(DefaultDomain other) {
checkNotNull(other);
for (String player : other.getPlayers()) {
addPlayer(player);
}
for (UUID uuid : other.getUniqueIds()) {
addPlayer(uuid);
}
for (String group : other.getGroups()) {
addGroup(group);
}
}
/**
* Remove all the entries from another domain.
*
* @param other the other domain
*/
public void removeAll(DefaultDomain other) {
checkNotNull(other);
for (String player : other.getPlayers()) {
removePlayer(player);
}
for (UUID uuid : other.getUniqueIds()) {
removePlayer(uuid);
}
for (String group : other.getGroups()) {
removeGroup(group);
}
}
/**
* Get the set of player names.
*
@ -165,11 +216,29 @@ public void removeAll() {
@SuppressWarnings("deprecation")
public String toPlayersString() {
return toPlayersString(null);
}
@SuppressWarnings("deprecation")
public String toPlayersString(@Nullable ProfileCache cache) {
StringBuilder str = new StringBuilder();
List<String> output = new ArrayList<String>();
output.addAll(playerDomain.getPlayers());
for (UUID uuid : playerDomain.getUniqueIds()) {
output.add("uuid:" + uuid);
if (cache != null) {
ImmutableMap<UUID, Profile> results = cache.getAllPresent(playerDomain.getUniqueIds());
for (UUID uuid : playerDomain.getUniqueIds()) {
Profile profile = results.get(uuid);
if (profile != null) {
output.add(profile.getName() + "*");
} else {
output.add("uuid:" + uuid);
}
}
} else {
for (UUID uuid : playerDomain.getUniqueIds()) {
output.add("uuid:" + uuid);
}
}
Collections.sort(output, String.CASE_INSENSITIVE_ORDER);
for (Iterator<String> it = output.iterator(); it.hasNext();) {
@ -192,22 +261,40 @@ public String toGroupsString() {
}
return str.toString();
}
public String toUserFriendlyString() {
StringBuilder str = new StringBuilder();
if (playerDomain.size() > 0) {
str.append(toPlayersString());
}
if (groupDomain.size() > 0) {
if (str.length() > 0) {
str.append("; ");
}
str.append(toGroupsString());
}
return str.toString();
}
public String toUserFriendlyString(ProfileCache cache) {
StringBuilder str = new StringBuilder();
if (playerDomain.size() > 0) {
str.append(toPlayersString(cache));
}
if (groupDomain.size() > 0) {
if (str.length() > 0) {
str.append("; ");
}
str.append(toGroupsString());
}
return str.toString();
}

View File

@ -98,6 +98,16 @@ public void removePlayer(String name) {
names.remove(name.toLowerCase());
}
/**
* Remove the given player from the domain, identified by the player's UUID.
*
* @param uuid the UUID of the player
*/
public void removePlayer(UUID uuid) {
checkNotNull(uuid);
uniqueIds.remove(uuid);
}
/**
* Remove the given player from the domain, identified by either the
* player's name, the player's unique ID, or both.

View File

@ -47,14 +47,6 @@ protected AbstractJob(MySQLDatabaseImpl database, Connection conn) {
this.logger = database.getLogger();
}
static void closeQuietly(Connection connection) {
if (connection != null) {
try {
connection.close();
} catch (SQLException ignored) {}
}
}
static void closeQuietly(ResultSet rs) {
if (rs != null) {
try {

View File

@ -19,19 +19,20 @@
package com.sk89q.worldguard.protection.databases;
import com.sk89q.worldguard.domains.DefaultDomain;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.sk89q.worldguard.domains.DefaultDomain;
/**
* Various utility functions for parsing region databases.
*
* @author sk89q
*/
public class RegionDBUtil {
private static Pattern groupPattern = Pattern.compile("(?i)^[G]:(.+)$");
private RegionDBUtil() {

View File

@ -0,0 +1,146 @@
/*
* 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.databases.util;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.sk89q.squirrelid.Profile;
import com.sk89q.squirrelid.resolver.ProfileService;
import com.sk89q.squirrelid.util.UUIDs;
import com.sk89q.worldguard.domains.DefaultDomain;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Resolves input for a domain (i.e. "player1 player2 &lt;uuid&gt; g:group").
*
* <p>Unless {@link #getUseNames()} is true, names will be resolved into
* UUIDs.</p>
*/
public class DomainInputResolver implements Callable<DefaultDomain> {
private static final Pattern GROUP_PATTERN = Pattern.compile("(?i)^[G]:(.+)$");
private final ProfileService profileService;
private final String[] input;
private boolean useNames = false;
/**
* Create a new instance.
*
* @param profileService the profile service
* @param input the input to parse
*/
public DomainInputResolver(ProfileService profileService, String[] input) {
checkNotNull(profileService);
checkNotNull(input);
this.profileService = profileService;
this.input = input;
}
/**
* Get whether names should be used rather than UUIDs.
*
* @return true to use names
*/
public boolean getUseNames() {
return useNames;
}
/**
* Set whether names should be used rather than UUIDs.
*
* @param useNames true to use names
*/
public void setUseNames(boolean useNames) {
this.useNames = useNames;
}
@Override
public DefaultDomain call() throws UnresolvedNamesException {
DefaultDomain domain = new DefaultDomain();
List<String> namesToQuery = new ArrayList<String>();
for (String s : input) {
Matcher m = GROUP_PATTERN.matcher(s);
if (m.matches()) {
domain.addGroup(m.group(1));
} else {
try {
domain.addPlayer(UUID.fromString(UUIDs.addDashes(s)));
} catch (IllegalArgumentException e) {
if (useNames) {
domain.addPlayer(s);
} else {
namesToQuery.add(s.toLowerCase());
}
}
}
}
if (!namesToQuery.isEmpty()) {
try {
for (Profile profile : profileService.findAllByName(namesToQuery)) {
namesToQuery.remove(profile.getName().toLowerCase());
domain.addPlayer(profile.getUniqueId());
}
} catch (IOException e) {
throw new UnresolvedNamesException("The UUID lookup service failed so the names entered could not be turned into UUIDs");
} catch (InterruptedException e) {
throw new UnresolvedNamesException("UUID lookup was interrupted");
}
}
if (!namesToQuery.isEmpty()) {
throw new UnresolvedNamesException("Unable to resolve the names " + Joiner.on(", ").join(namesToQuery));
}
return domain;
}
public Function<DefaultDomain, DefaultDomain> createAddAllFunction(final DefaultDomain target) {
return new Function<DefaultDomain, DefaultDomain>() {
@Override
public DefaultDomain apply(@Nullable DefaultDomain domain) {
target.addAll(domain);
return domain;
}
};
}
public Function<DefaultDomain, DefaultDomain> createRemoveAllFunction(final DefaultDomain target) {
return new Function<DefaultDomain, DefaultDomain>() {
@Override
public DefaultDomain apply(@Nullable DefaultDomain domain) {
target.removeAll(domain);
return domain;
}
};
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.databases.util;
/**
* Thrown when there are unresolved names.
*/
public class UnresolvedNamesException extends Exception {
public UnresolvedNamesException() {
}
public UnresolvedNamesException(String message) {
super(message);
}
public UnresolvedNamesException(String message, Throwable cause) {
super(message, cause);
}
public UnresolvedNamesException(Throwable cause) {
super(cause);
}
}